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