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