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