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