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