svc_rpcsec_gss.c revision 304843
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: stable/11/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 304843 2016-08-26 10:04:10Z kib $");
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
84static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
85static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
86static void     svc_rpc_gss_release(SVCAUTH *);
87static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
88static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
89
90static 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
96struct sx svc_rpc_gss_lock;
97
98struct svc_rpc_gss_callback {
99	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
100	rpc_gss_callback_t	cb_callback;
101};
102static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
103	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
104
105struct 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};
114static 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
117enum 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
125struct svc_rpc_gss_clientid {
126	unsigned long		ci_hostid;
127	uint32_t		ci_boottime;
128	uint32_t		ci_id;
129};
130
131struct 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};
156TAILQ_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 */
163struct 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
171struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
172struct svc_rpc_gss_client_list svc_rpc_gss_clients;
173static size_t svc_rpc_gss_client_count;
174static uint32_t svc_rpc_gss_next_clientid = 1;
175
176static void
177svc_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}
187SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
188
189bool_t
190rpc_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
207void
208rpc_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
227static bool_t
228rpc_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
261bool_t
262rpc_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
294void
295rpc_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
316bool_t
317rpc_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) + 1;
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
400bool_t
401rpc_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 */
425static int
426rpc_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
459int
460rpc_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
502static struct svc_rpc_gss_client *
503svc_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	struct timeval boottime;
508	unsigned long hostid;
509
510	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
511
512	getcredhostid(curthread->td_ucred, &hostid);
513	getboottime(&boottime);
514	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
515		return (NULL);
516
517	list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
518	sx_xlock(&svc_rpc_gss_lock);
519	TAILQ_FOREACH(client, list, cl_link) {
520		if (client->cl_id.ci_id == id->ci_id) {
521			/*
522			 * Move this client to the front of the LRU
523			 * list.
524			 */
525			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
526			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
527			    cl_alllink);
528			refcount_acquire(&client->cl_refs);
529			break;
530		}
531	}
532	sx_xunlock(&svc_rpc_gss_lock);
533
534	return (client);
535}
536
537static struct svc_rpc_gss_client *
538svc_rpc_gss_create_client(void)
539{
540	struct svc_rpc_gss_client *client;
541	struct svc_rpc_gss_client_list *list;
542	struct timeval boottime;
543	unsigned long hostid;
544
545	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
546
547	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
548	memset(client, 0, sizeof(struct svc_rpc_gss_client));
549	refcount_init(&client->cl_refs, 1);
550	sx_init(&client->cl_lock, "GSS-client");
551	getcredhostid(curthread->td_ucred, &hostid);
552	client->cl_id.ci_hostid = hostid;
553	getboottime(&boottime);
554	client->cl_id.ci_boottime = boottime.tv_sec;
555	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
556	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
557	sx_xlock(&svc_rpc_gss_lock);
558	TAILQ_INSERT_HEAD(list, client, cl_link);
559	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
560	svc_rpc_gss_client_count++;
561	sx_xunlock(&svc_rpc_gss_lock);
562
563	/*
564	 * Start the client off with a short expiration time. We will
565	 * try to get a saner value from the client creds later.
566	 */
567	client->cl_state = CLIENT_NEW;
568	client->cl_locked = FALSE;
569	client->cl_expiration = time_uptime + 5*60;
570
571	return (client);
572}
573
574static void
575svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
576{
577	OM_uint32 min_stat;
578
579	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
580
581	if (client->cl_ctx)
582		gss_delete_sec_context(&min_stat,
583		    &client->cl_ctx, GSS_C_NO_BUFFER);
584
585	if (client->cl_cname)
586		gss_release_name(&min_stat, &client->cl_cname);
587
588	if (client->cl_rawcred.client_principal)
589		mem_free(client->cl_rawcred.client_principal,
590		    sizeof(*client->cl_rawcred.client_principal)
591		    + client->cl_rawcred.client_principal->len);
592
593	if (client->cl_cred)
594		crfree(client->cl_cred);
595
596	sx_destroy(&client->cl_lock);
597	mem_free(client, sizeof(*client));
598}
599
600/*
601 * Drop a reference to a client and free it if that was the last reference.
602 */
603static void
604svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
605{
606
607	if (!refcount_release(&client->cl_refs))
608		return;
609	svc_rpc_gss_destroy_client(client);
610}
611
612/*
613 * Remove a client from our global lists.
614 * Must be called with svc_rpc_gss_lock held.
615 */
616static void
617svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
618{
619	struct svc_rpc_gss_client_list *list;
620
621	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
622	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
623	TAILQ_REMOVE(list, client, cl_link);
624	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
625	svc_rpc_gss_client_count--;
626}
627
628/*
629 * Remove a client from our global lists and free it if we can.
630 */
631static void
632svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
633{
634	struct svc_rpc_gss_client_list *list;
635	struct svc_rpc_gss_client *tclient;
636
637	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
638	sx_xlock(&svc_rpc_gss_lock);
639	TAILQ_FOREACH(tclient, list, cl_link) {
640		/*
641		 * Make sure this client has not already been removed
642		 * from the lists by svc_rpc_gss_forget_client() or
643		 * svc_rpc_gss_forget_client_locked().
644		 */
645		if (client == tclient) {
646			svc_rpc_gss_forget_client_locked(client);
647			sx_xunlock(&svc_rpc_gss_lock);
648			svc_rpc_gss_release_client(client);
649			return;
650		}
651	}
652	sx_xunlock(&svc_rpc_gss_lock);
653}
654
655static void
656svc_rpc_gss_timeout_clients(void)
657{
658	struct svc_rpc_gss_client *client;
659	time_t now = time_uptime;
660
661	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
662
663	/*
664	 * First enforce the max client limit. We keep
665	 * svc_rpc_gss_clients in LRU order.
666	 */
667	sx_xlock(&svc_rpc_gss_lock);
668	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
669	while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
670		svc_rpc_gss_forget_client_locked(client);
671		sx_xunlock(&svc_rpc_gss_lock);
672		svc_rpc_gss_release_client(client);
673		sx_xlock(&svc_rpc_gss_lock);
674		client = TAILQ_LAST(&svc_rpc_gss_clients,
675		    svc_rpc_gss_client_list);
676	}
677again:
678	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
679		if (client->cl_state == CLIENT_STALE
680		    || now > client->cl_expiration) {
681			svc_rpc_gss_forget_client_locked(client);
682			sx_xunlock(&svc_rpc_gss_lock);
683			rpc_gss_log_debug("expiring client %p", client);
684			svc_rpc_gss_release_client(client);
685			sx_xlock(&svc_rpc_gss_lock);
686			goto again;
687		}
688	}
689	sx_xunlock(&svc_rpc_gss_lock);
690}
691
692#ifdef DEBUG
693/*
694 * OID<->string routines.  These are uuuuugly.
695 */
696static OM_uint32
697gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
698{
699	char		numstr[128];
700	unsigned long	number;
701	int		numshift;
702	size_t		string_length;
703	size_t		i;
704	unsigned char	*cp;
705	char		*bp;
706
707	/* Decoded according to krb5/gssapi_krb5.c */
708
709	/* First determine the size of the string */
710	string_length = 0;
711	number = 0;
712	numshift = 0;
713	cp = (unsigned char *) oid->elements;
714	number = (unsigned long) cp[0];
715	sprintf(numstr, "%ld ", number/40);
716	string_length += strlen(numstr);
717	sprintf(numstr, "%ld ", number%40);
718	string_length += strlen(numstr);
719	for (i=1; i<oid->length; i++) {
720		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
721			number = (number << 7) | (cp[i] & 0x7f);
722			numshift += 7;
723		}
724		else {
725			*minor_status = 0;
726			return(GSS_S_FAILURE);
727		}
728		if ((cp[i] & 0x80) == 0) {
729			sprintf(numstr, "%ld ", number);
730			string_length += strlen(numstr);
731			number = 0;
732			numshift = 0;
733		}
734	}
735	/*
736	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
737	 * here for "{ " and "}\0".
738	 */
739	string_length += 4;
740	if ((bp = (char *) mem_alloc(string_length))) {
741		strcpy(bp, "{ ");
742		number = (unsigned long) cp[0];
743		sprintf(numstr, "%ld ", number/40);
744		strcat(bp, numstr);
745		sprintf(numstr, "%ld ", number%40);
746		strcat(bp, numstr);
747		number = 0;
748		cp = (unsigned char *) oid->elements;
749		for (i=1; i<oid->length; i++) {
750			number = (number << 7) | (cp[i] & 0x7f);
751			if ((cp[i] & 0x80) == 0) {
752				sprintf(numstr, "%ld ", number);
753				strcat(bp, numstr);
754				number = 0;
755			}
756		}
757		strcat(bp, "}");
758		oid_str->length = strlen(bp)+1;
759		oid_str->value = (void *) bp;
760		*minor_status = 0;
761		return(GSS_S_COMPLETE);
762	}
763	*minor_status = 0;
764	return(GSS_S_FAILURE);
765}
766#endif
767
768static void
769svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
770    const gss_name_t name)
771{
772	OM_uint32		maj_stat, min_stat;
773	rpc_gss_ucred_t		*uc = &client->cl_ucred;
774	int			numgroups;
775
776	uc->uid = 65534;
777	uc->gid = 65534;
778	uc->gidlist = client->cl_gid_storage;
779
780	numgroups = NGROUPS;
781	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
782	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
783	if (GSS_ERROR(maj_stat))
784		uc->gidlen = 0;
785	else
786		uc->gidlen = numgroups;
787}
788
789static void
790svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
791{
792	static gss_OID_desc krb5_mech_oid =
793		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
794
795	/*
796	 * Attempt to translate mech type and service into a
797	 * 'pseudo flavor'. Hardwire in krb5 support for now.
798	 */
799	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
800		switch (client->cl_rawcred.service) {
801		case rpc_gss_svc_default:
802		case rpc_gss_svc_none:
803			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
804			break;
805		case rpc_gss_svc_integrity:
806			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
807			break;
808		case rpc_gss_svc_privacy:
809			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
810			break;
811		}
812	} else {
813		client->cl_rpcflavor = RPCSEC_GSS;
814	}
815}
816
817static bool_t
818svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
819			       struct svc_req *rqst,
820			       struct rpc_gss_init_res *gr,
821			       struct rpc_gss_cred *gc)
822{
823	gss_buffer_desc		recv_tok;
824	gss_OID			mech;
825	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
826	OM_uint32		cred_lifetime;
827	struct svc_rpc_gss_svc_name *sname;
828
829	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
830
831	/* Deserialize arguments. */
832	memset(&recv_tok, 0, sizeof(recv_tok));
833
834	if (!svc_getargs(rqst,
835		(xdrproc_t) xdr_gss_buffer_desc,
836		(caddr_t) &recv_tok)) {
837		client->cl_state = CLIENT_STALE;
838		return (FALSE);
839	}
840
841	/*
842	 * First time round, try all the server names we have until
843	 * one matches. Afterwards, stick with that one.
844	 */
845	sx_xlock(&svc_rpc_gss_lock);
846	if (!client->cl_sname) {
847		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
848			if (sname->sn_program == rqst->rq_prog
849			    && sname->sn_version == rqst->rq_vers) {
850			retry:
851				gr->gr_major = gss_accept_sec_context(
852					&gr->gr_minor,
853					&client->cl_ctx,
854					sname->sn_cred,
855					&recv_tok,
856					GSS_C_NO_CHANNEL_BINDINGS,
857					&client->cl_cname,
858					&mech,
859					&gr->gr_token,
860					&ret_flags,
861					&cred_lifetime,
862					&client->cl_creds);
863				if (gr->gr_major ==
864				    GSS_S_CREDENTIALS_EXPIRED) {
865					/*
866					 * Either our creds really did
867					 * expire or gssd was
868					 * restarted.
869					 */
870					if (rpc_gss_acquire_svc_cred(sname))
871						goto retry;
872				}
873				client->cl_sname = sname;
874				break;
875			}
876		}
877		if (!sname) {
878			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
879			    (char *) &recv_tok);
880			sx_xunlock(&svc_rpc_gss_lock);
881			return (FALSE);
882		}
883	} else {
884		gr->gr_major = gss_accept_sec_context(
885			&gr->gr_minor,
886			&client->cl_ctx,
887			client->cl_sname->sn_cred,
888			&recv_tok,
889			GSS_C_NO_CHANNEL_BINDINGS,
890			&client->cl_cname,
891			&mech,
892			&gr->gr_token,
893			&ret_flags,
894			&cred_lifetime,
895			NULL);
896	}
897	sx_xunlock(&svc_rpc_gss_lock);
898
899	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
900
901	/*
902	 * If we get an error from gss_accept_sec_context, send the
903	 * reply anyway so that the client gets a chance to see what
904	 * is wrong.
905	 */
906	if (gr->gr_major != GSS_S_COMPLETE &&
907	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
908		rpc_gss_log_status("accept_sec_context", client->cl_mech,
909		    gr->gr_major, gr->gr_minor);
910		client->cl_state = CLIENT_STALE;
911		return (TRUE);
912	}
913
914	gr->gr_handle.value = &client->cl_id;
915	gr->gr_handle.length = sizeof(client->cl_id);
916	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
917
918	/* Save client info. */
919	client->cl_mech = mech;
920	client->cl_qop = GSS_C_QOP_DEFAULT;
921	client->cl_done_callback = FALSE;
922
923	if (gr->gr_major == GSS_S_COMPLETE) {
924		gss_buffer_desc	export_name;
925
926		/*
927		 * Change client expiration time to be near when the
928		 * client creds expire (or 24 hours if we can't figure
929		 * that out).
930		 */
931		if (cred_lifetime == GSS_C_INDEFINITE)
932			cred_lifetime = time_uptime + 24*60*60;
933
934		client->cl_expiration = time_uptime + cred_lifetime;
935
936		/*
937		 * Fill in cred details in the rawcred structure.
938		 */
939		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
940		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
941		maj_stat = gss_export_name(&min_stat, client->cl_cname,
942		    &export_name);
943		if (maj_stat != GSS_S_COMPLETE) {
944			rpc_gss_log_status("gss_export_name", client->cl_mech,
945			    maj_stat, min_stat);
946			return (FALSE);
947		}
948		client->cl_rawcred.client_principal =
949			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
950			    + export_name.length);
951		client->cl_rawcred.client_principal->len = export_name.length;
952		memcpy(client->cl_rawcred.client_principal->name,
953		    export_name.value, export_name.length);
954		gss_release_buffer(&min_stat, &export_name);
955		client->cl_rawcred.svc_principal =
956			client->cl_sname->sn_principal;
957		client->cl_rawcred.service = gc->gc_svc;
958
959		/*
960		 * Use gss_pname_to_uid to map to unix creds. For
961		 * kerberos5, this uses krb5_aname_to_localname.
962		 */
963		svc_rpc_gss_build_ucred(client, client->cl_cname);
964		svc_rpc_gss_set_flavor(client);
965		gss_release_name(&min_stat, &client->cl_cname);
966
967#ifdef DEBUG
968		{
969			gss_buffer_desc mechname;
970
971			gss_oid_to_str(&min_stat, mech, &mechname);
972
973			rpc_gss_log_debug("accepted context for %s with "
974			    "<mech %.*s, qop %d, svc %d>",
975			    client->cl_rawcred.client_principal->name,
976			    mechname.length, (char *)mechname.value,
977			    client->cl_qop, client->cl_rawcred.service);
978
979			gss_release_buffer(&min_stat, &mechname);
980		}
981#endif /* DEBUG */
982	}
983	return (TRUE);
984}
985
986static bool_t
987svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
988    gss_qop_t *qop, rpc_gss_proc_t gcproc)
989{
990	struct opaque_auth	*oa;
991	gss_buffer_desc		 rpcbuf, checksum;
992	OM_uint32		 maj_stat, min_stat;
993	gss_qop_t		 qop_state;
994	int32_t			 rpchdr[128 / sizeof(int32_t)];
995	int32_t			*buf;
996
997	rpc_gss_log_debug("in svc_rpc_gss_validate()");
998
999	memset(rpchdr, 0, sizeof(rpchdr));
1000
1001	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1002	buf = rpchdr;
1003	IXDR_PUT_LONG(buf, msg->rm_xid);
1004	IXDR_PUT_ENUM(buf, msg->rm_direction);
1005	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1006	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1007	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1008	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1009	oa = &msg->rm_call.cb_cred;
1010	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1011	IXDR_PUT_LONG(buf, oa->oa_length);
1012	if (oa->oa_length) {
1013		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1014		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1015	}
1016	rpcbuf.value = rpchdr;
1017	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1018
1019	checksum.value = msg->rm_call.cb_verf.oa_base;
1020	checksum.length = msg->rm_call.cb_verf.oa_length;
1021
1022	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1023				  &qop_state);
1024
1025	if (maj_stat != GSS_S_COMPLETE) {
1026		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1027		    maj_stat, min_stat);
1028		/*
1029		 * A bug in some versions of the Linux client generates a
1030		 * Destroy operation with a bogus encrypted checksum. Deleting
1031		 * the credential handle for that case causes the mount to fail.
1032		 * Since the checksum is bogus (gss_verify_mic() failed), it
1033		 * doesn't make sense to destroy the handle and not doing so
1034		 * fixes the Linux mount.
1035		 */
1036		if (gcproc != RPCSEC_GSS_DESTROY)
1037			client->cl_state = CLIENT_STALE;
1038		return (FALSE);
1039	}
1040
1041	*qop = qop_state;
1042	return (TRUE);
1043}
1044
1045static bool_t
1046svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1047    struct svc_req *rqst, u_int seq)
1048{
1049	gss_buffer_desc		signbuf;
1050	gss_buffer_desc		mic;
1051	OM_uint32		maj_stat, min_stat;
1052	uint32_t		nseq;
1053
1054	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1055
1056	nseq = htonl(seq);
1057	signbuf.value = &nseq;
1058	signbuf.length = sizeof(nseq);
1059
1060	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1061	    &signbuf, &mic);
1062
1063	if (maj_stat != GSS_S_COMPLETE) {
1064		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1065		client->cl_state = CLIENT_STALE;
1066		return (FALSE);
1067	}
1068
1069	KASSERT(mic.length <= MAX_AUTH_BYTES,
1070	    ("MIC too large for RPCSEC_GSS"));
1071
1072	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1073	rqst->rq_verf.oa_length = mic.length;
1074	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1075
1076	gss_release_buffer(&min_stat, &mic);
1077
1078	return (TRUE);
1079}
1080
1081static bool_t
1082svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1083{
1084	struct svc_rpc_gss_callback *scb;
1085	rpc_gss_lock_t	lock;
1086	void		*cookie;
1087	bool_t		cb_res;
1088	bool_t		result;
1089
1090	/*
1091	 * See if we have a callback for this guy.
1092	 */
1093	result = TRUE;
1094	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1095		if (scb->cb_callback.program == rqst->rq_prog
1096		    && scb->cb_callback.version == rqst->rq_vers) {
1097			/*
1098			 * This one matches. Call the callback and see
1099			 * if it wants to veto or something.
1100			 */
1101			lock.locked = FALSE;
1102			lock.raw_cred = &client->cl_rawcred;
1103			cb_res = scb->cb_callback.callback(rqst,
1104			    client->cl_creds,
1105			    client->cl_ctx,
1106			    &lock,
1107			    &cookie);
1108
1109			if (!cb_res) {
1110				client->cl_state = CLIENT_STALE;
1111				result = FALSE;
1112				break;
1113			}
1114
1115			/*
1116			 * The callback accepted the connection - it
1117			 * is responsible for freeing client->cl_creds
1118			 * now.
1119			 */
1120			client->cl_creds = GSS_C_NO_CREDENTIAL;
1121			client->cl_locked = lock.locked;
1122			client->cl_cookie = cookie;
1123			return (TRUE);
1124		}
1125	}
1126
1127	/*
1128	 * Either no callback exists for this program/version or one
1129	 * of the callbacks rejected the connection. We just need to
1130	 * clean up the delegated client creds, if any.
1131	 */
1132	if (client->cl_creds) {
1133		OM_uint32 min_ver;
1134		gss_release_cred(&min_ver, &client->cl_creds);
1135	}
1136	return (result);
1137}
1138
1139static bool_t
1140svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1141{
1142	u_int32_t offset;
1143	int word, bit;
1144	bool_t result;
1145
1146	sx_xlock(&client->cl_lock);
1147	if (seq <= client->cl_seqlast) {
1148		/*
1149		 * The request sequence number is less than
1150		 * the largest we have seen so far. If it is
1151		 * outside the window or if we have seen a
1152		 * request with this sequence before, silently
1153		 * discard it.
1154		 */
1155		offset = client->cl_seqlast - seq;
1156		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1157			result = FALSE;
1158			goto out;
1159		}
1160		word = offset / 32;
1161		bit = offset % 32;
1162		if (client->cl_seqmask[word] & (1 << bit)) {
1163			result = FALSE;
1164			goto out;
1165		}
1166	}
1167
1168	result = TRUE;
1169out:
1170	sx_xunlock(&client->cl_lock);
1171	return (result);
1172}
1173
1174static void
1175svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1176{
1177	int offset, i, word, bit;
1178	uint32_t carry, newcarry;
1179
1180	sx_xlock(&client->cl_lock);
1181	if (seq > client->cl_seqlast) {
1182		/*
1183		 * This request has a sequence number greater
1184		 * than any we have seen so far. Advance the
1185		 * seq window and set bit zero of the window
1186		 * (which corresponds to the new sequence
1187		 * number)
1188		 */
1189		offset = seq - client->cl_seqlast;
1190		while (offset > 32) {
1191			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1192			     i > 0; i--) {
1193				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1194			}
1195			client->cl_seqmask[0] = 0;
1196			offset -= 32;
1197		}
1198		carry = 0;
1199		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1200			newcarry = client->cl_seqmask[i] >> (32 - offset);
1201			client->cl_seqmask[i] =
1202				(client->cl_seqmask[i] << offset) | carry;
1203			carry = newcarry;
1204		}
1205		client->cl_seqmask[0] |= 1;
1206		client->cl_seqlast = seq;
1207	} else {
1208		offset = client->cl_seqlast - seq;
1209		word = offset / 32;
1210		bit = offset % 32;
1211		client->cl_seqmask[word] |= (1 << bit);
1212	}
1213	sx_xunlock(&client->cl_lock);
1214}
1215
1216enum auth_stat
1217svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1218
1219{
1220	OM_uint32		 min_stat;
1221	XDR	 		 xdrs;
1222	struct svc_rpc_gss_cookedcred *cc;
1223	struct svc_rpc_gss_client *client;
1224	struct rpc_gss_cred	 gc;
1225	struct rpc_gss_init_res	 gr;
1226	gss_qop_t		 qop;
1227	int			 call_stat;
1228	enum auth_stat		 result;
1229
1230	rpc_gss_log_debug("in svc_rpc_gss()");
1231
1232	/* Garbage collect old clients. */
1233	svc_rpc_gss_timeout_clients();
1234
1235	/* Initialize reply. */
1236	rqst->rq_verf = _null_auth;
1237
1238	/* Deserialize client credentials. */
1239	if (rqst->rq_cred.oa_length <= 0)
1240		return (AUTH_BADCRED);
1241
1242	memset(&gc, 0, sizeof(gc));
1243
1244	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1245	    rqst->rq_cred.oa_length, XDR_DECODE);
1246
1247	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1248		XDR_DESTROY(&xdrs);
1249		return (AUTH_BADCRED);
1250	}
1251	XDR_DESTROY(&xdrs);
1252
1253	client = NULL;
1254
1255	/* Check version. */
1256	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1257		result = AUTH_BADCRED;
1258		goto out;
1259	}
1260
1261	/* Check the proc and find the client (or create it) */
1262	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1263		if (gc.gc_handle.length != 0) {
1264			result = AUTH_BADCRED;
1265			goto out;
1266		}
1267		client = svc_rpc_gss_create_client();
1268		refcount_acquire(&client->cl_refs);
1269	} else {
1270		struct svc_rpc_gss_clientid *p;
1271		if (gc.gc_handle.length != sizeof(*p)) {
1272			result = AUTH_BADCRED;
1273			goto out;
1274		}
1275		p = gc.gc_handle.value;
1276		client = svc_rpc_gss_find_client(p);
1277		if (!client) {
1278			/*
1279			 * Can't find the client - we may have
1280			 * destroyed it - tell the other side to
1281			 * re-authenticate.
1282			 */
1283			result = RPCSEC_GSS_CREDPROBLEM;
1284			goto out;
1285		}
1286	}
1287	cc = rqst->rq_clntcred;
1288	cc->cc_client = client;
1289	cc->cc_service = gc.gc_svc;
1290	cc->cc_seq = gc.gc_seq;
1291
1292	/*
1293	 * The service and sequence number must be ignored for
1294	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1295	 */
1296	if (gc.gc_proc != RPCSEC_GSS_INIT
1297	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1298		/*
1299		 * Check for sequence number overflow.
1300		 */
1301		if (gc.gc_seq >= MAXSEQ) {
1302			result = RPCSEC_GSS_CTXPROBLEM;
1303			goto out;
1304		}
1305
1306		/*
1307		 * Check for valid service.
1308		 */
1309		if (gc.gc_svc != rpc_gss_svc_none &&
1310		    gc.gc_svc != rpc_gss_svc_integrity &&
1311		    gc.gc_svc != rpc_gss_svc_privacy) {
1312			result = AUTH_BADCRED;
1313			goto out;
1314		}
1315	}
1316
1317	/* Handle RPCSEC_GSS control procedure. */
1318	switch (gc.gc_proc) {
1319
1320	case RPCSEC_GSS_INIT:
1321	case RPCSEC_GSS_CONTINUE_INIT:
1322		if (rqst->rq_proc != NULLPROC) {
1323			result = AUTH_REJECTEDCRED;
1324			break;
1325		}
1326
1327		memset(&gr, 0, sizeof(gr));
1328		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1329			result = AUTH_REJECTEDCRED;
1330			break;
1331		}
1332
1333		if (gr.gr_major == GSS_S_COMPLETE) {
1334			/*
1335			 * We borrow the space for the call verf to
1336			 * pack our reply verf.
1337			 */
1338			rqst->rq_verf = msg->rm_call.cb_verf;
1339			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1340				result = AUTH_REJECTEDCRED;
1341				break;
1342			}
1343		} else {
1344			rqst->rq_verf = _null_auth;
1345		}
1346
1347		call_stat = svc_sendreply(rqst,
1348		    (xdrproc_t) xdr_rpc_gss_init_res,
1349		    (caddr_t) &gr);
1350
1351		gss_release_buffer(&min_stat, &gr.gr_token);
1352
1353		if (!call_stat) {
1354			result = AUTH_FAILED;
1355			break;
1356		}
1357
1358		if (gr.gr_major == GSS_S_COMPLETE)
1359			client->cl_state = CLIENT_ESTABLISHED;
1360
1361		result = RPCSEC_GSS_NODISPATCH;
1362		break;
1363
1364	case RPCSEC_GSS_DATA:
1365	case RPCSEC_GSS_DESTROY:
1366		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1367			result = RPCSEC_GSS_NODISPATCH;
1368			break;
1369		}
1370
1371		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1372			result = RPCSEC_GSS_CREDPROBLEM;
1373			break;
1374		}
1375
1376		/*
1377		 * We borrow the space for the call verf to pack our
1378		 * reply verf.
1379		 */
1380		rqst->rq_verf = msg->rm_call.cb_verf;
1381		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1382			result = RPCSEC_GSS_CTXPROBLEM;
1383			break;
1384		}
1385
1386		svc_rpc_gss_update_seq(client, gc.gc_seq);
1387
1388		/*
1389		 * Change the SVCAUTH ops on the request to point at
1390		 * our own code so that we can unwrap the arguments
1391		 * and wrap the result. The caller will re-set this on
1392		 * every request to point to a set of null wrap/unwrap
1393		 * methods. Acquire an extra reference to the client
1394		 * which will be released by svc_rpc_gss_release()
1395		 * after the request has finished processing.
1396		 */
1397		refcount_acquire(&client->cl_refs);
1398		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1399		rqst->rq_auth.svc_ah_private = cc;
1400
1401		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1402			/*
1403			 * We might be ready to do a callback to the server to
1404			 * see if it wants to accept/reject the connection.
1405			 */
1406			sx_xlock(&client->cl_lock);
1407			if (!client->cl_done_callback) {
1408				client->cl_done_callback = TRUE;
1409				client->cl_qop = qop;
1410				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1411					client->cl_rawcred.mechanism, qop);
1412				if (!svc_rpc_gss_callback(client, rqst)) {
1413					result = AUTH_REJECTEDCRED;
1414					sx_xunlock(&client->cl_lock);
1415					break;
1416				}
1417			}
1418			sx_xunlock(&client->cl_lock);
1419
1420			/*
1421			 * If the server has locked this client to a
1422			 * particular service+qop pair, enforce that
1423			 * restriction now.
1424			 */
1425			if (client->cl_locked) {
1426				if (client->cl_rawcred.service != gc.gc_svc) {
1427					result = AUTH_FAILED;
1428					break;
1429				} else if (client->cl_qop != qop) {
1430					result = AUTH_BADVERF;
1431					break;
1432				}
1433			}
1434
1435			/*
1436			 * If the qop changed, look up the new qop
1437			 * name for rawcred.
1438			 */
1439			if (client->cl_qop != qop) {
1440				client->cl_qop = qop;
1441				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1442					client->cl_rawcred.mechanism, qop);
1443			}
1444
1445			/*
1446			 * Make sure we use the right service value
1447			 * for unwrap/wrap.
1448			 */
1449			if (client->cl_rawcred.service != gc.gc_svc) {
1450				client->cl_rawcred.service = gc.gc_svc;
1451				svc_rpc_gss_set_flavor(client);
1452			}
1453
1454			result = AUTH_OK;
1455		} else {
1456			if (rqst->rq_proc != NULLPROC) {
1457				result = AUTH_REJECTEDCRED;
1458				break;
1459			}
1460
1461			call_stat = svc_sendreply(rqst,
1462			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1463
1464			if (!call_stat) {
1465				result = AUTH_FAILED;
1466				break;
1467			}
1468
1469			svc_rpc_gss_forget_client(client);
1470
1471			result = RPCSEC_GSS_NODISPATCH;
1472			break;
1473		}
1474		break;
1475
1476	default:
1477		result = AUTH_BADCRED;
1478		break;
1479	}
1480out:
1481	if (client)
1482		svc_rpc_gss_release_client(client);
1483
1484	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1485	return (result);
1486}
1487
1488static bool_t
1489svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1490{
1491	struct svc_rpc_gss_cookedcred *cc;
1492	struct svc_rpc_gss_client *client;
1493
1494	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1495
1496	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1497	client = cc->cc_client;
1498	if (client->cl_state != CLIENT_ESTABLISHED
1499	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1500		return (TRUE);
1501	}
1502
1503	return (xdr_rpc_gss_wrap_data(mp,
1504		client->cl_ctx, client->cl_qop,
1505		cc->cc_service, cc->cc_seq));
1506}
1507
1508static bool_t
1509svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1510{
1511	struct svc_rpc_gss_cookedcred *cc;
1512	struct svc_rpc_gss_client *client;
1513
1514	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1515
1516	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1517	client = cc->cc_client;
1518	if (client->cl_state != CLIENT_ESTABLISHED
1519	    || cc->cc_service == rpc_gss_svc_none) {
1520		return (TRUE);
1521	}
1522
1523	return (xdr_rpc_gss_unwrap_data(mp,
1524		client->cl_ctx, client->cl_qop,
1525		cc->cc_service, cc->cc_seq));
1526}
1527
1528static void
1529svc_rpc_gss_release(SVCAUTH *auth)
1530{
1531	struct svc_rpc_gss_cookedcred *cc;
1532	struct svc_rpc_gss_client *client;
1533
1534	rpc_gss_log_debug("in svc_rpc_gss_release()");
1535
1536	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1537	client = cc->cc_client;
1538	svc_rpc_gss_release_client(client);
1539}
1540