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