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