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