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