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 346344 2019-04-18 02:54:07Z rmacklem $");
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
547	/*
548	 * Set the initial value of cl_refs to two.  One for the caller
549	 * and the other to hold onto the client structure until it expires.
550	 */
551	refcount_init(&client->cl_refs, 2);
552	sx_init(&client->cl_lock, "GSS-client");
553	getcredhostid(curthread->td_ucred, &hostid);
554	client->cl_id.ci_hostid = hostid;
555	client->cl_id.ci_boottime = boottime.tv_sec;
556	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
557
558	/*
559	 * Start the client off with a short expiration time. We will
560	 * try to get a saner value from the client creds later.
561	 */
562	client->cl_state = CLIENT_NEW;
563	client->cl_locked = FALSE;
564	client->cl_expiration = time_uptime + 5*60;
565
566	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
567	sx_xlock(&svc_rpc_gss_lock);
568	TAILQ_INSERT_HEAD(list, client, cl_link);
569	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
570	svc_rpc_gss_client_count++;
571	sx_xunlock(&svc_rpc_gss_lock);
572	return (client);
573}
574
575static void
576svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
577{
578	OM_uint32 min_stat;
579
580	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
581
582	if (client->cl_ctx)
583		gss_delete_sec_context(&min_stat,
584		    &client->cl_ctx, GSS_C_NO_BUFFER);
585
586	if (client->cl_cname)
587		gss_release_name(&min_stat, &client->cl_cname);
588
589	if (client->cl_rawcred.client_principal)
590		mem_free(client->cl_rawcred.client_principal,
591		    sizeof(*client->cl_rawcred.client_principal)
592		    + client->cl_rawcred.client_principal->len);
593
594	if (client->cl_cred)
595		crfree(client->cl_cred);
596
597	sx_destroy(&client->cl_lock);
598	mem_free(client, sizeof(*client));
599}
600
601/*
602 * Drop a reference to a client and free it if that was the last reference.
603 */
604static void
605svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
606{
607
608	if (!refcount_release(&client->cl_refs))
609		return;
610	svc_rpc_gss_destroy_client(client);
611}
612
613/*
614 * Remove a client from our global lists.
615 * Must be called with svc_rpc_gss_lock held.
616 */
617static void
618svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
619{
620	struct svc_rpc_gss_client_list *list;
621
622	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
623	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
624	TAILQ_REMOVE(list, client, cl_link);
625	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
626	svc_rpc_gss_client_count--;
627}
628
629/*
630 * Remove a client from our global lists and free it if we can.
631 */
632static void
633svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
634{
635	struct svc_rpc_gss_client_list *list;
636	struct svc_rpc_gss_client *tclient;
637
638	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
639	sx_xlock(&svc_rpc_gss_lock);
640	TAILQ_FOREACH(tclient, list, cl_link) {
641		/*
642		 * Make sure this client has not already been removed
643		 * from the lists by svc_rpc_gss_forget_client() or
644		 * svc_rpc_gss_forget_client_locked().
645		 */
646		if (client == tclient) {
647			svc_rpc_gss_forget_client_locked(client);
648			sx_xunlock(&svc_rpc_gss_lock);
649			svc_rpc_gss_release_client(client);
650			return;
651		}
652	}
653	sx_xunlock(&svc_rpc_gss_lock);
654}
655
656static void
657svc_rpc_gss_timeout_clients(void)
658{
659	struct svc_rpc_gss_client *client;
660	time_t now = time_uptime;
661
662	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
663
664	/*
665	 * First enforce the max client limit. We keep
666	 * svc_rpc_gss_clients in LRU order.
667	 */
668	sx_xlock(&svc_rpc_gss_lock);
669	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
670	while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
671		svc_rpc_gss_forget_client_locked(client);
672		sx_xunlock(&svc_rpc_gss_lock);
673		svc_rpc_gss_release_client(client);
674		sx_xlock(&svc_rpc_gss_lock);
675		client = TAILQ_LAST(&svc_rpc_gss_clients,
676		    svc_rpc_gss_client_list);
677	}
678again:
679	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
680		if (client->cl_state == CLIENT_STALE
681		    || now > client->cl_expiration) {
682			svc_rpc_gss_forget_client_locked(client);
683			sx_xunlock(&svc_rpc_gss_lock);
684			rpc_gss_log_debug("expiring client %p", client);
685			svc_rpc_gss_release_client(client);
686			sx_xlock(&svc_rpc_gss_lock);
687			goto again;
688		}
689	}
690	sx_xunlock(&svc_rpc_gss_lock);
691}
692
693#ifdef DEBUG
694/*
695 * OID<->string routines.  These are uuuuugly.
696 */
697static OM_uint32
698gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
699{
700	char		numstr[128];
701	unsigned long	number;
702	int		numshift;
703	size_t		string_length;
704	size_t		i;
705	unsigned char	*cp;
706	char		*bp;
707
708	/* Decoded according to krb5/gssapi_krb5.c */
709
710	/* First determine the size of the string */
711	string_length = 0;
712	number = 0;
713	numshift = 0;
714	cp = (unsigned char *) oid->elements;
715	number = (unsigned long) cp[0];
716	sprintf(numstr, "%ld ", number/40);
717	string_length += strlen(numstr);
718	sprintf(numstr, "%ld ", number%40);
719	string_length += strlen(numstr);
720	for (i=1; i<oid->length; i++) {
721		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
722			number = (number << 7) | (cp[i] & 0x7f);
723			numshift += 7;
724		}
725		else {
726			*minor_status = 0;
727			return(GSS_S_FAILURE);
728		}
729		if ((cp[i] & 0x80) == 0) {
730			sprintf(numstr, "%ld ", number);
731			string_length += strlen(numstr);
732			number = 0;
733			numshift = 0;
734		}
735	}
736	/*
737	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
738	 * here for "{ " and "}\0".
739	 */
740	string_length += 4;
741	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
742		strcpy(bp, "{ ");
743		number = (unsigned long) cp[0];
744		sprintf(numstr, "%ld ", number/40);
745		strcat(bp, numstr);
746		sprintf(numstr, "%ld ", number%40);
747		strcat(bp, numstr);
748		number = 0;
749		cp = (unsigned char *) oid->elements;
750		for (i=1; i<oid->length; i++) {
751			number = (number << 7) | (cp[i] & 0x7f);
752			if ((cp[i] & 0x80) == 0) {
753				sprintf(numstr, "%ld ", number);
754				strcat(bp, numstr);
755				number = 0;
756			}
757		}
758		strcat(bp, "}");
759		oid_str->length = strlen(bp)+1;
760		oid_str->value = (void *) bp;
761		*minor_status = 0;
762		return(GSS_S_COMPLETE);
763	}
764	*minor_status = 0;
765	return(GSS_S_FAILURE);
766}
767#endif
768
769static void
770svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
771    const gss_name_t name)
772{
773	OM_uint32		maj_stat, min_stat;
774	rpc_gss_ucred_t		*uc = &client->cl_ucred;
775	int			numgroups;
776
777	uc->uid = 65534;
778	uc->gid = 65534;
779	uc->gidlist = client->cl_gid_storage;
780
781	numgroups = NGROUPS;
782	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
783	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
784	if (GSS_ERROR(maj_stat))
785		uc->gidlen = 0;
786	else
787		uc->gidlen = numgroups;
788}
789
790static void
791svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
792{
793	static gss_OID_desc krb5_mech_oid =
794		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
795
796	/*
797	 * Attempt to translate mech type and service into a
798	 * 'pseudo flavor'. Hardwire in krb5 support for now.
799	 */
800	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
801		switch (client->cl_rawcred.service) {
802		case rpc_gss_svc_default:
803		case rpc_gss_svc_none:
804			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
805			break;
806		case rpc_gss_svc_integrity:
807			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
808			break;
809		case rpc_gss_svc_privacy:
810			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
811			break;
812		}
813	} else {
814		client->cl_rpcflavor = RPCSEC_GSS;
815	}
816}
817
818static bool_t
819svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
820			       struct svc_req *rqst,
821			       struct rpc_gss_init_res *gr,
822			       struct rpc_gss_cred *gc)
823{
824	gss_buffer_desc		recv_tok;
825	gss_OID			mech;
826	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
827	OM_uint32		cred_lifetime;
828	struct svc_rpc_gss_svc_name *sname;
829
830	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
831
832	/* Deserialize arguments. */
833	memset(&recv_tok, 0, sizeof(recv_tok));
834
835	if (!svc_getargs(rqst,
836		(xdrproc_t) xdr_gss_buffer_desc,
837		(caddr_t) &recv_tok)) {
838		client->cl_state = CLIENT_STALE;
839		return (FALSE);
840	}
841
842	/*
843	 * First time round, try all the server names we have until
844	 * one matches. Afterwards, stick with that one.
845	 */
846	sx_xlock(&svc_rpc_gss_lock);
847	if (!client->cl_sname) {
848		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
849			if (sname->sn_program == rqst->rq_prog
850			    && sname->sn_version == rqst->rq_vers) {
851			retry:
852				gr->gr_major = gss_accept_sec_context(
853					&gr->gr_minor,
854					&client->cl_ctx,
855					sname->sn_cred,
856					&recv_tok,
857					GSS_C_NO_CHANNEL_BINDINGS,
858					&client->cl_cname,
859					&mech,
860					&gr->gr_token,
861					&ret_flags,
862					&cred_lifetime,
863					&client->cl_creds);
864				if (gr->gr_major ==
865				    GSS_S_CREDENTIALS_EXPIRED) {
866					/*
867					 * Either our creds really did
868					 * expire or gssd was
869					 * restarted.
870					 */
871					if (rpc_gss_acquire_svc_cred(sname))
872						goto retry;
873				}
874				client->cl_sname = sname;
875				break;
876			}
877		}
878		if (!sname) {
879			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
880			    (char *) &recv_tok);
881			sx_xunlock(&svc_rpc_gss_lock);
882			return (FALSE);
883		}
884	} else {
885		gr->gr_major = gss_accept_sec_context(
886			&gr->gr_minor,
887			&client->cl_ctx,
888			client->cl_sname->sn_cred,
889			&recv_tok,
890			GSS_C_NO_CHANNEL_BINDINGS,
891			&client->cl_cname,
892			&mech,
893			&gr->gr_token,
894			&ret_flags,
895			&cred_lifetime,
896			NULL);
897	}
898	sx_xunlock(&svc_rpc_gss_lock);
899
900	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
901
902	/*
903	 * If we get an error from gss_accept_sec_context, send the
904	 * reply anyway so that the client gets a chance to see what
905	 * is wrong.
906	 */
907	if (gr->gr_major != GSS_S_COMPLETE &&
908	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
909		rpc_gss_log_status("accept_sec_context", client->cl_mech,
910		    gr->gr_major, gr->gr_minor);
911		client->cl_state = CLIENT_STALE;
912		return (TRUE);
913	}
914
915	gr->gr_handle.value = &client->cl_id;
916	gr->gr_handle.length = sizeof(client->cl_id);
917	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
918
919	/* Save client info. */
920	client->cl_mech = mech;
921	client->cl_qop = GSS_C_QOP_DEFAULT;
922	client->cl_done_callback = FALSE;
923
924	if (gr->gr_major == GSS_S_COMPLETE) {
925		gss_buffer_desc	export_name;
926
927		/*
928		 * Change client expiration time to be near when the
929		 * client creds expire (or 24 hours if we can't figure
930		 * that out).
931		 */
932		if (cred_lifetime == GSS_C_INDEFINITE)
933			cred_lifetime = time_uptime + 24*60*60;
934
935		client->cl_expiration = time_uptime + cred_lifetime;
936
937		/*
938		 * Fill in cred details in the rawcred structure.
939		 */
940		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
941		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
942		maj_stat = gss_export_name(&min_stat, client->cl_cname,
943		    &export_name);
944		if (maj_stat != GSS_S_COMPLETE) {
945			rpc_gss_log_status("gss_export_name", client->cl_mech,
946			    maj_stat, min_stat);
947			return (FALSE);
948		}
949		client->cl_rawcred.client_principal =
950			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
951			    + export_name.length);
952		client->cl_rawcred.client_principal->len = export_name.length;
953		memcpy(client->cl_rawcred.client_principal->name,
954		    export_name.value, export_name.length);
955		gss_release_buffer(&min_stat, &export_name);
956		client->cl_rawcred.svc_principal =
957			client->cl_sname->sn_principal;
958		client->cl_rawcred.service = gc->gc_svc;
959
960		/*
961		 * Use gss_pname_to_uid to map to unix creds. For
962		 * kerberos5, this uses krb5_aname_to_localname.
963		 */
964		svc_rpc_gss_build_ucred(client, client->cl_cname);
965		svc_rpc_gss_set_flavor(client);
966		gss_release_name(&min_stat, &client->cl_cname);
967
968#ifdef DEBUG
969		{
970			gss_buffer_desc mechname;
971
972			gss_oid_to_str(&min_stat, mech, &mechname);
973
974			rpc_gss_log_debug("accepted context for %s with "
975			    "<mech %.*s, qop %d, svc %d>",
976			    client->cl_rawcred.client_principal->name,
977			    mechname.length, (char *)mechname.value,
978			    client->cl_qop, client->cl_rawcred.service);
979
980			gss_release_buffer(&min_stat, &mechname);
981		}
982#endif /* DEBUG */
983	}
984	return (TRUE);
985}
986
987static bool_t
988svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
989    gss_qop_t *qop, rpc_gss_proc_t gcproc)
990{
991	struct opaque_auth	*oa;
992	gss_buffer_desc		 rpcbuf, checksum;
993	OM_uint32		 maj_stat, min_stat;
994	gss_qop_t		 qop_state;
995	int32_t			 rpchdr[128 / sizeof(int32_t)];
996	int32_t			*buf;
997
998	rpc_gss_log_debug("in svc_rpc_gss_validate()");
999
1000	memset(rpchdr, 0, sizeof(rpchdr));
1001
1002	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1003	buf = rpchdr;
1004	IXDR_PUT_LONG(buf, msg->rm_xid);
1005	IXDR_PUT_ENUM(buf, msg->rm_direction);
1006	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1007	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1008	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1009	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1010	oa = &msg->rm_call.cb_cred;
1011	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1012	IXDR_PUT_LONG(buf, oa->oa_length);
1013	if (oa->oa_length) {
1014		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1015		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1016	}
1017	rpcbuf.value = rpchdr;
1018	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1019
1020	checksum.value = msg->rm_call.cb_verf.oa_base;
1021	checksum.length = msg->rm_call.cb_verf.oa_length;
1022
1023	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1024				  &qop_state);
1025
1026	if (maj_stat != GSS_S_COMPLETE) {
1027		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1028		    maj_stat, min_stat);
1029		/*
1030		 * A bug in some versions of the Linux client generates a
1031		 * Destroy operation with a bogus encrypted checksum. Deleting
1032		 * the credential handle for that case causes the mount to fail.
1033		 * Since the checksum is bogus (gss_verify_mic() failed), it
1034		 * doesn't make sense to destroy the handle and not doing so
1035		 * fixes the Linux mount.
1036		 */
1037		if (gcproc != RPCSEC_GSS_DESTROY)
1038			client->cl_state = CLIENT_STALE;
1039		return (FALSE);
1040	}
1041
1042	*qop = qop_state;
1043	return (TRUE);
1044}
1045
1046static bool_t
1047svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1048    struct svc_req *rqst, u_int seq)
1049{
1050	gss_buffer_desc		signbuf;
1051	gss_buffer_desc		mic;
1052	OM_uint32		maj_stat, min_stat;
1053	uint32_t		nseq;
1054
1055	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1056
1057	nseq = htonl(seq);
1058	signbuf.value = &nseq;
1059	signbuf.length = sizeof(nseq);
1060
1061	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1062	    &signbuf, &mic);
1063
1064	if (maj_stat != GSS_S_COMPLETE) {
1065		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1066		client->cl_state = CLIENT_STALE;
1067		return (FALSE);
1068	}
1069
1070	KASSERT(mic.length <= MAX_AUTH_BYTES,
1071	    ("MIC too large for RPCSEC_GSS"));
1072
1073	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1074	rqst->rq_verf.oa_length = mic.length;
1075	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1076
1077	gss_release_buffer(&min_stat, &mic);
1078
1079	return (TRUE);
1080}
1081
1082static bool_t
1083svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1084{
1085	struct svc_rpc_gss_callback *scb;
1086	rpc_gss_lock_t	lock;
1087	void		*cookie;
1088	bool_t		cb_res;
1089	bool_t		result;
1090
1091	/*
1092	 * See if we have a callback for this guy.
1093	 */
1094	result = TRUE;
1095	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1096		if (scb->cb_callback.program == rqst->rq_prog
1097		    && scb->cb_callback.version == rqst->rq_vers) {
1098			/*
1099			 * This one matches. Call the callback and see
1100			 * if it wants to veto or something.
1101			 */
1102			lock.locked = FALSE;
1103			lock.raw_cred = &client->cl_rawcred;
1104			cb_res = scb->cb_callback.callback(rqst,
1105			    client->cl_creds,
1106			    client->cl_ctx,
1107			    &lock,
1108			    &cookie);
1109
1110			if (!cb_res) {
1111				client->cl_state = CLIENT_STALE;
1112				result = FALSE;
1113				break;
1114			}
1115
1116			/*
1117			 * The callback accepted the connection - it
1118			 * is responsible for freeing client->cl_creds
1119			 * now.
1120			 */
1121			client->cl_creds = GSS_C_NO_CREDENTIAL;
1122			client->cl_locked = lock.locked;
1123			client->cl_cookie = cookie;
1124			return (TRUE);
1125		}
1126	}
1127
1128	/*
1129	 * Either no callback exists for this program/version or one
1130	 * of the callbacks rejected the connection. We just need to
1131	 * clean up the delegated client creds, if any.
1132	 */
1133	if (client->cl_creds) {
1134		OM_uint32 min_ver;
1135		gss_release_cred(&min_ver, &client->cl_creds);
1136	}
1137	return (result);
1138}
1139
1140static bool_t
1141svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1142{
1143	u_int32_t offset;
1144	int word, bit;
1145	bool_t result;
1146
1147	sx_xlock(&client->cl_lock);
1148	if (seq <= client->cl_seqlast) {
1149		/*
1150		 * The request sequence number is less than
1151		 * the largest we have seen so far. If it is
1152		 * outside the window or if we have seen a
1153		 * request with this sequence before, silently
1154		 * discard it.
1155		 */
1156		offset = client->cl_seqlast - seq;
1157		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1158			result = FALSE;
1159			goto out;
1160		}
1161		word = offset / 32;
1162		bit = offset % 32;
1163		if (client->cl_seqmask[word] & (1 << bit)) {
1164			result = FALSE;
1165			goto out;
1166		}
1167	}
1168
1169	result = TRUE;
1170out:
1171	sx_xunlock(&client->cl_lock);
1172	return (result);
1173}
1174
1175static void
1176svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1177{
1178	int offset, i, word, bit;
1179	uint32_t carry, newcarry;
1180
1181	sx_xlock(&client->cl_lock);
1182	if (seq > client->cl_seqlast) {
1183		/*
1184		 * This request has a sequence number greater
1185		 * than any we have seen so far. Advance the
1186		 * seq window and set bit zero of the window
1187		 * (which corresponds to the new sequence
1188		 * number)
1189		 */
1190		offset = seq - client->cl_seqlast;
1191		while (offset > 32) {
1192			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1193			     i > 0; i--) {
1194				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1195			}
1196			client->cl_seqmask[0] = 0;
1197			offset -= 32;
1198		}
1199		carry = 0;
1200		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1201			newcarry = client->cl_seqmask[i] >> (32 - offset);
1202			client->cl_seqmask[i] =
1203				(client->cl_seqmask[i] << offset) | carry;
1204			carry = newcarry;
1205		}
1206		client->cl_seqmask[0] |= 1;
1207		client->cl_seqlast = seq;
1208	} else {
1209		offset = client->cl_seqlast - seq;
1210		word = offset / 32;
1211		bit = offset % 32;
1212		client->cl_seqmask[word] |= (1 << bit);
1213	}
1214	sx_xunlock(&client->cl_lock);
1215}
1216
1217enum auth_stat
1218svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1219
1220{
1221	OM_uint32		 min_stat;
1222	XDR	 		 xdrs;
1223	struct svc_rpc_gss_cookedcred *cc;
1224	struct svc_rpc_gss_client *client;
1225	struct rpc_gss_cred	 gc;
1226	struct rpc_gss_init_res	 gr;
1227	gss_qop_t		 qop;
1228	int			 call_stat;
1229	enum auth_stat		 result;
1230
1231	rpc_gss_log_debug("in svc_rpc_gss()");
1232
1233	/* Garbage collect old clients. */
1234	svc_rpc_gss_timeout_clients();
1235
1236	/* Initialize reply. */
1237	rqst->rq_verf = _null_auth;
1238
1239	/* Deserialize client credentials. */
1240	if (rqst->rq_cred.oa_length <= 0)
1241		return (AUTH_BADCRED);
1242
1243	memset(&gc, 0, sizeof(gc));
1244
1245	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1246	    rqst->rq_cred.oa_length, XDR_DECODE);
1247
1248	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1249		XDR_DESTROY(&xdrs);
1250		return (AUTH_BADCRED);
1251	}
1252	XDR_DESTROY(&xdrs);
1253
1254	client = NULL;
1255
1256	/* Check version. */
1257	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1258		result = AUTH_BADCRED;
1259		goto out;
1260	}
1261
1262	/* Check the proc and find the client (or create it) */
1263	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1264		if (gc.gc_handle.length != 0) {
1265			result = AUTH_BADCRED;
1266			goto out;
1267		}
1268		client = svc_rpc_gss_create_client();
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