xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision 29fc4075e69fd27de0cded313ac6000165d99f8b)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright (c) 1990 The Regents of the University of California.
4  *
5  * Copyright (c) 2008 Doug Rabson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30   svc_rpcsec_gss.c
31 
32   Copyright (c) 2000 The Regents of the University of Michigan.
33   All rights reserved.
34 
35   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
36   All rights reserved, all wrongs reversed.
37 
38   Redistribution and use in source and binary forms, with or without
39   modification, are permitted provided that the following conditions
40   are met:
41 
42   1. Redistributions of source code must retain the above copyright
43      notice, this list of conditions and the following disclaimer.
44   2. Redistributions in binary form must reproduce the above copyright
45      notice, this list of conditions and the following disclaimer in the
46      documentation and/or other materials provided with the distribution.
47   3. Neither the name of the University nor the names of its
48      contributors may be used to endorse or promote products derived
49      from this software without specific prior written permission.
50 
51   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
52   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
58   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
59   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
60   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
61   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 
63   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64  */
65 
66 #include <sys/cdefs.h>
67 __FBSDID("$FreeBSD$");
68 
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/jail.h>
72 #include <sys/kernel.h>
73 #include <sys/kobj.h>
74 #include <sys/lock.h>
75 #include <sys/malloc.h>
76 #include <sys/mbuf.h>
77 #include <sys/mutex.h>
78 #include <sys/proc.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 bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
88 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
89 static void     svc_rpc_gss_release(SVCAUTH *);
90 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
91 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
92 
93 static const struct svc_auth_ops svc_auth_gss_ops = {
94 	.svc_ah_wrap =		svc_rpc_gss_wrap,
95 	.svc_ah_unwrap =	svc_rpc_gss_unwrap,
96 	.svc_ah_release =	svc_rpc_gss_release,
97 };
98 
99 struct sx svc_rpc_gss_lock;
100 
101 struct svc_rpc_gss_callback {
102 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
103 	rpc_gss_callback_t	cb_callback;
104 };
105 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
106 	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
107 
108 struct svc_rpc_gss_svc_name {
109 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
110 	char			*sn_principal;
111 	gss_OID			sn_mech;
112 	u_int			sn_req_time;
113 	gss_cred_id_t		sn_cred;
114 	u_int			sn_program;
115 	u_int			sn_version;
116 };
117 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
118 	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
119 
120 enum svc_rpc_gss_client_state {
121 	CLIENT_NEW,				/* still authenticating */
122 	CLIENT_ESTABLISHED,			/* context established */
123 	CLIENT_STALE				/* garbage to collect */
124 };
125 
126 #define SVC_RPC_GSS_SEQWINDOW	128
127 
128 struct svc_rpc_gss_clientid {
129 	unsigned long		ci_hostid;
130 	uint32_t		ci_boottime;
131 	uint32_t		ci_id;
132 };
133 
134 struct svc_rpc_gss_client {
135 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
136 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
137 	volatile u_int		cl_refs;
138 	struct sx		cl_lock;
139 	struct svc_rpc_gss_clientid cl_id;
140 	time_t			cl_expiration;	/* when to gc */
141 	enum svc_rpc_gss_client_state cl_state;	/* client state */
142 	bool_t			cl_locked;	/* fixed service+qop */
143 	gss_ctx_id_t		cl_ctx;		/* context id */
144 	gss_cred_id_t		cl_creds;	/* delegated creds */
145 	gss_name_t		cl_cname;	/* client name */
146 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
147 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
148 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
149 	struct ucred		*cl_cred;	/* kernel-style credentials */
150 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
151 	bool_t			cl_done_callback; /* TRUE after call */
152 	void			*cl_cookie;	/* user cookie from callback */
153 	gid_t			cl_gid_storage[NGROUPS];
154 	gss_OID			cl_mech;	/* mechanism */
155 	gss_qop_t		cl_qop;		/* quality of protection */
156 	uint32_t		cl_seqlast;	/* sequence window origin */
157 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
158 };
159 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
160 
161 /*
162  * This structure holds enough information to unwrap arguments or wrap
163  * results for a given request. We use the rq_clntcred area for this
164  * (which is a per-request buffer).
165  */
166 struct svc_rpc_gss_cookedcred {
167 	struct svc_rpc_gss_client *cc_client;
168 	rpc_gss_service_t	cc_service;
169 	uint32_t		cc_seq;
170 };
171 
172 #define CLIENT_HASH_SIZE	256
173 #define CLIENT_MAX		1024
174 u_int svc_rpc_gss_client_max = CLIENT_MAX;
175 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
176 
177 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
178     "RPC");
179 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
180     "GSS");
181 
182 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
183     &svc_rpc_gss_client_max, 0,
184     "Max number of rpc-gss clients");
185 
186 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
187     &svc_rpc_gss_client_hash_size, 0,
188     "Size of rpc-gss client hash table");
189 
190 static u_int svc_rpc_gss_lifetime_max = 0;
191 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
192     &svc_rpc_gss_lifetime_max, 0,
193     "Maximum lifetime (seconds) of rpc-gss clients");
194 
195 static u_int svc_rpc_gss_client_count;
196 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
197     &svc_rpc_gss_client_count, 0,
198     "Number of rpc-gss clients");
199 
200 struct svc_rpc_gss_client_list *svc_rpc_gss_client_hash;
201 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
202 static uint32_t svc_rpc_gss_next_clientid = 1;
203 
204 static void
205 svc_rpc_gss_init(void *arg)
206 {
207 	int i;
208 
209 	svc_rpc_gss_client_hash = mem_alloc(sizeof(struct svc_rpc_gss_client_list) * svc_rpc_gss_client_hash_size);
210 	for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
211 		TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
212 	TAILQ_INIT(&svc_rpc_gss_clients);
213 	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
214 	sx_init(&svc_rpc_gss_lock, "gsslock");
215 }
216 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
217 
218 bool_t
219 rpc_gss_set_callback(rpc_gss_callback_t *cb)
220 {
221 	struct svc_rpc_gss_callback *scb;
222 
223 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
224 	if (!scb) {
225 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
226 		return (FALSE);
227 	}
228 	scb->cb_callback = *cb;
229 	sx_xlock(&svc_rpc_gss_lock);
230 	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
231 	sx_xunlock(&svc_rpc_gss_lock);
232 
233 	return (TRUE);
234 }
235 
236 void
237 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
238 {
239 	struct svc_rpc_gss_callback *scb;
240 
241 	sx_xlock(&svc_rpc_gss_lock);
242 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
243 		if (scb->cb_callback.program == cb->program
244 		    && scb->cb_callback.version == cb->version
245 		    && scb->cb_callback.callback == cb->callback) {
246 			SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
247 			    svc_rpc_gss_callback, cb_link);
248 			sx_xunlock(&svc_rpc_gss_lock);
249 			mem_free(scb, sizeof(*scb));
250 			return;
251 		}
252 	}
253 	sx_xunlock(&svc_rpc_gss_lock);
254 }
255 
256 static bool_t
257 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
258 {
259 	OM_uint32		maj_stat, min_stat;
260 	gss_buffer_desc		namebuf;
261 	gss_name_t		name;
262 	gss_OID_set_desc	oid_set;
263 
264 	oid_set.count = 1;
265 	oid_set.elements = sname->sn_mech;
266 
267 	namebuf.value = (void *) sname->sn_principal;
268 	namebuf.length = strlen(sname->sn_principal);
269 
270 	maj_stat = gss_import_name(&min_stat, &namebuf,
271 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
272 	if (maj_stat != GSS_S_COMPLETE)
273 		return (FALSE);
274 
275 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
276 		gss_release_cred(&min_stat, &sname->sn_cred);
277 
278 	maj_stat = gss_acquire_cred(&min_stat, name,
279 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
280 	    NULL, NULL);
281 	if (maj_stat != GSS_S_COMPLETE) {
282 		gss_release_name(&min_stat, &name);
283 		return (FALSE);
284 	}
285 	gss_release_name(&min_stat, &name);
286 
287 	return (TRUE);
288 }
289 
290 bool_t
291 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
292     u_int req_time, u_int program, u_int version)
293 {
294 	struct svc_rpc_gss_svc_name *sname;
295 	gss_OID			mech_oid;
296 
297 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
298 		return (FALSE);
299 
300 	sname = mem_alloc(sizeof(*sname));
301 	if (!sname)
302 		return (FALSE);
303 	sname->sn_principal = strdup(principal, M_RPC);
304 	sname->sn_mech = mech_oid;
305 	sname->sn_req_time = req_time;
306 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
307 	sname->sn_program = program;
308 	sname->sn_version = version;
309 
310 	if (!rpc_gss_acquire_svc_cred(sname)) {
311 		free(sname->sn_principal, M_RPC);
312 		mem_free(sname, sizeof(*sname));
313 		return (FALSE);
314 	}
315 
316 	sx_xlock(&svc_rpc_gss_lock);
317 	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
318 	sx_xunlock(&svc_rpc_gss_lock);
319 
320 	return (TRUE);
321 }
322 
323 void
324 rpc_gss_clear_svc_name(u_int program, u_int version)
325 {
326 	OM_uint32		min_stat;
327 	struct svc_rpc_gss_svc_name *sname;
328 
329 	sx_xlock(&svc_rpc_gss_lock);
330 	SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
331 		if (sname->sn_program == program
332 		    && sname->sn_version == version) {
333 			SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
334 			    svc_rpc_gss_svc_name, sn_link);
335 			sx_xunlock(&svc_rpc_gss_lock);
336 			gss_release_cred(&min_stat, &sname->sn_cred);
337 			free(sname->sn_principal, M_RPC);
338 			mem_free(sname, sizeof(*sname));
339 			return;
340 		}
341 	}
342 	sx_xunlock(&svc_rpc_gss_lock);
343 }
344 
345 bool_t
346 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
347     const char *mech, const char *name, const char *node, const char *domain)
348 {
349 	OM_uint32		maj_stat, min_stat;
350 	gss_OID			mech_oid;
351 	size_t			namelen;
352 	gss_buffer_desc		buf;
353 	gss_name_t		gss_name, gss_mech_name;
354 	rpc_gss_principal_t	result;
355 
356 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
357 		return (FALSE);
358 
359 	/*
360 	 * Construct a gss_buffer containing the full name formatted
361 	 * as "name/node@domain" where node and domain are optional.
362 	 */
363 	namelen = strlen(name) + 1;
364 	if (node) {
365 		namelen += strlen(node) + 1;
366 	}
367 	if (domain) {
368 		namelen += strlen(domain) + 1;
369 	}
370 
371 	buf.value = mem_alloc(namelen);
372 	buf.length = namelen;
373 	strcpy((char *) buf.value, name);
374 	if (node) {
375 		strcat((char *) buf.value, "/");
376 		strcat((char *) buf.value, node);
377 	}
378 	if (domain) {
379 		strcat((char *) buf.value, "@");
380 		strcat((char *) buf.value, domain);
381 	}
382 
383 	/*
384 	 * Convert that to a gss_name_t and then convert that to a
385 	 * mechanism name in the selected mechanism.
386 	 */
387 	maj_stat = gss_import_name(&min_stat, &buf,
388 	    GSS_C_NT_USER_NAME, &gss_name);
389 	mem_free(buf.value, buf.length);
390 	if (maj_stat != GSS_S_COMPLETE) {
391 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
392 		return (FALSE);
393 	}
394 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
395 	    &gss_mech_name);
396 	if (maj_stat != GSS_S_COMPLETE) {
397 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
398 		    min_stat);
399 		gss_release_name(&min_stat, &gss_name);
400 		return (FALSE);
401 	}
402 	gss_release_name(&min_stat, &gss_name);
403 
404 	/*
405 	 * Export the mechanism name and use that to construct the
406 	 * rpc_gss_principal_t result.
407 	 */
408 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
409 	if (maj_stat != GSS_S_COMPLETE) {
410 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
411 		gss_release_name(&min_stat, &gss_mech_name);
412 		return (FALSE);
413 	}
414 	gss_release_name(&min_stat, &gss_mech_name);
415 
416 	result = mem_alloc(sizeof(int) + buf.length);
417 	if (!result) {
418 		gss_release_buffer(&min_stat, &buf);
419 		return (FALSE);
420 	}
421 	result->len = buf.length;
422 	memcpy(result->name, buf.value, buf.length);
423 	gss_release_buffer(&min_stat, &buf);
424 
425 	*principal = result;
426 	return (TRUE);
427 }
428 
429 bool_t
430 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
431     rpc_gss_ucred_t **ucred, void **cookie)
432 {
433 	struct svc_rpc_gss_cookedcred *cc;
434 	struct svc_rpc_gss_client *client;
435 
436 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
437 		return (FALSE);
438 
439 	cc = req->rq_clntcred;
440 	client = cc->cc_client;
441 	if (rcred)
442 		*rcred = &client->cl_rawcred;
443 	if (ucred)
444 		*ucred = &client->cl_ucred;
445 	if (cookie)
446 		*cookie = client->cl_cookie;
447 	return (TRUE);
448 }
449 
450 /*
451  * This simpler interface is used by svc_getcred to copy the cred data
452  * into a kernel cred structure.
453  */
454 static int
455 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
456 {
457 	struct ucred *cr;
458 	struct svc_rpc_gss_cookedcred *cc;
459 	struct svc_rpc_gss_client *client;
460 	rpc_gss_ucred_t *uc;
461 
462 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
463 		return (FALSE);
464 
465 	cc = req->rq_clntcred;
466 	client = cc->cc_client;
467 
468 	if (flavorp)
469 		*flavorp = client->cl_rpcflavor;
470 
471 	if (client->cl_cred) {
472 		*crp = crhold(client->cl_cred);
473 		return (TRUE);
474 	}
475 
476 	uc = &client->cl_ucred;
477 	cr = client->cl_cred = crget();
478 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
479 	cr->cr_rgid = cr->cr_svgid = uc->gid;
480 	crsetgroups(cr, uc->gidlen, uc->gidlist);
481 	cr->cr_prison = &prison0;
482 	prison_hold(cr->cr_prison);
483 	*crp = crhold(cr);
484 
485 	return (TRUE);
486 }
487 
488 int
489 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
490 {
491 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
492 	struct svc_rpc_gss_client *client = cc->cc_client;
493 	int			want_conf;
494 	OM_uint32		max;
495 	OM_uint32		maj_stat, min_stat;
496 	int			result;
497 
498 	switch (client->cl_rawcred.service) {
499 	case rpc_gss_svc_none:
500 		return (max_tp_unit_len);
501 		break;
502 
503 	case rpc_gss_svc_default:
504 	case rpc_gss_svc_integrity:
505 		want_conf = FALSE;
506 		break;
507 
508 	case rpc_gss_svc_privacy:
509 		want_conf = TRUE;
510 		break;
511 
512 	default:
513 		return (0);
514 	}
515 
516 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
517 	    client->cl_qop, max_tp_unit_len, &max);
518 
519 	if (maj_stat == GSS_S_COMPLETE) {
520 		result = (int) max;
521 		if (result < 0)
522 			result = 0;
523 		return (result);
524 	} else {
525 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
526 		    maj_stat, min_stat);
527 		return (0);
528 	}
529 }
530 
531 static struct svc_rpc_gss_client *
532 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
533 {
534 	struct svc_rpc_gss_client *client;
535 	struct svc_rpc_gss_client_list *list;
536 	struct timeval boottime;
537 	unsigned long hostid;
538 
539 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
540 
541 	getcredhostid(curthread->td_ucred, &hostid);
542 	getboottime(&boottime);
543 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
544 		return (NULL);
545 
546 	list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size];
547 	sx_xlock(&svc_rpc_gss_lock);
548 	TAILQ_FOREACH(client, list, cl_link) {
549 		if (client->cl_id.ci_id == id->ci_id) {
550 			/*
551 			 * Move this client to the front of the LRU
552 			 * list.
553 			 */
554 			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
555 			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
556 			    cl_alllink);
557 			refcount_acquire(&client->cl_refs);
558 			break;
559 		}
560 	}
561 	sx_xunlock(&svc_rpc_gss_lock);
562 
563 	return (client);
564 }
565 
566 static struct svc_rpc_gss_client *
567 svc_rpc_gss_create_client(void)
568 {
569 	struct svc_rpc_gss_client *client;
570 	struct svc_rpc_gss_client_list *list;
571 	struct timeval boottime;
572 	unsigned long hostid;
573 
574 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
575 
576 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
577 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
578 
579 	/*
580 	 * Set the initial value of cl_refs to two.  One for the caller
581 	 * and the other to hold onto the client structure until it expires.
582 	 */
583 	refcount_init(&client->cl_refs, 2);
584 	sx_init(&client->cl_lock, "GSS-client");
585 	getcredhostid(curthread->td_ucred, &hostid);
586 	client->cl_id.ci_hostid = hostid;
587 	getboottime(&boottime);
588 	client->cl_id.ci_boottime = boottime.tv_sec;
589 	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
590 
591 	/*
592 	 * Start the client off with a short expiration time. We will
593 	 * try to get a saner value from the client creds later.
594 	 */
595 	client->cl_state = CLIENT_NEW;
596 	client->cl_locked = FALSE;
597 	client->cl_expiration = time_uptime + 5*60;
598 
599 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
600 	sx_xlock(&svc_rpc_gss_lock);
601 	TAILQ_INSERT_HEAD(list, client, cl_link);
602 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
603 	svc_rpc_gss_client_count++;
604 	sx_xunlock(&svc_rpc_gss_lock);
605 	return (client);
606 }
607 
608 static void
609 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
610 {
611 	OM_uint32 min_stat;
612 
613 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
614 
615 	if (client->cl_ctx)
616 		gss_delete_sec_context(&min_stat,
617 		    &client->cl_ctx, GSS_C_NO_BUFFER);
618 
619 	if (client->cl_cname)
620 		gss_release_name(&min_stat, &client->cl_cname);
621 
622 	if (client->cl_rawcred.client_principal)
623 		mem_free(client->cl_rawcred.client_principal,
624 		    sizeof(*client->cl_rawcred.client_principal)
625 		    + client->cl_rawcred.client_principal->len);
626 
627 	if (client->cl_cred)
628 		crfree(client->cl_cred);
629 
630 	sx_destroy(&client->cl_lock);
631 	mem_free(client, sizeof(*client));
632 }
633 
634 /*
635  * Drop a reference to a client and free it if that was the last reference.
636  */
637 static void
638 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
639 {
640 
641 	if (!refcount_release(&client->cl_refs))
642 		return;
643 	svc_rpc_gss_destroy_client(client);
644 }
645 
646 /*
647  * Remove a client from our global lists.
648  * Must be called with svc_rpc_gss_lock held.
649  */
650 static void
651 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
652 {
653 	struct svc_rpc_gss_client_list *list;
654 
655 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
656 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
657 	TAILQ_REMOVE(list, client, cl_link);
658 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
659 	svc_rpc_gss_client_count--;
660 }
661 
662 /*
663  * Remove a client from our global lists and free it if we can.
664  */
665 static void
666 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
667 {
668 	struct svc_rpc_gss_client_list *list;
669 	struct svc_rpc_gss_client *tclient;
670 
671 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
672 	sx_xlock(&svc_rpc_gss_lock);
673 	TAILQ_FOREACH(tclient, list, cl_link) {
674 		/*
675 		 * Make sure this client has not already been removed
676 		 * from the lists by svc_rpc_gss_forget_client() or
677 		 * svc_rpc_gss_forget_client_locked().
678 		 */
679 		if (client == tclient) {
680 			svc_rpc_gss_forget_client_locked(client);
681 			sx_xunlock(&svc_rpc_gss_lock);
682 			svc_rpc_gss_release_client(client);
683 			return;
684 		}
685 	}
686 	sx_xunlock(&svc_rpc_gss_lock);
687 }
688 
689 static void
690 svc_rpc_gss_timeout_clients(void)
691 {
692 	struct svc_rpc_gss_client *client;
693 	time_t now = time_uptime;
694 
695 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
696 
697 	/*
698 	 * First enforce the max client limit. We keep
699 	 * svc_rpc_gss_clients in LRU order.
700 	 */
701 	sx_xlock(&svc_rpc_gss_lock);
702 	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
703 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
704 		svc_rpc_gss_forget_client_locked(client);
705 		sx_xunlock(&svc_rpc_gss_lock);
706 		svc_rpc_gss_release_client(client);
707 		sx_xlock(&svc_rpc_gss_lock);
708 		client = TAILQ_LAST(&svc_rpc_gss_clients,
709 		    svc_rpc_gss_client_list);
710 	}
711 again:
712 	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
713 		if (client->cl_state == CLIENT_STALE
714 		    || now > client->cl_expiration) {
715 			svc_rpc_gss_forget_client_locked(client);
716 			sx_xunlock(&svc_rpc_gss_lock);
717 			rpc_gss_log_debug("expiring client %p", client);
718 			svc_rpc_gss_release_client(client);
719 			sx_xlock(&svc_rpc_gss_lock);
720 			goto again;
721 		}
722 	}
723 	sx_xunlock(&svc_rpc_gss_lock);
724 }
725 
726 #ifdef DEBUG
727 /*
728  * OID<->string routines.  These are uuuuugly.
729  */
730 static OM_uint32
731 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
732 {
733 	char		numstr[128];
734 	unsigned long	number;
735 	int		numshift;
736 	size_t		string_length;
737 	size_t		i;
738 	unsigned char	*cp;
739 	char		*bp;
740 
741 	/* Decoded according to krb5/gssapi_krb5.c */
742 
743 	/* First determine the size of the string */
744 	string_length = 0;
745 	number = 0;
746 	numshift = 0;
747 	cp = (unsigned char *) oid->elements;
748 	number = (unsigned long) cp[0];
749 	sprintf(numstr, "%ld ", number/40);
750 	string_length += strlen(numstr);
751 	sprintf(numstr, "%ld ", number%40);
752 	string_length += strlen(numstr);
753 	for (i=1; i<oid->length; i++) {
754 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
755 			number = (number << 7) | (cp[i] & 0x7f);
756 			numshift += 7;
757 		}
758 		else {
759 			*minor_status = 0;
760 			return(GSS_S_FAILURE);
761 		}
762 		if ((cp[i] & 0x80) == 0) {
763 			sprintf(numstr, "%ld ", number);
764 			string_length += strlen(numstr);
765 			number = 0;
766 			numshift = 0;
767 		}
768 	}
769 	/*
770 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
771 	 * here for "{ " and "}\0".
772 	 */
773 	string_length += 4;
774 	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
775 		strcpy(bp, "{ ");
776 		number = (unsigned long) cp[0];
777 		sprintf(numstr, "%ld ", number/40);
778 		strcat(bp, numstr);
779 		sprintf(numstr, "%ld ", number%40);
780 		strcat(bp, numstr);
781 		number = 0;
782 		cp = (unsigned char *) oid->elements;
783 		for (i=1; i<oid->length; i++) {
784 			number = (number << 7) | (cp[i] & 0x7f);
785 			if ((cp[i] & 0x80) == 0) {
786 				sprintf(numstr, "%ld ", number);
787 				strcat(bp, numstr);
788 				number = 0;
789 			}
790 		}
791 		strcat(bp, "}");
792 		oid_str->length = strlen(bp)+1;
793 		oid_str->value = (void *) bp;
794 		*minor_status = 0;
795 		return(GSS_S_COMPLETE);
796 	}
797 	*minor_status = 0;
798 	return(GSS_S_FAILURE);
799 }
800 #endif
801 
802 static void
803 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
804     const gss_name_t name)
805 {
806 	OM_uint32		maj_stat, min_stat;
807 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
808 	int			numgroups;
809 
810 	uc->uid = 65534;
811 	uc->gid = 65534;
812 	uc->gidlist = client->cl_gid_storage;
813 
814 	numgroups = NGROUPS;
815 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
816 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
817 	if (GSS_ERROR(maj_stat))
818 		uc->gidlen = 0;
819 	else
820 		uc->gidlen = numgroups;
821 }
822 
823 static void
824 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
825 {
826 	static gss_OID_desc krb5_mech_oid =
827 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
828 
829 	/*
830 	 * Attempt to translate mech type and service into a
831 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
832 	 */
833 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
834 		switch (client->cl_rawcred.service) {
835 		case rpc_gss_svc_default:
836 		case rpc_gss_svc_none:
837 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
838 			break;
839 		case rpc_gss_svc_integrity:
840 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
841 			break;
842 		case rpc_gss_svc_privacy:
843 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
844 			break;
845 		}
846 	} else {
847 		client->cl_rpcflavor = RPCSEC_GSS;
848 	}
849 }
850 
851 static bool_t
852 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
853 			       struct svc_req *rqst,
854 			       struct rpc_gss_init_res *gr,
855 			       struct rpc_gss_cred *gc)
856 {
857 	gss_buffer_desc		recv_tok;
858 	gss_OID			mech;
859 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
860 	OM_uint32		cred_lifetime;
861 	struct svc_rpc_gss_svc_name *sname;
862 
863 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
864 
865 	/* Deserialize arguments. */
866 	memset(&recv_tok, 0, sizeof(recv_tok));
867 
868 	if (!svc_getargs(rqst,
869 		(xdrproc_t) xdr_gss_buffer_desc,
870 		(caddr_t) &recv_tok)) {
871 		client->cl_state = CLIENT_STALE;
872 		return (FALSE);
873 	}
874 
875 	/*
876 	 * First time round, try all the server names we have until
877 	 * one matches. Afterwards, stick with that one.
878 	 */
879 	sx_xlock(&svc_rpc_gss_lock);
880 	if (!client->cl_sname) {
881 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
882 			if (sname->sn_program == rqst->rq_prog
883 			    && sname->sn_version == rqst->rq_vers) {
884 			retry:
885 				gr->gr_major = gss_accept_sec_context(
886 					&gr->gr_minor,
887 					&client->cl_ctx,
888 					sname->sn_cred,
889 					&recv_tok,
890 					GSS_C_NO_CHANNEL_BINDINGS,
891 					&client->cl_cname,
892 					&mech,
893 					&gr->gr_token,
894 					&ret_flags,
895 					&cred_lifetime,
896 					&client->cl_creds);
897 				if (gr->gr_major ==
898 				    GSS_S_CREDENTIALS_EXPIRED) {
899 					/*
900 					 * Either our creds really did
901 					 * expire or gssd was
902 					 * restarted.
903 					 */
904 					if (rpc_gss_acquire_svc_cred(sname))
905 						goto retry;
906 				}
907 				client->cl_sname = sname;
908 				break;
909 			}
910 		}
911 		if (!sname) {
912 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
913 			    (char *) &recv_tok);
914 			sx_xunlock(&svc_rpc_gss_lock);
915 			return (FALSE);
916 		}
917 	} else {
918 		gr->gr_major = gss_accept_sec_context(
919 			&gr->gr_minor,
920 			&client->cl_ctx,
921 			client->cl_sname->sn_cred,
922 			&recv_tok,
923 			GSS_C_NO_CHANNEL_BINDINGS,
924 			&client->cl_cname,
925 			&mech,
926 			&gr->gr_token,
927 			&ret_flags,
928 			&cred_lifetime,
929 			NULL);
930 	}
931 	sx_xunlock(&svc_rpc_gss_lock);
932 
933 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
934 
935 	/*
936 	 * If we get an error from gss_accept_sec_context, send the
937 	 * reply anyway so that the client gets a chance to see what
938 	 * is wrong.
939 	 */
940 	if (gr->gr_major != GSS_S_COMPLETE &&
941 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
942 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
943 		    gr->gr_major, gr->gr_minor);
944 		client->cl_state = CLIENT_STALE;
945 		return (TRUE);
946 	}
947 
948 	gr->gr_handle.value = &client->cl_id;
949 	gr->gr_handle.length = sizeof(client->cl_id);
950 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
951 
952 	/* Save client info. */
953 	client->cl_mech = mech;
954 	client->cl_qop = GSS_C_QOP_DEFAULT;
955 	client->cl_done_callback = FALSE;
956 
957 	if (gr->gr_major == GSS_S_COMPLETE) {
958 		gss_buffer_desc	export_name;
959 
960 		/*
961 		 * Change client expiration time to be near when the
962 		 * client creds expire (or 24 hours if we can't figure
963 		 * that out).
964 		 */
965 		if (cred_lifetime == GSS_C_INDEFINITE)
966 			cred_lifetime = 24*60*60;
967 
968 		/*
969 		 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
970 		 */
971 		if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
972 		    svc_rpc_gss_lifetime_max)
973 			cred_lifetime = svc_rpc_gss_lifetime_max;
974 
975 		client->cl_expiration = time_uptime + cred_lifetime;
976 
977 		/*
978 		 * Fill in cred details in the rawcred structure.
979 		 */
980 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
981 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
982 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
983 		    &export_name);
984 		if (maj_stat != GSS_S_COMPLETE) {
985 			rpc_gss_log_status("gss_export_name", client->cl_mech,
986 			    maj_stat, min_stat);
987 			return (FALSE);
988 		}
989 		client->cl_rawcred.client_principal =
990 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
991 			    + export_name.length);
992 		client->cl_rawcred.client_principal->len = export_name.length;
993 		memcpy(client->cl_rawcred.client_principal->name,
994 		    export_name.value, export_name.length);
995 		gss_release_buffer(&min_stat, &export_name);
996 		client->cl_rawcred.svc_principal =
997 			client->cl_sname->sn_principal;
998 		client->cl_rawcred.service = gc->gc_svc;
999 
1000 		/*
1001 		 * Use gss_pname_to_uid to map to unix creds. For
1002 		 * kerberos5, this uses krb5_aname_to_localname.
1003 		 */
1004 		svc_rpc_gss_build_ucred(client, client->cl_cname);
1005 		svc_rpc_gss_set_flavor(client);
1006 		gss_release_name(&min_stat, &client->cl_cname);
1007 
1008 #ifdef DEBUG
1009 		{
1010 			gss_buffer_desc mechname;
1011 
1012 			gss_oid_to_str(&min_stat, mech, &mechname);
1013 
1014 			rpc_gss_log_debug("accepted context for %s with "
1015 			    "<mech %.*s, qop %d, svc %d>",
1016 			    client->cl_rawcred.client_principal->name,
1017 			    mechname.length, (char *)mechname.value,
1018 			    client->cl_qop, client->cl_rawcred.service);
1019 
1020 			gss_release_buffer(&min_stat, &mechname);
1021 		}
1022 #endif /* DEBUG */
1023 	}
1024 	return (TRUE);
1025 }
1026 
1027 static bool_t
1028 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1029     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1030 {
1031 	struct opaque_auth	*oa;
1032 	gss_buffer_desc		 rpcbuf, checksum;
1033 	OM_uint32		 maj_stat, min_stat;
1034 	gss_qop_t		 qop_state;
1035 	int32_t			 rpchdr[128 / sizeof(int32_t)];
1036 	int32_t			*buf;
1037 
1038 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
1039 
1040 	memset(rpchdr, 0, sizeof(rpchdr));
1041 
1042 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1043 	buf = rpchdr;
1044 	IXDR_PUT_LONG(buf, msg->rm_xid);
1045 	IXDR_PUT_ENUM(buf, msg->rm_direction);
1046 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1047 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1048 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1049 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1050 	oa = &msg->rm_call.cb_cred;
1051 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1052 	IXDR_PUT_LONG(buf, oa->oa_length);
1053 	if (oa->oa_length) {
1054 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1055 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1056 	}
1057 	rpcbuf.value = rpchdr;
1058 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1059 
1060 	checksum.value = msg->rm_call.cb_verf.oa_base;
1061 	checksum.length = msg->rm_call.cb_verf.oa_length;
1062 
1063 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1064 				  &qop_state);
1065 
1066 	if (maj_stat != GSS_S_COMPLETE) {
1067 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1068 		    maj_stat, min_stat);
1069 		/*
1070 		 * A bug in some versions of the Linux client generates a
1071 		 * Destroy operation with a bogus encrypted checksum. Deleting
1072 		 * the credential handle for that case causes the mount to fail.
1073 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1074 		 * doesn't make sense to destroy the handle and not doing so
1075 		 * fixes the Linux mount.
1076 		 */
1077 		if (gcproc != RPCSEC_GSS_DESTROY)
1078 			client->cl_state = CLIENT_STALE;
1079 		return (FALSE);
1080 	}
1081 
1082 	*qop = qop_state;
1083 	return (TRUE);
1084 }
1085 
1086 static bool_t
1087 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1088     struct svc_req *rqst, u_int seq)
1089 {
1090 	gss_buffer_desc		signbuf;
1091 	gss_buffer_desc		mic;
1092 	OM_uint32		maj_stat, min_stat;
1093 	uint32_t		nseq;
1094 
1095 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1096 
1097 	nseq = htonl(seq);
1098 	signbuf.value = &nseq;
1099 	signbuf.length = sizeof(nseq);
1100 
1101 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1102 	    &signbuf, &mic);
1103 
1104 	if (maj_stat != GSS_S_COMPLETE) {
1105 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1106 		client->cl_state = CLIENT_STALE;
1107 		return (FALSE);
1108 	}
1109 
1110 	KASSERT(mic.length <= MAX_AUTH_BYTES,
1111 	    ("MIC too large for RPCSEC_GSS"));
1112 
1113 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1114 	rqst->rq_verf.oa_length = mic.length;
1115 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1116 
1117 	gss_release_buffer(&min_stat, &mic);
1118 
1119 	return (TRUE);
1120 }
1121 
1122 static bool_t
1123 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1124 {
1125 	struct svc_rpc_gss_callback *scb;
1126 	rpc_gss_lock_t	lock;
1127 	void		*cookie;
1128 	bool_t		cb_res;
1129 	bool_t		result;
1130 
1131 	/*
1132 	 * See if we have a callback for this guy.
1133 	 */
1134 	result = TRUE;
1135 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1136 		if (scb->cb_callback.program == rqst->rq_prog
1137 		    && scb->cb_callback.version == rqst->rq_vers) {
1138 			/*
1139 			 * This one matches. Call the callback and see
1140 			 * if it wants to veto or something.
1141 			 */
1142 			lock.locked = FALSE;
1143 			lock.raw_cred = &client->cl_rawcred;
1144 			cb_res = scb->cb_callback.callback(rqst,
1145 			    client->cl_creds,
1146 			    client->cl_ctx,
1147 			    &lock,
1148 			    &cookie);
1149 
1150 			if (!cb_res) {
1151 				client->cl_state = CLIENT_STALE;
1152 				result = FALSE;
1153 				break;
1154 			}
1155 
1156 			/*
1157 			 * The callback accepted the connection - it
1158 			 * is responsible for freeing client->cl_creds
1159 			 * now.
1160 			 */
1161 			client->cl_creds = GSS_C_NO_CREDENTIAL;
1162 			client->cl_locked = lock.locked;
1163 			client->cl_cookie = cookie;
1164 			return (TRUE);
1165 		}
1166 	}
1167 
1168 	/*
1169 	 * Either no callback exists for this program/version or one
1170 	 * of the callbacks rejected the connection. We just need to
1171 	 * clean up the delegated client creds, if any.
1172 	 */
1173 	if (client->cl_creds) {
1174 		OM_uint32 min_ver;
1175 		gss_release_cred(&min_ver, &client->cl_creds);
1176 	}
1177 	return (result);
1178 }
1179 
1180 static bool_t
1181 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1182 {
1183 	uint32_t offset;
1184 	int word, bit;
1185 	bool_t result;
1186 
1187 	sx_xlock(&client->cl_lock);
1188 	if (seq <= client->cl_seqlast) {
1189 		/*
1190 		 * The request sequence number is less than
1191 		 * the largest we have seen so far. If it is
1192 		 * outside the window or if we have seen a
1193 		 * request with this sequence before, silently
1194 		 * discard it.
1195 		 */
1196 		offset = client->cl_seqlast - seq;
1197 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1198 			result = FALSE;
1199 			goto out;
1200 		}
1201 		word = offset / 32;
1202 		bit = offset % 32;
1203 		if (client->cl_seqmask[word] & (1 << bit)) {
1204 			result = FALSE;
1205 			goto out;
1206 		}
1207 	}
1208 
1209 	result = TRUE;
1210 out:
1211 	sx_xunlock(&client->cl_lock);
1212 	return (result);
1213 }
1214 
1215 static void
1216 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1217 {
1218 	int offset, i, word, bit;
1219 	uint32_t carry, newcarry;
1220 
1221 	sx_xlock(&client->cl_lock);
1222 	if (seq > client->cl_seqlast) {
1223 		/*
1224 		 * This request has a sequence number greater
1225 		 * than any we have seen so far. Advance the
1226 		 * seq window and set bit zero of the window
1227 		 * (which corresponds to the new sequence
1228 		 * number)
1229 		 */
1230 		offset = seq - client->cl_seqlast;
1231 		while (offset > 32) {
1232 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1233 			     i > 0; i--) {
1234 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1235 			}
1236 			client->cl_seqmask[0] = 0;
1237 			offset -= 32;
1238 		}
1239 		carry = 0;
1240 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1241 			newcarry = client->cl_seqmask[i] >> (32 - offset);
1242 			client->cl_seqmask[i] =
1243 				(client->cl_seqmask[i] << offset) | carry;
1244 			carry = newcarry;
1245 		}
1246 		client->cl_seqmask[0] |= 1;
1247 		client->cl_seqlast = seq;
1248 	} else {
1249 		offset = client->cl_seqlast - seq;
1250 		word = offset / 32;
1251 		bit = offset % 32;
1252 		client->cl_seqmask[word] |= (1 << bit);
1253 	}
1254 	sx_xunlock(&client->cl_lock);
1255 }
1256 
1257 enum auth_stat
1258 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1259 
1260 {
1261 	OM_uint32		 min_stat;
1262 	XDR	 		 xdrs;
1263 	struct svc_rpc_gss_cookedcred *cc;
1264 	struct svc_rpc_gss_client *client;
1265 	struct rpc_gss_cred	 gc;
1266 	struct rpc_gss_init_res	 gr;
1267 	gss_qop_t		 qop;
1268 	int			 call_stat;
1269 	enum auth_stat		 result;
1270 
1271 	rpc_gss_log_debug("in svc_rpc_gss()");
1272 
1273 	/* Garbage collect old clients. */
1274 	svc_rpc_gss_timeout_clients();
1275 
1276 	/* Initialize reply. */
1277 	rqst->rq_verf = _null_auth;
1278 
1279 	/* Deserialize client credentials. */
1280 	if (rqst->rq_cred.oa_length <= 0)
1281 		return (AUTH_BADCRED);
1282 
1283 	memset(&gc, 0, sizeof(gc));
1284 
1285 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1286 	    rqst->rq_cred.oa_length, XDR_DECODE);
1287 
1288 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1289 		XDR_DESTROY(&xdrs);
1290 		return (AUTH_BADCRED);
1291 	}
1292 	XDR_DESTROY(&xdrs);
1293 
1294 	client = NULL;
1295 
1296 	/* Check version. */
1297 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1298 		result = AUTH_BADCRED;
1299 		goto out;
1300 	}
1301 
1302 	/* Check the proc and find the client (or create it) */
1303 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1304 		if (gc.gc_handle.length != 0) {
1305 			result = AUTH_BADCRED;
1306 			goto out;
1307 		}
1308 		client = svc_rpc_gss_create_client();
1309 	} else {
1310 		struct svc_rpc_gss_clientid *p;
1311 		if (gc.gc_handle.length != sizeof(*p)) {
1312 			result = AUTH_BADCRED;
1313 			goto out;
1314 		}
1315 		p = gc.gc_handle.value;
1316 		client = svc_rpc_gss_find_client(p);
1317 		if (!client) {
1318 			/*
1319 			 * Can't find the client - we may have
1320 			 * destroyed it - tell the other side to
1321 			 * re-authenticate.
1322 			 */
1323 			result = RPCSEC_GSS_CREDPROBLEM;
1324 			goto out;
1325 		}
1326 	}
1327 	cc = rqst->rq_clntcred;
1328 	cc->cc_client = client;
1329 	cc->cc_service = gc.gc_svc;
1330 	cc->cc_seq = gc.gc_seq;
1331 
1332 	/*
1333 	 * The service and sequence number must be ignored for
1334 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1335 	 */
1336 	if (gc.gc_proc != RPCSEC_GSS_INIT
1337 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1338 		/*
1339 		 * Check for sequence number overflow.
1340 		 */
1341 		if (gc.gc_seq >= MAXSEQ) {
1342 			result = RPCSEC_GSS_CTXPROBLEM;
1343 			goto out;
1344 		}
1345 
1346 		/*
1347 		 * Check for valid service.
1348 		 */
1349 		if (gc.gc_svc != rpc_gss_svc_none &&
1350 		    gc.gc_svc != rpc_gss_svc_integrity &&
1351 		    gc.gc_svc != rpc_gss_svc_privacy) {
1352 			result = AUTH_BADCRED;
1353 			goto out;
1354 		}
1355 	}
1356 
1357 	/* Handle RPCSEC_GSS control procedure. */
1358 	switch (gc.gc_proc) {
1359 
1360 	case RPCSEC_GSS_INIT:
1361 	case RPCSEC_GSS_CONTINUE_INIT:
1362 		if (rqst->rq_proc != NULLPROC) {
1363 			result = AUTH_REJECTEDCRED;
1364 			break;
1365 		}
1366 
1367 		memset(&gr, 0, sizeof(gr));
1368 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1369 			result = AUTH_REJECTEDCRED;
1370 			break;
1371 		}
1372 
1373 		if (gr.gr_major == GSS_S_COMPLETE) {
1374 			/*
1375 			 * We borrow the space for the call verf to
1376 			 * pack our reply verf.
1377 			 */
1378 			rqst->rq_verf = msg->rm_call.cb_verf;
1379 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1380 				result = AUTH_REJECTEDCRED;
1381 				break;
1382 			}
1383 		} else {
1384 			rqst->rq_verf = _null_auth;
1385 		}
1386 
1387 		call_stat = svc_sendreply(rqst,
1388 		    (xdrproc_t) xdr_rpc_gss_init_res,
1389 		    (caddr_t) &gr);
1390 
1391 		gss_release_buffer(&min_stat, &gr.gr_token);
1392 
1393 		if (!call_stat) {
1394 			result = AUTH_FAILED;
1395 			break;
1396 		}
1397 
1398 		if (gr.gr_major == GSS_S_COMPLETE)
1399 			client->cl_state = CLIENT_ESTABLISHED;
1400 
1401 		result = RPCSEC_GSS_NODISPATCH;
1402 		break;
1403 
1404 	case RPCSEC_GSS_DATA:
1405 	case RPCSEC_GSS_DESTROY:
1406 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1407 			result = RPCSEC_GSS_NODISPATCH;
1408 			break;
1409 		}
1410 
1411 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1412 			result = RPCSEC_GSS_CREDPROBLEM;
1413 			break;
1414 		}
1415 
1416 		/*
1417 		 * We borrow the space for the call verf to pack our
1418 		 * reply verf.
1419 		 */
1420 		rqst->rq_verf = msg->rm_call.cb_verf;
1421 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1422 			result = RPCSEC_GSS_CTXPROBLEM;
1423 			break;
1424 		}
1425 
1426 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1427 
1428 		/*
1429 		 * Change the SVCAUTH ops on the request to point at
1430 		 * our own code so that we can unwrap the arguments
1431 		 * and wrap the result. The caller will re-set this on
1432 		 * every request to point to a set of null wrap/unwrap
1433 		 * methods. Acquire an extra reference to the client
1434 		 * which will be released by svc_rpc_gss_release()
1435 		 * after the request has finished processing.
1436 		 */
1437 		refcount_acquire(&client->cl_refs);
1438 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1439 		rqst->rq_auth.svc_ah_private = cc;
1440 
1441 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1442 			/*
1443 			 * We might be ready to do a callback to the server to
1444 			 * see if it wants to accept/reject the connection.
1445 			 */
1446 			sx_xlock(&client->cl_lock);
1447 			if (!client->cl_done_callback) {
1448 				client->cl_done_callback = TRUE;
1449 				client->cl_qop = qop;
1450 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1451 					client->cl_rawcred.mechanism, qop);
1452 				if (!svc_rpc_gss_callback(client, rqst)) {
1453 					result = AUTH_REJECTEDCRED;
1454 					sx_xunlock(&client->cl_lock);
1455 					break;
1456 				}
1457 			}
1458 			sx_xunlock(&client->cl_lock);
1459 
1460 			/*
1461 			 * If the server has locked this client to a
1462 			 * particular service+qop pair, enforce that
1463 			 * restriction now.
1464 			 */
1465 			if (client->cl_locked) {
1466 				if (client->cl_rawcred.service != gc.gc_svc) {
1467 					result = AUTH_FAILED;
1468 					break;
1469 				} else if (client->cl_qop != qop) {
1470 					result = AUTH_BADVERF;
1471 					break;
1472 				}
1473 			}
1474 
1475 			/*
1476 			 * If the qop changed, look up the new qop
1477 			 * name for rawcred.
1478 			 */
1479 			if (client->cl_qop != qop) {
1480 				client->cl_qop = qop;
1481 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1482 					client->cl_rawcred.mechanism, qop);
1483 			}
1484 
1485 			/*
1486 			 * Make sure we use the right service value
1487 			 * for unwrap/wrap.
1488 			 */
1489 			if (client->cl_rawcred.service != gc.gc_svc) {
1490 				client->cl_rawcred.service = gc.gc_svc;
1491 				svc_rpc_gss_set_flavor(client);
1492 			}
1493 
1494 			result = AUTH_OK;
1495 		} else {
1496 			if (rqst->rq_proc != NULLPROC) {
1497 				result = AUTH_REJECTEDCRED;
1498 				break;
1499 			}
1500 
1501 			call_stat = svc_sendreply(rqst,
1502 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1503 
1504 			if (!call_stat) {
1505 				result = AUTH_FAILED;
1506 				break;
1507 			}
1508 
1509 			svc_rpc_gss_forget_client(client);
1510 
1511 			result = RPCSEC_GSS_NODISPATCH;
1512 			break;
1513 		}
1514 		break;
1515 
1516 	default:
1517 		result = AUTH_BADCRED;
1518 		break;
1519 	}
1520 out:
1521 	if (client)
1522 		svc_rpc_gss_release_client(client);
1523 
1524 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1525 	return (result);
1526 }
1527 
1528 static bool_t
1529 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1530 {
1531 	struct svc_rpc_gss_cookedcred *cc;
1532 	struct svc_rpc_gss_client *client;
1533 
1534 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1535 
1536 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537 	client = cc->cc_client;
1538 	if (client->cl_state != CLIENT_ESTABLISHED
1539 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1540 		return (TRUE);
1541 	}
1542 
1543 	return (xdr_rpc_gss_wrap_data(mp,
1544 		client->cl_ctx, client->cl_qop,
1545 		cc->cc_service, cc->cc_seq));
1546 }
1547 
1548 static bool_t
1549 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1550 {
1551 	struct svc_rpc_gss_cookedcred *cc;
1552 	struct svc_rpc_gss_client *client;
1553 
1554 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1555 
1556 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1557 	client = cc->cc_client;
1558 	if (client->cl_state != CLIENT_ESTABLISHED
1559 	    || cc->cc_service == rpc_gss_svc_none) {
1560 		return (TRUE);
1561 	}
1562 
1563 	return (xdr_rpc_gss_unwrap_data(mp,
1564 		client->cl_ctx, client->cl_qop,
1565 		cc->cc_service, cc->cc_seq));
1566 }
1567 
1568 static void
1569 svc_rpc_gss_release(SVCAUTH *auth)
1570 {
1571 	struct svc_rpc_gss_cookedcred *cc;
1572 	struct svc_rpc_gss_client *client;
1573 
1574 	rpc_gss_log_debug("in svc_rpc_gss_release()");
1575 
1576 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1577 	client = cc->cc_client;
1578 	svc_rpc_gss_release_client(client);
1579 }
1580