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