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