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