xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision 0957b409)
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 	refcount_init(&client->cl_refs, 1);
572 	sx_init(&client->cl_lock, "GSS-client");
573 	getcredhostid(curthread->td_ucred, &hostid);
574 	client->cl_id.ci_hostid = hostid;
575 	getboottime(&boottime);
576 	client->cl_id.ci_boottime = boottime.tv_sec;
577 	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
578 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
579 	sx_xlock(&svc_rpc_gss_lock);
580 	TAILQ_INSERT_HEAD(list, client, cl_link);
581 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
582 	svc_rpc_gss_client_count++;
583 	sx_xunlock(&svc_rpc_gss_lock);
584 
585 	/*
586 	 * Start the client off with a short expiration time. We will
587 	 * try to get a saner value from the client creds later.
588 	 */
589 	client->cl_state = CLIENT_NEW;
590 	client->cl_locked = FALSE;
591 	client->cl_expiration = time_uptime + 5*60;
592 
593 	return (client);
594 }
595 
596 static void
597 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
598 {
599 	OM_uint32 min_stat;
600 
601 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
602 
603 	if (client->cl_ctx)
604 		gss_delete_sec_context(&min_stat,
605 		    &client->cl_ctx, GSS_C_NO_BUFFER);
606 
607 	if (client->cl_cname)
608 		gss_release_name(&min_stat, &client->cl_cname);
609 
610 	if (client->cl_rawcred.client_principal)
611 		mem_free(client->cl_rawcred.client_principal,
612 		    sizeof(*client->cl_rawcred.client_principal)
613 		    + client->cl_rawcred.client_principal->len);
614 
615 	if (client->cl_cred)
616 		crfree(client->cl_cred);
617 
618 	sx_destroy(&client->cl_lock);
619 	mem_free(client, sizeof(*client));
620 }
621 
622 /*
623  * Drop a reference to a client and free it if that was the last reference.
624  */
625 static void
626 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
627 {
628 
629 	if (!refcount_release(&client->cl_refs))
630 		return;
631 	svc_rpc_gss_destroy_client(client);
632 }
633 
634 /*
635  * Remove a client from our global lists.
636  * Must be called with svc_rpc_gss_lock held.
637  */
638 static void
639 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
640 {
641 	struct svc_rpc_gss_client_list *list;
642 
643 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
644 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
645 	TAILQ_REMOVE(list, client, cl_link);
646 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
647 	svc_rpc_gss_client_count--;
648 }
649 
650 /*
651  * Remove a client from our global lists and free it if we can.
652  */
653 static void
654 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
655 {
656 	struct svc_rpc_gss_client_list *list;
657 	struct svc_rpc_gss_client *tclient;
658 
659 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
660 	sx_xlock(&svc_rpc_gss_lock);
661 	TAILQ_FOREACH(tclient, list, cl_link) {
662 		/*
663 		 * Make sure this client has not already been removed
664 		 * from the lists by svc_rpc_gss_forget_client() or
665 		 * svc_rpc_gss_forget_client_locked().
666 		 */
667 		if (client == tclient) {
668 			svc_rpc_gss_forget_client_locked(client);
669 			sx_xunlock(&svc_rpc_gss_lock);
670 			svc_rpc_gss_release_client(client);
671 			return;
672 		}
673 	}
674 	sx_xunlock(&svc_rpc_gss_lock);
675 }
676 
677 static void
678 svc_rpc_gss_timeout_clients(void)
679 {
680 	struct svc_rpc_gss_client *client;
681 	time_t now = time_uptime;
682 
683 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
684 
685 	/*
686 	 * First enforce the max client limit. We keep
687 	 * svc_rpc_gss_clients in LRU order.
688 	 */
689 	sx_xlock(&svc_rpc_gss_lock);
690 	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
691 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
692 		svc_rpc_gss_forget_client_locked(client);
693 		sx_xunlock(&svc_rpc_gss_lock);
694 		svc_rpc_gss_release_client(client);
695 		sx_xlock(&svc_rpc_gss_lock);
696 		client = TAILQ_LAST(&svc_rpc_gss_clients,
697 		    svc_rpc_gss_client_list);
698 	}
699 again:
700 	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
701 		if (client->cl_state == CLIENT_STALE
702 		    || now > client->cl_expiration) {
703 			svc_rpc_gss_forget_client_locked(client);
704 			sx_xunlock(&svc_rpc_gss_lock);
705 			rpc_gss_log_debug("expiring client %p", client);
706 			svc_rpc_gss_release_client(client);
707 			sx_xlock(&svc_rpc_gss_lock);
708 			goto again;
709 		}
710 	}
711 	sx_xunlock(&svc_rpc_gss_lock);
712 }
713 
714 #ifdef DEBUG
715 /*
716  * OID<->string routines.  These are uuuuugly.
717  */
718 static OM_uint32
719 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
720 {
721 	char		numstr[128];
722 	unsigned long	number;
723 	int		numshift;
724 	size_t		string_length;
725 	size_t		i;
726 	unsigned char	*cp;
727 	char		*bp;
728 
729 	/* Decoded according to krb5/gssapi_krb5.c */
730 
731 	/* First determine the size of the string */
732 	string_length = 0;
733 	number = 0;
734 	numshift = 0;
735 	cp = (unsigned char *) oid->elements;
736 	number = (unsigned long) cp[0];
737 	sprintf(numstr, "%ld ", number/40);
738 	string_length += strlen(numstr);
739 	sprintf(numstr, "%ld ", number%40);
740 	string_length += strlen(numstr);
741 	for (i=1; i<oid->length; i++) {
742 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
743 			number = (number << 7) | (cp[i] & 0x7f);
744 			numshift += 7;
745 		}
746 		else {
747 			*minor_status = 0;
748 			return(GSS_S_FAILURE);
749 		}
750 		if ((cp[i] & 0x80) == 0) {
751 			sprintf(numstr, "%ld ", number);
752 			string_length += strlen(numstr);
753 			number = 0;
754 			numshift = 0;
755 		}
756 	}
757 	/*
758 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
759 	 * here for "{ " and "}\0".
760 	 */
761 	string_length += 4;
762 	if ((bp = (char *) mem_alloc(string_length))) {
763 		strcpy(bp, "{ ");
764 		number = (unsigned long) cp[0];
765 		sprintf(numstr, "%ld ", number/40);
766 		strcat(bp, numstr);
767 		sprintf(numstr, "%ld ", number%40);
768 		strcat(bp, numstr);
769 		number = 0;
770 		cp = (unsigned char *) oid->elements;
771 		for (i=1; i<oid->length; i++) {
772 			number = (number << 7) | (cp[i] & 0x7f);
773 			if ((cp[i] & 0x80) == 0) {
774 				sprintf(numstr, "%ld ", number);
775 				strcat(bp, numstr);
776 				number = 0;
777 			}
778 		}
779 		strcat(bp, "}");
780 		oid_str->length = strlen(bp)+1;
781 		oid_str->value = (void *) bp;
782 		*minor_status = 0;
783 		return(GSS_S_COMPLETE);
784 	}
785 	*minor_status = 0;
786 	return(GSS_S_FAILURE);
787 }
788 #endif
789 
790 static void
791 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
792     const gss_name_t name)
793 {
794 	OM_uint32		maj_stat, min_stat;
795 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
796 	int			numgroups;
797 
798 	uc->uid = 65534;
799 	uc->gid = 65534;
800 	uc->gidlist = client->cl_gid_storage;
801 
802 	numgroups = NGROUPS;
803 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
804 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
805 	if (GSS_ERROR(maj_stat))
806 		uc->gidlen = 0;
807 	else
808 		uc->gidlen = numgroups;
809 }
810 
811 static void
812 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
813 {
814 	static gss_OID_desc krb5_mech_oid =
815 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
816 
817 	/*
818 	 * Attempt to translate mech type and service into a
819 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
820 	 */
821 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
822 		switch (client->cl_rawcred.service) {
823 		case rpc_gss_svc_default:
824 		case rpc_gss_svc_none:
825 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
826 			break;
827 		case rpc_gss_svc_integrity:
828 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
829 			break;
830 		case rpc_gss_svc_privacy:
831 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
832 			break;
833 		}
834 	} else {
835 		client->cl_rpcflavor = RPCSEC_GSS;
836 	}
837 }
838 
839 static bool_t
840 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
841 			       struct svc_req *rqst,
842 			       struct rpc_gss_init_res *gr,
843 			       struct rpc_gss_cred *gc)
844 {
845 	gss_buffer_desc		recv_tok;
846 	gss_OID			mech;
847 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
848 	OM_uint32		cred_lifetime;
849 	struct svc_rpc_gss_svc_name *sname;
850 
851 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
852 
853 	/* Deserialize arguments. */
854 	memset(&recv_tok, 0, sizeof(recv_tok));
855 
856 	if (!svc_getargs(rqst,
857 		(xdrproc_t) xdr_gss_buffer_desc,
858 		(caddr_t) &recv_tok)) {
859 		client->cl_state = CLIENT_STALE;
860 		return (FALSE);
861 	}
862 
863 	/*
864 	 * First time round, try all the server names we have until
865 	 * one matches. Afterwards, stick with that one.
866 	 */
867 	sx_xlock(&svc_rpc_gss_lock);
868 	if (!client->cl_sname) {
869 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
870 			if (sname->sn_program == rqst->rq_prog
871 			    && sname->sn_version == rqst->rq_vers) {
872 			retry:
873 				gr->gr_major = gss_accept_sec_context(
874 					&gr->gr_minor,
875 					&client->cl_ctx,
876 					sname->sn_cred,
877 					&recv_tok,
878 					GSS_C_NO_CHANNEL_BINDINGS,
879 					&client->cl_cname,
880 					&mech,
881 					&gr->gr_token,
882 					&ret_flags,
883 					&cred_lifetime,
884 					&client->cl_creds);
885 				if (gr->gr_major ==
886 				    GSS_S_CREDENTIALS_EXPIRED) {
887 					/*
888 					 * Either our creds really did
889 					 * expire or gssd was
890 					 * restarted.
891 					 */
892 					if (rpc_gss_acquire_svc_cred(sname))
893 						goto retry;
894 				}
895 				client->cl_sname = sname;
896 				break;
897 			}
898 		}
899 		if (!sname) {
900 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
901 			    (char *) &recv_tok);
902 			sx_xunlock(&svc_rpc_gss_lock);
903 			return (FALSE);
904 		}
905 	} else {
906 		gr->gr_major = gss_accept_sec_context(
907 			&gr->gr_minor,
908 			&client->cl_ctx,
909 			client->cl_sname->sn_cred,
910 			&recv_tok,
911 			GSS_C_NO_CHANNEL_BINDINGS,
912 			&client->cl_cname,
913 			&mech,
914 			&gr->gr_token,
915 			&ret_flags,
916 			&cred_lifetime,
917 			NULL);
918 	}
919 	sx_xunlock(&svc_rpc_gss_lock);
920 
921 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
922 
923 	/*
924 	 * If we get an error from gss_accept_sec_context, send the
925 	 * reply anyway so that the client gets a chance to see what
926 	 * is wrong.
927 	 */
928 	if (gr->gr_major != GSS_S_COMPLETE &&
929 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
930 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
931 		    gr->gr_major, gr->gr_minor);
932 		client->cl_state = CLIENT_STALE;
933 		return (TRUE);
934 	}
935 
936 	gr->gr_handle.value = &client->cl_id;
937 	gr->gr_handle.length = sizeof(client->cl_id);
938 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
939 
940 	/* Save client info. */
941 	client->cl_mech = mech;
942 	client->cl_qop = GSS_C_QOP_DEFAULT;
943 	client->cl_done_callback = FALSE;
944 
945 	if (gr->gr_major == GSS_S_COMPLETE) {
946 		gss_buffer_desc	export_name;
947 
948 		/*
949 		 * Change client expiration time to be near when the
950 		 * client creds expire (or 24 hours if we can't figure
951 		 * that out).
952 		 */
953 		if (cred_lifetime == GSS_C_INDEFINITE)
954 			cred_lifetime = time_uptime + 24*60*60;
955 
956 		client->cl_expiration = time_uptime + cred_lifetime;
957 
958 		/*
959 		 * Fill in cred details in the rawcred structure.
960 		 */
961 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
962 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
963 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
964 		    &export_name);
965 		if (maj_stat != GSS_S_COMPLETE) {
966 			rpc_gss_log_status("gss_export_name", client->cl_mech,
967 			    maj_stat, min_stat);
968 			return (FALSE);
969 		}
970 		client->cl_rawcred.client_principal =
971 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
972 			    + export_name.length);
973 		client->cl_rawcred.client_principal->len = export_name.length;
974 		memcpy(client->cl_rawcred.client_principal->name,
975 		    export_name.value, export_name.length);
976 		gss_release_buffer(&min_stat, &export_name);
977 		client->cl_rawcred.svc_principal =
978 			client->cl_sname->sn_principal;
979 		client->cl_rawcred.service = gc->gc_svc;
980 
981 		/*
982 		 * Use gss_pname_to_uid to map to unix creds. For
983 		 * kerberos5, this uses krb5_aname_to_localname.
984 		 */
985 		svc_rpc_gss_build_ucred(client, client->cl_cname);
986 		svc_rpc_gss_set_flavor(client);
987 		gss_release_name(&min_stat, &client->cl_cname);
988 
989 #ifdef DEBUG
990 		{
991 			gss_buffer_desc mechname;
992 
993 			gss_oid_to_str(&min_stat, mech, &mechname);
994 
995 			rpc_gss_log_debug("accepted context for %s with "
996 			    "<mech %.*s, qop %d, svc %d>",
997 			    client->cl_rawcred.client_principal->name,
998 			    mechname.length, (char *)mechname.value,
999 			    client->cl_qop, client->cl_rawcred.service);
1000 
1001 			gss_release_buffer(&min_stat, &mechname);
1002 		}
1003 #endif /* DEBUG */
1004 	}
1005 	return (TRUE);
1006 }
1007 
1008 static bool_t
1009 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
1010     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1011 {
1012 	struct opaque_auth	*oa;
1013 	gss_buffer_desc		 rpcbuf, checksum;
1014 	OM_uint32		 maj_stat, min_stat;
1015 	gss_qop_t		 qop_state;
1016 	int32_t			 rpchdr[128 / sizeof(int32_t)];
1017 	int32_t			*buf;
1018 
1019 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
1020 
1021 	memset(rpchdr, 0, sizeof(rpchdr));
1022 
1023 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1024 	buf = rpchdr;
1025 	IXDR_PUT_LONG(buf, msg->rm_xid);
1026 	IXDR_PUT_ENUM(buf, msg->rm_direction);
1027 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1028 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1029 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1030 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1031 	oa = &msg->rm_call.cb_cred;
1032 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1033 	IXDR_PUT_LONG(buf, oa->oa_length);
1034 	if (oa->oa_length) {
1035 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1036 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1037 	}
1038 	rpcbuf.value = rpchdr;
1039 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1040 
1041 	checksum.value = msg->rm_call.cb_verf.oa_base;
1042 	checksum.length = msg->rm_call.cb_verf.oa_length;
1043 
1044 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1045 				  &qop_state);
1046 
1047 	if (maj_stat != GSS_S_COMPLETE) {
1048 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1049 		    maj_stat, min_stat);
1050 		/*
1051 		 * A bug in some versions of the Linux client generates a
1052 		 * Destroy operation with a bogus encrypted checksum. Deleting
1053 		 * the credential handle for that case causes the mount to fail.
1054 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1055 		 * doesn't make sense to destroy the handle and not doing so
1056 		 * fixes the Linux mount.
1057 		 */
1058 		if (gcproc != RPCSEC_GSS_DESTROY)
1059 			client->cl_state = CLIENT_STALE;
1060 		return (FALSE);
1061 	}
1062 
1063 	*qop = qop_state;
1064 	return (TRUE);
1065 }
1066 
1067 static bool_t
1068 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1069     struct svc_req *rqst, u_int seq)
1070 {
1071 	gss_buffer_desc		signbuf;
1072 	gss_buffer_desc		mic;
1073 	OM_uint32		maj_stat, min_stat;
1074 	uint32_t		nseq;
1075 
1076 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1077 
1078 	nseq = htonl(seq);
1079 	signbuf.value = &nseq;
1080 	signbuf.length = sizeof(nseq);
1081 
1082 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1083 	    &signbuf, &mic);
1084 
1085 	if (maj_stat != GSS_S_COMPLETE) {
1086 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1087 		client->cl_state = CLIENT_STALE;
1088 		return (FALSE);
1089 	}
1090 
1091 	KASSERT(mic.length <= MAX_AUTH_BYTES,
1092 	    ("MIC too large for RPCSEC_GSS"));
1093 
1094 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1095 	rqst->rq_verf.oa_length = mic.length;
1096 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1097 
1098 	gss_release_buffer(&min_stat, &mic);
1099 
1100 	return (TRUE);
1101 }
1102 
1103 static bool_t
1104 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1105 {
1106 	struct svc_rpc_gss_callback *scb;
1107 	rpc_gss_lock_t	lock;
1108 	void		*cookie;
1109 	bool_t		cb_res;
1110 	bool_t		result;
1111 
1112 	/*
1113 	 * See if we have a callback for this guy.
1114 	 */
1115 	result = TRUE;
1116 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1117 		if (scb->cb_callback.program == rqst->rq_prog
1118 		    && scb->cb_callback.version == rqst->rq_vers) {
1119 			/*
1120 			 * This one matches. Call the callback and see
1121 			 * if it wants to veto or something.
1122 			 */
1123 			lock.locked = FALSE;
1124 			lock.raw_cred = &client->cl_rawcred;
1125 			cb_res = scb->cb_callback.callback(rqst,
1126 			    client->cl_creds,
1127 			    client->cl_ctx,
1128 			    &lock,
1129 			    &cookie);
1130 
1131 			if (!cb_res) {
1132 				client->cl_state = CLIENT_STALE;
1133 				result = FALSE;
1134 				break;
1135 			}
1136 
1137 			/*
1138 			 * The callback accepted the connection - it
1139 			 * is responsible for freeing client->cl_creds
1140 			 * now.
1141 			 */
1142 			client->cl_creds = GSS_C_NO_CREDENTIAL;
1143 			client->cl_locked = lock.locked;
1144 			client->cl_cookie = cookie;
1145 			return (TRUE);
1146 		}
1147 	}
1148 
1149 	/*
1150 	 * Either no callback exists for this program/version or one
1151 	 * of the callbacks rejected the connection. We just need to
1152 	 * clean up the delegated client creds, if any.
1153 	 */
1154 	if (client->cl_creds) {
1155 		OM_uint32 min_ver;
1156 		gss_release_cred(&min_ver, &client->cl_creds);
1157 	}
1158 	return (result);
1159 }
1160 
1161 static bool_t
1162 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1163 {
1164 	u_int32_t offset;
1165 	int word, bit;
1166 	bool_t result;
1167 
1168 	sx_xlock(&client->cl_lock);
1169 	if (seq <= client->cl_seqlast) {
1170 		/*
1171 		 * The request sequence number is less than
1172 		 * the largest we have seen so far. If it is
1173 		 * outside the window or if we have seen a
1174 		 * request with this sequence before, silently
1175 		 * discard it.
1176 		 */
1177 		offset = client->cl_seqlast - seq;
1178 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1179 			result = FALSE;
1180 			goto out;
1181 		}
1182 		word = offset / 32;
1183 		bit = offset % 32;
1184 		if (client->cl_seqmask[word] & (1 << bit)) {
1185 			result = FALSE;
1186 			goto out;
1187 		}
1188 	}
1189 
1190 	result = TRUE;
1191 out:
1192 	sx_xunlock(&client->cl_lock);
1193 	return (result);
1194 }
1195 
1196 static void
1197 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1198 {
1199 	int offset, i, word, bit;
1200 	uint32_t carry, newcarry;
1201 
1202 	sx_xlock(&client->cl_lock);
1203 	if (seq > client->cl_seqlast) {
1204 		/*
1205 		 * This request has a sequence number greater
1206 		 * than any we have seen so far. Advance the
1207 		 * seq window and set bit zero of the window
1208 		 * (which corresponds to the new sequence
1209 		 * number)
1210 		 */
1211 		offset = seq - client->cl_seqlast;
1212 		while (offset > 32) {
1213 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1214 			     i > 0; i--) {
1215 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1216 			}
1217 			client->cl_seqmask[0] = 0;
1218 			offset -= 32;
1219 		}
1220 		carry = 0;
1221 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1222 			newcarry = client->cl_seqmask[i] >> (32 - offset);
1223 			client->cl_seqmask[i] =
1224 				(client->cl_seqmask[i] << offset) | carry;
1225 			carry = newcarry;
1226 		}
1227 		client->cl_seqmask[0] |= 1;
1228 		client->cl_seqlast = seq;
1229 	} else {
1230 		offset = client->cl_seqlast - seq;
1231 		word = offset / 32;
1232 		bit = offset % 32;
1233 		client->cl_seqmask[word] |= (1 << bit);
1234 	}
1235 	sx_xunlock(&client->cl_lock);
1236 }
1237 
1238 enum auth_stat
1239 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1240 
1241 {
1242 	OM_uint32		 min_stat;
1243 	XDR	 		 xdrs;
1244 	struct svc_rpc_gss_cookedcred *cc;
1245 	struct svc_rpc_gss_client *client;
1246 	struct rpc_gss_cred	 gc;
1247 	struct rpc_gss_init_res	 gr;
1248 	gss_qop_t		 qop;
1249 	int			 call_stat;
1250 	enum auth_stat		 result;
1251 
1252 	rpc_gss_log_debug("in svc_rpc_gss()");
1253 
1254 	/* Garbage collect old clients. */
1255 	svc_rpc_gss_timeout_clients();
1256 
1257 	/* Initialize reply. */
1258 	rqst->rq_verf = _null_auth;
1259 
1260 	/* Deserialize client credentials. */
1261 	if (rqst->rq_cred.oa_length <= 0)
1262 		return (AUTH_BADCRED);
1263 
1264 	memset(&gc, 0, sizeof(gc));
1265 
1266 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1267 	    rqst->rq_cred.oa_length, XDR_DECODE);
1268 
1269 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1270 		XDR_DESTROY(&xdrs);
1271 		return (AUTH_BADCRED);
1272 	}
1273 	XDR_DESTROY(&xdrs);
1274 
1275 	client = NULL;
1276 
1277 	/* Check version. */
1278 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1279 		result = AUTH_BADCRED;
1280 		goto out;
1281 	}
1282 
1283 	/* Check the proc and find the client (or create it) */
1284 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1285 		if (gc.gc_handle.length != 0) {
1286 			result = AUTH_BADCRED;
1287 			goto out;
1288 		}
1289 		client = svc_rpc_gss_create_client();
1290 		refcount_acquire(&client->cl_refs);
1291 	} else {
1292 		struct svc_rpc_gss_clientid *p;
1293 		if (gc.gc_handle.length != sizeof(*p)) {
1294 			result = AUTH_BADCRED;
1295 			goto out;
1296 		}
1297 		p = gc.gc_handle.value;
1298 		client = svc_rpc_gss_find_client(p);
1299 		if (!client) {
1300 			/*
1301 			 * Can't find the client - we may have
1302 			 * destroyed it - tell the other side to
1303 			 * re-authenticate.
1304 			 */
1305 			result = RPCSEC_GSS_CREDPROBLEM;
1306 			goto out;
1307 		}
1308 	}
1309 	cc = rqst->rq_clntcred;
1310 	cc->cc_client = client;
1311 	cc->cc_service = gc.gc_svc;
1312 	cc->cc_seq = gc.gc_seq;
1313 
1314 	/*
1315 	 * The service and sequence number must be ignored for
1316 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1317 	 */
1318 	if (gc.gc_proc != RPCSEC_GSS_INIT
1319 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1320 		/*
1321 		 * Check for sequence number overflow.
1322 		 */
1323 		if (gc.gc_seq >= MAXSEQ) {
1324 			result = RPCSEC_GSS_CTXPROBLEM;
1325 			goto out;
1326 		}
1327 
1328 		/*
1329 		 * Check for valid service.
1330 		 */
1331 		if (gc.gc_svc != rpc_gss_svc_none &&
1332 		    gc.gc_svc != rpc_gss_svc_integrity &&
1333 		    gc.gc_svc != rpc_gss_svc_privacy) {
1334 			result = AUTH_BADCRED;
1335 			goto out;
1336 		}
1337 	}
1338 
1339 	/* Handle RPCSEC_GSS control procedure. */
1340 	switch (gc.gc_proc) {
1341 
1342 	case RPCSEC_GSS_INIT:
1343 	case RPCSEC_GSS_CONTINUE_INIT:
1344 		if (rqst->rq_proc != NULLPROC) {
1345 			result = AUTH_REJECTEDCRED;
1346 			break;
1347 		}
1348 
1349 		memset(&gr, 0, sizeof(gr));
1350 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1351 			result = AUTH_REJECTEDCRED;
1352 			break;
1353 		}
1354 
1355 		if (gr.gr_major == GSS_S_COMPLETE) {
1356 			/*
1357 			 * We borrow the space for the call verf to
1358 			 * pack our reply verf.
1359 			 */
1360 			rqst->rq_verf = msg->rm_call.cb_verf;
1361 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1362 				result = AUTH_REJECTEDCRED;
1363 				break;
1364 			}
1365 		} else {
1366 			rqst->rq_verf = _null_auth;
1367 		}
1368 
1369 		call_stat = svc_sendreply(rqst,
1370 		    (xdrproc_t) xdr_rpc_gss_init_res,
1371 		    (caddr_t) &gr);
1372 
1373 		gss_release_buffer(&min_stat, &gr.gr_token);
1374 
1375 		if (!call_stat) {
1376 			result = AUTH_FAILED;
1377 			break;
1378 		}
1379 
1380 		if (gr.gr_major == GSS_S_COMPLETE)
1381 			client->cl_state = CLIENT_ESTABLISHED;
1382 
1383 		result = RPCSEC_GSS_NODISPATCH;
1384 		break;
1385 
1386 	case RPCSEC_GSS_DATA:
1387 	case RPCSEC_GSS_DESTROY:
1388 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1389 			result = RPCSEC_GSS_NODISPATCH;
1390 			break;
1391 		}
1392 
1393 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1394 			result = RPCSEC_GSS_CREDPROBLEM;
1395 			break;
1396 		}
1397 
1398 		/*
1399 		 * We borrow the space for the call verf to pack our
1400 		 * reply verf.
1401 		 */
1402 		rqst->rq_verf = msg->rm_call.cb_verf;
1403 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1404 			result = RPCSEC_GSS_CTXPROBLEM;
1405 			break;
1406 		}
1407 
1408 		svc_rpc_gss_update_seq(client, gc.gc_seq);
1409 
1410 		/*
1411 		 * Change the SVCAUTH ops on the request to point at
1412 		 * our own code so that we can unwrap the arguments
1413 		 * and wrap the result. The caller will re-set this on
1414 		 * every request to point to a set of null wrap/unwrap
1415 		 * methods. Acquire an extra reference to the client
1416 		 * which will be released by svc_rpc_gss_release()
1417 		 * after the request has finished processing.
1418 		 */
1419 		refcount_acquire(&client->cl_refs);
1420 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1421 		rqst->rq_auth.svc_ah_private = cc;
1422 
1423 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1424 			/*
1425 			 * We might be ready to do a callback to the server to
1426 			 * see if it wants to accept/reject the connection.
1427 			 */
1428 			sx_xlock(&client->cl_lock);
1429 			if (!client->cl_done_callback) {
1430 				client->cl_done_callback = TRUE;
1431 				client->cl_qop = qop;
1432 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1433 					client->cl_rawcred.mechanism, qop);
1434 				if (!svc_rpc_gss_callback(client, rqst)) {
1435 					result = AUTH_REJECTEDCRED;
1436 					sx_xunlock(&client->cl_lock);
1437 					break;
1438 				}
1439 			}
1440 			sx_xunlock(&client->cl_lock);
1441 
1442 			/*
1443 			 * If the server has locked this client to a
1444 			 * particular service+qop pair, enforce that
1445 			 * restriction now.
1446 			 */
1447 			if (client->cl_locked) {
1448 				if (client->cl_rawcred.service != gc.gc_svc) {
1449 					result = AUTH_FAILED;
1450 					break;
1451 				} else if (client->cl_qop != qop) {
1452 					result = AUTH_BADVERF;
1453 					break;
1454 				}
1455 			}
1456 
1457 			/*
1458 			 * If the qop changed, look up the new qop
1459 			 * name for rawcred.
1460 			 */
1461 			if (client->cl_qop != qop) {
1462 				client->cl_qop = qop;
1463 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1464 					client->cl_rawcred.mechanism, qop);
1465 			}
1466 
1467 			/*
1468 			 * Make sure we use the right service value
1469 			 * for unwrap/wrap.
1470 			 */
1471 			if (client->cl_rawcred.service != gc.gc_svc) {
1472 				client->cl_rawcred.service = gc.gc_svc;
1473 				svc_rpc_gss_set_flavor(client);
1474 			}
1475 
1476 			result = AUTH_OK;
1477 		} else {
1478 			if (rqst->rq_proc != NULLPROC) {
1479 				result = AUTH_REJECTEDCRED;
1480 				break;
1481 			}
1482 
1483 			call_stat = svc_sendreply(rqst,
1484 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1485 
1486 			if (!call_stat) {
1487 				result = AUTH_FAILED;
1488 				break;
1489 			}
1490 
1491 			svc_rpc_gss_forget_client(client);
1492 
1493 			result = RPCSEC_GSS_NODISPATCH;
1494 			break;
1495 		}
1496 		break;
1497 
1498 	default:
1499 		result = AUTH_BADCRED;
1500 		break;
1501 	}
1502 out:
1503 	if (client)
1504 		svc_rpc_gss_release_client(client);
1505 
1506 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1507 	return (result);
1508 }
1509 
1510 static bool_t
1511 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1512 {
1513 	struct svc_rpc_gss_cookedcred *cc;
1514 	struct svc_rpc_gss_client *client;
1515 
1516 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1517 
1518 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1519 	client = cc->cc_client;
1520 	if (client->cl_state != CLIENT_ESTABLISHED
1521 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1522 		return (TRUE);
1523 	}
1524 
1525 	return (xdr_rpc_gss_wrap_data(mp,
1526 		client->cl_ctx, client->cl_qop,
1527 		cc->cc_service, cc->cc_seq));
1528 }
1529 
1530 static bool_t
1531 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1532 {
1533 	struct svc_rpc_gss_cookedcred *cc;
1534 	struct svc_rpc_gss_client *client;
1535 
1536 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1537 
1538 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1539 	client = cc->cc_client;
1540 	if (client->cl_state != CLIENT_ESTABLISHED
1541 	    || cc->cc_service == rpc_gss_svc_none) {
1542 		return (TRUE);
1543 	}
1544 
1545 	return (xdr_rpc_gss_unwrap_data(mp,
1546 		client->cl_ctx, client->cl_qop,
1547 		cc->cc_service, cc->cc_seq));
1548 }
1549 
1550 static void
1551 svc_rpc_gss_release(SVCAUTH *auth)
1552 {
1553 	struct svc_rpc_gss_cookedcred *cc;
1554 	struct svc_rpc_gss_client *client;
1555 
1556 	rpc_gss_log_debug("in svc_rpc_gss_release()");
1557 
1558 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1559 	client = cc->cc_client;
1560 	svc_rpc_gss_release_client(client);
1561 }
1562