xref: /freebsd/sys/rpc/rpcsec_gss/rpcsec_gss.c (revision 66612e673652fce27f83402b7871fcd58447c5a0)
1a9148abdSDoug Rabson /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4a9148abdSDoug Rabson  * Copyright (c) 2008 Doug Rabson
5a9148abdSDoug Rabson  * All rights reserved.
6a9148abdSDoug Rabson  *
7a9148abdSDoug Rabson  * Redistribution and use in source and binary forms, with or without
8a9148abdSDoug Rabson  * modification, are permitted provided that the following conditions
9a9148abdSDoug Rabson  * are met:
10a9148abdSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12a9148abdSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
13a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
14a9148abdSDoug Rabson  *    documentation and/or other materials provided with the distribution.
15a9148abdSDoug Rabson  *
16a9148abdSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17a9148abdSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9148abdSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19a9148abdSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20a9148abdSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21a9148abdSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22a9148abdSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23a9148abdSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a9148abdSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25a9148abdSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26a9148abdSDoug Rabson  * SUCH DAMAGE.
27a9148abdSDoug Rabson  */
28a9148abdSDoug Rabson /*
29a9148abdSDoug Rabson   auth_gss.c
30a9148abdSDoug Rabson 
31a9148abdSDoug Rabson   RPCSEC_GSS client routines.
32a9148abdSDoug Rabson 
33a9148abdSDoug Rabson   Copyright (c) 2000 The Regents of the University of Michigan.
34a9148abdSDoug Rabson   All rights reserved.
35a9148abdSDoug Rabson 
36a9148abdSDoug Rabson   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37a9148abdSDoug Rabson   All rights reserved, all wrongs reversed.
38a9148abdSDoug Rabson 
39a9148abdSDoug Rabson   Redistribution and use in source and binary forms, with or without
40a9148abdSDoug Rabson   modification, are permitted provided that the following conditions
41a9148abdSDoug Rabson   are met:
42a9148abdSDoug Rabson 
43a9148abdSDoug Rabson   1. Redistributions of source code must retain the above copyright
44a9148abdSDoug Rabson      notice, this list of conditions and the following disclaimer.
45a9148abdSDoug Rabson   2. Redistributions in binary form must reproduce the above copyright
46a9148abdSDoug Rabson      notice, this list of conditions and the following disclaimer in the
47a9148abdSDoug Rabson      documentation and/or other materials provided with the distribution.
48a9148abdSDoug Rabson   3. Neither the name of the University nor the names of its
49a9148abdSDoug Rabson      contributors may be used to endorse or promote products derived
50a9148abdSDoug Rabson      from this software without specific prior written permission.
51a9148abdSDoug Rabson 
52a9148abdSDoug Rabson   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53a9148abdSDoug Rabson   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54a9148abdSDoug Rabson   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55a9148abdSDoug Rabson   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56a9148abdSDoug Rabson   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57a9148abdSDoug Rabson   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58a9148abdSDoug Rabson   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59a9148abdSDoug Rabson   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60a9148abdSDoug Rabson   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61a9148abdSDoug Rabson   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62a9148abdSDoug Rabson   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63a9148abdSDoug Rabson 
64a9148abdSDoug Rabson   $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65a9148abdSDoug Rabson */
66a9148abdSDoug Rabson 
67a9148abdSDoug Rabson #include <sys/param.h>
68a9148abdSDoug Rabson #include <sys/systm.h>
69a9148abdSDoug Rabson #include <sys/hash.h>
70a9148abdSDoug Rabson #include <sys/kernel.h>
71a9148abdSDoug Rabson #include <sys/kobj.h>
72a9148abdSDoug Rabson #include <sys/lock.h>
73a9148abdSDoug Rabson #include <sys/malloc.h>
74a9148abdSDoug Rabson #include <sys/mbuf.h>
75a9148abdSDoug Rabson #include <sys/mutex.h>
76a9148abdSDoug Rabson #include <sys/proc.h>
77a9148abdSDoug Rabson #include <sys/refcount.h>
78a9148abdSDoug Rabson #include <sys/sx.h>
79a9148abdSDoug Rabson #include <sys/ucred.h>
80a9148abdSDoug Rabson 
81a9148abdSDoug Rabson #include <rpc/rpc.h>
82a9148abdSDoug Rabson #include <rpc/rpcsec_gss.h>
83a9148abdSDoug Rabson 
8488a2437aSRick Macklem #include <kgssapi/krb5/kcrypto.h>
8588a2437aSRick Macklem 
86a9148abdSDoug Rabson #include "rpcsec_gss_int.h"
87a9148abdSDoug Rabson 
88a9148abdSDoug Rabson static void	rpc_gss_nextverf(AUTH*);
89a9148abdSDoug Rabson static bool_t	rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
90a9148abdSDoug Rabson static bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
91a9148abdSDoug Rabson static bool_t	rpc_gss_refresh(AUTH *, void *);
92a9148abdSDoug Rabson static bool_t	rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
93a9148abdSDoug Rabson     struct mbuf **);
94a9148abdSDoug Rabson static void	rpc_gss_destroy(AUTH *);
95a9148abdSDoug Rabson static void	rpc_gss_destroy_context(AUTH *, bool_t);
96a9148abdSDoug Rabson 
9720d728b5SMark Johnston static const struct auth_ops rpc_gss_ops = {
98ba5bc6e8SMark Johnston 	.ah_nextverf =	rpc_gss_nextverf,
99ba5bc6e8SMark Johnston 	.ah_marshal =	rpc_gss_marshal,
100ba5bc6e8SMark Johnston 	.ah_validate =	rpc_gss_validate,
101ba5bc6e8SMark Johnston 	.ah_refresh =	rpc_gss_refresh,
102ba5bc6e8SMark Johnston 	.ah_destroy =	rpc_gss_destroy,
103a9148abdSDoug Rabson };
104a9148abdSDoug Rabson 
105a9148abdSDoug Rabson enum rpcsec_gss_state {
106a9148abdSDoug Rabson 	RPCSEC_GSS_START,
107a9148abdSDoug Rabson 	RPCSEC_GSS_CONTEXT,
108a9148abdSDoug Rabson 	RPCSEC_GSS_ESTABLISHED,
109a9148abdSDoug Rabson 	RPCSEC_GSS_DESTROYING
110a9148abdSDoug Rabson };
111a9148abdSDoug Rabson 
112a9148abdSDoug Rabson struct rpc_pending_request {
113a9148abdSDoug Rabson 	uint32_t		pr_xid;		/* XID of rpc */
114a9148abdSDoug Rabson 	uint32_t		pr_seq;		/* matching GSS seq */
115a9148abdSDoug Rabson 	LIST_ENTRY(rpc_pending_request) pr_link;
116a9148abdSDoug Rabson };
117a9148abdSDoug Rabson LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
118a9148abdSDoug Rabson 
119a9148abdSDoug Rabson struct rpc_gss_data {
120a9148abdSDoug Rabson 	volatile u_int		gd_refs;	/* number of current users */
121a9148abdSDoug Rabson 	struct mtx		gd_lock;
122a9148abdSDoug Rabson 	uint32_t		gd_hash;
123a9148abdSDoug Rabson 	AUTH			*gd_auth;	/* link back to AUTH */
124a9148abdSDoug Rabson 	struct ucred		*gd_ucred;	/* matching local cred */
125a9148abdSDoug Rabson 	char			*gd_principal;	/* server principal name */
12688a2437aSRick Macklem 	char			*gd_clntprincipal; /* client principal name */
127a9148abdSDoug Rabson 	rpc_gss_options_req_t	gd_options;	/* GSS context options */
128a9148abdSDoug Rabson 	enum rpcsec_gss_state	gd_state;	/* connection state */
129a9148abdSDoug Rabson 	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
130a9148abdSDoug Rabson 						 * NULL RPC verfier to
131a9148abdSDoug Rabson 						 * process at end of
132a9148abdSDoug Rabson 						 * context negotiation */
133a9148abdSDoug Rabson 	CLIENT			*gd_clnt;	/* client handle */
134a9148abdSDoug Rabson 	gss_OID			gd_mech;	/* mechanism to use */
135a9148abdSDoug Rabson 	gss_qop_t		gd_qop;		/* quality of protection */
136a9148abdSDoug Rabson 	gss_ctx_id_t		gd_ctx;		/* context id */
137a9148abdSDoug Rabson 	struct rpc_gss_cred	gd_cred;	/* client credentials */
138a9148abdSDoug Rabson 	uint32_t		gd_seq;		/* next sequence number */
139a9148abdSDoug Rabson 	u_int			gd_win;		/* sequence window */
140a9148abdSDoug Rabson 	struct rpc_pending_request_list gd_reqs;
141a9148abdSDoug Rabson 	TAILQ_ENTRY(rpc_gss_data) gd_link;
142a9148abdSDoug Rabson 	TAILQ_ENTRY(rpc_gss_data) gd_alllink;
143a9148abdSDoug Rabson };
144a9148abdSDoug Rabson TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
145a9148abdSDoug Rabson 
146a9148abdSDoug Rabson #define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
147a9148abdSDoug Rabson 
148a9148abdSDoug Rabson static struct timeval AUTH_TIMEOUT = { 25, 0 };
149a9148abdSDoug Rabson 
150a9148abdSDoug Rabson #define RPC_GSS_HASH_SIZE	11
151a9148abdSDoug Rabson #define RPC_GSS_MAX		256
152a9148abdSDoug Rabson static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
153a9148abdSDoug Rabson static struct rpc_gss_data_list rpc_gss_all;
154a9148abdSDoug Rabson static struct sx rpc_gss_lock;
155a9148abdSDoug Rabson static int rpc_gss_count;
156a9148abdSDoug Rabson 
157a9148abdSDoug Rabson static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
15888a2437aSRick Macklem     const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
159a9148abdSDoug Rabson     rpc_gss_options_ret_t *);
160a9148abdSDoug Rabson 
161a9148abdSDoug Rabson static void
rpc_gss_hashinit(void * dummy)162a9148abdSDoug Rabson rpc_gss_hashinit(void *dummy)
163a9148abdSDoug Rabson {
164a9148abdSDoug Rabson 	int i;
165a9148abdSDoug Rabson 
166a9148abdSDoug Rabson 	for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
167a9148abdSDoug Rabson 		TAILQ_INIT(&rpc_gss_cache[i]);
168a9148abdSDoug Rabson 	TAILQ_INIT(&rpc_gss_all);
169a9148abdSDoug Rabson 	sx_init(&rpc_gss_lock, "rpc_gss_lock");
170a9148abdSDoug Rabson }
171a9148abdSDoug Rabson SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
172a9148abdSDoug Rabson 
173a9148abdSDoug Rabson static uint32_t
rpc_gss_hash(const char * principal,gss_OID mech,struct ucred * cred,rpc_gss_service_t service)174a9148abdSDoug Rabson rpc_gss_hash(const char *principal, gss_OID mech,
175a9148abdSDoug Rabson     struct ucred *cred, rpc_gss_service_t service)
176a9148abdSDoug Rabson {
177a9148abdSDoug Rabson 	uint32_t h;
178a9148abdSDoug Rabson 
179a9148abdSDoug Rabson 	h = HASHSTEP(HASHINIT, cred->cr_uid);
180a9148abdSDoug Rabson 	h = hash32_str(principal, h);
181a9148abdSDoug Rabson 	h = hash32_buf(mech->elements, mech->length, h);
182a9148abdSDoug Rabson 	h = HASHSTEP(h, (int) service);
183a9148abdSDoug Rabson 
184a9148abdSDoug Rabson 	return (h % RPC_GSS_HASH_SIZE);
185a9148abdSDoug Rabson }
186a9148abdSDoug Rabson 
187a9148abdSDoug Rabson /*
188a9148abdSDoug Rabson  * Simplified interface to create a security association for the
189a9148abdSDoug Rabson  * current thread's * ucred.
190a9148abdSDoug Rabson  */
191a9148abdSDoug Rabson AUTH *
rpc_gss_secfind(CLIENT * clnt,struct ucred * cred,const char * principal,gss_OID mech_oid,rpc_gss_service_t service)192a9148abdSDoug Rabson rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
193a9148abdSDoug Rabson     gss_OID mech_oid, rpc_gss_service_t service)
194a9148abdSDoug Rabson {
195a9148abdSDoug Rabson 	uint32_t		h, th;
196a9148abdSDoug Rabson 	AUTH			*auth;
197a9148abdSDoug Rabson 	struct rpc_gss_data	*gd, *tgd;
198b2115885SRick Macklem 	rpc_gss_options_ret_t	options;
199a9148abdSDoug Rabson 
200a9148abdSDoug Rabson 	if (rpc_gss_count > RPC_GSS_MAX) {
201a9148abdSDoug Rabson 		while (rpc_gss_count > RPC_GSS_MAX) {
202a9148abdSDoug Rabson 			sx_xlock(&rpc_gss_lock);
203a9148abdSDoug Rabson 			tgd = TAILQ_FIRST(&rpc_gss_all);
204a9148abdSDoug Rabson 			th = tgd->gd_hash;
205a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
206a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
207a9148abdSDoug Rabson 			rpc_gss_count--;
208a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
209a9148abdSDoug Rabson 			AUTH_DESTROY(tgd->gd_auth);
210a9148abdSDoug Rabson 		}
211a9148abdSDoug Rabson 	}
212a9148abdSDoug Rabson 
213a9148abdSDoug Rabson 	/*
214a9148abdSDoug Rabson 	 * See if we already have an AUTH which matches.
215a9148abdSDoug Rabson 	 */
216a9148abdSDoug Rabson 	h = rpc_gss_hash(principal, mech_oid, cred, service);
217a9148abdSDoug Rabson 
218a9148abdSDoug Rabson again:
219a9148abdSDoug Rabson 	sx_slock(&rpc_gss_lock);
220a9148abdSDoug Rabson 	TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
221a9148abdSDoug Rabson 		if (gd->gd_ucred->cr_uid == cred->cr_uid
222a9148abdSDoug Rabson 		    && !strcmp(gd->gd_principal, principal)
223a9148abdSDoug Rabson 		    && gd->gd_mech == mech_oid
224a9148abdSDoug Rabson 		    && gd->gd_cred.gc_svc == service) {
225a9148abdSDoug Rabson 			refcount_acquire(&gd->gd_refs);
226a9148abdSDoug Rabson 			if (sx_try_upgrade(&rpc_gss_lock)) {
227a9148abdSDoug Rabson 				/*
228a9148abdSDoug Rabson 				 * Keep rpc_gss_all LRU sorted.
229a9148abdSDoug Rabson 				 */
230a9148abdSDoug Rabson 				TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
231a9148abdSDoug Rabson 				TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
232a9148abdSDoug Rabson 				    gd_alllink);
233a9148abdSDoug Rabson 				sx_xunlock(&rpc_gss_lock);
234a9148abdSDoug Rabson 			} else {
235a9148abdSDoug Rabson 				sx_sunlock(&rpc_gss_lock);
236a9148abdSDoug Rabson 			}
237b2115885SRick Macklem 
238b2115885SRick Macklem 			/*
239b2115885SRick Macklem 			 * If the state != ESTABLISHED, try and initialize
240b2115885SRick Macklem 			 * the authenticator again. This will happen if the
241b2115885SRick Macklem 			 * user's credentials have expired. It may succeed now,
242b2115885SRick Macklem 			 * if they have done a kinit or similar.
243b2115885SRick Macklem 			 */
244b2115885SRick Macklem 			if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
245b2115885SRick Macklem 				memset(&options, 0, sizeof (options));
246b2115885SRick Macklem 				(void) rpc_gss_init(gd->gd_auth, &options);
247b2115885SRick Macklem 			}
248a9148abdSDoug Rabson 			return (gd->gd_auth);
249a9148abdSDoug Rabson 		}
250a9148abdSDoug Rabson 	}
251a9148abdSDoug Rabson 	sx_sunlock(&rpc_gss_lock);
252a9148abdSDoug Rabson 
253a9148abdSDoug Rabson 	/*
254a9148abdSDoug Rabson 	 * We missed in the cache - create a new association.
255a9148abdSDoug Rabson 	 */
25688a2437aSRick Macklem 	auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid,
25788a2437aSRick Macklem 	    service, GSS_C_QOP_DEFAULT, NULL, NULL);
258a9148abdSDoug Rabson 	if (!auth)
259a9148abdSDoug Rabson 		return (NULL);
260a9148abdSDoug Rabson 
261a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
262a9148abdSDoug Rabson 	gd->gd_hash = h;
263a9148abdSDoug Rabson 
264a9148abdSDoug Rabson 	sx_xlock(&rpc_gss_lock);
265a9148abdSDoug Rabson 	TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
266a9148abdSDoug Rabson 		if (tgd->gd_ucred->cr_uid == cred->cr_uid
267a9148abdSDoug Rabson 		    && !strcmp(tgd->gd_principal, principal)
268a9148abdSDoug Rabson 		    && tgd->gd_mech == mech_oid
269a9148abdSDoug Rabson 		    && tgd->gd_cred.gc_svc == service) {
270a9148abdSDoug Rabson 			/*
271a9148abdSDoug Rabson 			 * We lost a race to create the AUTH that
272a9148abdSDoug Rabson 			 * matches this cred.
273a9148abdSDoug Rabson 			 */
274a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
275a9148abdSDoug Rabson 			AUTH_DESTROY(auth);
276a9148abdSDoug Rabson 			goto again;
277a9148abdSDoug Rabson 		}
278a9148abdSDoug Rabson 	}
279a9148abdSDoug Rabson 
280a9148abdSDoug Rabson 	rpc_gss_count++;
281a9148abdSDoug Rabson 	TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
282a9148abdSDoug Rabson 	TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
283a9148abdSDoug Rabson 	refcount_acquire(&gd->gd_refs);	/* one for the cache, one for user */
284a9148abdSDoug Rabson 	sx_xunlock(&rpc_gss_lock);
285a9148abdSDoug Rabson 
286a9148abdSDoug Rabson 	return (auth);
287a9148abdSDoug Rabson }
288a9148abdSDoug Rabson 
289a9148abdSDoug Rabson void
rpc_gss_secpurge(CLIENT * clnt)290a9148abdSDoug Rabson rpc_gss_secpurge(CLIENT *clnt)
291a9148abdSDoug Rabson {
292a9148abdSDoug Rabson 	uint32_t		h;
293a9148abdSDoug Rabson 	struct rpc_gss_data	*gd, *tgd;
294a9148abdSDoug Rabson 
295a9148abdSDoug Rabson 	TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
296a9148abdSDoug Rabson 		if (gd->gd_clnt == clnt) {
297a9148abdSDoug Rabson 			sx_xlock(&rpc_gss_lock);
298a9148abdSDoug Rabson 			h = gd->gd_hash;
299a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
300a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
301a9148abdSDoug Rabson 			rpc_gss_count--;
302a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
303a9148abdSDoug Rabson 			AUTH_DESTROY(gd->gd_auth);
304a9148abdSDoug Rabson 		}
305a9148abdSDoug Rabson 	}
306a9148abdSDoug Rabson }
307a9148abdSDoug Rabson 
308a9148abdSDoug Rabson AUTH *
rpc_gss_seccreate(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,const char * mechanism,rpc_gss_service_t service,const char * qop,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)30988a2437aSRick Macklem rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal,
31088a2437aSRick Macklem     const char *principal, const char *mechanism, rpc_gss_service_t service,
31188a2437aSRick Macklem     const char *qop, rpc_gss_options_req_t *options_req,
31288a2437aSRick Macklem     rpc_gss_options_ret_t *options_ret)
313a9148abdSDoug Rabson {
314a9148abdSDoug Rabson 	gss_OID			oid;
315a9148abdSDoug Rabson 	u_int			qop_num;
316a9148abdSDoug Rabson 
317a9148abdSDoug Rabson 	/*
318a9148abdSDoug Rabson 	 * Bail out now if we don't know this mechanism.
319a9148abdSDoug Rabson 	 */
320a9148abdSDoug Rabson 	if (!rpc_gss_mech_to_oid(mechanism, &oid))
321a9148abdSDoug Rabson 		return (NULL);
322a9148abdSDoug Rabson 
323a9148abdSDoug Rabson 	if (qop) {
324a9148abdSDoug Rabson 		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
325a9148abdSDoug Rabson 			return (NULL);
326a9148abdSDoug Rabson 	} else {
327a9148abdSDoug Rabson 		qop_num = GSS_C_QOP_DEFAULT;
328a9148abdSDoug Rabson 	}
329a9148abdSDoug Rabson 
33088a2437aSRick Macklem 	return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal,
33188a2437aSRick Macklem 		oid, service, qop_num, options_req, options_ret));
33288a2437aSRick Macklem }
33388a2437aSRick Macklem 
33488a2437aSRick Macklem void
rpc_gss_refresh_auth(AUTH * auth)33588a2437aSRick Macklem rpc_gss_refresh_auth(AUTH *auth)
33688a2437aSRick Macklem {
33788a2437aSRick Macklem 	struct rpc_gss_data	*gd;
33888a2437aSRick Macklem 	rpc_gss_options_ret_t	options;
33988a2437aSRick Macklem 
34088a2437aSRick Macklem 	gd = AUTH_PRIVATE(auth);
34188a2437aSRick Macklem 	/*
34288a2437aSRick Macklem 	 * If the state != ESTABLISHED, try and initialize
34388a2437aSRick Macklem 	 * the authenticator again. This will happen if the
34488a2437aSRick Macklem 	 * user's credentials have expired. It may succeed now,
34588a2437aSRick Macklem 	 * if they have done a kinit or similar.
34688a2437aSRick Macklem 	 */
34788a2437aSRick Macklem 	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
34888a2437aSRick Macklem 		memset(&options, 0, sizeof (options));
34988a2437aSRick Macklem 		(void) rpc_gss_init(auth, &options);
35088a2437aSRick Macklem 	}
351a9148abdSDoug Rabson }
352a9148abdSDoug Rabson 
353a9148abdSDoug Rabson static AUTH *
rpc_gss_seccreate_int(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,gss_OID mech_oid,rpc_gss_service_t service,u_int qop_num,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)35488a2437aSRick Macklem rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred,
35588a2437aSRick Macklem     const char *clnt_principal, const char *principal, gss_OID mech_oid,
35688a2437aSRick Macklem     rpc_gss_service_t service, u_int qop_num,
357a9148abdSDoug Rabson     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
358a9148abdSDoug Rabson {
359a9148abdSDoug Rabson 	AUTH			*auth;
360a9148abdSDoug Rabson 	rpc_gss_options_ret_t	options;
361a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
362a9148abdSDoug Rabson 
363a9148abdSDoug Rabson 	/*
364a9148abdSDoug Rabson 	 * If the caller doesn't want the options, point at local
365a9148abdSDoug Rabson 	 * storage to simplify the code below.
366a9148abdSDoug Rabson 	 */
367a9148abdSDoug Rabson 	if (!options_ret)
368a9148abdSDoug Rabson 		options_ret = &options;
369a9148abdSDoug Rabson 
370a9148abdSDoug Rabson 	/*
371a9148abdSDoug Rabson 	 * Default service is integrity.
372a9148abdSDoug Rabson 	 */
373a9148abdSDoug Rabson 	if (service == rpc_gss_svc_default)
374a9148abdSDoug Rabson 		service = rpc_gss_svc_integrity;
375a9148abdSDoug Rabson 
376a9148abdSDoug Rabson 	memset(options_ret, 0, sizeof(*options_ret));
377a9148abdSDoug Rabson 
378a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_seccreate()");
379a9148abdSDoug Rabson 
380a9148abdSDoug Rabson 	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
381a9148abdSDoug Rabson 
382a9148abdSDoug Rabson 	auth = mem_alloc(sizeof(*auth));
383a9148abdSDoug Rabson 	if (auth == NULL) {
384a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
385a9148abdSDoug Rabson 		rpc_createerr.cf_error.re_errno = ENOMEM;
386a9148abdSDoug Rabson 		return (NULL);
387a9148abdSDoug Rabson 	}
388a9148abdSDoug Rabson 	gd = mem_alloc(sizeof(*gd));
389a9148abdSDoug Rabson 	if (gd == NULL) {
390a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
391a9148abdSDoug Rabson 		rpc_createerr.cf_error.re_errno = ENOMEM;
392a9148abdSDoug Rabson 		mem_free(auth, sizeof(*auth));
393a9148abdSDoug Rabson 		return (NULL);
394a9148abdSDoug Rabson 	}
395a9148abdSDoug Rabson 
396a9148abdSDoug Rabson 	auth->ah_ops = &rpc_gss_ops;
397a9148abdSDoug Rabson 	auth->ah_private = (caddr_t) gd;
398a9148abdSDoug Rabson 	auth->ah_cred.oa_flavor = RPCSEC_GSS;
399a9148abdSDoug Rabson 
400a9148abdSDoug Rabson 	refcount_init(&gd->gd_refs, 1);
401a9148abdSDoug Rabson 	mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
402a9148abdSDoug Rabson 	gd->gd_auth = auth;
403a9148abdSDoug Rabson 	gd->gd_ucred = crdup(cred);
404a9148abdSDoug Rabson 	gd->gd_principal = strdup(principal, M_RPC);
40588a2437aSRick Macklem 	if (clnt_principal != NULL)
40688a2437aSRick Macklem 		gd->gd_clntprincipal = strdup(clnt_principal, M_RPC);
40788a2437aSRick Macklem 	else
40888a2437aSRick Macklem 		gd->gd_clntprincipal = NULL;
409a9148abdSDoug Rabson 
410a9148abdSDoug Rabson 
411a9148abdSDoug Rabson 	if (options_req) {
412a9148abdSDoug Rabson 		gd->gd_options = *options_req;
413a9148abdSDoug Rabson 	} else {
414a9148abdSDoug Rabson 		gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
415a9148abdSDoug Rabson 		gd->gd_options.time_req = 0;
416a9148abdSDoug Rabson 		gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
417a9148abdSDoug Rabson 		gd->gd_options.input_channel_bindings = NULL;
418a9148abdSDoug Rabson 	}
419a9148abdSDoug Rabson 	CLNT_ACQUIRE(clnt);
420a9148abdSDoug Rabson 	gd->gd_clnt = clnt;
421a9148abdSDoug Rabson 	gd->gd_ctx = GSS_C_NO_CONTEXT;
422a9148abdSDoug Rabson 	gd->gd_mech = mech_oid;
423a9148abdSDoug Rabson 	gd->gd_qop = qop_num;
424a9148abdSDoug Rabson 
425a9148abdSDoug Rabson 	gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
426a9148abdSDoug Rabson 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
427a9148abdSDoug Rabson 	gd->gd_cred.gc_seq = 0;
428a9148abdSDoug Rabson 	gd->gd_cred.gc_svc = service;
429a9148abdSDoug Rabson 	LIST_INIT(&gd->gd_reqs);
430a9148abdSDoug Rabson 
431a9148abdSDoug Rabson 	if (!rpc_gss_init(auth, options_ret)) {
432a9148abdSDoug Rabson 		goto bad;
433a9148abdSDoug Rabson 	}
434a9148abdSDoug Rabson 
435a9148abdSDoug Rabson 	return (auth);
436a9148abdSDoug Rabson 
437a9148abdSDoug Rabson  bad:
438a9148abdSDoug Rabson 	AUTH_DESTROY(auth);
439a9148abdSDoug Rabson 	return (NULL);
440a9148abdSDoug Rabson }
441a9148abdSDoug Rabson 
442a9148abdSDoug Rabson bool_t
rpc_gss_set_defaults(AUTH * auth,rpc_gss_service_t service,const char * qop)443a9148abdSDoug Rabson rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
444a9148abdSDoug Rabson {
445a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
446a9148abdSDoug Rabson 	u_int			qop_num;
447a9148abdSDoug Rabson 	const char		*mechanism;
448a9148abdSDoug Rabson 
449a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
450a9148abdSDoug Rabson 	if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
451a9148abdSDoug Rabson 		return (FALSE);
452a9148abdSDoug Rabson 	}
453a9148abdSDoug Rabson 
454a9148abdSDoug Rabson 	if (qop) {
455a9148abdSDoug Rabson 		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
456a9148abdSDoug Rabson 			return (FALSE);
457a9148abdSDoug Rabson 		}
458a9148abdSDoug Rabson 	} else {
459a9148abdSDoug Rabson 		qop_num = GSS_C_QOP_DEFAULT;
460a9148abdSDoug Rabson 	}
461a9148abdSDoug Rabson 
462a9148abdSDoug Rabson 	gd->gd_cred.gc_svc = service;
463a9148abdSDoug Rabson 	gd->gd_qop = qop_num;
464a9148abdSDoug Rabson 	return (TRUE);
465a9148abdSDoug Rabson }
466a9148abdSDoug Rabson 
467a9148abdSDoug Rabson static void
rpc_gss_purge_xid(struct rpc_gss_data * gd,uint32_t xid)468a9148abdSDoug Rabson rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
469a9148abdSDoug Rabson {
470a9148abdSDoug Rabson 	struct rpc_pending_request *pr, *npr;
471a9148abdSDoug Rabson 	struct rpc_pending_request_list reqs;
472a9148abdSDoug Rabson 
473a9148abdSDoug Rabson 	LIST_INIT(&reqs);
474a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
475a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
476a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
477a9148abdSDoug Rabson 			LIST_REMOVE(pr, pr_link);
478a9148abdSDoug Rabson 			LIST_INSERT_HEAD(&reqs, pr, pr_link);
479a9148abdSDoug Rabson 		}
480a9148abdSDoug Rabson 	}
481a9148abdSDoug Rabson 
482a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
483a9148abdSDoug Rabson 
484a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
485a9148abdSDoug Rabson 		mem_free(pr, sizeof(*pr));
486a9148abdSDoug Rabson 	}
487a9148abdSDoug Rabson }
488a9148abdSDoug Rabson 
489a9148abdSDoug Rabson static uint32_t
rpc_gss_alloc_seq(struct rpc_gss_data * gd)490a9148abdSDoug Rabson rpc_gss_alloc_seq(struct rpc_gss_data *gd)
491a9148abdSDoug Rabson {
492a9148abdSDoug Rabson 	uint32_t seq;
493a9148abdSDoug Rabson 
494a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
495a9148abdSDoug Rabson 	seq = gd->gd_seq;
496a9148abdSDoug Rabson 	gd->gd_seq++;
497a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
498a9148abdSDoug Rabson 
499a9148abdSDoug Rabson 	return (seq);
500a9148abdSDoug Rabson }
501a9148abdSDoug Rabson 
502a9148abdSDoug Rabson static void
rpc_gss_nextverf(__unused AUTH * auth)503a9148abdSDoug Rabson rpc_gss_nextverf(__unused AUTH *auth)
504a9148abdSDoug Rabson {
505a9148abdSDoug Rabson 
506a9148abdSDoug Rabson 	/* not used */
507a9148abdSDoug Rabson }
508a9148abdSDoug Rabson 
509a9148abdSDoug Rabson static bool_t
rpc_gss_marshal(AUTH * auth,uint32_t xid,XDR * xdrs,struct mbuf * args)510a9148abdSDoug Rabson rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
511a9148abdSDoug Rabson {
512a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
513a9148abdSDoug Rabson 	struct rpc_pending_request *pr;
514a9148abdSDoug Rabson 	uint32_t		 seq;
515a9148abdSDoug Rabson 	XDR			 tmpxdrs;
516a9148abdSDoug Rabson 	struct rpc_gss_cred	 gsscred;
517a9148abdSDoug Rabson 	char			 credbuf[MAX_AUTH_BYTES];
518a9148abdSDoug Rabson 	struct opaque_auth	 creds, verf;
519a9148abdSDoug Rabson 	gss_buffer_desc		 rpcbuf, checksum;
520a9148abdSDoug Rabson 	OM_uint32		 maj_stat, min_stat;
521a9148abdSDoug Rabson 	bool_t			 xdr_stat;
522a9148abdSDoug Rabson 
523a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_marshal()");
524a9148abdSDoug Rabson 
525a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
526a9148abdSDoug Rabson 
527a9148abdSDoug Rabson 	gsscred = gd->gd_cred;
528a9148abdSDoug Rabson 	seq = rpc_gss_alloc_seq(gd);
529a9148abdSDoug Rabson 	gsscred.gc_seq = seq;
530a9148abdSDoug Rabson 
531a9148abdSDoug Rabson 	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
532a9148abdSDoug Rabson 	if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
533a9148abdSDoug Rabson 		XDR_DESTROY(&tmpxdrs);
534a9148abdSDoug Rabson 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
535a9148abdSDoug Rabson 		return (FALSE);
536a9148abdSDoug Rabson 	}
537a9148abdSDoug Rabson 	creds.oa_flavor = RPCSEC_GSS;
538a9148abdSDoug Rabson 	creds.oa_base = credbuf;
539a9148abdSDoug Rabson 	creds.oa_length = XDR_GETPOS(&tmpxdrs);
540a9148abdSDoug Rabson 	XDR_DESTROY(&tmpxdrs);
541a9148abdSDoug Rabson 
542a9148abdSDoug Rabson 	xdr_opaque_auth(xdrs, &creds);
543a9148abdSDoug Rabson 
544a9148abdSDoug Rabson 	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
545a9148abdSDoug Rabson 	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
546a9148abdSDoug Rabson 		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
547a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
548a9148abdSDoug Rabson 			return (FALSE);
549a9148abdSDoug Rabson 		}
550*66612e67SGleb Smirnoff 		return (xdr_putmbuf(xdrs, args));
551a9148abdSDoug Rabson 	} else {
552a9148abdSDoug Rabson 		/*
553a9148abdSDoug Rabson 		 * Keep track of this XID + seq pair so that we can do
554a9148abdSDoug Rabson 		 * the matching gss_verify_mic in AUTH_VALIDATE.
555a9148abdSDoug Rabson 		 */
556a9148abdSDoug Rabson 		pr = mem_alloc(sizeof(struct rpc_pending_request));
557a9148abdSDoug Rabson 		mtx_lock(&gd->gd_lock);
558a9148abdSDoug Rabson 		pr->pr_xid = xid;
559a9148abdSDoug Rabson 		pr->pr_seq = seq;
560a9148abdSDoug Rabson 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
561a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
562a9148abdSDoug Rabson 
563a9148abdSDoug Rabson 		/*
564a9148abdSDoug Rabson 		 * Checksum serialized RPC header, up to and including
565a9148abdSDoug Rabson 		 * credential. For the in-kernel environment, we
566a9148abdSDoug Rabson 		 * assume that our XDR stream is on a contiguous
567a9148abdSDoug Rabson 		 * memory buffer (e.g. an mbuf).
568a9148abdSDoug Rabson 		 */
569a9148abdSDoug Rabson 		rpcbuf.length = XDR_GETPOS(xdrs);
570a9148abdSDoug Rabson 		XDR_SETPOS(xdrs, 0);
571a9148abdSDoug Rabson 		rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
572a9148abdSDoug Rabson 
573a9148abdSDoug Rabson 		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
574a9148abdSDoug Rabson 		    &rpcbuf, &checksum);
575a9148abdSDoug Rabson 
576a9148abdSDoug Rabson 		if (maj_stat != GSS_S_COMPLETE) {
577a9148abdSDoug Rabson 			rpc_gss_log_status("gss_get_mic", gd->gd_mech,
578a9148abdSDoug Rabson 			    maj_stat, min_stat);
579a9148abdSDoug Rabson 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
580a9148abdSDoug Rabson 				rpc_gss_destroy_context(auth, TRUE);
581a9148abdSDoug Rabson 			}
582a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
583a9148abdSDoug Rabson 			return (FALSE);
584a9148abdSDoug Rabson 		}
585a9148abdSDoug Rabson 
586a9148abdSDoug Rabson 		verf.oa_flavor = RPCSEC_GSS;
587a9148abdSDoug Rabson 		verf.oa_base = checksum.value;
588a9148abdSDoug Rabson 		verf.oa_length = checksum.length;
589a9148abdSDoug Rabson 
590a9148abdSDoug Rabson 		xdr_stat = xdr_opaque_auth(xdrs, &verf);
591a9148abdSDoug Rabson 		gss_release_buffer(&min_stat, &checksum);
592a9148abdSDoug Rabson 		if (!xdr_stat) {
593a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
594a9148abdSDoug Rabson 			return (FALSE);
595a9148abdSDoug Rabson 		}
596a9148abdSDoug Rabson 		if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
597a9148abdSDoug Rabson 		    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
598*66612e67SGleb Smirnoff 			return (xdr_putmbuf(xdrs, args));
599a9148abdSDoug Rabson 		} else {
600a9148abdSDoug Rabson 			if (!xdr_rpc_gss_wrap_data(&args,
601a9148abdSDoug Rabson 				gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
602a9148abdSDoug Rabson 				seq))
603a9148abdSDoug Rabson 				return (FALSE);
604*66612e67SGleb Smirnoff 			return (xdr_putmbuf(xdrs, args));
605a9148abdSDoug Rabson 		}
606a9148abdSDoug Rabson 	}
607a9148abdSDoug Rabson 
608a9148abdSDoug Rabson 	return (TRUE);
609a9148abdSDoug Rabson }
610a9148abdSDoug Rabson 
611a9148abdSDoug Rabson static bool_t
rpc_gss_validate(AUTH * auth,uint32_t xid,struct opaque_auth * verf,struct mbuf ** resultsp)612a9148abdSDoug Rabson rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
613a9148abdSDoug Rabson     struct mbuf **resultsp)
614a9148abdSDoug Rabson {
615a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
616a9148abdSDoug Rabson 	struct rpc_pending_request *pr, *npr;
617a9148abdSDoug Rabson 	struct rpc_pending_request_list reqs;
618a9148abdSDoug Rabson 	gss_qop_t		qop_state;
619a9148abdSDoug Rabson 	uint32_t		num, seq;
620a9148abdSDoug Rabson 	gss_buffer_desc		signbuf, checksum;
621a9148abdSDoug Rabson 	OM_uint32		maj_stat, min_stat;
622a9148abdSDoug Rabson 
623a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_validate()");
624a9148abdSDoug Rabson 
625a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
626a9148abdSDoug Rabson 
627a9148abdSDoug Rabson 	/*
628a9148abdSDoug Rabson 	 * The client will call us with a NULL verf when it gives up
629a9148abdSDoug Rabson 	 * on an XID.
630a9148abdSDoug Rabson 	 */
631a9148abdSDoug Rabson 	if (!verf) {
632a9148abdSDoug Rabson 		rpc_gss_purge_xid(gd, xid);
633a9148abdSDoug Rabson 		return (TRUE);
634a9148abdSDoug Rabson 	}
635a9148abdSDoug Rabson 
636a9148abdSDoug Rabson 	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
637a9148abdSDoug Rabson 		/*
638a9148abdSDoug Rabson 		 * Save the on the wire verifier to validate last INIT
639a9148abdSDoug Rabson 		 * phase packet after decode if the major status is
640a9148abdSDoug Rabson 		 * GSS_S_COMPLETE.
641a9148abdSDoug Rabson 		 */
642a9148abdSDoug Rabson 		if (gd->gd_verf.value)
643a9148abdSDoug Rabson 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
644a9148abdSDoug Rabson 			    (char *) &gd->gd_verf);
645a9148abdSDoug Rabson 		gd->gd_verf.value = mem_alloc(verf->oa_length);
646a9148abdSDoug Rabson 		if (gd->gd_verf.value == NULL) {
647a9148abdSDoug Rabson 			printf("gss_validate: out of memory\n");
648a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
649a9148abdSDoug Rabson 			m_freem(*resultsp);
650a9148abdSDoug Rabson 			*resultsp = NULL;
651a9148abdSDoug Rabson 			return (FALSE);
652a9148abdSDoug Rabson 		}
653a9148abdSDoug Rabson 		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
654a9148abdSDoug Rabson 		gd->gd_verf.length = verf->oa_length;
655a9148abdSDoug Rabson 
656a9148abdSDoug Rabson 		return (TRUE);
657a9148abdSDoug Rabson 	}
658a9148abdSDoug Rabson 
659a9148abdSDoug Rabson 	/*
660a9148abdSDoug Rabson 	 * We need to check the verifier against all the requests
661a9148abdSDoug Rabson 	 * we've send for this XID - for unreliable protocols, we
662a9148abdSDoug Rabson 	 * retransmit with the same XID but different sequence
663a9148abdSDoug Rabson 	 * number. We temporarily take this set of requests out of the
664a9148abdSDoug Rabson 	 * list so that we can work through the list without having to
665a9148abdSDoug Rabson 	 * hold the lock.
666a9148abdSDoug Rabson 	 */
667a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
668a9148abdSDoug Rabson 	LIST_INIT(&reqs);
669a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
670a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
671a9148abdSDoug Rabson 			LIST_REMOVE(pr, pr_link);
672a9148abdSDoug Rabson 			LIST_INSERT_HEAD(&reqs, pr, pr_link);
673a9148abdSDoug Rabson 		}
674a9148abdSDoug Rabson 	}
675a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
676a9148abdSDoug Rabson 	LIST_FOREACH(pr, &reqs, pr_link) {
677a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
678a9148abdSDoug Rabson 			seq = pr->pr_seq;
679a9148abdSDoug Rabson 			num = htonl(seq);
680a9148abdSDoug Rabson 			signbuf.value = &num;
681a9148abdSDoug Rabson 			signbuf.length = sizeof(num);
682a9148abdSDoug Rabson 
683a9148abdSDoug Rabson 			checksum.value = verf->oa_base;
684a9148abdSDoug Rabson 			checksum.length = verf->oa_length;
685a9148abdSDoug Rabson 
686a9148abdSDoug Rabson 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
687a9148abdSDoug Rabson 			    &signbuf, &checksum, &qop_state);
688a9148abdSDoug Rabson 			if (maj_stat != GSS_S_COMPLETE
689a9148abdSDoug Rabson 			    || qop_state != gd->gd_qop) {
690a9148abdSDoug Rabson 				continue;
691a9148abdSDoug Rabson 			}
692a9148abdSDoug Rabson 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
693a9148abdSDoug Rabson 				rpc_gss_destroy_context(auth, TRUE);
694a9148abdSDoug Rabson 				break;
695a9148abdSDoug Rabson 			}
696a9148abdSDoug Rabson 			//rpc_gss_purge_reqs(gd, seq);
697a9148abdSDoug Rabson 			LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
698a9148abdSDoug Rabson 				mem_free(pr, sizeof(*pr));
699a9148abdSDoug Rabson 
700a9148abdSDoug Rabson 			if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
701a9148abdSDoug Rabson 				return (TRUE);
702a9148abdSDoug Rabson 			} else {
703a9148abdSDoug Rabson 				if (!xdr_rpc_gss_unwrap_data(resultsp,
704a9148abdSDoug Rabson 					gd->gd_ctx, gd->gd_qop,
705a9148abdSDoug Rabson 					gd->gd_cred.gc_svc, seq)) {
706a9148abdSDoug Rabson 					return (FALSE);
707a9148abdSDoug Rabson 				}
708a9148abdSDoug Rabson 			}
709a9148abdSDoug Rabson 			return (TRUE);
710a9148abdSDoug Rabson 		}
711a9148abdSDoug Rabson 	}
712a9148abdSDoug Rabson 
713a9148abdSDoug Rabson 	/*
714a9148abdSDoug Rabson 	 * We didn't match - put back any entries for this XID so that
715a9148abdSDoug Rabson 	 * a future call to validate can retry.
716a9148abdSDoug Rabson 	 */
717a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
718a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
719a9148abdSDoug Rabson 		LIST_REMOVE(pr, pr_link);
720a9148abdSDoug Rabson 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
721a9148abdSDoug Rabson 	}
722a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
723a9148abdSDoug Rabson 
724a9148abdSDoug Rabson 	/*
725a9148abdSDoug Rabson 	 * Nothing matches - give up.
726a9148abdSDoug Rabson 	 */
727a9148abdSDoug Rabson 	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
728a9148abdSDoug Rabson 	m_freem(*resultsp);
729a9148abdSDoug Rabson 	*resultsp = NULL;
730a9148abdSDoug Rabson 	return (FALSE);
731a9148abdSDoug Rabson }
732a9148abdSDoug Rabson 
733a9148abdSDoug Rabson static bool_t
rpc_gss_init(AUTH * auth,rpc_gss_options_ret_t * options_ret)734a9148abdSDoug Rabson rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
735a9148abdSDoug Rabson {
736a9148abdSDoug Rabson 	struct thread		*td = curthread;
737a9148abdSDoug Rabson 	struct ucred		*crsave;
738a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
739a9148abdSDoug Rabson 	struct rpc_gss_init_res	 gr;
740a9148abdSDoug Rabson 	gss_buffer_desc		principal_desc;
741a9148abdSDoug Rabson 	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
742a9148abdSDoug Rabson 	gss_name_t		name;
743a9148abdSDoug Rabson 	OM_uint32		 maj_stat, min_stat, call_stat;
744a9148abdSDoug Rabson 	const char		*mech;
745a9148abdSDoug Rabson 	struct rpc_callextra	 ext;
74688a2437aSRick Macklem 	gss_OID			mech_oid;
74788a2437aSRick Macklem 	gss_OID_set		mechlist;
748a9148abdSDoug Rabson 
749a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_refresh()");
750a9148abdSDoug Rabson 
751a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
752a9148abdSDoug Rabson 
753a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
754a9148abdSDoug Rabson 	/*
755a9148abdSDoug Rabson 	 * If the context isn't in START state, someone else is
756a9148abdSDoug Rabson 	 * refreshing - we wait till they are done. If they fail, they
757a9148abdSDoug Rabson 	 * will put the state back to START and we can try (most
758a9148abdSDoug Rabson 	 * likely to also fail).
759a9148abdSDoug Rabson 	 */
760a9148abdSDoug Rabson 	while (gd->gd_state != RPCSEC_GSS_START
761a9148abdSDoug Rabson 	    && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
762a9148abdSDoug Rabson 		msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
763a9148abdSDoug Rabson 	}
764a9148abdSDoug Rabson 	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
765a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
766a9148abdSDoug Rabson 		return (TRUE);
767a9148abdSDoug Rabson 	}
768a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_CONTEXT;
769a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
770a9148abdSDoug Rabson 
771b2115885SRick Macklem 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
772b2115885SRick Macklem 	gd->gd_cred.gc_seq = 0;
773b2115885SRick Macklem 
77488a2437aSRick Macklem 	/*
77588a2437aSRick Macklem 	 * For KerberosV, if there is a client principal name, that implies
77688a2437aSRick Macklem 	 * that this is a host based initiator credential in the default
77788a2437aSRick Macklem 	 * keytab file. For this case, it is necessary to do a
77888a2437aSRick Macklem 	 * gss_acquire_cred(). When this is done, the gssd daemon will
77988a2437aSRick Macklem 	 * do the equivalent of "kinit -k" to put a TGT for the name in
78088a2437aSRick Macklem 	 * the credential cache file for the gssd daemon.
78188a2437aSRick Macklem 	 */
78288a2437aSRick Macklem 	if (gd->gd_clntprincipal != NULL &&
78388a2437aSRick Macklem 	    rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
78488a2437aSRick Macklem 	    gd->gd_mech == mech_oid) {
78588a2437aSRick Macklem 		/* Get rid of any old credential. */
78688a2437aSRick Macklem 		if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
78788a2437aSRick Macklem 			gss_release_cred(&min_stat, &gd->gd_options.my_cred);
78888a2437aSRick Macklem 			gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
78988a2437aSRick Macklem 		}
79088a2437aSRick Macklem 
79188a2437aSRick Macklem 		/*
79288a2437aSRick Macklem 		 * The mechanism must be set to KerberosV for acquisition
79388a2437aSRick Macklem 		 * of credentials to work reliably.
79488a2437aSRick Macklem 		 */
79588a2437aSRick Macklem 		maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
79688a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
79788a2437aSRick Macklem 			options_ret->major_status = maj_stat;
79888a2437aSRick Macklem 			options_ret->minor_status = min_stat;
79988a2437aSRick Macklem 			goto out;
80088a2437aSRick Macklem 		}
80188a2437aSRick Macklem 		maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
80288a2437aSRick Macklem 		    &mechlist);
80388a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
80488a2437aSRick Macklem 			options_ret->major_status = maj_stat;
80588a2437aSRick Macklem 			options_ret->minor_status = min_stat;
80688a2437aSRick Macklem 			gss_release_oid_set(&min_stat, &mechlist);
80788a2437aSRick Macklem 			goto out;
80888a2437aSRick Macklem 		}
80988a2437aSRick Macklem 
81088a2437aSRick Macklem 		principal_desc.value = (void *)gd->gd_clntprincipal;
81188a2437aSRick Macklem 		principal_desc.length = strlen(gd->gd_clntprincipal);
81288a2437aSRick Macklem 		maj_stat = gss_import_name(&min_stat, &principal_desc,
81388a2437aSRick Macklem 		    GSS_C_NT_HOSTBASED_SERVICE, &name);
81488a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
81588a2437aSRick Macklem 			options_ret->major_status = maj_stat;
81688a2437aSRick Macklem 			options_ret->minor_status = min_stat;
81788a2437aSRick Macklem 			gss_release_oid_set(&min_stat, &mechlist);
81888a2437aSRick Macklem 			goto out;
81988a2437aSRick Macklem 		}
82088a2437aSRick Macklem 		/* Acquire the credentials. */
82188a2437aSRick Macklem 		maj_stat = gss_acquire_cred(&min_stat, name, 0,
82288a2437aSRick Macklem 		    mechlist, GSS_C_INITIATE,
82388a2437aSRick Macklem 		    &gd->gd_options.my_cred, NULL, NULL);
82488a2437aSRick Macklem 		gss_release_name(&min_stat, &name);
82588a2437aSRick Macklem 		gss_release_oid_set(&min_stat, &mechlist);
82688a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
82788a2437aSRick Macklem 			options_ret->major_status = maj_stat;
82888a2437aSRick Macklem 			options_ret->minor_status = min_stat;
82988a2437aSRick Macklem 			goto out;
83088a2437aSRick Macklem 		}
83188a2437aSRick Macklem 	}
83288a2437aSRick Macklem 
833a9148abdSDoug Rabson 	principal_desc.value = (void *)gd->gd_principal;
834a9148abdSDoug Rabson 	principal_desc.length = strlen(gd->gd_principal);
835a9148abdSDoug Rabson 	maj_stat = gss_import_name(&min_stat, &principal_desc,
836a9148abdSDoug Rabson 	    GSS_C_NT_HOSTBASED_SERVICE, &name);
837a9148abdSDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
838a9148abdSDoug Rabson 		options_ret->major_status = maj_stat;
839a9148abdSDoug Rabson 		options_ret->minor_status = min_stat;
840a9148abdSDoug Rabson 		goto out;
841a9148abdSDoug Rabson 	}
842a9148abdSDoug Rabson 
843a9148abdSDoug Rabson 	/* GSS context establishment loop. */
844a9148abdSDoug Rabson 	memset(&recv_token, 0, sizeof(recv_token));
845a9148abdSDoug Rabson 	memset(&gr, 0, sizeof(gr));
846a9148abdSDoug Rabson 	memset(options_ret, 0, sizeof(*options_ret));
847a9148abdSDoug Rabson 	options_ret->major_status = GSS_S_FAILURE;
848a9148abdSDoug Rabson 	recv_tokenp = GSS_C_NO_BUFFER;
849a9148abdSDoug Rabson 
850a9148abdSDoug Rabson 	for (;;) {
851a9148abdSDoug Rabson 		crsave = td->td_ucred;
852a9148abdSDoug Rabson 		td->td_ucred = gd->gd_ucred;
853a9148abdSDoug Rabson 		maj_stat = gss_init_sec_context(&min_stat,
854a9148abdSDoug Rabson 		    gd->gd_options.my_cred,
855a9148abdSDoug Rabson 		    &gd->gd_ctx,
856a9148abdSDoug Rabson 		    name,
857a9148abdSDoug Rabson 		    gd->gd_mech,
858a9148abdSDoug Rabson 		    gd->gd_options.req_flags,
859a9148abdSDoug Rabson 		    gd->gd_options.time_req,
860a9148abdSDoug Rabson 		    gd->gd_options.input_channel_bindings,
861a9148abdSDoug Rabson 		    recv_tokenp,
862a9148abdSDoug Rabson 		    &gd->gd_mech,	/* used mech */
863a9148abdSDoug Rabson 		    &send_token,
864a9148abdSDoug Rabson 		    &options_ret->ret_flags,
865a9148abdSDoug Rabson 		    &options_ret->time_req);
866a9148abdSDoug Rabson 		td->td_ucred = crsave;
867a9148abdSDoug Rabson 
868a9148abdSDoug Rabson 		/*
869a9148abdSDoug Rabson 		 * Free the token which we got from the server (if
870a9148abdSDoug Rabson 		 * any).  Remember that this was allocated by XDR, not
871a9148abdSDoug Rabson 		 * GSS-API.
872a9148abdSDoug Rabson 		 */
873a9148abdSDoug Rabson 		if (recv_tokenp != GSS_C_NO_BUFFER) {
874a9148abdSDoug Rabson 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
875a9148abdSDoug Rabson 			    (char *) &recv_token);
876a9148abdSDoug Rabson 			recv_tokenp = GSS_C_NO_BUFFER;
877a9148abdSDoug Rabson 		}
878a9148abdSDoug Rabson 		if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
879a9148abdSDoug Rabson 			strlcpy(options_ret->actual_mechanism,
880a9148abdSDoug Rabson 			    mech,
881a9148abdSDoug Rabson 			    sizeof(options_ret->actual_mechanism));
882a9148abdSDoug Rabson 		}
883a9148abdSDoug Rabson 		if (maj_stat != GSS_S_COMPLETE &&
884a9148abdSDoug Rabson 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
885a9148abdSDoug Rabson 			rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
886a9148abdSDoug Rabson 			    maj_stat, min_stat);
887a9148abdSDoug Rabson 			options_ret->major_status = maj_stat;
888a9148abdSDoug Rabson 			options_ret->minor_status = min_stat;
889a9148abdSDoug Rabson 			break;
890a9148abdSDoug Rabson 		}
891a9148abdSDoug Rabson 		if (send_token.length != 0) {
892a9148abdSDoug Rabson 			memset(&gr, 0, sizeof(gr));
893a9148abdSDoug Rabson 
894a9148abdSDoug Rabson 			bzero(&ext, sizeof(ext));
895a9148abdSDoug Rabson 			ext.rc_auth = auth;
896a9148abdSDoug Rabson 			call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
897a9148abdSDoug Rabson 			    (xdrproc_t)xdr_gss_buffer_desc,
898a9148abdSDoug Rabson 			    &send_token,
899a9148abdSDoug Rabson 			    (xdrproc_t)xdr_rpc_gss_init_res,
900a9148abdSDoug Rabson 			    (caddr_t)&gr, AUTH_TIMEOUT);
901a9148abdSDoug Rabson 
902a9148abdSDoug Rabson 			gss_release_buffer(&min_stat, &send_token);
903a9148abdSDoug Rabson 
904a9148abdSDoug Rabson 			if (call_stat != RPC_SUCCESS)
905a9148abdSDoug Rabson 				break;
906a9148abdSDoug Rabson 
907a9148abdSDoug Rabson 			if (gr.gr_major != GSS_S_COMPLETE &&
908a9148abdSDoug Rabson 			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
909a9148abdSDoug Rabson 				rpc_gss_log_status("server reply", gd->gd_mech,
910a9148abdSDoug Rabson 				    gr.gr_major, gr.gr_minor);
911a9148abdSDoug Rabson 				options_ret->major_status = gr.gr_major;
912a9148abdSDoug Rabson 				options_ret->minor_status = gr.gr_minor;
913a9148abdSDoug Rabson 				break;
914a9148abdSDoug Rabson 			}
915a9148abdSDoug Rabson 
916a9148abdSDoug Rabson 			/*
917a9148abdSDoug Rabson 			 * Save the server's gr_handle value, freeing
918a9148abdSDoug Rabson 			 * what we have already (remember that this
919a9148abdSDoug Rabson 			 * was allocated by XDR, not GSS-API).
920a9148abdSDoug Rabson 			 */
921a9148abdSDoug Rabson 			if (gr.gr_handle.length != 0) {
922a9148abdSDoug Rabson 				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
923a9148abdSDoug Rabson 				    (char *) &gd->gd_cred.gc_handle);
924a9148abdSDoug Rabson 				gd->gd_cred.gc_handle = gr.gr_handle;
925a9148abdSDoug Rabson 			}
926a9148abdSDoug Rabson 
927a9148abdSDoug Rabson 			/*
928a9148abdSDoug Rabson 			 * Save the server's token as well.
929a9148abdSDoug Rabson 			 */
930a9148abdSDoug Rabson 			if (gr.gr_token.length != 0) {
931a9148abdSDoug Rabson 				recv_token = gr.gr_token;
932a9148abdSDoug Rabson 				recv_tokenp = &recv_token;
933a9148abdSDoug Rabson 			}
934a9148abdSDoug Rabson 
935a9148abdSDoug Rabson 			/*
936a9148abdSDoug Rabson 			 * Since we have copied out all the bits of gr
937a9148abdSDoug Rabson 			 * which XDR allocated for us, we don't need
938a9148abdSDoug Rabson 			 * to free it.
939a9148abdSDoug Rabson 			 */
940a9148abdSDoug Rabson 			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
941a9148abdSDoug Rabson 		}
942a9148abdSDoug Rabson 
943a9148abdSDoug Rabson 		if (maj_stat == GSS_S_COMPLETE) {
944a9148abdSDoug Rabson 			gss_buffer_desc   bufin;
945a9148abdSDoug Rabson 			u_int seq, qop_state = 0;
946a9148abdSDoug Rabson 
947a9148abdSDoug Rabson 			/*
948a9148abdSDoug Rabson 			 * gss header verifier,
949a9148abdSDoug Rabson 			 * usually checked in gss_validate
950a9148abdSDoug Rabson 			 */
951a9148abdSDoug Rabson 			seq = htonl(gr.gr_win);
952a9148abdSDoug Rabson 			bufin.value = (unsigned char *)&seq;
953a9148abdSDoug Rabson 			bufin.length = sizeof(seq);
954a9148abdSDoug Rabson 
955a9148abdSDoug Rabson 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
956a9148abdSDoug Rabson 			    &bufin, &gd->gd_verf, &qop_state);
957a9148abdSDoug Rabson 
958a9148abdSDoug Rabson 			if (maj_stat != GSS_S_COMPLETE ||
959a9148abdSDoug Rabson 			    qop_state != gd->gd_qop) {
960a9148abdSDoug Rabson 				rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
961a9148abdSDoug Rabson 				    maj_stat, min_stat);
962a9148abdSDoug Rabson 				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
963a9148abdSDoug Rabson 					rpc_gss_destroy_context(auth, TRUE);
964a9148abdSDoug Rabson 				}
965a9148abdSDoug Rabson 				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
966a9148abdSDoug Rabson 				    EPERM);
967a9148abdSDoug Rabson 				options_ret->major_status = maj_stat;
968a9148abdSDoug Rabson 				options_ret->minor_status = min_stat;
969a9148abdSDoug Rabson 				break;
970a9148abdSDoug Rabson 			}
971a9148abdSDoug Rabson 
972a9148abdSDoug Rabson 			options_ret->major_status = GSS_S_COMPLETE;
973a9148abdSDoug Rabson 			options_ret->minor_status = 0;
974a9148abdSDoug Rabson 			options_ret->rpcsec_version = gd->gd_cred.gc_version;
975a9148abdSDoug Rabson 			options_ret->gss_context = gd->gd_ctx;
976a9148abdSDoug Rabson 
977a9148abdSDoug Rabson 			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
978a9148abdSDoug Rabson 			gd->gd_seq = 1;
979a9148abdSDoug Rabson 			gd->gd_win = gr.gr_win;
980a9148abdSDoug Rabson 			break;
981a9148abdSDoug Rabson 		}
982a9148abdSDoug Rabson 	}
983a9148abdSDoug Rabson 
984a9148abdSDoug Rabson 	gss_release_name(&min_stat, &name);
985a9148abdSDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
986a9148abdSDoug Rabson 	    (char *) &gd->gd_verf);
987a9148abdSDoug Rabson 
988a9148abdSDoug Rabson out:
989a9148abdSDoug Rabson 	/* End context negotiation loop. */
990a9148abdSDoug Rabson 	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
991a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_AUTHERROR;
992a9148abdSDoug Rabson 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
993a9148abdSDoug Rabson 		if (gd->gd_ctx) {
994a9148abdSDoug Rabson 			gss_delete_sec_context(&min_stat, &gd->gd_ctx,
995a9148abdSDoug Rabson 				GSS_C_NO_BUFFER);
996a9148abdSDoug Rabson 		}
997a9148abdSDoug Rabson 		mtx_lock(&gd->gd_lock);
998a9148abdSDoug Rabson 		gd->gd_state = RPCSEC_GSS_START;
999a9148abdSDoug Rabson 		wakeup(gd);
1000a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
1001a9148abdSDoug Rabson 		return (FALSE);
1002a9148abdSDoug Rabson 	}
1003a9148abdSDoug Rabson 
1004a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1005a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_ESTABLISHED;
1006a9148abdSDoug Rabson 	wakeup(gd);
1007a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1008a9148abdSDoug Rabson 
1009a9148abdSDoug Rabson 	return (TRUE);
1010a9148abdSDoug Rabson }
1011a9148abdSDoug Rabson 
1012a9148abdSDoug Rabson static bool_t
rpc_gss_refresh(AUTH * auth,void * msg)1013a9148abdSDoug Rabson rpc_gss_refresh(AUTH *auth, void *msg)
1014a9148abdSDoug Rabson {
1015a9148abdSDoug Rabson 	struct rpc_msg *reply = (struct rpc_msg *) msg;
1016a9148abdSDoug Rabson 	rpc_gss_options_ret_t options;
1017a4c5a1c3SRick Macklem 	struct rpc_gss_data *gd;
1018a4c5a1c3SRick Macklem 
1019a4c5a1c3SRick Macklem 	gd = AUTH_PRIVATE(auth);
1020a4c5a1c3SRick Macklem 
1021a4c5a1c3SRick Macklem 	/*
1022a4c5a1c3SRick Macklem 	 * If the context is in DESTROYING state, then just return, since
1023a4c5a1c3SRick Macklem 	 * there is no point in refreshing the credentials.
1024a4c5a1c3SRick Macklem 	 */
1025a4c5a1c3SRick Macklem 	mtx_lock(&gd->gd_lock);
1026a4c5a1c3SRick Macklem 	if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
1027a4c5a1c3SRick Macklem 		mtx_unlock(&gd->gd_lock);
1028a4c5a1c3SRick Macklem 		return (FALSE);
1029a4c5a1c3SRick Macklem 	}
1030a4c5a1c3SRick Macklem 	mtx_unlock(&gd->gd_lock);
1031a9148abdSDoug Rabson 
1032a9148abdSDoug Rabson 	/*
1033a9148abdSDoug Rabson 	 * If the error was RPCSEC_GSS_CREDPROBLEM of
1034a9148abdSDoug Rabson 	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
1035a9148abdSDoug Rabson 	 * other errors are fatal.
1036a9148abdSDoug Rabson 	 */
1037a9148abdSDoug Rabson 	if (reply->rm_reply.rp_stat == MSG_DENIED
1038a9148abdSDoug Rabson 	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
1039a9148abdSDoug Rabson 	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
1040a9148abdSDoug Rabson 		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
1041a9148abdSDoug Rabson 		rpc_gss_destroy_context(auth, FALSE);
1042a9148abdSDoug Rabson 		memset(&options, 0, sizeof(options));
1043a9148abdSDoug Rabson 		return (rpc_gss_init(auth, &options));
1044a9148abdSDoug Rabson 	}
1045a9148abdSDoug Rabson 
1046a9148abdSDoug Rabson 	return (FALSE);
1047a9148abdSDoug Rabson }
1048a9148abdSDoug Rabson 
1049a9148abdSDoug Rabson static void
rpc_gss_destroy_context(AUTH * auth,bool_t send_destroy)1050a9148abdSDoug Rabson rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
1051a9148abdSDoug Rabson {
1052a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1053a9148abdSDoug Rabson 	struct rpc_pending_request *pr;
1054a9148abdSDoug Rabson 	OM_uint32		 min_stat;
1055a9148abdSDoug Rabson 	struct rpc_callextra	 ext;
1056a9148abdSDoug Rabson 
1057a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_destroy_context()");
1058a9148abdSDoug Rabson 
1059a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1060a9148abdSDoug Rabson 
1061a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1062a9148abdSDoug Rabson 	/*
1063a9148abdSDoug Rabson 	 * If the context isn't in ESTABISHED state, someone else is
1064a9148abdSDoug Rabson 	 * destroying/refreshing - we wait till they are done.
1065a9148abdSDoug Rabson 	 */
1066a9148abdSDoug Rabson 	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
1067a9148abdSDoug Rabson 		while (gd->gd_state != RPCSEC_GSS_START
1068a9148abdSDoug Rabson 		    && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
1069a9148abdSDoug Rabson 			msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
1070a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
1071a9148abdSDoug Rabson 		return;
1072a9148abdSDoug Rabson 	}
1073a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_DESTROYING;
1074a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1075a9148abdSDoug Rabson 
1076a9148abdSDoug Rabson 	if (send_destroy) {
1077a9148abdSDoug Rabson 		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
1078a9148abdSDoug Rabson 		bzero(&ext, sizeof(ext));
1079a9148abdSDoug Rabson 		ext.rc_auth = auth;
1080a9148abdSDoug Rabson 		CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
1081a9148abdSDoug Rabson 		    (xdrproc_t)xdr_void, NULL,
1082a9148abdSDoug Rabson 		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1083a9148abdSDoug Rabson 	}
1084a9148abdSDoug Rabson 
1085a9148abdSDoug Rabson 	while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1086a9148abdSDoug Rabson 		LIST_REMOVE(pr, pr_link);
1087a9148abdSDoug Rabson 		mem_free(pr, sizeof(*pr));
1088a9148abdSDoug Rabson 	}
1089a9148abdSDoug Rabson 
1090a9148abdSDoug Rabson 	/*
1091a9148abdSDoug Rabson 	 * Free the context token. Remember that this was
1092a9148abdSDoug Rabson 	 * allocated by XDR, not GSS-API.
1093a9148abdSDoug Rabson 	 */
1094a9148abdSDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1095a9148abdSDoug Rabson 	    (char *) &gd->gd_cred.gc_handle);
1096a9148abdSDoug Rabson 	gd->gd_cred.gc_handle.length = 0;
1097a9148abdSDoug Rabson 
1098a9148abdSDoug Rabson 	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1099a9148abdSDoug Rabson 		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1100a9148abdSDoug Rabson 
1101a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1102a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_START;
1103a9148abdSDoug Rabson 	wakeup(gd);
1104a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1105a9148abdSDoug Rabson }
1106a9148abdSDoug Rabson 
1107a9148abdSDoug Rabson static void
rpc_gss_destroy(AUTH * auth)1108a9148abdSDoug Rabson rpc_gss_destroy(AUTH *auth)
1109a9148abdSDoug Rabson {
1110a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1111a9148abdSDoug Rabson 
1112a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_destroy()");
1113a9148abdSDoug Rabson 
1114a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1115a9148abdSDoug Rabson 
1116a9148abdSDoug Rabson 	if (!refcount_release(&gd->gd_refs))
1117a9148abdSDoug Rabson 		return;
1118a9148abdSDoug Rabson 
1119a9148abdSDoug Rabson 	rpc_gss_destroy_context(auth, TRUE);
1120a9148abdSDoug Rabson 
1121a9148abdSDoug Rabson 	CLNT_RELEASE(gd->gd_clnt);
1122a9148abdSDoug Rabson 	crfree(gd->gd_ucred);
1123a9148abdSDoug Rabson 	free(gd->gd_principal, M_RPC);
112488a2437aSRick Macklem 	if (gd->gd_clntprincipal != NULL)
112588a2437aSRick Macklem 		free(gd->gd_clntprincipal, M_RPC);
1126a9148abdSDoug Rabson 	if (gd->gd_verf.value)
1127a9148abdSDoug Rabson 		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1128a9148abdSDoug Rabson 		    (char *) &gd->gd_verf);
1129a9148abdSDoug Rabson 	mtx_destroy(&gd->gd_lock);
1130a9148abdSDoug Rabson 
1131a9148abdSDoug Rabson 	mem_free(gd, sizeof(*gd));
1132a9148abdSDoug Rabson 	mem_free(auth, sizeof(*auth));
1133a9148abdSDoug Rabson }
1134a9148abdSDoug Rabson 
1135a9148abdSDoug Rabson int
rpc_gss_max_data_length(AUTH * auth,int max_tp_unit_len)1136a9148abdSDoug Rabson rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1137a9148abdSDoug Rabson {
1138a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1139a9148abdSDoug Rabson 	int			want_conf;
1140a9148abdSDoug Rabson 	OM_uint32		max;
1141a9148abdSDoug Rabson 	OM_uint32		maj_stat, min_stat;
1142a9148abdSDoug Rabson 	int			result;
1143a9148abdSDoug Rabson 
1144a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1145a9148abdSDoug Rabson 
1146a9148abdSDoug Rabson 	switch (gd->gd_cred.gc_svc) {
1147a9148abdSDoug Rabson 	case rpc_gss_svc_none:
1148a9148abdSDoug Rabson 		return (max_tp_unit_len);
1149a9148abdSDoug Rabson 		break;
1150a9148abdSDoug Rabson 
1151a9148abdSDoug Rabson 	case rpc_gss_svc_default:
1152a9148abdSDoug Rabson 	case rpc_gss_svc_integrity:
1153a9148abdSDoug Rabson 		want_conf = FALSE;
1154a9148abdSDoug Rabson 		break;
1155a9148abdSDoug Rabson 
1156a9148abdSDoug Rabson 	case rpc_gss_svc_privacy:
1157a9148abdSDoug Rabson 		want_conf = TRUE;
1158a9148abdSDoug Rabson 		break;
1159a9148abdSDoug Rabson 
1160a9148abdSDoug Rabson 	default:
1161a9148abdSDoug Rabson 		return (0);
1162a9148abdSDoug Rabson 	}
1163a9148abdSDoug Rabson 
1164a9148abdSDoug Rabson 	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1165a9148abdSDoug Rabson 	    gd->gd_qop, max_tp_unit_len, &max);
1166a9148abdSDoug Rabson 
1167a9148abdSDoug Rabson 	if (maj_stat == GSS_S_COMPLETE) {
1168a9148abdSDoug Rabson 		result = (int) max;
1169a9148abdSDoug Rabson 		if (result < 0)
1170a9148abdSDoug Rabson 			result = 0;
1171a9148abdSDoug Rabson 		return (result);
1172a9148abdSDoug Rabson 	} else {
1173a9148abdSDoug Rabson 		rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1174a9148abdSDoug Rabson 		    maj_stat, min_stat);
1175a9148abdSDoug Rabson 		return (0);
1176a9148abdSDoug Rabson 	}
1177a9148abdSDoug Rabson }
1178