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