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