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