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