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