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