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