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