1184588Sdfr/*-
2197583Sjamie * Copyright (c) 2008 Doug Rabson
3197583Sjamie * All rights reserved.
4184588Sdfr *
5184588Sdfr * Redistribution and use in source and binary forms, with or without
6184588Sdfr * modification, are permitted provided that the following conditions
7184588Sdfr * are met:
8184588Sdfr * 1. Redistributions of source code must retain the above copyright
9184588Sdfr *    notice, this list of conditions and the following disclaimer.
10184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11184588Sdfr *    notice, this list of conditions and the following disclaimer in the
12184588Sdfr *    documentation and/or other materials provided with the distribution.
13184588Sdfr *
14197583Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17197583Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184588Sdfr * SUCH DAMAGE.
25184588Sdfr */
26197583Sjamie/*
27197583Sjamie  svc_rpcsec_gss.c
28197583Sjamie
29197583Sjamie  Copyright (c) 2000 The Regents of the University of Michigan.
30197583Sjamie  All rights reserved.
31184588Sdfr
32197583Sjamie  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
33197583Sjamie  All rights reserved, all wrongs reversed.
34197583Sjamie
35197583Sjamie  Redistribution and use in source and binary forms, with or without
36197583Sjamie  modification, are permitted provided that the following conditions
37197583Sjamie  are met:
38197583Sjamie
39197583Sjamie  1. Redistributions of source code must retain the above copyright
40197583Sjamie     notice, this list of conditions and the following disclaimer.
41197583Sjamie  2. Redistributions in binary form must reproduce the above copyright
42197583Sjamie     notice, this list of conditions and the following disclaimer in the
43197583Sjamie     documentation and/or other materials provided with the distribution.
44197583Sjamie  3. Neither the name of the University nor the names of its
45197583Sjamie     contributors may be used to endorse or promote products derived
46197583Sjamie     from this software without specific prior written permission.
47197583Sjamie
48197583Sjamie  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
49197583Sjamie  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
50197583Sjamie  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
51197583Sjamie  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52197583Sjamie  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53197583Sjamie  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54197583Sjamie  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55197583Sjamie  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56197583Sjamie  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57197583Sjamie  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58197583Sjamie  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59197583Sjamie
60197583Sjamie  $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
61197583Sjamie */
62197583Sjamie
63184588Sdfr#include <sys/cdefs.h>
64184588Sdfr__FBSDID("$FreeBSD$");
65184588Sdfr
66184588Sdfr#include <sys/param.h>
67197583Sjamie#include <sys/systm.h>
68194239Srmacklem#include <sys/jail.h>
69184588Sdfr#include <sys/kernel.h>
70197583Sjamie#include <sys/kobj.h>
71184588Sdfr#include <sys/lock.h>
72184588Sdfr#include <sys/malloc.h>
73184588Sdfr#include <sys/mbuf.h>
74184588Sdfr#include <sys/mutex.h>
75197583Sjamie#include <sys/proc.h>
76197583Sjamie#include <sys/sx.h>
77197583Sjamie#include <sys/ucred.h>
78184588Sdfr
79197583Sjamie#include <rpc/rpc.h>
80197583Sjamie#include <rpc/rpcsec_gss.h>
81184588Sdfr
82197583Sjamie#include "rpcsec_gss_int.h"
83184588Sdfr
84197583Sjamiestatic bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
85197583Sjamiestatic bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
86197583Sjamiestatic void     svc_rpc_gss_release(SVCAUTH *);
87197583Sjamiestatic enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
88197583Sjamiestatic int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
89184588Sdfr
90197583Sjamiestatic struct svc_auth_ops svc_auth_gss_ops = {
91197583Sjamie	svc_rpc_gss_wrap,
92197583Sjamie	svc_rpc_gss_unwrap,
93197583Sjamie	svc_rpc_gss_release,
94184588Sdfr};
95184588Sdfr
96197583Sjamiestruct sx svc_rpc_gss_lock;
97197583Sjamie
98197583Sjamiestruct svc_rpc_gss_callback {
99197583Sjamie	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
100197583Sjamie	rpc_gss_callback_t	cb_callback;
101197583Sjamie};
102197583Sjamiestatic SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
103201145Santoine	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
104197583Sjamie
105197583Sjamiestruct svc_rpc_gss_svc_name {
106197583Sjamie	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
107197583Sjamie	char			*sn_principal;
108197583Sjamie	gss_OID			sn_mech;
109197583Sjamie	u_int			sn_req_time;
110197583Sjamie	gss_cred_id_t		sn_cred;
111197583Sjamie	u_int			sn_program;
112197583Sjamie	u_int			sn_version;
113197583Sjamie};
114197583Sjamiestatic SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
115201145Santoine	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
116197583Sjamie
117197583Sjamieenum svc_rpc_gss_client_state {
118197583Sjamie	CLIENT_NEW,				/* still authenticating */
119197583Sjamie	CLIENT_ESTABLISHED,			/* context established */
120197583Sjamie	CLIENT_STALE				/* garbage to collect */
121197583Sjamie};
122197583Sjamie
123197583Sjamie#define SVC_RPC_GSS_SEQWINDOW	128
124201853Sbrooks#ifndef RPCAUTH_UNIXGIDS
125201853Sbrooks#define RPCAUTH_UNIXGIDS	16
126201853Sbrooks#endif
127197583Sjamie
128197583Sjamiestruct svc_rpc_gss_clientid {
129197583Sjamie	unsigned long		ci_hostid;
130197583Sjamie	uint32_t		ci_boottime;
131197583Sjamie	uint32_t		ci_id;
132197583Sjamie};
133197583Sjamie
134197583Sjamiestruct svc_rpc_gss_client {
135197583Sjamie	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
136197583Sjamie	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
137197583Sjamie	volatile u_int		cl_refs;
138197583Sjamie	struct sx		cl_lock;
139197583Sjamie	struct svc_rpc_gss_clientid cl_id;
140197583Sjamie	time_t			cl_expiration;	/* when to gc */
141197583Sjamie	enum svc_rpc_gss_client_state cl_state;	/* client state */
142197583Sjamie	bool_t			cl_locked;	/* fixed service+qop */
143197583Sjamie	gss_ctx_id_t		cl_ctx;		/* context id */
144197583Sjamie	gss_cred_id_t		cl_creds;	/* delegated creds */
145197583Sjamie	gss_name_t		cl_cname;	/* client name */
146197583Sjamie	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
147197583Sjamie	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
148197583Sjamie	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
149197583Sjamie	struct ucred		*cl_cred;	/* kernel-style credentials */
150197583Sjamie	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
151197583Sjamie	bool_t			cl_done_callback; /* TRUE after call */
152197583Sjamie	void			*cl_cookie;	/* user cookie from callback */
153201853Sbrooks	gid_t			cl_gid_storage[RPCAUTH_UNIXGIDS];
154197583Sjamie	gss_OID			cl_mech;	/* mechanism */
155197583Sjamie	gss_qop_t		cl_qop;		/* quality of protection */
156197583Sjamie	uint32_t		cl_seqlast;	/* sequence window origin */
157197583Sjamie	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
158197583Sjamie};
159197583SjamieTAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
160197583Sjamie
161197581Sjamie/*
162197583Sjamie * This structure holds enough information to unwrap arguments or wrap
163197583Sjamie * results for a given request. We use the rq_clntcred area for this
164197583Sjamie * (which is a per-request buffer).
165197581Sjamie */
166197583Sjamiestruct svc_rpc_gss_cookedcred {
167197583Sjamie	struct svc_rpc_gss_client *cc_client;
168197583Sjamie	rpc_gss_service_t	cc_service;
169197583Sjamie	uint32_t		cc_seq;
170184588Sdfr};
171184588Sdfr
172197583Sjamie#define CLIENT_HASH_SIZE	256
173197583Sjamie#define CLIENT_MAX		128
174197583Sjamiestruct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
175197583Sjamiestruct svc_rpc_gss_client_list svc_rpc_gss_clients;
176197583Sjamiestatic size_t svc_rpc_gss_client_count;
177197583Sjamiestatic uint32_t svc_rpc_gss_next_clientid = 1;
178197583Sjamie
179197583Sjamiestatic void
180197583Sjamiesvc_rpc_gss_init(void *arg)
181184588Sdfr{
182197583Sjamie	int i;
183184588Sdfr
184197583Sjamie	for (i = 0; i < CLIENT_HASH_SIZE; i++)
185197583Sjamie		TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
186197583Sjamie	TAILQ_INIT(&svc_rpc_gss_clients);
187197583Sjamie	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
188197583Sjamie	sx_init(&svc_rpc_gss_lock, "gsslock");
189197583Sjamie}
190197583SjamieSYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
191197583Sjamie
192197583Sjamiebool_t
193197583Sjamierpc_gss_set_callback(rpc_gss_callback_t *cb)
194197583Sjamie{
195197583Sjamie	struct svc_rpc_gss_callback *scb;
196197583Sjamie
197197583Sjamie	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
198197583Sjamie	if (!scb) {
199197583Sjamie		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
200197583Sjamie		return (FALSE);
201184588Sdfr	}
202197583Sjamie	scb->cb_callback = *cb;
203197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
204197583Sjamie	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
205197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
206184588Sdfr
207197583Sjamie	return (TRUE);
208197583Sjamie}
209197583Sjamie
210197583Sjamievoid
211197583Sjamierpc_gss_clear_callback(rpc_gss_callback_t *cb)
212197583Sjamie{
213197583Sjamie	struct svc_rpc_gss_callback *scb;
214197583Sjamie
215197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
216197583Sjamie	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
217197583Sjamie		if (scb->cb_callback.program == cb->program
218197583Sjamie		    && scb->cb_callback.version == cb->version
219197583Sjamie		    && scb->cb_callback.callback == cb->callback) {
220197583Sjamie			SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
221197583Sjamie			    svc_rpc_gss_callback, cb_link);
222197583Sjamie			sx_xunlock(&svc_rpc_gss_lock);
223197583Sjamie			mem_free(scb, sizeof(*scb));
224197583Sjamie			return;
225184588Sdfr		}
226184588Sdfr	}
227197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
228197583Sjamie}
229184588Sdfr
230197583Sjamiestatic bool_t
231197583Sjamierpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
232197583Sjamie{
233197583Sjamie	OM_uint32		maj_stat, min_stat;
234197583Sjamie	gss_buffer_desc		namebuf;
235197583Sjamie	gss_name_t		name;
236197583Sjamie	gss_OID_set_desc	oid_set;
237197583Sjamie
238197583Sjamie	oid_set.count = 1;
239197583Sjamie	oid_set.elements = sname->sn_mech;
240197583Sjamie
241197583Sjamie	namebuf.value = (void *) sname->sn_principal;
242197583Sjamie	namebuf.length = strlen(sname->sn_principal);
243197583Sjamie
244197583Sjamie	maj_stat = gss_import_name(&min_stat, &namebuf,
245197583Sjamie				   GSS_C_NT_HOSTBASED_SERVICE, &name);
246197583Sjamie	if (maj_stat != GSS_S_COMPLETE)
247197583Sjamie		return (FALSE);
248197583Sjamie
249197583Sjamie	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
250197583Sjamie		gss_release_cred(&min_stat, &sname->sn_cred);
251197583Sjamie
252197583Sjamie	maj_stat = gss_acquire_cred(&min_stat, name,
253197583Sjamie	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
254197583Sjamie	    NULL, NULL);
255197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
256197583Sjamie		gss_release_name(&min_stat, &name);
257197583Sjamie		return (FALSE);
258184588Sdfr	}
259197583Sjamie	gss_release_name(&min_stat, &name);
260184588Sdfr
261197583Sjamie	return (TRUE);
262197583Sjamie}
263197583Sjamie
264197583Sjamiebool_t
265197583Sjamierpc_gss_set_svc_name(const char *principal, const char *mechanism,
266197583Sjamie    u_int req_time, u_int program, u_int version)
267197583Sjamie{
268197583Sjamie	struct svc_rpc_gss_svc_name *sname;
269197583Sjamie	gss_OID			mech_oid;
270197583Sjamie
271197583Sjamie	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
272197583Sjamie		return (FALSE);
273197583Sjamie
274197583Sjamie	sname = mem_alloc(sizeof(*sname));
275197583Sjamie	if (!sname)
276197583Sjamie		return (FALSE);
277197583Sjamie	sname->sn_principal = strdup(principal, M_RPC);
278197583Sjamie	sname->sn_mech = mech_oid;
279197583Sjamie	sname->sn_req_time = req_time;
280197583Sjamie	sname->sn_cred = GSS_C_NO_CREDENTIAL;
281197583Sjamie	sname->sn_program = program;
282197583Sjamie	sname->sn_version = version;
283197583Sjamie
284197583Sjamie	if (!rpc_gss_acquire_svc_cred(sname)) {
285197583Sjamie		free(sname->sn_principal, M_RPC);
286197583Sjamie		mem_free(sname, sizeof(*sname));
287197583Sjamie		return (FALSE);
288184588Sdfr	}
289197583Sjamie
290197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
291197583Sjamie	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
292197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
293197583Sjamie
294197583Sjamie	return (TRUE);
295197583Sjamie}
296197583Sjamie
297197583Sjamievoid
298197583Sjamierpc_gss_clear_svc_name(u_int program, u_int version)
299197583Sjamie{
300197583Sjamie	OM_uint32		min_stat;
301197583Sjamie	struct svc_rpc_gss_svc_name *sname;
302197583Sjamie
303197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
304197583Sjamie	SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
305197583Sjamie		if (sname->sn_program == program
306197583Sjamie		    && sname->sn_version == version) {
307197583Sjamie			SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
308197583Sjamie			    svc_rpc_gss_svc_name, sn_link);
309197583Sjamie			sx_xunlock(&svc_rpc_gss_lock);
310197583Sjamie			gss_release_cred(&min_stat, &sname->sn_cred);
311197583Sjamie			free(sname->sn_principal, M_RPC);
312197583Sjamie			mem_free(sname, sizeof(*sname));
313197583Sjamie			return;
314184588Sdfr		}
315184588Sdfr	}
316197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
317197583Sjamie}
318197583Sjamie
319197583Sjamiebool_t
320197583Sjamierpc_gss_get_principal_name(rpc_gss_principal_t *principal,
321197583Sjamie    const char *mech, const char *name, const char *node, const char *domain)
322197583Sjamie{
323197583Sjamie	OM_uint32		maj_stat, min_stat;
324197583Sjamie	gss_OID			mech_oid;
325197583Sjamie	size_t			namelen;
326197583Sjamie	gss_buffer_desc		buf;
327197583Sjamie	gss_name_t		gss_name, gss_mech_name;
328197583Sjamie	rpc_gss_principal_t	result;
329197583Sjamie
330197583Sjamie	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
331197583Sjamie		return (FALSE);
332197583Sjamie
333197583Sjamie	/*
334197583Sjamie	 * Construct a gss_buffer containing the full name formatted
335197583Sjamie	 * as "name/node@domain" where node and domain are optional.
336197583Sjamie	 */
337197583Sjamie	namelen = strlen(name);
338197583Sjamie	if (node) {
339197583Sjamie		namelen += strlen(node) + 1;
340184588Sdfr	}
341197583Sjamie	if (domain) {
342197583Sjamie		namelen += strlen(domain) + 1;
343197583Sjamie	}
344197583Sjamie
345197583Sjamie	buf.value = mem_alloc(namelen);
346197583Sjamie	buf.length = namelen;
347197583Sjamie	strcpy((char *) buf.value, name);
348197583Sjamie	if (node) {
349197583Sjamie		strcat((char *) buf.value, "/");
350197583Sjamie		strcat((char *) buf.value, node);
351197583Sjamie	}
352197583Sjamie	if (domain) {
353197583Sjamie		strcat((char *) buf.value, "@");
354197583Sjamie		strcat((char *) buf.value, domain);
355197583Sjamie	}
356197583Sjamie
357197583Sjamie	/*
358197583Sjamie	 * Convert that to a gss_name_t and then convert that to a
359197583Sjamie	 * mechanism name in the selected mechanism.
360197583Sjamie	 */
361197583Sjamie	maj_stat = gss_import_name(&min_stat, &buf,
362197583Sjamie	    GSS_C_NT_USER_NAME, &gss_name);
363197583Sjamie	mem_free(buf.value, buf.length);
364197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
365197583Sjamie		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
366197583Sjamie		return (FALSE);
367197583Sjamie	}
368197583Sjamie	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
369197583Sjamie	    &gss_mech_name);
370197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
371197583Sjamie		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
372197583Sjamie		    min_stat);
373197583Sjamie		gss_release_name(&min_stat, &gss_name);
374197583Sjamie		return (FALSE);
375197583Sjamie	}
376197583Sjamie	gss_release_name(&min_stat, &gss_name);
377197583Sjamie
378197583Sjamie	/*
379197583Sjamie	 * Export the mechanism name and use that to construct the
380197583Sjamie	 * rpc_gss_principal_t result.
381197583Sjamie	 */
382197583Sjamie	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
383197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
384197583Sjamie		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
385197583Sjamie		gss_release_name(&min_stat, &gss_mech_name);
386197583Sjamie		return (FALSE);
387197583Sjamie	}
388197583Sjamie	gss_release_name(&min_stat, &gss_mech_name);
389197583Sjamie
390197583Sjamie	result = mem_alloc(sizeof(int) + buf.length);
391197583Sjamie	if (!result) {
392197583Sjamie		gss_release_buffer(&min_stat, &buf);
393197583Sjamie		return (FALSE);
394197583Sjamie	}
395197583Sjamie	result->len = buf.length;
396197583Sjamie	memcpy(result->name, buf.value, buf.length);
397197583Sjamie	gss_release_buffer(&min_stat, &buf);
398197583Sjamie
399197583Sjamie	*principal = result;
400197583Sjamie	return (TRUE);
401184588Sdfr}
402184588Sdfr
403197583Sjamiebool_t
404197583Sjamierpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
405197583Sjamie    rpc_gss_ucred_t **ucred, void **cookie)
406197583Sjamie{
407197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
408197583Sjamie	struct svc_rpc_gss_client *client;
409197583Sjamie
410197583Sjamie	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
411197583Sjamie		return (FALSE);
412197583Sjamie
413197583Sjamie	cc = req->rq_clntcred;
414197583Sjamie	client = cc->cc_client;
415197583Sjamie	if (rcred)
416197583Sjamie		*rcred = &client->cl_rawcred;
417197583Sjamie	if (ucred)
418197583Sjamie		*ucred = &client->cl_ucred;
419197583Sjamie	if (cookie)
420197583Sjamie		*cookie = client->cl_cookie;
421197583Sjamie	return (TRUE);
422197583Sjamie}
423197583Sjamie
424197583Sjamie/*
425197583Sjamie * This simpler interface is used by svc_getcred to copy the cred data
426197583Sjamie * into a kernel cred structure.
427197583Sjamie */
428184588Sdfrstatic int
429197583Sjamierpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
430184588Sdfr{
431197583Sjamie	struct ucred *cr;
432197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
433197583Sjamie	struct svc_rpc_gss_client *client;
434197583Sjamie	rpc_gss_ucred_t *uc;
435184588Sdfr
436197583Sjamie	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
437197583Sjamie		return (FALSE);
438197583Sjamie
439197583Sjamie	cc = req->rq_clntcred;
440197583Sjamie	client = cc->cc_client;
441197583Sjamie
442197583Sjamie	if (flavorp)
443197583Sjamie		*flavorp = client->cl_rpcflavor;
444197583Sjamie
445197583Sjamie	if (client->cl_cred) {
446197583Sjamie		*crp = crhold(client->cl_cred);
447197583Sjamie		return (TRUE);
448197583Sjamie	}
449197583Sjamie
450197583Sjamie	uc = &client->cl_ucred;
451197583Sjamie	cr = client->cl_cred = crget();
452197583Sjamie	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
453197583Sjamie	cr->cr_rgid = cr->cr_svgid = uc->gid;
454197583Sjamie	crsetgroups(cr, uc->gidlen, uc->gidlist);
455197584Sjamie	cr->cr_prison = &prison0;
456197584Sjamie	prison_hold(cr->cr_prison);
457197583Sjamie	*crp = crhold(cr);
458197583Sjamie
459197583Sjamie	return (TRUE);
460184588Sdfr}
461184588Sdfr
462197583Sjamieint
463197583Sjamierpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
464197583Sjamie{
465197583Sjamie	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
466197583Sjamie	struct svc_rpc_gss_client *client = cc->cc_client;
467197583Sjamie	int			want_conf;
468197583Sjamie	OM_uint32		max;
469197583Sjamie	OM_uint32		maj_stat, min_stat;
470197583Sjamie	int			result;
471197583Sjamie
472197583Sjamie	switch (client->cl_rawcred.service) {
473197583Sjamie	case rpc_gss_svc_none:
474197583Sjamie		return (max_tp_unit_len);
475197583Sjamie		break;
476197583Sjamie
477197583Sjamie	case rpc_gss_svc_default:
478197583Sjamie	case rpc_gss_svc_integrity:
479197583Sjamie		want_conf = FALSE;
480197583Sjamie		break;
481197583Sjamie
482197583Sjamie	case rpc_gss_svc_privacy:
483197583Sjamie		want_conf = TRUE;
484197583Sjamie		break;
485197583Sjamie
486197583Sjamie	default:
487197583Sjamie		return (0);
488197583Sjamie	}
489197583Sjamie
490197583Sjamie	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
491197583Sjamie	    client->cl_qop, max_tp_unit_len, &max);
492197583Sjamie
493197583Sjamie	if (maj_stat == GSS_S_COMPLETE) {
494197583Sjamie		result = (int) max;
495197583Sjamie		if (result < 0)
496197583Sjamie			result = 0;
497197583Sjamie		return (result);
498197583Sjamie	} else {
499197583Sjamie		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
500197583Sjamie		    maj_stat, min_stat);
501197583Sjamie		return (0);
502197583Sjamie	}
503197583Sjamie}
504197583Sjamie
505197583Sjamiestatic struct svc_rpc_gss_client *
506197583Sjamiesvc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
507197583Sjamie{
508197583Sjamie	struct svc_rpc_gss_client *client;
509197583Sjamie	struct svc_rpc_gss_client_list *list;
510197583Sjamie	unsigned long hostid;
511197583Sjamie
512197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
513197583Sjamie
514197583Sjamie	getcredhostid(curthread->td_ucred, &hostid);
515197583Sjamie	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
516197583Sjamie		return (NULL);
517197583Sjamie
518197583Sjamie	list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
519197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
520197583Sjamie	TAILQ_FOREACH(client, list, cl_link) {
521197583Sjamie		if (client->cl_id.ci_id == id->ci_id) {
522197583Sjamie			/*
523197583Sjamie			 * Move this client to the front of the LRU
524197583Sjamie			 * list.
525197583Sjamie			 */
526197583Sjamie			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
527197583Sjamie			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
528197583Sjamie			    cl_alllink);
529197583Sjamie			refcount_acquire(&client->cl_refs);
530197583Sjamie			break;
531197583Sjamie		}
532197583Sjamie	}
533197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
534197583Sjamie
535197583Sjamie	return (client);
536197583Sjamie}
537197583Sjamie
538197583Sjamiestatic struct svc_rpc_gss_client *
539197583Sjamiesvc_rpc_gss_create_client(void)
540197583Sjamie{
541197583Sjamie	struct svc_rpc_gss_client *client;
542197583Sjamie	struct svc_rpc_gss_client_list *list;
543197583Sjamie	unsigned long hostid;
544197583Sjamie
545197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
546197583Sjamie
547197583Sjamie	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
548197583Sjamie	memset(client, 0, sizeof(struct svc_rpc_gss_client));
549197583Sjamie	refcount_init(&client->cl_refs, 1);
550197583Sjamie	sx_init(&client->cl_lock, "GSS-client");
551197583Sjamie	getcredhostid(curthread->td_ucred, &hostid);
552197583Sjamie	client->cl_id.ci_hostid = hostid;
553197583Sjamie	client->cl_id.ci_boottime = boottime.tv_sec;
554197583Sjamie	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
555197583Sjamie	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
556197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
557197583Sjamie	TAILQ_INSERT_HEAD(list, client, cl_link);
558197583Sjamie	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
559197583Sjamie	svc_rpc_gss_client_count++;
560197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
561197583Sjamie
562197583Sjamie	/*
563197583Sjamie	 * Start the client off with a short expiration time. We will
564197583Sjamie	 * try to get a saner value from the client creds later.
565197583Sjamie	 */
566197583Sjamie	client->cl_state = CLIENT_NEW;
567197583Sjamie	client->cl_locked = FALSE;
568197583Sjamie	client->cl_expiration = time_uptime + 5*60;
569197583Sjamie
570197583Sjamie	return (client);
571197583Sjamie}
572197583Sjamie
573197583Sjamiestatic void
574197583Sjamiesvc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
575197583Sjamie{
576197583Sjamie	OM_uint32 min_stat;
577197583Sjamie
578197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
579197583Sjamie
580197583Sjamie	if (client->cl_ctx)
581197583Sjamie		gss_delete_sec_context(&min_stat,
582197583Sjamie		    &client->cl_ctx, GSS_C_NO_BUFFER);
583197583Sjamie
584197583Sjamie	if (client->cl_cname)
585197583Sjamie		gss_release_name(&min_stat, &client->cl_cname);
586197583Sjamie
587197583Sjamie	if (client->cl_rawcred.client_principal)
588197583Sjamie		mem_free(client->cl_rawcred.client_principal,
589197583Sjamie		    sizeof(*client->cl_rawcred.client_principal)
590197583Sjamie		    + client->cl_rawcred.client_principal->len);
591197583Sjamie
592197583Sjamie	if (client->cl_cred)
593197583Sjamie		crfree(client->cl_cred);
594197583Sjamie
595197583Sjamie	sx_destroy(&client->cl_lock);
596197583Sjamie	mem_free(client, sizeof(*client));
597197583Sjamie}
598197583Sjamie
599184588Sdfr/*
600197583Sjamie * Drop a reference to a client and free it if that was the last reference.
601184588Sdfr */
602184588Sdfrstatic void
603197583Sjamiesvc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
604184588Sdfr{
605184588Sdfr
606197583Sjamie	if (!refcount_release(&client->cl_refs))
607197583Sjamie		return;
608197583Sjamie	svc_rpc_gss_destroy_client(client);
609197583Sjamie}
610197583Sjamie
611197583Sjamie/*
612226209Srmacklem * Remove a client from our global lists.
613226209Srmacklem * Must be called with svc_rpc_gss_lock held.
614226209Srmacklem */
615226209Srmacklemstatic void
616226209Srmacklemsvc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
617226209Srmacklem{
618226209Srmacklem	struct svc_rpc_gss_client_list *list;
619226209Srmacklem
620226209Srmacklem	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
621226209Srmacklem	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
622226209Srmacklem	TAILQ_REMOVE(list, client, cl_link);
623226209Srmacklem	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
624226209Srmacklem	svc_rpc_gss_client_count--;
625226209Srmacklem}
626226209Srmacklem
627226209Srmacklem/*
628197583Sjamie * Remove a client from our global lists and free it if we can.
629197583Sjamie */
630197583Sjamiestatic void
631197583Sjamiesvc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
632197583Sjamie{
633197583Sjamie	struct svc_rpc_gss_client_list *list;
634226209Srmacklem	struct svc_rpc_gss_client *tclient;
635197583Sjamie
636197583Sjamie	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
637197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
638226209Srmacklem	TAILQ_FOREACH(tclient, list, cl_link) {
639226209Srmacklem		/*
640226209Srmacklem		 * Make sure this client has not already been removed
641226209Srmacklem		 * from the lists by svc_rpc_gss_forget_client() or
642226209Srmacklem		 * svc_rpc_gss_forget_client_locked().
643226209Srmacklem		 */
644226209Srmacklem		if (client == tclient) {
645226209Srmacklem			svc_rpc_gss_forget_client_locked(client);
646226209Srmacklem			sx_xunlock(&svc_rpc_gss_lock);
647226209Srmacklem			svc_rpc_gss_release_client(client);
648226209Srmacklem			return;
649226209Srmacklem		}
650226209Srmacklem	}
651197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
652197583Sjamie}
653197583Sjamie
654197583Sjamiestatic void
655197583Sjamiesvc_rpc_gss_timeout_clients(void)
656197583Sjamie{
657197583Sjamie	struct svc_rpc_gss_client *client;
658197583Sjamie	time_t now = time_uptime;
659197583Sjamie
660197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
661197583Sjamie
662197583Sjamie	/*
663197583Sjamie	 * First enforce the max client limit. We keep
664197583Sjamie	 * svc_rpc_gss_clients in LRU order.
665197583Sjamie	 */
666226209Srmacklem	sx_xlock(&svc_rpc_gss_lock);
667226209Srmacklem	client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
668226209Srmacklem	while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
669226209Srmacklem		svc_rpc_gss_forget_client_locked(client);
670226209Srmacklem		sx_xunlock(&svc_rpc_gss_lock);
671226209Srmacklem		svc_rpc_gss_release_client(client);
672226209Srmacklem		sx_xlock(&svc_rpc_gss_lock);
673226209Srmacklem		client = TAILQ_LAST(&svc_rpc_gss_clients,
674226209Srmacklem		    svc_rpc_gss_client_list);
675226209Srmacklem	}
676226209Srmacklemagain:
677226209Srmacklem	TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
678197583Sjamie		if (client->cl_state == CLIENT_STALE
679197583Sjamie		    || now > client->cl_expiration) {
680226209Srmacklem			svc_rpc_gss_forget_client_locked(client);
681226209Srmacklem			sx_xunlock(&svc_rpc_gss_lock);
682197583Sjamie			rpc_gss_log_debug("expiring client %p", client);
683226209Srmacklem			svc_rpc_gss_release_client(client);
684226209Srmacklem			sx_xlock(&svc_rpc_gss_lock);
685226209Srmacklem			goto again;
686184588Sdfr		}
687184588Sdfr	}
688226209Srmacklem	sx_xunlock(&svc_rpc_gss_lock);
689184588Sdfr}
690184588Sdfr
691197583Sjamie#ifdef DEBUG
692184588Sdfr/*
693197583Sjamie * OID<->string routines.  These are uuuuugly.
694184588Sdfr */
695197583Sjamiestatic OM_uint32
696197583Sjamiegss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
697184588Sdfr{
698197583Sjamie	char		numstr[128];
699197583Sjamie	unsigned long	number;
700197583Sjamie	int		numshift;
701197583Sjamie	size_t		string_length;
702197583Sjamie	size_t		i;
703197583Sjamie	unsigned char	*cp;
704197583Sjamie	char		*bp;
705184588Sdfr
706197583Sjamie	/* Decoded according to krb5/gssapi_krb5.c */
707184588Sdfr
708197583Sjamie	/* First determine the size of the string */
709197583Sjamie	string_length = 0;
710197583Sjamie	number = 0;
711197583Sjamie	numshift = 0;
712197583Sjamie	cp = (unsigned char *) oid->elements;
713197583Sjamie	number = (unsigned long) cp[0];
714197583Sjamie	sprintf(numstr, "%ld ", number/40);
715197583Sjamie	string_length += strlen(numstr);
716197583Sjamie	sprintf(numstr, "%ld ", number%40);
717197583Sjamie	string_length += strlen(numstr);
718197583Sjamie	for (i=1; i<oid->length; i++) {
719197583Sjamie		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
720197583Sjamie			number = (number << 7) | (cp[i] & 0x7f);
721197583Sjamie			numshift += 7;
722184588Sdfr		}
723197583Sjamie		else {
724197583Sjamie			*minor_status = 0;
725197583Sjamie			return(GSS_S_FAILURE);
726184588Sdfr		}
727197583Sjamie		if ((cp[i] & 0x80) == 0) {
728197583Sjamie			sprintf(numstr, "%ld ", number);
729197583Sjamie			string_length += strlen(numstr);
730197583Sjamie			number = 0;
731197583Sjamie			numshift = 0;
732197583Sjamie		}
733184588Sdfr	}
734197583Sjamie	/*
735197583Sjamie	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
736197583Sjamie	 * here for "{ " and "}\0".
737197583Sjamie	 */
738197583Sjamie	string_length += 4;
739197583Sjamie	if ((bp = (char *) mem_alloc(string_length))) {
740197583Sjamie		strcpy(bp, "{ ");
741197583Sjamie		number = (unsigned long) cp[0];
742197583Sjamie		sprintf(numstr, "%ld ", number/40);
743197583Sjamie		strcat(bp, numstr);
744197583Sjamie		sprintf(numstr, "%ld ", number%40);
745197583Sjamie		strcat(bp, numstr);
746197583Sjamie		number = 0;
747197583Sjamie		cp = (unsigned char *) oid->elements;
748197583Sjamie		for (i=1; i<oid->length; i++) {
749197583Sjamie			number = (number << 7) | (cp[i] & 0x7f);
750197583Sjamie			if ((cp[i] & 0x80) == 0) {
751197583Sjamie				sprintf(numstr, "%ld ", number);
752197583Sjamie				strcat(bp, numstr);
753197583Sjamie				number = 0;
754197583Sjamie			}
755184588Sdfr		}
756197583Sjamie		strcat(bp, "}");
757197583Sjamie		oid_str->length = strlen(bp)+1;
758197583Sjamie		oid_str->value = (void *) bp;
759197583Sjamie		*minor_status = 0;
760197583Sjamie		return(GSS_S_COMPLETE);
761184588Sdfr	}
762197583Sjamie	*minor_status = 0;
763197583Sjamie	return(GSS_S_FAILURE);
764197583Sjamie}
765197583Sjamie#endif
766184588Sdfr
767197583Sjamiestatic void
768197583Sjamiesvc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
769197583Sjamie    const gss_name_t name)
770197583Sjamie{
771197583Sjamie	OM_uint32		maj_stat, min_stat;
772197583Sjamie	rpc_gss_ucred_t		*uc = &client->cl_ucred;
773197583Sjamie	int			numgroups;
774197583Sjamie
775197583Sjamie	uc->uid = 65534;
776197583Sjamie	uc->gid = 65534;
777197583Sjamie	uc->gidlist = client->cl_gid_storage;
778197583Sjamie
779201853Sbrooks	numgroups = RPCAUTH_UNIXGIDS;
780197583Sjamie	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
781197583Sjamie	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
782197583Sjamie	if (GSS_ERROR(maj_stat))
783197583Sjamie		uc->gidlen = 0;
784197583Sjamie	else
785197583Sjamie		uc->gidlen = numgroups;
786197583Sjamie}
787197583Sjamie
788197583Sjamiestatic void
789197583Sjamiesvc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
790197583Sjamie{
791197583Sjamie	static gss_OID_desc krb5_mech_oid =
792197583Sjamie		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
793197583Sjamie
794184588Sdfr	/*
795197583Sjamie	 * Attempt to translate mech type and service into a
796197583Sjamie	 * 'pseudo flavor'. Hardwire in krb5 support for now.
797184588Sdfr	 */
798197583Sjamie	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
799197583Sjamie		switch (client->cl_rawcred.service) {
800197583Sjamie		case rpc_gss_svc_default:
801197583Sjamie		case rpc_gss_svc_none:
802197583Sjamie			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
803197583Sjamie			break;
804197583Sjamie		case rpc_gss_svc_integrity:
805197583Sjamie			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
806197583Sjamie			break;
807197583Sjamie		case rpc_gss_svc_privacy:
808197583Sjamie			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
809197583Sjamie			break;
810197583Sjamie		}
811197583Sjamie	} else {
812197583Sjamie		client->cl_rpcflavor = RPCSEC_GSS;
813197583Sjamie	}
814184588Sdfr}
815184588Sdfr
816197583Sjamiestatic bool_t
817197583Sjamiesvc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
818197583Sjamie			       struct svc_req *rqst,
819197583Sjamie			       struct rpc_gss_init_res *gr,
820197583Sjamie			       struct rpc_gss_cred *gc)
821184588Sdfr{
822197583Sjamie	gss_buffer_desc		recv_tok;
823197583Sjamie	gss_OID			mech;
824197583Sjamie	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
825197583Sjamie	OM_uint32		cred_lifetime;
826197583Sjamie	struct svc_rpc_gss_svc_name *sname;
827184588Sdfr
828197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
829197583Sjamie
830197583Sjamie	/* Deserialize arguments. */
831197583Sjamie	memset(&recv_tok, 0, sizeof(recv_tok));
832197583Sjamie
833197583Sjamie	if (!svc_getargs(rqst,
834197583Sjamie		(xdrproc_t) xdr_gss_buffer_desc,
835197583Sjamie		(caddr_t) &recv_tok)) {
836197583Sjamie		client->cl_state = CLIENT_STALE;
837197583Sjamie		return (FALSE);
838197583Sjamie	}
839197583Sjamie
840184588Sdfr	/*
841197583Sjamie	 * First time round, try all the server names we have until
842197583Sjamie	 * one matches. Afterwards, stick with that one.
843184588Sdfr	 */
844197583Sjamie	sx_xlock(&svc_rpc_gss_lock);
845197583Sjamie	if (!client->cl_sname) {
846197583Sjamie		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
847197583Sjamie			if (sname->sn_program == rqst->rq_prog
848197583Sjamie			    && sname->sn_version == rqst->rq_vers) {
849197583Sjamie			retry:
850197583Sjamie				gr->gr_major = gss_accept_sec_context(
851197583Sjamie					&gr->gr_minor,
852197583Sjamie					&client->cl_ctx,
853197583Sjamie					sname->sn_cred,
854197583Sjamie					&recv_tok,
855197583Sjamie					GSS_C_NO_CHANNEL_BINDINGS,
856197583Sjamie					&client->cl_cname,
857197583Sjamie					&mech,
858197583Sjamie					&gr->gr_token,
859197583Sjamie					&ret_flags,
860197583Sjamie					&cred_lifetime,
861197583Sjamie					&client->cl_creds);
862197583Sjamie				if (gr->gr_major ==
863197583Sjamie				    GSS_S_CREDENTIALS_EXPIRED) {
864197583Sjamie					/*
865197583Sjamie					 * Either our creds really did
866197583Sjamie					 * expire or gssd was
867197583Sjamie					 * restarted.
868197583Sjamie					 */
869197583Sjamie					if (rpc_gss_acquire_svc_cred(sname))
870197583Sjamie						goto retry;
871197583Sjamie				}
872197583Sjamie				client->cl_sname = sname;
873197583Sjamie				break;
874184588Sdfr			}
875184588Sdfr		}
876197583Sjamie		if (!sname) {
877197583Sjamie			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
878197583Sjamie			    (char *) &recv_tok);
879197583Sjamie			sx_xunlock(&svc_rpc_gss_lock);
880197583Sjamie			return (FALSE);
881197583Sjamie		}
882197583Sjamie	} else {
883197583Sjamie		gr->gr_major = gss_accept_sec_context(
884197583Sjamie			&gr->gr_minor,
885197583Sjamie			&client->cl_ctx,
886197583Sjamie			client->cl_sname->sn_cred,
887197583Sjamie			&recv_tok,
888197583Sjamie			GSS_C_NO_CHANNEL_BINDINGS,
889197583Sjamie			&client->cl_cname,
890197583Sjamie			&mech,
891197583Sjamie			&gr->gr_token,
892197583Sjamie			&ret_flags,
893197583Sjamie			&cred_lifetime,
894197583Sjamie			NULL);
895184588Sdfr	}
896197583Sjamie	sx_xunlock(&svc_rpc_gss_lock);
897197583Sjamie
898197583Sjamie	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
899184588Sdfr
900184588Sdfr	/*
901197583Sjamie	 * If we get an error from gss_accept_sec_context, send the
902197583Sjamie	 * reply anyway so that the client gets a chance to see what
903197583Sjamie	 * is wrong.
904184588Sdfr	 */
905197583Sjamie	if (gr->gr_major != GSS_S_COMPLETE &&
906197583Sjamie	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
907197583Sjamie		rpc_gss_log_status("accept_sec_context", client->cl_mech,
908197583Sjamie		    gr->gr_major, gr->gr_minor);
909197583Sjamie		client->cl_state = CLIENT_STALE;
910197583Sjamie		return (TRUE);
911197583Sjamie	}
912184588Sdfr
913197583Sjamie	gr->gr_handle.value = &client->cl_id;
914197583Sjamie	gr->gr_handle.length = sizeof(client->cl_id);
915197583Sjamie	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
916197583Sjamie
917197583Sjamie	/* Save client info. */
918197583Sjamie	client->cl_mech = mech;
919197583Sjamie	client->cl_qop = GSS_C_QOP_DEFAULT;
920197583Sjamie	client->cl_done_callback = FALSE;
921184588Sdfr
922197583Sjamie	if (gr->gr_major == GSS_S_COMPLETE) {
923197583Sjamie		gss_buffer_desc	export_name;
924184588Sdfr
925197583Sjamie		/*
926197583Sjamie		 * Change client expiration time to be near when the
927197583Sjamie		 * client creds expire (or 24 hours if we can't figure
928197583Sjamie		 * that out).
929197583Sjamie		 */
930197583Sjamie		if (cred_lifetime == GSS_C_INDEFINITE)
931197583Sjamie			cred_lifetime = time_uptime + 24*60*60;
932184588Sdfr
933197583Sjamie		client->cl_expiration = time_uptime + cred_lifetime;
934184588Sdfr
935197583Sjamie		/*
936197583Sjamie		 * Fill in cred details in the rawcred structure.
937197583Sjamie		 */
938197583Sjamie		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
939197583Sjamie		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
940197583Sjamie		maj_stat = gss_export_name(&min_stat, client->cl_cname,
941197583Sjamie		    &export_name);
942197583Sjamie		if (maj_stat != GSS_S_COMPLETE) {
943197583Sjamie			rpc_gss_log_status("gss_export_name", client->cl_mech,
944197583Sjamie			    maj_stat, min_stat);
945197583Sjamie			return (FALSE);
946197583Sjamie		}
947197583Sjamie		client->cl_rawcred.client_principal =
948197583Sjamie			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
949197583Sjamie			    + export_name.length);
950197583Sjamie		client->cl_rawcred.client_principal->len = export_name.length;
951197583Sjamie		memcpy(client->cl_rawcred.client_principal->name,
952197583Sjamie		    export_name.value, export_name.length);
953197583Sjamie		gss_release_buffer(&min_stat, &export_name);
954197583Sjamie		client->cl_rawcred.svc_principal =
955197583Sjamie			client->cl_sname->sn_principal;
956197583Sjamie		client->cl_rawcred.service = gc->gc_svc;
957197583Sjamie
958197583Sjamie		/*
959197583Sjamie		 * Use gss_pname_to_uid to map to unix creds. For
960197583Sjamie		 * kerberos5, this uses krb5_aname_to_localname.
961197583Sjamie		 */
962197583Sjamie		svc_rpc_gss_build_ucred(client, client->cl_cname);
963197583Sjamie		svc_rpc_gss_set_flavor(client);
964197583Sjamie		gss_release_name(&min_stat, &client->cl_cname);
965197583Sjamie
966197583Sjamie#ifdef DEBUG
967197583Sjamie		{
968197583Sjamie			gss_buffer_desc mechname;
969197583Sjamie
970197583Sjamie			gss_oid_to_str(&min_stat, mech, &mechname);
971197583Sjamie
972197583Sjamie			rpc_gss_log_debug("accepted context for %s with "
973197583Sjamie			    "<mech %.*s, qop %d, svc %d>",
974197583Sjamie			    client->cl_rawcred.client_principal->name,
975197583Sjamie			    mechname.length, (char *)mechname.value,
976201853Sbrooks			    client->cl_qop, client->cl_rawcred.service);
977197583Sjamie
978197583Sjamie			gss_release_buffer(&min_stat, &mechname);
979197583Sjamie		}
980197583Sjamie#endif /* DEBUG */
981197583Sjamie	}
982197583Sjamie	return (TRUE);
983197583Sjamie}
984197583Sjamie
985197583Sjamiestatic bool_t
986197583Sjamiesvc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
987241570Srmacklem    gss_qop_t *qop, rpc_gss_proc_t gcproc)
988197583Sjamie{
989197583Sjamie	struct opaque_auth	*oa;
990197583Sjamie	gss_buffer_desc		 rpcbuf, checksum;
991197583Sjamie	OM_uint32		 maj_stat, min_stat;
992197583Sjamie	gss_qop_t		 qop_state;
993197583Sjamie	int32_t			 rpchdr[128 / sizeof(int32_t)];
994197583Sjamie	int32_t			*buf;
995197583Sjamie
996197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_validate()");
997197583Sjamie
998197583Sjamie	memset(rpchdr, 0, sizeof(rpchdr));
999197583Sjamie
1000197583Sjamie	/* Reconstruct RPC header for signing (from xdr_callmsg). */
1001197583Sjamie	buf = rpchdr;
1002197583Sjamie	IXDR_PUT_LONG(buf, msg->rm_xid);
1003197583Sjamie	IXDR_PUT_ENUM(buf, msg->rm_direction);
1004197583Sjamie	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
1005197583Sjamie	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
1006197583Sjamie	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
1007197583Sjamie	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
1008197583Sjamie	oa = &msg->rm_call.cb_cred;
1009197583Sjamie	IXDR_PUT_ENUM(buf, oa->oa_flavor);
1010197583Sjamie	IXDR_PUT_LONG(buf, oa->oa_length);
1011197583Sjamie	if (oa->oa_length) {
1012197583Sjamie		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
1013197583Sjamie		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1014197583Sjamie	}
1015197583Sjamie	rpcbuf.value = rpchdr;
1016197583Sjamie	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
1017197583Sjamie
1018197583Sjamie	checksum.value = msg->rm_call.cb_verf.oa_base;
1019197583Sjamie	checksum.length = msg->rm_call.cb_verf.oa_length;
1020197583Sjamie
1021197583Sjamie	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
1022197583Sjamie				  &qop_state);
1023197583Sjamie
1024197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
1025197583Sjamie		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
1026197583Sjamie		    maj_stat, min_stat);
1027242017Srmacklem		/*
1028242220Srmacklem		 * A bug in some versions of the Linux client generates a
1029242220Srmacklem		 * Destroy operation with a bogus encrypted checksum. Deleting
1030242220Srmacklem		 * the credential handle for that case causes the mount to fail.
1031242017Srmacklem		 * Since the checksum is bogus (gss_verify_mic() failed), it
1032242017Srmacklem		 * doesn't make sense to destroy the handle and not doing so
1033242017Srmacklem		 * fixes the Linux mount.
1034242017Srmacklem		 */
1035241570Srmacklem		if (gcproc != RPCSEC_GSS_DESTROY)
1036241570Srmacklem			client->cl_state = CLIENT_STALE;
1037197583Sjamie		return (FALSE);
1038197583Sjamie	}
1039197583Sjamie
1040197583Sjamie	*qop = qop_state;
1041197583Sjamie	return (TRUE);
1042197583Sjamie}
1043197583Sjamie
1044197583Sjamiestatic bool_t
1045197583Sjamiesvc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
1046197583Sjamie    struct svc_req *rqst, u_int seq)
1047197583Sjamie{
1048197583Sjamie	gss_buffer_desc		signbuf;
1049197583Sjamie	gss_buffer_desc		mic;
1050197583Sjamie	OM_uint32		maj_stat, min_stat;
1051197583Sjamie	uint32_t		nseq;
1052197583Sjamie
1053197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
1054197583Sjamie
1055197583Sjamie	nseq = htonl(seq);
1056197583Sjamie	signbuf.value = &nseq;
1057197583Sjamie	signbuf.length = sizeof(nseq);
1058197583Sjamie
1059197583Sjamie	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
1060197583Sjamie	    &signbuf, &mic);
1061197583Sjamie
1062197583Sjamie	if (maj_stat != GSS_S_COMPLETE) {
1063197583Sjamie		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
1064197583Sjamie		client->cl_state = CLIENT_STALE;
1065197583Sjamie		return (FALSE);
1066197583Sjamie	}
1067197583Sjamie
1068197583Sjamie	KASSERT(mic.length <= MAX_AUTH_BYTES,
1069197583Sjamie	    ("MIC too large for RPCSEC_GSS"));
1070197583Sjamie
1071197583Sjamie	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
1072197583Sjamie	rqst->rq_verf.oa_length = mic.length;
1073197583Sjamie	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
1074197583Sjamie
1075197583Sjamie	gss_release_buffer(&min_stat, &mic);
1076197583Sjamie
1077197583Sjamie	return (TRUE);
1078197583Sjamie}
1079197583Sjamie
1080197583Sjamiestatic bool_t
1081197583Sjamiesvc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
1082197583Sjamie{
1083197583Sjamie	struct svc_rpc_gss_callback *scb;
1084197583Sjamie	rpc_gss_lock_t	lock;
1085197583Sjamie	void		*cookie;
1086197583Sjamie	bool_t		cb_res;
1087197583Sjamie	bool_t		result;
1088197583Sjamie
1089184588Sdfr	/*
1090197583Sjamie	 * See if we have a callback for this guy.
1091184588Sdfr	 */
1092197583Sjamie	result = TRUE;
1093197583Sjamie	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
1094197583Sjamie		if (scb->cb_callback.program == rqst->rq_prog
1095197583Sjamie		    && scb->cb_callback.version == rqst->rq_vers) {
1096184588Sdfr			/*
1097197583Sjamie			 * This one matches. Call the callback and see
1098197583Sjamie			 * if it wants to veto or something.
1099184588Sdfr			 */
1100197583Sjamie			lock.locked = FALSE;
1101197583Sjamie			lock.raw_cred = &client->cl_rawcred;
1102197583Sjamie			cb_res = scb->cb_callback.callback(rqst,
1103197583Sjamie			    client->cl_creds,
1104197583Sjamie			    client->cl_ctx,
1105197583Sjamie			    &lock,
1106197583Sjamie			    &cookie);
1107197583Sjamie
1108197583Sjamie			if (!cb_res) {
1109197583Sjamie				client->cl_state = CLIENT_STALE;
1110197583Sjamie				result = FALSE;
1111197583Sjamie				break;
1112184588Sdfr			}
1113197583Sjamie
1114197583Sjamie			/*
1115197583Sjamie			 * The callback accepted the connection - it
1116197583Sjamie			 * is responsible for freeing client->cl_creds
1117197583Sjamie			 * now.
1118197583Sjamie			 */
1119197583Sjamie			client->cl_creds = GSS_C_NO_CREDENTIAL;
1120197583Sjamie			client->cl_locked = lock.locked;
1121197583Sjamie			client->cl_cookie = cookie;
1122197583Sjamie			return (TRUE);
1123184588Sdfr		}
1124197583Sjamie	}
1125197583Sjamie
1126197583Sjamie	/*
1127197583Sjamie	 * Either no callback exists for this program/version or one
1128197583Sjamie	 * of the callbacks rejected the connection. We just need to
1129197583Sjamie	 * clean up the delegated client creds, if any.
1130197583Sjamie	 */
1131197583Sjamie	if (client->cl_creds) {
1132197583Sjamie		OM_uint32 min_ver;
1133197583Sjamie		gss_release_cred(&min_ver, &client->cl_creds);
1134197583Sjamie	}
1135197583Sjamie	return (result);
1136197583Sjamie}
1137197583Sjamie
1138197583Sjamiestatic bool_t
1139197583Sjamiesvc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
1140197583Sjamie{
1141197583Sjamie	u_int32_t offset;
1142197583Sjamie	int word, bit;
1143197583Sjamie	bool_t result;
1144197583Sjamie
1145197583Sjamie	sx_xlock(&client->cl_lock);
1146197583Sjamie	if (seq <= client->cl_seqlast) {
1147197583Sjamie		/*
1148197583Sjamie		 * The request sequence number is less than
1149197583Sjamie		 * the largest we have seen so far. If it is
1150197583Sjamie		 * outside the window or if we have seen a
1151197583Sjamie		 * request with this sequence before, silently
1152197583Sjamie		 * discard it.
1153197583Sjamie		 */
1154197583Sjamie		offset = client->cl_seqlast - seq;
1155197583Sjamie		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
1156197583Sjamie			result = FALSE;
1157197583Sjamie			goto out;
1158184588Sdfr		}
1159197583Sjamie		word = offset / 32;
1160197583Sjamie		bit = offset % 32;
1161197583Sjamie		if (client->cl_seqmask[word] & (1 << bit)) {
1162197583Sjamie			result = FALSE;
1163197583Sjamie			goto out;
1164197583Sjamie		}
1165184588Sdfr	}
1166184588Sdfr
1167197583Sjamie	result = TRUE;
1168197583Sjamieout:
1169197583Sjamie	sx_xunlock(&client->cl_lock);
1170197583Sjamie	return (result);
1171184588Sdfr}
1172184588Sdfr
1173197583Sjamiestatic void
1174197583Sjamiesvc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
1175184588Sdfr{
1176197583Sjamie	int offset, i, word, bit;
1177197583Sjamie	uint32_t carry, newcarry;
1178184588Sdfr
1179197583Sjamie	sx_xlock(&client->cl_lock);
1180197583Sjamie	if (seq > client->cl_seqlast) {
1181184588Sdfr		/*
1182197583Sjamie		 * This request has a sequence number greater
1183197583Sjamie		 * than any we have seen so far. Advance the
1184197583Sjamie		 * seq window and set bit zero of the window
1185197583Sjamie		 * (which corresponds to the new sequence
1186197583Sjamie		 * number)
1187184588Sdfr		 */
1188197583Sjamie		offset = seq - client->cl_seqlast;
1189197583Sjamie		while (offset > 32) {
1190197583Sjamie			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
1191197583Sjamie			     i > 0; i--) {
1192197583Sjamie				client->cl_seqmask[i] = client->cl_seqmask[i-1];
1193184588Sdfr			}
1194197583Sjamie			client->cl_seqmask[0] = 0;
1195197583Sjamie			offset -= 32;
1196184588Sdfr		}
1197197583Sjamie		carry = 0;
1198197583Sjamie		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
1199197583Sjamie			newcarry = client->cl_seqmask[i] >> (32 - offset);
1200197583Sjamie			client->cl_seqmask[i] =
1201197583Sjamie				(client->cl_seqmask[i] << offset) | carry;
1202197583Sjamie			carry = newcarry;
1203197583Sjamie		}
1204197583Sjamie		client->cl_seqmask[0] |= 1;
1205197583Sjamie		client->cl_seqlast = seq;
1206197583Sjamie	} else {
1207197583Sjamie		offset = client->cl_seqlast - seq;
1208197583Sjamie		word = offset / 32;
1209197583Sjamie		bit = offset % 32;
1210197583Sjamie		client->cl_seqmask[word] |= (1 << bit);
1211197583Sjamie	}
1212197583Sjamie	sx_xunlock(&client->cl_lock);
1213197583Sjamie}
1214197583Sjamie
1215197583Sjamieenum auth_stat
1216197583Sjamiesvc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
1217197583Sjamie
1218197583Sjamie{
1219197583Sjamie	OM_uint32		 min_stat;
1220197583Sjamie	XDR	 		 xdrs;
1221197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
1222197583Sjamie	struct svc_rpc_gss_client *client;
1223197583Sjamie	struct rpc_gss_cred	 gc;
1224197583Sjamie	struct rpc_gss_init_res	 gr;
1225197583Sjamie	gss_qop_t		 qop;
1226197583Sjamie	int			 call_stat;
1227197583Sjamie	enum auth_stat		 result;
1228197583Sjamie
1229197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss()");
1230197583Sjamie
1231197583Sjamie	/* Garbage collect old clients. */
1232197583Sjamie	svc_rpc_gss_timeout_clients();
1233197583Sjamie
1234197583Sjamie	/* Initialize reply. */
1235197583Sjamie	rqst->rq_verf = _null_auth;
1236197583Sjamie
1237197583Sjamie	/* Deserialize client credentials. */
1238197583Sjamie	if (rqst->rq_cred.oa_length <= 0)
1239197583Sjamie		return (AUTH_BADCRED);
1240197583Sjamie
1241197583Sjamie	memset(&gc, 0, sizeof(gc));
1242197583Sjamie
1243197583Sjamie	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
1244197583Sjamie	    rqst->rq_cred.oa_length, XDR_DECODE);
1245197583Sjamie
1246197583Sjamie	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
1247197583Sjamie		XDR_DESTROY(&xdrs);
1248197583Sjamie		return (AUTH_BADCRED);
1249197583Sjamie	}
1250197583Sjamie	XDR_DESTROY(&xdrs);
1251197583Sjamie
1252197583Sjamie	client = NULL;
1253197583Sjamie
1254197583Sjamie	/* Check version. */
1255197583Sjamie	if (gc.gc_version != RPCSEC_GSS_VERSION) {
1256197583Sjamie		result = AUTH_BADCRED;
1257197583Sjamie		goto out;
1258197583Sjamie	}
1259197583Sjamie
1260197583Sjamie	/* Check the proc and find the client (or create it) */
1261197583Sjamie	if (gc.gc_proc == RPCSEC_GSS_INIT) {
1262197583Sjamie		if (gc.gc_handle.length != 0) {
1263197583Sjamie			result = AUTH_BADCRED;
1264197583Sjamie			goto out;
1265197583Sjamie		}
1266197583Sjamie		client = svc_rpc_gss_create_client();
1267197583Sjamie		refcount_acquire(&client->cl_refs);
1268197583Sjamie	} else {
1269197583Sjamie		struct svc_rpc_gss_clientid *p;
1270197583Sjamie		if (gc.gc_handle.length != sizeof(*p)) {
1271197583Sjamie			result = AUTH_BADCRED;
1272197583Sjamie			goto out;
1273197583Sjamie		}
1274197583Sjamie		p = gc.gc_handle.value;
1275197583Sjamie		client = svc_rpc_gss_find_client(p);
1276197583Sjamie		if (!client) {
1277197583Sjamie			/*
1278197583Sjamie			 * Can't find the client - we may have
1279197583Sjamie			 * destroyed it - tell the other side to
1280197583Sjamie			 * re-authenticate.
1281197583Sjamie			 */
1282197583Sjamie			result = RPCSEC_GSS_CREDPROBLEM;
1283197583Sjamie			goto out;
1284197583Sjamie		}
1285197583Sjamie	}
1286197583Sjamie	cc = rqst->rq_clntcred;
1287197583Sjamie	cc->cc_client = client;
1288197583Sjamie	cc->cc_service = gc.gc_svc;
1289197583Sjamie	cc->cc_seq = gc.gc_seq;
1290197583Sjamie
1291197583Sjamie	/*
1292197583Sjamie	 * The service and sequence number must be ignored for
1293197583Sjamie	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1294197583Sjamie	 */
1295197583Sjamie	if (gc.gc_proc != RPCSEC_GSS_INIT
1296197583Sjamie	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1297184588Sdfr		/*
1298197583Sjamie		 * Check for sequence number overflow.
1299184588Sdfr		 */
1300197583Sjamie		if (gc.gc_seq >= MAXSEQ) {
1301197583Sjamie			result = RPCSEC_GSS_CTXPROBLEM;
1302197583Sjamie			goto out;
1303197583Sjamie		}
1304197583Sjamie
1305197583Sjamie		/*
1306197583Sjamie		 * Check for valid service.
1307197583Sjamie		 */
1308197583Sjamie		if (gc.gc_svc != rpc_gss_svc_none &&
1309197583Sjamie		    gc.gc_svc != rpc_gss_svc_integrity &&
1310197583Sjamie		    gc.gc_svc != rpc_gss_svc_privacy) {
1311197583Sjamie			result = AUTH_BADCRED;
1312197583Sjamie			goto out;
1313197583Sjamie		}
1314184588Sdfr	}
1315197583Sjamie
1316197583Sjamie	/* Handle RPCSEC_GSS control procedure. */
1317197583Sjamie	switch (gc.gc_proc) {
1318197583Sjamie
1319197583Sjamie	case RPCSEC_GSS_INIT:
1320197583Sjamie	case RPCSEC_GSS_CONTINUE_INIT:
1321197583Sjamie		if (rqst->rq_proc != NULLPROC) {
1322197583Sjamie			result = AUTH_REJECTEDCRED;
1323197583Sjamie			break;
1324197583Sjamie		}
1325197583Sjamie
1326197583Sjamie		memset(&gr, 0, sizeof(gr));
1327197583Sjamie		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1328197583Sjamie			result = AUTH_REJECTEDCRED;
1329197583Sjamie			break;
1330197583Sjamie		}
1331197583Sjamie
1332197583Sjamie		if (gr.gr_major == GSS_S_COMPLETE) {
1333197583Sjamie			/*
1334197583Sjamie			 * We borrow the space for the call verf to
1335197583Sjamie			 * pack our reply verf.
1336197583Sjamie			 */
1337197583Sjamie			rqst->rq_verf = msg->rm_call.cb_verf;
1338197583Sjamie			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1339197583Sjamie				result = AUTH_REJECTEDCRED;
1340197583Sjamie				break;
1341197583Sjamie			}
1342197583Sjamie		} else {
1343197583Sjamie			rqst->rq_verf = _null_auth;
1344197583Sjamie		}
1345197583Sjamie
1346197583Sjamie		call_stat = svc_sendreply(rqst,
1347197583Sjamie		    (xdrproc_t) xdr_rpc_gss_init_res,
1348197583Sjamie		    (caddr_t) &gr);
1349197583Sjamie
1350197583Sjamie		gss_release_buffer(&min_stat, &gr.gr_token);
1351197583Sjamie
1352197583Sjamie		if (!call_stat) {
1353197583Sjamie			result = AUTH_FAILED;
1354197583Sjamie			break;
1355197583Sjamie		}
1356197583Sjamie
1357197583Sjamie		if (gr.gr_major == GSS_S_COMPLETE)
1358197583Sjamie			client->cl_state = CLIENT_ESTABLISHED;
1359197583Sjamie
1360197583Sjamie		result = RPCSEC_GSS_NODISPATCH;
1361197583Sjamie		break;
1362197583Sjamie
1363197583Sjamie	case RPCSEC_GSS_DATA:
1364197583Sjamie	case RPCSEC_GSS_DESTROY:
1365197583Sjamie		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1366197583Sjamie			result = RPCSEC_GSS_NODISPATCH;
1367197583Sjamie			break;
1368197583Sjamie		}
1369197583Sjamie
1370241570Srmacklem		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
1371197583Sjamie			result = RPCSEC_GSS_CREDPROBLEM;
1372197583Sjamie			break;
1373197583Sjamie		}
1374197583Sjamie
1375197583Sjamie		/*
1376197583Sjamie		 * We borrow the space for the call verf to pack our
1377197583Sjamie		 * reply verf.
1378197583Sjamie		 */
1379197583Sjamie		rqst->rq_verf = msg->rm_call.cb_verf;
1380197583Sjamie		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1381197583Sjamie			result = RPCSEC_GSS_CTXPROBLEM;
1382197583Sjamie			break;
1383197583Sjamie		}
1384197583Sjamie
1385197583Sjamie		svc_rpc_gss_update_seq(client, gc.gc_seq);
1386197583Sjamie
1387197583Sjamie		/*
1388197583Sjamie		 * Change the SVCAUTH ops on the request to point at
1389197583Sjamie		 * our own code so that we can unwrap the arguments
1390197583Sjamie		 * and wrap the result. The caller will re-set this on
1391197583Sjamie		 * every request to point to a set of null wrap/unwrap
1392197583Sjamie		 * methods. Acquire an extra reference to the client
1393197583Sjamie		 * which will be released by svc_rpc_gss_release()
1394197583Sjamie		 * after the request has finished processing.
1395197583Sjamie		 */
1396197583Sjamie		refcount_acquire(&client->cl_refs);
1397197583Sjamie		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
1398197583Sjamie		rqst->rq_auth.svc_ah_private = cc;
1399197583Sjamie
1400197583Sjamie		if (gc.gc_proc == RPCSEC_GSS_DATA) {
1401197583Sjamie			/*
1402197583Sjamie			 * We might be ready to do a callback to the server to
1403197583Sjamie			 * see if it wants to accept/reject the connection.
1404197583Sjamie			 */
1405197583Sjamie			sx_xlock(&client->cl_lock);
1406197583Sjamie			if (!client->cl_done_callback) {
1407197583Sjamie				client->cl_done_callback = TRUE;
1408197583Sjamie				client->cl_qop = qop;
1409197583Sjamie				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1410197583Sjamie					client->cl_rawcred.mechanism, qop);
1411197583Sjamie				if (!svc_rpc_gss_callback(client, rqst)) {
1412197583Sjamie					result = AUTH_REJECTEDCRED;
1413197583Sjamie					sx_xunlock(&client->cl_lock);
1414197583Sjamie					break;
1415197583Sjamie				}
1416197583Sjamie			}
1417197583Sjamie			sx_xunlock(&client->cl_lock);
1418197583Sjamie
1419197583Sjamie			/*
1420197583Sjamie			 * If the server has locked this client to a
1421197583Sjamie			 * particular service+qop pair, enforce that
1422197583Sjamie			 * restriction now.
1423197583Sjamie			 */
1424197583Sjamie			if (client->cl_locked) {
1425197583Sjamie				if (client->cl_rawcred.service != gc.gc_svc) {
1426197583Sjamie					result = AUTH_FAILED;
1427197583Sjamie					break;
1428197583Sjamie				} else if (client->cl_qop != qop) {
1429197583Sjamie					result = AUTH_BADVERF;
1430197583Sjamie					break;
1431197583Sjamie				}
1432197583Sjamie			}
1433197583Sjamie
1434197583Sjamie			/*
1435197583Sjamie			 * If the qop changed, look up the new qop
1436197583Sjamie			 * name for rawcred.
1437197583Sjamie			 */
1438197583Sjamie			if (client->cl_qop != qop) {
1439197583Sjamie				client->cl_qop = qop;
1440197583Sjamie				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1441197583Sjamie					client->cl_rawcred.mechanism, qop);
1442197583Sjamie			}
1443197583Sjamie
1444197583Sjamie			/*
1445197583Sjamie			 * Make sure we use the right service value
1446197583Sjamie			 * for unwrap/wrap.
1447197583Sjamie			 */
1448197583Sjamie			if (client->cl_rawcred.service != gc.gc_svc) {
1449197583Sjamie				client->cl_rawcred.service = gc.gc_svc;
1450197583Sjamie				svc_rpc_gss_set_flavor(client);
1451197583Sjamie			}
1452197583Sjamie
1453197583Sjamie			result = AUTH_OK;
1454197583Sjamie		} else {
1455197583Sjamie			if (rqst->rq_proc != NULLPROC) {
1456197583Sjamie				result = AUTH_REJECTEDCRED;
1457197583Sjamie				break;
1458197583Sjamie			}
1459197583Sjamie
1460197583Sjamie			call_stat = svc_sendreply(rqst,
1461197583Sjamie			    (xdrproc_t) xdr_void, (caddr_t) NULL);
1462197583Sjamie
1463197583Sjamie			if (!call_stat) {
1464197583Sjamie				result = AUTH_FAILED;
1465197583Sjamie				break;
1466197583Sjamie			}
1467197583Sjamie
1468197583Sjamie			svc_rpc_gss_forget_client(client);
1469197583Sjamie
1470197583Sjamie			result = RPCSEC_GSS_NODISPATCH;
1471197583Sjamie			break;
1472197583Sjamie		}
1473197583Sjamie		break;
1474197583Sjamie
1475197583Sjamie	default:
1476197583Sjamie		result = AUTH_BADCRED;
1477197583Sjamie		break;
1478197583Sjamie	}
1479197583Sjamieout:
1480197583Sjamie	if (client)
1481197583Sjamie		svc_rpc_gss_release_client(client);
1482197583Sjamie
1483197583Sjamie	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1484197583Sjamie	return (result);
1485184588Sdfr}
1486184588Sdfr
1487197583Sjamiestatic bool_t
1488197583Sjamiesvc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
1489197583Sjamie{
1490197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
1491197583Sjamie	struct svc_rpc_gss_client *client;
1492197583Sjamie
1493197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
1494184588Sdfr
1495197583Sjamie	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1496197583Sjamie	client = cc->cc_client;
1497197583Sjamie	if (client->cl_state != CLIENT_ESTABLISHED
1498197583Sjamie	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
1499197583Sjamie		return (TRUE);
1500197583Sjamie	}
1501197583Sjamie
1502197583Sjamie	return (xdr_rpc_gss_wrap_data(mp,
1503197583Sjamie		client->cl_ctx, client->cl_qop,
1504197583Sjamie		cc->cc_service, cc->cc_seq));
1505197583Sjamie}
1506197583Sjamie
1507197583Sjamiestatic bool_t
1508197583Sjamiesvc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
1509184588Sdfr{
1510197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
1511197583Sjamie	struct svc_rpc_gss_client *client;
1512184588Sdfr
1513197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
1514197583Sjamie
1515197583Sjamie	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1516197583Sjamie	client = cc->cc_client;
1517197583Sjamie	if (client->cl_state != CLIENT_ESTABLISHED
1518197583Sjamie	    || cc->cc_service == rpc_gss_svc_none) {
1519197583Sjamie		return (TRUE);
1520184588Sdfr	}
1521197583Sjamie
1522197583Sjamie	return (xdr_rpc_gss_unwrap_data(mp,
1523197583Sjamie		client->cl_ctx, client->cl_qop,
1524197583Sjamie		cc->cc_service, cc->cc_seq));
1525184588Sdfr}
1526184588Sdfr
1527197583Sjamiestatic void
1528197583Sjamiesvc_rpc_gss_release(SVCAUTH *auth)
1529197583Sjamie{
1530197583Sjamie	struct svc_rpc_gss_cookedcred *cc;
1531197583Sjamie	struct svc_rpc_gss_client *client;
1532197583Sjamie
1533197583Sjamie	rpc_gss_log_debug("in svc_rpc_gss_release()");
1534197583Sjamie
1535197583Sjamie	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
1536197583Sjamie	client = cc->cc_client;
1537197583Sjamie	svc_rpc_gss_release_client(client);
1538197583Sjamie}
1539