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