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