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