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