xref: /freebsd/sys/rpc/rpcsec_gss/rpcsec_gss.c (revision 38f0b757fd84d17d0fc24739a7cda160c4516d81)
1 /*-
2  * Copyright (c) 2008 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 /*
27   auth_gss.c
28 
29   RPCSEC_GSS client routines.
30 
31   Copyright (c) 2000 The Regents of the University of Michigan.
32   All rights reserved.
33 
34   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35   All rights reserved, all wrongs reversed.
36 
37   Redistribution and use in source and binary forms, with or without
38   modification, are permitted provided that the following conditions
39   are met:
40 
41   1. Redistributions of source code must retain the above copyright
42      notice, this list of conditions and the following disclaimer.
43   2. Redistributions in binary form must reproduce the above copyright
44      notice, this list of conditions and the following disclaimer in the
45      documentation and/or other materials provided with the distribution.
46   3. Neither the name of the University nor the names of its
47      contributors may be used to endorse or promote products derived
48      from this software without specific prior written permission.
49 
50   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 
62   $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
63 */
64 
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67 
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/hash.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 struct auth_ops rpc_gss_ops = {
99 	rpc_gss_nextverf,
100 	rpc_gss_marshal,
101 	rpc_gss_validate,
102 	rpc_gss_refresh,
103 	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
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
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 *
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
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 *
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
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 *
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
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
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
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
504 rpc_gss_nextverf(__unused AUTH *auth)
505 {
506 
507 	/* not used */
508 }
509 
510 static bool_t
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 		xdrmbuf_append(xdrs, args);
552 		return (TRUE);
553 	} else {
554 		/*
555 		 * Keep track of this XID + seq pair so that we can do
556 		 * the matching gss_verify_mic in AUTH_VALIDATE.
557 		 */
558 		pr = mem_alloc(sizeof(struct rpc_pending_request));
559 		mtx_lock(&gd->gd_lock);
560 		pr->pr_xid = xid;
561 		pr->pr_seq = seq;
562 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
563 		mtx_unlock(&gd->gd_lock);
564 
565 		/*
566 		 * Checksum serialized RPC header, up to and including
567 		 * credential. For the in-kernel environment, we
568 		 * assume that our XDR stream is on a contiguous
569 		 * memory buffer (e.g. an mbuf).
570 		 */
571 		rpcbuf.length = XDR_GETPOS(xdrs);
572 		XDR_SETPOS(xdrs, 0);
573 		rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
574 
575 		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
576 		    &rpcbuf, &checksum);
577 
578 		if (maj_stat != GSS_S_COMPLETE) {
579 			rpc_gss_log_status("gss_get_mic", gd->gd_mech,
580 			    maj_stat, min_stat);
581 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
582 				rpc_gss_destroy_context(auth, TRUE);
583 			}
584 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
585 			return (FALSE);
586 		}
587 
588 		verf.oa_flavor = RPCSEC_GSS;
589 		verf.oa_base = checksum.value;
590 		verf.oa_length = checksum.length;
591 
592 		xdr_stat = xdr_opaque_auth(xdrs, &verf);
593 		gss_release_buffer(&min_stat, &checksum);
594 		if (!xdr_stat) {
595 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
596 			return (FALSE);
597 		}
598 		if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
599 		    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
600 			xdrmbuf_append(xdrs, args);
601 			return (TRUE);
602 		} else {
603 			if (!xdr_rpc_gss_wrap_data(&args,
604 				gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
605 				seq))
606 				return (FALSE);
607 			xdrmbuf_append(xdrs, args);
608 			return (TRUE);
609 		}
610 	}
611 
612 	return (TRUE);
613 }
614 
615 static bool_t
616 rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
617     struct mbuf **resultsp)
618 {
619 	struct rpc_gss_data	*gd;
620 	struct rpc_pending_request *pr, *npr;
621 	struct rpc_pending_request_list reqs;
622 	gss_qop_t		qop_state;
623 	uint32_t		num, seq;
624 	gss_buffer_desc		signbuf, checksum;
625 	OM_uint32		maj_stat, min_stat;
626 
627 	rpc_gss_log_debug("in rpc_gss_validate()");
628 
629 	gd = AUTH_PRIVATE(auth);
630 
631 	/*
632 	 * The client will call us with a NULL verf when it gives up
633 	 * on an XID.
634 	 */
635 	if (!verf) {
636 		rpc_gss_purge_xid(gd, xid);
637 		return (TRUE);
638 	}
639 
640 	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
641 		/*
642 		 * Save the on the wire verifier to validate last INIT
643 		 * phase packet after decode if the major status is
644 		 * GSS_S_COMPLETE.
645 		 */
646 		if (gd->gd_verf.value)
647 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
648 			    (char *) &gd->gd_verf);
649 		gd->gd_verf.value = mem_alloc(verf->oa_length);
650 		if (gd->gd_verf.value == NULL) {
651 			printf("gss_validate: out of memory\n");
652 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
653 			m_freem(*resultsp);
654 			*resultsp = NULL;
655 			return (FALSE);
656 		}
657 		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
658 		gd->gd_verf.length = verf->oa_length;
659 
660 		return (TRUE);
661 	}
662 
663 	/*
664 	 * We need to check the verifier against all the requests
665 	 * we've send for this XID - for unreliable protocols, we
666 	 * retransmit with the same XID but different sequence
667 	 * number. We temporarily take this set of requests out of the
668 	 * list so that we can work through the list without having to
669 	 * hold the lock.
670 	 */
671 	mtx_lock(&gd->gd_lock);
672 	LIST_INIT(&reqs);
673 	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
674 		if (pr->pr_xid == xid) {
675 			LIST_REMOVE(pr, pr_link);
676 			LIST_INSERT_HEAD(&reqs, pr, pr_link);
677 		}
678 	}
679 	mtx_unlock(&gd->gd_lock);
680 	LIST_FOREACH(pr, &reqs, pr_link) {
681 		if (pr->pr_xid == xid) {
682 			seq = pr->pr_seq;
683 			num = htonl(seq);
684 			signbuf.value = &num;
685 			signbuf.length = sizeof(num);
686 
687 			checksum.value = verf->oa_base;
688 			checksum.length = verf->oa_length;
689 
690 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
691 			    &signbuf, &checksum, &qop_state);
692 			if (maj_stat != GSS_S_COMPLETE
693 			    || qop_state != gd->gd_qop) {
694 				continue;
695 			}
696 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
697 				rpc_gss_destroy_context(auth, TRUE);
698 				break;
699 			}
700 			//rpc_gss_purge_reqs(gd, seq);
701 			LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
702 				mem_free(pr, sizeof(*pr));
703 
704 			if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
705 				return (TRUE);
706 			} else {
707 				if (!xdr_rpc_gss_unwrap_data(resultsp,
708 					gd->gd_ctx, gd->gd_qop,
709 					gd->gd_cred.gc_svc, seq)) {
710 					return (FALSE);
711 				}
712 			}
713 			return (TRUE);
714 		}
715 	}
716 
717 	/*
718 	 * We didn't match - put back any entries for this XID so that
719 	 * a future call to validate can retry.
720 	 */
721 	mtx_lock(&gd->gd_lock);
722 	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
723 		LIST_REMOVE(pr, pr_link);
724 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
725 	}
726 	mtx_unlock(&gd->gd_lock);
727 
728 	/*
729 	 * Nothing matches - give up.
730 	 */
731 	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
732 	m_freem(*resultsp);
733 	*resultsp = NULL;
734 	return (FALSE);
735 }
736 
737 static bool_t
738 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
739 {
740 	struct thread		*td = curthread;
741 	struct ucred		*crsave;
742 	struct rpc_gss_data	*gd;
743 	struct rpc_gss_init_res	 gr;
744 	gss_buffer_desc		principal_desc;
745 	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
746 	gss_name_t		name;
747 	OM_uint32		 maj_stat, min_stat, call_stat;
748 	const char		*mech;
749 	struct rpc_callextra	 ext;
750 	gss_OID			mech_oid;
751 	gss_OID_set		mechlist;
752 
753 	rpc_gss_log_debug("in rpc_gss_refresh()");
754 
755 	gd = AUTH_PRIVATE(auth);
756 
757 	mtx_lock(&gd->gd_lock);
758 	/*
759 	 * If the context isn't in START state, someone else is
760 	 * refreshing - we wait till they are done. If they fail, they
761 	 * will put the state back to START and we can try (most
762 	 * likely to also fail).
763 	 */
764 	while (gd->gd_state != RPCSEC_GSS_START
765 	    && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
766 		msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
767 	}
768 	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
769 		mtx_unlock(&gd->gd_lock);
770 		return (TRUE);
771 	}
772 	gd->gd_state = RPCSEC_GSS_CONTEXT;
773 	mtx_unlock(&gd->gd_lock);
774 
775 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
776 	gd->gd_cred.gc_seq = 0;
777 
778 	/*
779 	 * For KerberosV, if there is a client principal name, that implies
780 	 * that this is a host based initiator credential in the default
781 	 * keytab file. For this case, it is necessary to do a
782 	 * gss_acquire_cred(). When this is done, the gssd daemon will
783 	 * do the equivalent of "kinit -k" to put a TGT for the name in
784 	 * the credential cache file for the gssd daemon.
785 	 */
786 	if (gd->gd_clntprincipal != NULL &&
787 	    rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
788 	    gd->gd_mech == mech_oid) {
789 		/* Get rid of any old credential. */
790 		if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
791 			gss_release_cred(&min_stat, &gd->gd_options.my_cred);
792 			gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
793 		}
794 
795 		/*
796 		 * The mechanism must be set to KerberosV for acquisition
797 		 * of credentials to work reliably.
798 		 */
799 		maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
800 		if (maj_stat != GSS_S_COMPLETE) {
801 			options_ret->major_status = maj_stat;
802 			options_ret->minor_status = min_stat;
803 			goto out;
804 		}
805 		maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
806 		    &mechlist);
807 		if (maj_stat != GSS_S_COMPLETE) {
808 			options_ret->major_status = maj_stat;
809 			options_ret->minor_status = min_stat;
810 			gss_release_oid_set(&min_stat, &mechlist);
811 			goto out;
812 		}
813 
814 		principal_desc.value = (void *)gd->gd_clntprincipal;
815 		principal_desc.length = strlen(gd->gd_clntprincipal);
816 		maj_stat = gss_import_name(&min_stat, &principal_desc,
817 		    GSS_C_NT_HOSTBASED_SERVICE, &name);
818 		if (maj_stat != GSS_S_COMPLETE) {
819 			options_ret->major_status = maj_stat;
820 			options_ret->minor_status = min_stat;
821 			gss_release_oid_set(&min_stat, &mechlist);
822 			goto out;
823 		}
824 		/* Acquire the credentials. */
825 		maj_stat = gss_acquire_cred(&min_stat, name, 0,
826 		    mechlist, GSS_C_INITIATE,
827 		    &gd->gd_options.my_cred, NULL, NULL);
828 		gss_release_name(&min_stat, &name);
829 		gss_release_oid_set(&min_stat, &mechlist);
830 		if (maj_stat != GSS_S_COMPLETE) {
831 			options_ret->major_status = maj_stat;
832 			options_ret->minor_status = min_stat;
833 			goto out;
834 		}
835 	}
836 
837 	principal_desc.value = (void *)gd->gd_principal;
838 	principal_desc.length = strlen(gd->gd_principal);
839 	maj_stat = gss_import_name(&min_stat, &principal_desc,
840 	    GSS_C_NT_HOSTBASED_SERVICE, &name);
841 	if (maj_stat != GSS_S_COMPLETE) {
842 		options_ret->major_status = maj_stat;
843 		options_ret->minor_status = min_stat;
844 		goto out;
845 	}
846 
847 	/* GSS context establishment loop. */
848 	memset(&recv_token, 0, sizeof(recv_token));
849 	memset(&gr, 0, sizeof(gr));
850 	memset(options_ret, 0, sizeof(*options_ret));
851 	options_ret->major_status = GSS_S_FAILURE;
852 	recv_tokenp = GSS_C_NO_BUFFER;
853 
854 	for (;;) {
855 		crsave = td->td_ucred;
856 		td->td_ucred = gd->gd_ucred;
857 		maj_stat = gss_init_sec_context(&min_stat,
858 		    gd->gd_options.my_cred,
859 		    &gd->gd_ctx,
860 		    name,
861 		    gd->gd_mech,
862 		    gd->gd_options.req_flags,
863 		    gd->gd_options.time_req,
864 		    gd->gd_options.input_channel_bindings,
865 		    recv_tokenp,
866 		    &gd->gd_mech,	/* used mech */
867 		    &send_token,
868 		    &options_ret->ret_flags,
869 		    &options_ret->time_req);
870 		td->td_ucred = crsave;
871 
872 		/*
873 		 * Free the token which we got from the server (if
874 		 * any).  Remember that this was allocated by XDR, not
875 		 * GSS-API.
876 		 */
877 		if (recv_tokenp != GSS_C_NO_BUFFER) {
878 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
879 			    (char *) &recv_token);
880 			recv_tokenp = GSS_C_NO_BUFFER;
881 		}
882 		if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
883 			strlcpy(options_ret->actual_mechanism,
884 			    mech,
885 			    sizeof(options_ret->actual_mechanism));
886 		}
887 		if (maj_stat != GSS_S_COMPLETE &&
888 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
889 			rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
890 			    maj_stat, min_stat);
891 			options_ret->major_status = maj_stat;
892 			options_ret->minor_status = min_stat;
893 			break;
894 		}
895 		if (send_token.length != 0) {
896 			memset(&gr, 0, sizeof(gr));
897 
898 			bzero(&ext, sizeof(ext));
899 			ext.rc_auth = auth;
900 			call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
901 			    (xdrproc_t)xdr_gss_buffer_desc,
902 			    &send_token,
903 			    (xdrproc_t)xdr_rpc_gss_init_res,
904 			    (caddr_t)&gr, AUTH_TIMEOUT);
905 
906 			gss_release_buffer(&min_stat, &send_token);
907 
908 			if (call_stat != RPC_SUCCESS)
909 				break;
910 
911 			if (gr.gr_major != GSS_S_COMPLETE &&
912 			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
913 				rpc_gss_log_status("server reply", gd->gd_mech,
914 				    gr.gr_major, gr.gr_minor);
915 				options_ret->major_status = gr.gr_major;
916 				options_ret->minor_status = gr.gr_minor;
917 				break;
918 			}
919 
920 			/*
921 			 * Save the server's gr_handle value, freeing
922 			 * what we have already (remember that this
923 			 * was allocated by XDR, not GSS-API).
924 			 */
925 			if (gr.gr_handle.length != 0) {
926 				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
927 				    (char *) &gd->gd_cred.gc_handle);
928 				gd->gd_cred.gc_handle = gr.gr_handle;
929 			}
930 
931 			/*
932 			 * Save the server's token as well.
933 			 */
934 			if (gr.gr_token.length != 0) {
935 				recv_token = gr.gr_token;
936 				recv_tokenp = &recv_token;
937 			}
938 
939 			/*
940 			 * Since we have copied out all the bits of gr
941 			 * which XDR allocated for us, we don't need
942 			 * to free it.
943 			 */
944 			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
945 		}
946 
947 		if (maj_stat == GSS_S_COMPLETE) {
948 			gss_buffer_desc   bufin;
949 			u_int seq, qop_state = 0;
950 
951 			/*
952 			 * gss header verifier,
953 			 * usually checked in gss_validate
954 			 */
955 			seq = htonl(gr.gr_win);
956 			bufin.value = (unsigned char *)&seq;
957 			bufin.length = sizeof(seq);
958 
959 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
960 			    &bufin, &gd->gd_verf, &qop_state);
961 
962 			if (maj_stat != GSS_S_COMPLETE ||
963 			    qop_state != gd->gd_qop) {
964 				rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
965 				    maj_stat, min_stat);
966 				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
967 					rpc_gss_destroy_context(auth, TRUE);
968 				}
969 				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
970 				    EPERM);
971 				options_ret->major_status = maj_stat;
972 				options_ret->minor_status = min_stat;
973 				break;
974 			}
975 
976 			options_ret->major_status = GSS_S_COMPLETE;
977 			options_ret->minor_status = 0;
978 			options_ret->rpcsec_version = gd->gd_cred.gc_version;
979 			options_ret->gss_context = gd->gd_ctx;
980 
981 			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
982 			gd->gd_seq = 1;
983 			gd->gd_win = gr.gr_win;
984 			break;
985 		}
986 	}
987 
988 	gss_release_name(&min_stat, &name);
989 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
990 	    (char *) &gd->gd_verf);
991 
992 out:
993 	/* End context negotiation loop. */
994 	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
995 		rpc_createerr.cf_stat = RPC_AUTHERROR;
996 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
997 		if (gd->gd_ctx) {
998 			gss_delete_sec_context(&min_stat, &gd->gd_ctx,
999 				GSS_C_NO_BUFFER);
1000 		}
1001 		mtx_lock(&gd->gd_lock);
1002 		gd->gd_state = RPCSEC_GSS_START;
1003 		wakeup(gd);
1004 		mtx_unlock(&gd->gd_lock);
1005 		return (FALSE);
1006 	}
1007 
1008 	mtx_lock(&gd->gd_lock);
1009 	gd->gd_state = RPCSEC_GSS_ESTABLISHED;
1010 	wakeup(gd);
1011 	mtx_unlock(&gd->gd_lock);
1012 
1013 	return (TRUE);
1014 }
1015 
1016 static bool_t
1017 rpc_gss_refresh(AUTH *auth, void *msg)
1018 {
1019 	struct rpc_msg *reply = (struct rpc_msg *) msg;
1020 	rpc_gss_options_ret_t options;
1021 	struct rpc_gss_data *gd;
1022 
1023 	gd = AUTH_PRIVATE(auth);
1024 
1025 	/*
1026 	 * If the context is in DESTROYING state, then just return, since
1027 	 * there is no point in refreshing the credentials.
1028 	 */
1029 	mtx_lock(&gd->gd_lock);
1030 	if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
1031 		mtx_unlock(&gd->gd_lock);
1032 		return (FALSE);
1033 	}
1034 	mtx_unlock(&gd->gd_lock);
1035 
1036 	/*
1037 	 * If the error was RPCSEC_GSS_CREDPROBLEM of
1038 	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
1039 	 * other errors are fatal.
1040 	 */
1041 	if (reply->rm_reply.rp_stat == MSG_DENIED
1042 	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
1043 	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
1044 		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
1045 		rpc_gss_destroy_context(auth, FALSE);
1046 		memset(&options, 0, sizeof(options));
1047 		return (rpc_gss_init(auth, &options));
1048 	}
1049 
1050 	return (FALSE);
1051 }
1052 
1053 static void
1054 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
1055 {
1056 	struct rpc_gss_data	*gd;
1057 	struct rpc_pending_request *pr;
1058 	OM_uint32		 min_stat;
1059 	struct rpc_callextra	 ext;
1060 
1061 	rpc_gss_log_debug("in rpc_gss_destroy_context()");
1062 
1063 	gd = AUTH_PRIVATE(auth);
1064 
1065 	mtx_lock(&gd->gd_lock);
1066 	/*
1067 	 * If the context isn't in ESTABISHED state, someone else is
1068 	 * destroying/refreshing - we wait till they are done.
1069 	 */
1070 	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
1071 		while (gd->gd_state != RPCSEC_GSS_START
1072 		    && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
1073 			msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
1074 		mtx_unlock(&gd->gd_lock);
1075 		return;
1076 	}
1077 	gd->gd_state = RPCSEC_GSS_DESTROYING;
1078 	mtx_unlock(&gd->gd_lock);
1079 
1080 	if (send_destroy) {
1081 		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
1082 		bzero(&ext, sizeof(ext));
1083 		ext.rc_auth = auth;
1084 		CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
1085 		    (xdrproc_t)xdr_void, NULL,
1086 		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1087 	}
1088 
1089 	while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1090 		LIST_REMOVE(pr, pr_link);
1091 		mem_free(pr, sizeof(*pr));
1092 	}
1093 
1094 	/*
1095 	 * Free the context token. Remember that this was
1096 	 * allocated by XDR, not GSS-API.
1097 	 */
1098 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1099 	    (char *) &gd->gd_cred.gc_handle);
1100 	gd->gd_cred.gc_handle.length = 0;
1101 
1102 	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1103 		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1104 
1105 	mtx_lock(&gd->gd_lock);
1106 	gd->gd_state = RPCSEC_GSS_START;
1107 	wakeup(gd);
1108 	mtx_unlock(&gd->gd_lock);
1109 }
1110 
1111 static void
1112 rpc_gss_destroy(AUTH *auth)
1113 {
1114 	struct rpc_gss_data	*gd;
1115 
1116 	rpc_gss_log_debug("in rpc_gss_destroy()");
1117 
1118 	gd = AUTH_PRIVATE(auth);
1119 
1120 	if (!refcount_release(&gd->gd_refs))
1121 		return;
1122 
1123 	rpc_gss_destroy_context(auth, TRUE);
1124 
1125 	CLNT_RELEASE(gd->gd_clnt);
1126 	crfree(gd->gd_ucred);
1127 	free(gd->gd_principal, M_RPC);
1128 	if (gd->gd_clntprincipal != NULL)
1129 		free(gd->gd_clntprincipal, M_RPC);
1130 	if (gd->gd_verf.value)
1131 		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1132 		    (char *) &gd->gd_verf);
1133 	mtx_destroy(&gd->gd_lock);
1134 
1135 	mem_free(gd, sizeof(*gd));
1136 	mem_free(auth, sizeof(*auth));
1137 }
1138 
1139 int
1140 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1141 {
1142 	struct rpc_gss_data	*gd;
1143 	int			want_conf;
1144 	OM_uint32		max;
1145 	OM_uint32		maj_stat, min_stat;
1146 	int			result;
1147 
1148 	gd = AUTH_PRIVATE(auth);
1149 
1150 	switch (gd->gd_cred.gc_svc) {
1151 	case rpc_gss_svc_none:
1152 		return (max_tp_unit_len);
1153 		break;
1154 
1155 	case rpc_gss_svc_default:
1156 	case rpc_gss_svc_integrity:
1157 		want_conf = FALSE;
1158 		break;
1159 
1160 	case rpc_gss_svc_privacy:
1161 		want_conf = TRUE;
1162 		break;
1163 
1164 	default:
1165 		return (0);
1166 	}
1167 
1168 	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1169 	    gd->gd_qop, max_tp_unit_len, &max);
1170 
1171 	if (maj_stat == GSS_S_COMPLETE) {
1172 		result = (int) max;
1173 		if (result < 0)
1174 			result = 0;
1175 		return (result);
1176 	} else {
1177 		rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1178 		    maj_stat, min_stat);
1179 		return (0);
1180 	}
1181 }
1182