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