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