1184588Sdfr/*-
2184588Sdfr * Copyright (c) 2008 Doug Rabson
3184588Sdfr * 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 *
14184588Sdfr * 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
17184588Sdfr * 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 */
26184588Sdfr/*
27184588Sdfr  auth_gss.c
28184588Sdfr
29184588Sdfr  RPCSEC_GSS client routines.
30184588Sdfr
31184588Sdfr  Copyright (c) 2000 The Regents of the University of Michigan.
32184588Sdfr  All rights reserved.
33184588Sdfr
34184588Sdfr  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35184588Sdfr  All rights reserved, all wrongs reversed.
36184588Sdfr
37184588Sdfr  Redistribution and use in source and binary forms, with or without
38184588Sdfr  modification, are permitted provided that the following conditions
39184588Sdfr  are met:
40184588Sdfr
41184588Sdfr  1. Redistributions of source code must retain the above copyright
42184588Sdfr     notice, this list of conditions and the following disclaimer.
43184588Sdfr  2. Redistributions in binary form must reproduce the above copyright
44184588Sdfr     notice, this list of conditions and the following disclaimer in the
45184588Sdfr     documentation and/or other materials provided with the distribution.
46184588Sdfr  3. Neither the name of the University nor the names of its
47184588Sdfr     contributors may be used to endorse or promote products derived
48184588Sdfr     from this software without specific prior written permission.
49184588Sdfr
50184588Sdfr  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51184588Sdfr  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52184588Sdfr  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53184588Sdfr  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54184588Sdfr  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55184588Sdfr  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56184588Sdfr  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57184588Sdfr  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58184588Sdfr  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59184588Sdfr  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60184588Sdfr  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61184588Sdfr
62184588Sdfr  $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
63184588Sdfr*/
64184588Sdfr
65184588Sdfr#include <sys/cdefs.h>
66184588Sdfr__FBSDID("$FreeBSD$");
67184588Sdfr
68184588Sdfr#include <sys/param.h>
69184588Sdfr#include <sys/systm.h>
70184588Sdfr#include <sys/hash.h>
71184588Sdfr#include <sys/kernel.h>
72184588Sdfr#include <sys/kobj.h>
73184588Sdfr#include <sys/lock.h>
74184588Sdfr#include <sys/malloc.h>
75184588Sdfr#include <sys/mbuf.h>
76184588Sdfr#include <sys/mutex.h>
77184588Sdfr#include <sys/proc.h>
78184588Sdfr#include <sys/refcount.h>
79184588Sdfr#include <sys/sx.h>
80184588Sdfr#include <sys/ucred.h>
81184588Sdfr
82184588Sdfr#include <rpc/rpc.h>
83184588Sdfr#include <rpc/rpcsec_gss.h>
84184588Sdfr
85184588Sdfr#include "rpcsec_gss_int.h"
86184588Sdfr
87184588Sdfrstatic void	rpc_gss_nextverf(AUTH*);
88184588Sdfrstatic bool_t	rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
89184588Sdfrstatic bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
90184588Sdfrstatic bool_t	rpc_gss_refresh(AUTH *, void *);
91184588Sdfrstatic bool_t	rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
92184588Sdfr    struct mbuf **);
93184588Sdfrstatic void	rpc_gss_destroy(AUTH *);
94184588Sdfrstatic void	rpc_gss_destroy_context(AUTH *, bool_t);
95184588Sdfr
96184588Sdfrstatic struct auth_ops rpc_gss_ops = {
97184588Sdfr	rpc_gss_nextverf,
98184588Sdfr	rpc_gss_marshal,
99184588Sdfr	rpc_gss_validate,
100184588Sdfr	rpc_gss_refresh,
101184588Sdfr	rpc_gss_destroy,
102184588Sdfr};
103184588Sdfr
104184588Sdfrenum rpcsec_gss_state {
105184588Sdfr	RPCSEC_GSS_START,
106184588Sdfr	RPCSEC_GSS_CONTEXT,
107184588Sdfr	RPCSEC_GSS_ESTABLISHED,
108184588Sdfr	RPCSEC_GSS_DESTROYING
109184588Sdfr};
110184588Sdfr
111184588Sdfrstruct rpc_pending_request {
112184588Sdfr	uint32_t		pr_xid;		/* XID of rpc */
113184588Sdfr	uint32_t		pr_seq;		/* matching GSS seq */
114184588Sdfr	LIST_ENTRY(rpc_pending_request) pr_link;
115184588Sdfr};
116184588SdfrLIST_HEAD(rpc_pending_request_list, rpc_pending_request);
117184588Sdfr
118184588Sdfrstruct rpc_gss_data {
119184588Sdfr	volatile u_int		gd_refs;	/* number of current users */
120184588Sdfr	struct mtx		gd_lock;
121184588Sdfr	uint32_t		gd_hash;
122184588Sdfr	AUTH			*gd_auth;	/* link back to AUTH */
123184588Sdfr	struct ucred		*gd_ucred;	/* matching local cred */
124184588Sdfr	char			*gd_principal;	/* server principal name */
125184588Sdfr	rpc_gss_options_req_t	gd_options;	/* GSS context options */
126184588Sdfr	enum rpcsec_gss_state	gd_state;	/* connection state */
127184588Sdfr	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
128184588Sdfr						 * NULL RPC verfier to
129184588Sdfr						 * process at end of
130184588Sdfr						 * context negotiation */
131184588Sdfr	CLIENT			*gd_clnt;	/* client handle */
132184588Sdfr	gss_OID			gd_mech;	/* mechanism to use */
133184588Sdfr	gss_qop_t		gd_qop;		/* quality of protection */
134184588Sdfr	gss_ctx_id_t		gd_ctx;		/* context id */
135184588Sdfr	struct rpc_gss_cred	gd_cred;	/* client credentials */
136184588Sdfr	uint32_t		gd_seq;		/* next sequence number */
137184588Sdfr	u_int			gd_win;		/* sequence window */
138184588Sdfr	struct rpc_pending_request_list gd_reqs;
139184588Sdfr	TAILQ_ENTRY(rpc_gss_data) gd_link;
140184588Sdfr	TAILQ_ENTRY(rpc_gss_data) gd_alllink;
141184588Sdfr};
142184588SdfrTAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
143184588Sdfr
144184588Sdfr#define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
145184588Sdfr
146184588Sdfrstatic struct timeval AUTH_TIMEOUT = { 25, 0 };
147184588Sdfr
148184588Sdfr#define RPC_GSS_HASH_SIZE	11
149184588Sdfr#define RPC_GSS_MAX		256
150184588Sdfrstatic struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
151184588Sdfrstatic struct rpc_gss_data_list rpc_gss_all;
152184588Sdfrstatic struct sx rpc_gss_lock;
153184588Sdfrstatic int rpc_gss_count;
154184588Sdfr
155184588Sdfrstatic AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
156184588Sdfr    gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
157184588Sdfr    rpc_gss_options_ret_t *);
158184588Sdfr
159184588Sdfrstatic void
160184588Sdfrrpc_gss_hashinit(void *dummy)
161184588Sdfr{
162184588Sdfr	int i;
163184588Sdfr
164184588Sdfr	for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
165184588Sdfr		TAILQ_INIT(&rpc_gss_cache[i]);
166184588Sdfr	TAILQ_INIT(&rpc_gss_all);
167184588Sdfr	sx_init(&rpc_gss_lock, "rpc_gss_lock");
168184588Sdfr}
169184588SdfrSYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
170184588Sdfr
171184588Sdfrstatic uint32_t
172184588Sdfrrpc_gss_hash(const char *principal, gss_OID mech,
173184588Sdfr    struct ucred *cred, rpc_gss_service_t service)
174184588Sdfr{
175184588Sdfr	uint32_t h;
176184588Sdfr
177184588Sdfr	h = HASHSTEP(HASHINIT, cred->cr_uid);
178184588Sdfr	h = hash32_str(principal, h);
179184588Sdfr	h = hash32_buf(mech->elements, mech->length, h);
180184588Sdfr	h = HASHSTEP(h, (int) service);
181184588Sdfr
182184588Sdfr	return (h % RPC_GSS_HASH_SIZE);
183184588Sdfr}
184184588Sdfr
185184588Sdfr/*
186184588Sdfr * Simplified interface to create a security association for the
187184588Sdfr * current thread's * ucred.
188184588Sdfr */
189184588SdfrAUTH *
190184588Sdfrrpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
191184588Sdfr    gss_OID mech_oid, rpc_gss_service_t service)
192184588Sdfr{
193184588Sdfr	uint32_t		h, th;
194184588Sdfr	AUTH			*auth;
195184588Sdfr	struct rpc_gss_data	*gd, *tgd;
196194878Srmacklem	rpc_gss_options_ret_t	options;
197184588Sdfr
198184588Sdfr	if (rpc_gss_count > RPC_GSS_MAX) {
199184588Sdfr		while (rpc_gss_count > RPC_GSS_MAX) {
200184588Sdfr			sx_xlock(&rpc_gss_lock);
201184588Sdfr			tgd = TAILQ_FIRST(&rpc_gss_all);
202184588Sdfr			th = tgd->gd_hash;
203184588Sdfr			TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
204184588Sdfr			TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
205184588Sdfr			rpc_gss_count--;
206184588Sdfr			sx_xunlock(&rpc_gss_lock);
207184588Sdfr			AUTH_DESTROY(tgd->gd_auth);
208184588Sdfr		}
209184588Sdfr	}
210184588Sdfr
211184588Sdfr	/*
212184588Sdfr	 * See if we already have an AUTH which matches.
213184588Sdfr	 */
214184588Sdfr	h = rpc_gss_hash(principal, mech_oid, cred, service);
215184588Sdfr
216184588Sdfragain:
217184588Sdfr	sx_slock(&rpc_gss_lock);
218184588Sdfr	TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
219184588Sdfr		if (gd->gd_ucred->cr_uid == cred->cr_uid
220184588Sdfr		    && !strcmp(gd->gd_principal, principal)
221184588Sdfr		    && gd->gd_mech == mech_oid
222184588Sdfr		    && gd->gd_cred.gc_svc == service) {
223184588Sdfr			refcount_acquire(&gd->gd_refs);
224184588Sdfr			if (sx_try_upgrade(&rpc_gss_lock)) {
225184588Sdfr				/*
226184588Sdfr				 * Keep rpc_gss_all LRU sorted.
227184588Sdfr				 */
228184588Sdfr				TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
229184588Sdfr				TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
230184588Sdfr				    gd_alllink);
231184588Sdfr				sx_xunlock(&rpc_gss_lock);
232184588Sdfr			} else {
233184588Sdfr				sx_sunlock(&rpc_gss_lock);
234184588Sdfr			}
235194878Srmacklem
236194878Srmacklem			/*
237194878Srmacklem			 * If the state != ESTABLISHED, try and initialize
238194878Srmacklem			 * the authenticator again. This will happen if the
239194878Srmacklem			 * user's credentials have expired. It may succeed now,
240194878Srmacklem			 * if they have done a kinit or similar.
241194878Srmacklem			 */
242194878Srmacklem			if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
243194878Srmacklem				memset(&options, 0, sizeof (options));
244194878Srmacklem				(void) rpc_gss_init(gd->gd_auth, &options);
245194878Srmacklem			}
246184588Sdfr			return (gd->gd_auth);
247184588Sdfr		}
248184588Sdfr	}
249184588Sdfr	sx_sunlock(&rpc_gss_lock);
250184588Sdfr
251184588Sdfr	/*
252184588Sdfr	 * We missed in the cache - create a new association.
253184588Sdfr	 */
254184588Sdfr	auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service,
255184588Sdfr	    GSS_C_QOP_DEFAULT, NULL, NULL);
256184588Sdfr	if (!auth)
257184588Sdfr		return (NULL);
258184588Sdfr
259184588Sdfr	gd = AUTH_PRIVATE(auth);
260184588Sdfr	gd->gd_hash = h;
261184588Sdfr
262184588Sdfr	sx_xlock(&rpc_gss_lock);
263184588Sdfr	TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
264184588Sdfr		if (tgd->gd_ucred->cr_uid == cred->cr_uid
265184588Sdfr		    && !strcmp(tgd->gd_principal, principal)
266184588Sdfr		    && tgd->gd_mech == mech_oid
267184588Sdfr		    && tgd->gd_cred.gc_svc == service) {
268184588Sdfr			/*
269184588Sdfr			 * We lost a race to create the AUTH that
270184588Sdfr			 * matches this cred.
271184588Sdfr			 */
272184588Sdfr			sx_xunlock(&rpc_gss_lock);
273184588Sdfr			AUTH_DESTROY(auth);
274184588Sdfr			goto again;
275184588Sdfr		}
276184588Sdfr	}
277184588Sdfr
278184588Sdfr	rpc_gss_count++;
279184588Sdfr	TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
280184588Sdfr	TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
281184588Sdfr	refcount_acquire(&gd->gd_refs);	/* one for the cache, one for user */
282184588Sdfr	sx_xunlock(&rpc_gss_lock);
283184588Sdfr
284184588Sdfr	return (auth);
285184588Sdfr}
286184588Sdfr
287184588Sdfrvoid
288184588Sdfrrpc_gss_secpurge(CLIENT *clnt)
289184588Sdfr{
290184588Sdfr	uint32_t		h;
291184588Sdfr	struct rpc_gss_data	*gd, *tgd;
292184588Sdfr
293184588Sdfr	TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
294184588Sdfr		if (gd->gd_clnt == clnt) {
295184588Sdfr			sx_xlock(&rpc_gss_lock);
296184588Sdfr			h = gd->gd_hash;
297184588Sdfr			TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
298184588Sdfr			TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
299184588Sdfr			rpc_gss_count--;
300184588Sdfr			sx_xunlock(&rpc_gss_lock);
301184588Sdfr			AUTH_DESTROY(gd->gd_auth);
302184588Sdfr		}
303184588Sdfr	}
304184588Sdfr}
305184588Sdfr
306184588SdfrAUTH *
307184588Sdfrrpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *principal,
308184588Sdfr    const char *mechanism, rpc_gss_service_t service, const char *qop,
309184588Sdfr    rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
310184588Sdfr{
311184588Sdfr	gss_OID			oid;
312184588Sdfr	u_int			qop_num;
313184588Sdfr
314184588Sdfr	/*
315184588Sdfr	 * Bail out now if we don't know this mechanism.
316184588Sdfr	 */
317184588Sdfr	if (!rpc_gss_mech_to_oid(mechanism, &oid))
318184588Sdfr		return (NULL);
319184588Sdfr
320184588Sdfr	if (qop) {
321184588Sdfr		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
322184588Sdfr			return (NULL);
323184588Sdfr	} else {
324184588Sdfr		qop_num = GSS_C_QOP_DEFAULT;
325184588Sdfr	}
326184588Sdfr
327184588Sdfr	return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service,
328184588Sdfr		qop_num, options_req, options_ret));
329184588Sdfr}
330184588Sdfr
331184588Sdfrstatic AUTH *
332184588Sdfrrpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal,
333184588Sdfr    gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num,
334184588Sdfr    rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
335184588Sdfr{
336184588Sdfr	AUTH			*auth;
337184588Sdfr	rpc_gss_options_ret_t	options;
338184588Sdfr	struct rpc_gss_data	*gd;
339184588Sdfr
340184588Sdfr	/*
341184588Sdfr	 * If the caller doesn't want the options, point at local
342184588Sdfr	 * storage to simplify the code below.
343184588Sdfr	 */
344184588Sdfr	if (!options_ret)
345184588Sdfr		options_ret = &options;
346184588Sdfr
347184588Sdfr	/*
348184588Sdfr	 * Default service is integrity.
349184588Sdfr	 */
350184588Sdfr	if (service == rpc_gss_svc_default)
351184588Sdfr		service = rpc_gss_svc_integrity;
352184588Sdfr
353184588Sdfr	memset(options_ret, 0, sizeof(*options_ret));
354184588Sdfr
355184588Sdfr	rpc_gss_log_debug("in rpc_gss_seccreate()");
356184588Sdfr
357184588Sdfr	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
358184588Sdfr
359184588Sdfr	auth = mem_alloc(sizeof(*auth));
360184588Sdfr	if (auth == NULL) {
361184588Sdfr		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
362184588Sdfr		rpc_createerr.cf_error.re_errno = ENOMEM;
363184588Sdfr		return (NULL);
364184588Sdfr	}
365184588Sdfr	gd = mem_alloc(sizeof(*gd));
366184588Sdfr	if (gd == NULL) {
367184588Sdfr		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
368184588Sdfr		rpc_createerr.cf_error.re_errno = ENOMEM;
369184588Sdfr		mem_free(auth, sizeof(*auth));
370184588Sdfr		return (NULL);
371184588Sdfr	}
372184588Sdfr
373184588Sdfr	auth->ah_ops = &rpc_gss_ops;
374184588Sdfr	auth->ah_private = (caddr_t) gd;
375184588Sdfr	auth->ah_cred.oa_flavor = RPCSEC_GSS;
376184588Sdfr
377184588Sdfr	refcount_init(&gd->gd_refs, 1);
378184588Sdfr	mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
379184588Sdfr	gd->gd_auth = auth;
380184588Sdfr	gd->gd_ucred = crdup(cred);
381184588Sdfr	gd->gd_principal = strdup(principal, M_RPC);
382184588Sdfr
383184588Sdfr
384184588Sdfr	if (options_req) {
385184588Sdfr		gd->gd_options = *options_req;
386184588Sdfr	} else {
387184588Sdfr		gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
388184588Sdfr		gd->gd_options.time_req = 0;
389184588Sdfr		gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
390184588Sdfr		gd->gd_options.input_channel_bindings = NULL;
391184588Sdfr	}
392184588Sdfr	CLNT_ACQUIRE(clnt);
393184588Sdfr	gd->gd_clnt = clnt;
394184588Sdfr	gd->gd_ctx = GSS_C_NO_CONTEXT;
395184588Sdfr	gd->gd_mech = mech_oid;
396184588Sdfr	gd->gd_qop = qop_num;
397184588Sdfr
398184588Sdfr	gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
399184588Sdfr	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
400184588Sdfr	gd->gd_cred.gc_seq = 0;
401184588Sdfr	gd->gd_cred.gc_svc = service;
402184588Sdfr	LIST_INIT(&gd->gd_reqs);
403184588Sdfr
404184588Sdfr	if (!rpc_gss_init(auth, options_ret)) {
405184588Sdfr		goto bad;
406184588Sdfr	}
407184588Sdfr
408184588Sdfr	return (auth);
409184588Sdfr
410184588Sdfr bad:
411184588Sdfr	AUTH_DESTROY(auth);
412184588Sdfr	return (NULL);
413184588Sdfr}
414184588Sdfr
415184588Sdfrbool_t
416184588Sdfrrpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
417184588Sdfr{
418184588Sdfr	struct rpc_gss_data	*gd;
419184588Sdfr	u_int			qop_num;
420184588Sdfr	const char		*mechanism;
421184588Sdfr
422184588Sdfr	gd = AUTH_PRIVATE(auth);
423184588Sdfr	if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
424184588Sdfr		return (FALSE);
425184588Sdfr	}
426184588Sdfr
427184588Sdfr	if (qop) {
428184588Sdfr		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
429184588Sdfr			return (FALSE);
430184588Sdfr		}
431184588Sdfr	} else {
432184588Sdfr		qop_num = GSS_C_QOP_DEFAULT;
433184588Sdfr	}
434184588Sdfr
435184588Sdfr	gd->gd_cred.gc_svc = service;
436184588Sdfr	gd->gd_qop = qop_num;
437184588Sdfr	return (TRUE);
438184588Sdfr}
439184588Sdfr
440184588Sdfrstatic void
441184588Sdfrrpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
442184588Sdfr{
443184588Sdfr	struct rpc_pending_request *pr, *npr;
444184588Sdfr	struct rpc_pending_request_list reqs;
445184588Sdfr
446184588Sdfr	LIST_INIT(&reqs);
447184588Sdfr	mtx_lock(&gd->gd_lock);
448184588Sdfr	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
449184588Sdfr		if (pr->pr_xid == xid) {
450184588Sdfr			LIST_REMOVE(pr, pr_link);
451184588Sdfr			LIST_INSERT_HEAD(&reqs, pr, pr_link);
452184588Sdfr		}
453184588Sdfr	}
454184588Sdfr
455184588Sdfr	mtx_unlock(&gd->gd_lock);
456184588Sdfr
457184588Sdfr	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
458184588Sdfr		mem_free(pr, sizeof(*pr));
459184588Sdfr	}
460184588Sdfr}
461184588Sdfr
462184588Sdfrstatic uint32_t
463184588Sdfrrpc_gss_alloc_seq(struct rpc_gss_data *gd)
464184588Sdfr{
465184588Sdfr	uint32_t seq;
466184588Sdfr
467184588Sdfr	mtx_lock(&gd->gd_lock);
468184588Sdfr	seq = gd->gd_seq;
469184588Sdfr	gd->gd_seq++;
470184588Sdfr	mtx_unlock(&gd->gd_lock);
471184588Sdfr
472184588Sdfr	return (seq);
473184588Sdfr}
474184588Sdfr
475184588Sdfrstatic void
476184588Sdfrrpc_gss_nextverf(__unused AUTH *auth)
477184588Sdfr{
478184588Sdfr
479184588Sdfr	/* not used */
480184588Sdfr}
481184588Sdfr
482184588Sdfrstatic bool_t
483184588Sdfrrpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
484184588Sdfr{
485184588Sdfr	struct rpc_gss_data	*gd;
486184588Sdfr	struct rpc_pending_request *pr;
487184588Sdfr	uint32_t		 seq;
488184588Sdfr	XDR			 tmpxdrs;
489184588Sdfr	struct rpc_gss_cred	 gsscred;
490184588Sdfr	char			 credbuf[MAX_AUTH_BYTES];
491184588Sdfr	struct opaque_auth	 creds, verf;
492184588Sdfr	gss_buffer_desc		 rpcbuf, checksum;
493184588Sdfr	OM_uint32		 maj_stat, min_stat;
494184588Sdfr	bool_t			 xdr_stat;
495184588Sdfr
496184588Sdfr	rpc_gss_log_debug("in rpc_gss_marshal()");
497184588Sdfr
498184588Sdfr	gd = AUTH_PRIVATE(auth);
499184588Sdfr
500184588Sdfr	gsscred = gd->gd_cred;
501184588Sdfr	seq = rpc_gss_alloc_seq(gd);
502184588Sdfr	gsscred.gc_seq = seq;
503184588Sdfr
504184588Sdfr	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
505184588Sdfr	if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
506184588Sdfr		XDR_DESTROY(&tmpxdrs);
507184588Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
508184588Sdfr		return (FALSE);
509184588Sdfr	}
510184588Sdfr	creds.oa_flavor = RPCSEC_GSS;
511184588Sdfr	creds.oa_base = credbuf;
512184588Sdfr	creds.oa_length = XDR_GETPOS(&tmpxdrs);
513184588Sdfr	XDR_DESTROY(&tmpxdrs);
514184588Sdfr
515184588Sdfr	xdr_opaque_auth(xdrs, &creds);
516184588Sdfr
517184588Sdfr	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
518184588Sdfr	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
519184588Sdfr		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
520184588Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
521184588Sdfr			return (FALSE);
522184588Sdfr		}
523184588Sdfr		xdrmbuf_append(xdrs, args);
524184588Sdfr		return (TRUE);
525184588Sdfr	} else {
526184588Sdfr		/*
527184588Sdfr		 * Keep track of this XID + seq pair so that we can do
528184588Sdfr		 * the matching gss_verify_mic in AUTH_VALIDATE.
529184588Sdfr		 */
530184588Sdfr		pr = mem_alloc(sizeof(struct rpc_pending_request));
531184588Sdfr		mtx_lock(&gd->gd_lock);
532184588Sdfr		pr->pr_xid = xid;
533184588Sdfr		pr->pr_seq = seq;
534184588Sdfr		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
535184588Sdfr		mtx_unlock(&gd->gd_lock);
536184588Sdfr
537184588Sdfr		/*
538184588Sdfr		 * Checksum serialized RPC header, up to and including
539184588Sdfr		 * credential. For the in-kernel environment, we
540184588Sdfr		 * assume that our XDR stream is on a contiguous
541184588Sdfr		 * memory buffer (e.g. an mbuf).
542184588Sdfr		 */
543184588Sdfr		rpcbuf.length = XDR_GETPOS(xdrs);
544184588Sdfr		XDR_SETPOS(xdrs, 0);
545184588Sdfr		rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
546184588Sdfr
547184588Sdfr		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
548184588Sdfr		    &rpcbuf, &checksum);
549184588Sdfr
550184588Sdfr		if (maj_stat != GSS_S_COMPLETE) {
551184588Sdfr			rpc_gss_log_status("gss_get_mic", gd->gd_mech,
552184588Sdfr			    maj_stat, min_stat);
553184588Sdfr			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
554184588Sdfr				rpc_gss_destroy_context(auth, TRUE);
555184588Sdfr			}
556184588Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
557184588Sdfr			return (FALSE);
558184588Sdfr		}
559184588Sdfr
560184588Sdfr		verf.oa_flavor = RPCSEC_GSS;
561184588Sdfr		verf.oa_base = checksum.value;
562184588Sdfr		verf.oa_length = checksum.length;
563184588Sdfr
564184588Sdfr		xdr_stat = xdr_opaque_auth(xdrs, &verf);
565184588Sdfr		gss_release_buffer(&min_stat, &checksum);
566184588Sdfr		if (!xdr_stat) {
567184588Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
568184588Sdfr			return (FALSE);
569184588Sdfr		}
570184588Sdfr		if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
571184588Sdfr		    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
572184588Sdfr			xdrmbuf_append(xdrs, args);
573184588Sdfr			return (TRUE);
574184588Sdfr		} else {
575184588Sdfr			if (!xdr_rpc_gss_wrap_data(&args,
576184588Sdfr				gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
577184588Sdfr				seq))
578184588Sdfr				return (FALSE);
579184588Sdfr			xdrmbuf_append(xdrs, args);
580184588Sdfr			return (TRUE);
581184588Sdfr		}
582184588Sdfr	}
583184588Sdfr
584184588Sdfr	return (TRUE);
585184588Sdfr}
586184588Sdfr
587184588Sdfrstatic bool_t
588184588Sdfrrpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
589184588Sdfr    struct mbuf **resultsp)
590184588Sdfr{
591184588Sdfr	struct rpc_gss_data	*gd;
592184588Sdfr	struct rpc_pending_request *pr, *npr;
593184588Sdfr	struct rpc_pending_request_list reqs;
594184588Sdfr	gss_qop_t		qop_state;
595184588Sdfr	uint32_t		num, seq;
596184588Sdfr	gss_buffer_desc		signbuf, checksum;
597184588Sdfr	OM_uint32		maj_stat, min_stat;
598184588Sdfr
599184588Sdfr	rpc_gss_log_debug("in rpc_gss_validate()");
600184588Sdfr
601184588Sdfr	gd = AUTH_PRIVATE(auth);
602184588Sdfr
603184588Sdfr	/*
604184588Sdfr	 * The client will call us with a NULL verf when it gives up
605184588Sdfr	 * on an XID.
606184588Sdfr	 */
607184588Sdfr	if (!verf) {
608184588Sdfr		rpc_gss_purge_xid(gd, xid);
609184588Sdfr		return (TRUE);
610184588Sdfr	}
611184588Sdfr
612184588Sdfr	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
613184588Sdfr		/*
614184588Sdfr		 * Save the on the wire verifier to validate last INIT
615184588Sdfr		 * phase packet after decode if the major status is
616184588Sdfr		 * GSS_S_COMPLETE.
617184588Sdfr		 */
618184588Sdfr		if (gd->gd_verf.value)
619184588Sdfr			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
620184588Sdfr			    (char *) &gd->gd_verf);
621184588Sdfr		gd->gd_verf.value = mem_alloc(verf->oa_length);
622184588Sdfr		if (gd->gd_verf.value == NULL) {
623184588Sdfr			printf("gss_validate: out of memory\n");
624184588Sdfr			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
625184588Sdfr			m_freem(*resultsp);
626184588Sdfr			*resultsp = NULL;
627184588Sdfr			return (FALSE);
628184588Sdfr		}
629184588Sdfr		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
630184588Sdfr		gd->gd_verf.length = verf->oa_length;
631184588Sdfr
632184588Sdfr		return (TRUE);
633184588Sdfr	}
634184588Sdfr
635184588Sdfr	/*
636184588Sdfr	 * We need to check the verifier against all the requests
637184588Sdfr	 * we've send for this XID - for unreliable protocols, we
638184588Sdfr	 * retransmit with the same XID but different sequence
639184588Sdfr	 * number. We temporarily take this set of requests out of the
640184588Sdfr	 * list so that we can work through the list without having to
641184588Sdfr	 * hold the lock.
642184588Sdfr	 */
643184588Sdfr	mtx_lock(&gd->gd_lock);
644184588Sdfr	LIST_INIT(&reqs);
645184588Sdfr	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
646184588Sdfr		if (pr->pr_xid == xid) {
647184588Sdfr			LIST_REMOVE(pr, pr_link);
648184588Sdfr			LIST_INSERT_HEAD(&reqs, pr, pr_link);
649184588Sdfr		}
650184588Sdfr	}
651184588Sdfr	mtx_unlock(&gd->gd_lock);
652184588Sdfr	LIST_FOREACH(pr, &reqs, pr_link) {
653184588Sdfr		if (pr->pr_xid == xid) {
654184588Sdfr			seq = pr->pr_seq;
655184588Sdfr			num = htonl(seq);
656184588Sdfr			signbuf.value = &num;
657184588Sdfr			signbuf.length = sizeof(num);
658184588Sdfr
659184588Sdfr			checksum.value = verf->oa_base;
660184588Sdfr			checksum.length = verf->oa_length;
661184588Sdfr
662184588Sdfr			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
663184588Sdfr			    &signbuf, &checksum, &qop_state);
664184588Sdfr			if (maj_stat != GSS_S_COMPLETE
665184588Sdfr			    || qop_state != gd->gd_qop) {
666184588Sdfr				continue;
667184588Sdfr			}
668184588Sdfr			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
669184588Sdfr				rpc_gss_destroy_context(auth, TRUE);
670184588Sdfr				break;
671184588Sdfr			}
672184588Sdfr			//rpc_gss_purge_reqs(gd, seq);
673184588Sdfr			LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
674184588Sdfr				mem_free(pr, sizeof(*pr));
675184588Sdfr
676184588Sdfr			if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
677184588Sdfr				return (TRUE);
678184588Sdfr			} else {
679184588Sdfr				if (!xdr_rpc_gss_unwrap_data(resultsp,
680184588Sdfr					gd->gd_ctx, gd->gd_qop,
681184588Sdfr					gd->gd_cred.gc_svc, seq)) {
682184588Sdfr					return (FALSE);
683184588Sdfr				}
684184588Sdfr			}
685184588Sdfr			return (TRUE);
686184588Sdfr		}
687184588Sdfr	}
688184588Sdfr
689184588Sdfr	/*
690184588Sdfr	 * We didn't match - put back any entries for this XID so that
691184588Sdfr	 * a future call to validate can retry.
692184588Sdfr	 */
693184588Sdfr	mtx_lock(&gd->gd_lock);
694184588Sdfr	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
695184588Sdfr		LIST_REMOVE(pr, pr_link);
696184588Sdfr		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
697184588Sdfr	}
698184588Sdfr	mtx_unlock(&gd->gd_lock);
699184588Sdfr
700184588Sdfr	/*
701184588Sdfr	 * Nothing matches - give up.
702184588Sdfr	 */
703184588Sdfr	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
704184588Sdfr	m_freem(*resultsp);
705184588Sdfr	*resultsp = NULL;
706184588Sdfr	return (FALSE);
707184588Sdfr}
708184588Sdfr
709184588Sdfrstatic bool_t
710184588Sdfrrpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
711184588Sdfr{
712184588Sdfr	struct thread		*td = curthread;
713184588Sdfr	struct ucred		*crsave;
714184588Sdfr	struct rpc_gss_data	*gd;
715184588Sdfr	struct rpc_gss_init_res	 gr;
716184588Sdfr	gss_buffer_desc		principal_desc;
717184588Sdfr	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
718184588Sdfr	gss_name_t		name;
719184588Sdfr	OM_uint32		 maj_stat, min_stat, call_stat;
720184588Sdfr	const char		*mech;
721184588Sdfr	struct rpc_callextra	 ext;
722184588Sdfr
723184588Sdfr	rpc_gss_log_debug("in rpc_gss_refresh()");
724184588Sdfr
725184588Sdfr	gd = AUTH_PRIVATE(auth);
726184588Sdfr
727184588Sdfr	mtx_lock(&gd->gd_lock);
728184588Sdfr	/*
729184588Sdfr	 * If the context isn't in START state, someone else is
730184588Sdfr	 * refreshing - we wait till they are done. If they fail, they
731184588Sdfr	 * will put the state back to START and we can try (most
732184588Sdfr	 * likely to also fail).
733184588Sdfr	 */
734184588Sdfr	while (gd->gd_state != RPCSEC_GSS_START
735184588Sdfr	    && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
736184588Sdfr		msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
737184588Sdfr	}
738184588Sdfr	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
739184588Sdfr		mtx_unlock(&gd->gd_lock);
740184588Sdfr		return (TRUE);
741184588Sdfr	}
742184588Sdfr	gd->gd_state = RPCSEC_GSS_CONTEXT;
743184588Sdfr	mtx_unlock(&gd->gd_lock);
744184588Sdfr
745194878Srmacklem	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
746194878Srmacklem	gd->gd_cred.gc_seq = 0;
747194878Srmacklem
748184588Sdfr	principal_desc.value = (void *)gd->gd_principal;
749184588Sdfr	principal_desc.length = strlen(gd->gd_principal);
750184588Sdfr	maj_stat = gss_import_name(&min_stat, &principal_desc,
751184588Sdfr	    GSS_C_NT_HOSTBASED_SERVICE, &name);
752184588Sdfr	if (maj_stat != GSS_S_COMPLETE) {
753184588Sdfr		options_ret->major_status = maj_stat;
754184588Sdfr		options_ret->minor_status = min_stat;
755184588Sdfr		goto out;
756184588Sdfr	}
757184588Sdfr
758184588Sdfr	/* GSS context establishment loop. */
759184588Sdfr	memset(&recv_token, 0, sizeof(recv_token));
760184588Sdfr	memset(&gr, 0, sizeof(gr));
761184588Sdfr	memset(options_ret, 0, sizeof(*options_ret));
762184588Sdfr	options_ret->major_status = GSS_S_FAILURE;
763184588Sdfr	recv_tokenp = GSS_C_NO_BUFFER;
764184588Sdfr
765184588Sdfr	for (;;) {
766184588Sdfr		crsave = td->td_ucred;
767184588Sdfr		td->td_ucred = gd->gd_ucred;
768184588Sdfr		maj_stat = gss_init_sec_context(&min_stat,
769184588Sdfr		    gd->gd_options.my_cred,
770184588Sdfr		    &gd->gd_ctx,
771184588Sdfr		    name,
772184588Sdfr		    gd->gd_mech,
773184588Sdfr		    gd->gd_options.req_flags,
774184588Sdfr		    gd->gd_options.time_req,
775184588Sdfr		    gd->gd_options.input_channel_bindings,
776184588Sdfr		    recv_tokenp,
777184588Sdfr		    &gd->gd_mech,	/* used mech */
778184588Sdfr		    &send_token,
779184588Sdfr		    &options_ret->ret_flags,
780184588Sdfr		    &options_ret->time_req);
781184588Sdfr		td->td_ucred = crsave;
782184588Sdfr
783184588Sdfr		/*
784184588Sdfr		 * Free the token which we got from the server (if
785184588Sdfr		 * any).  Remember that this was allocated by XDR, not
786184588Sdfr		 * GSS-API.
787184588Sdfr		 */
788184588Sdfr		if (recv_tokenp != GSS_C_NO_BUFFER) {
789184588Sdfr			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
790184588Sdfr			    (char *) &recv_token);
791184588Sdfr			recv_tokenp = GSS_C_NO_BUFFER;
792184588Sdfr		}
793184588Sdfr		if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
794184588Sdfr			strlcpy(options_ret->actual_mechanism,
795184588Sdfr			    mech,
796184588Sdfr			    sizeof(options_ret->actual_mechanism));
797184588Sdfr		}
798184588Sdfr		if (maj_stat != GSS_S_COMPLETE &&
799184588Sdfr		    maj_stat != GSS_S_CONTINUE_NEEDED) {
800184588Sdfr			rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
801184588Sdfr			    maj_stat, min_stat);
802184588Sdfr			options_ret->major_status = maj_stat;
803184588Sdfr			options_ret->minor_status = min_stat;
804184588Sdfr			break;
805184588Sdfr		}
806184588Sdfr		if (send_token.length != 0) {
807184588Sdfr			memset(&gr, 0, sizeof(gr));
808184588Sdfr
809184588Sdfr			bzero(&ext, sizeof(ext));
810184588Sdfr			ext.rc_auth = auth;
811184588Sdfr			call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
812184588Sdfr			    (xdrproc_t)xdr_gss_buffer_desc,
813184588Sdfr			    &send_token,
814184588Sdfr			    (xdrproc_t)xdr_rpc_gss_init_res,
815184588Sdfr			    (caddr_t)&gr, AUTH_TIMEOUT);
816184588Sdfr
817184588Sdfr			gss_release_buffer(&min_stat, &send_token);
818184588Sdfr
819184588Sdfr			if (call_stat != RPC_SUCCESS)
820184588Sdfr				break;
821184588Sdfr
822184588Sdfr			if (gr.gr_major != GSS_S_COMPLETE &&
823184588Sdfr			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
824184588Sdfr				rpc_gss_log_status("server reply", gd->gd_mech,
825184588Sdfr				    gr.gr_major, gr.gr_minor);
826184588Sdfr				options_ret->major_status = gr.gr_major;
827184588Sdfr				options_ret->minor_status = gr.gr_minor;
828184588Sdfr				break;
829184588Sdfr			}
830184588Sdfr
831184588Sdfr			/*
832184588Sdfr			 * Save the server's gr_handle value, freeing
833184588Sdfr			 * what we have already (remember that this
834184588Sdfr			 * was allocated by XDR, not GSS-API).
835184588Sdfr			 */
836184588Sdfr			if (gr.gr_handle.length != 0) {
837184588Sdfr				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
838184588Sdfr				    (char *) &gd->gd_cred.gc_handle);
839184588Sdfr				gd->gd_cred.gc_handle = gr.gr_handle;
840184588Sdfr			}
841184588Sdfr
842184588Sdfr			/*
843184588Sdfr			 * Save the server's token as well.
844184588Sdfr			 */
845184588Sdfr			if (gr.gr_token.length != 0) {
846184588Sdfr				recv_token = gr.gr_token;
847184588Sdfr				recv_tokenp = &recv_token;
848184588Sdfr			}
849184588Sdfr
850184588Sdfr			/*
851184588Sdfr			 * Since we have copied out all the bits of gr
852184588Sdfr			 * which XDR allocated for us, we don't need
853184588Sdfr			 * to free it.
854184588Sdfr			 */
855184588Sdfr			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
856184588Sdfr		}
857184588Sdfr
858184588Sdfr		if (maj_stat == GSS_S_COMPLETE) {
859184588Sdfr			gss_buffer_desc   bufin;
860184588Sdfr			u_int seq, qop_state = 0;
861184588Sdfr
862184588Sdfr			/*
863184588Sdfr			 * gss header verifier,
864184588Sdfr			 * usually checked in gss_validate
865184588Sdfr			 */
866184588Sdfr			seq = htonl(gr.gr_win);
867184588Sdfr			bufin.value = (unsigned char *)&seq;
868184588Sdfr			bufin.length = sizeof(seq);
869184588Sdfr
870184588Sdfr			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
871184588Sdfr			    &bufin, &gd->gd_verf, &qop_state);
872184588Sdfr
873184588Sdfr			if (maj_stat != GSS_S_COMPLETE ||
874184588Sdfr			    qop_state != gd->gd_qop) {
875184588Sdfr				rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
876184588Sdfr				    maj_stat, min_stat);
877184588Sdfr				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
878184588Sdfr					rpc_gss_destroy_context(auth, TRUE);
879184588Sdfr				}
880184588Sdfr				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
881184588Sdfr				    EPERM);
882184588Sdfr				options_ret->major_status = maj_stat;
883184588Sdfr				options_ret->minor_status = min_stat;
884184588Sdfr				break;
885184588Sdfr			}
886184588Sdfr
887184588Sdfr			options_ret->major_status = GSS_S_COMPLETE;
888184588Sdfr			options_ret->minor_status = 0;
889184588Sdfr			options_ret->rpcsec_version = gd->gd_cred.gc_version;
890184588Sdfr			options_ret->gss_context = gd->gd_ctx;
891184588Sdfr
892184588Sdfr			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
893184588Sdfr			gd->gd_seq = 1;
894184588Sdfr			gd->gd_win = gr.gr_win;
895184588Sdfr			break;
896184588Sdfr		}
897184588Sdfr	}
898184588Sdfr
899184588Sdfr	gss_release_name(&min_stat, &name);
900184588Sdfr	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
901184588Sdfr	    (char *) &gd->gd_verf);
902184588Sdfr
903184588Sdfrout:
904184588Sdfr	/* End context negotiation loop. */
905184588Sdfr	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
906184588Sdfr		rpc_createerr.cf_stat = RPC_AUTHERROR;
907184588Sdfr		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
908184588Sdfr		if (gd->gd_ctx) {
909184588Sdfr			gss_delete_sec_context(&min_stat, &gd->gd_ctx,
910184588Sdfr				GSS_C_NO_BUFFER);
911184588Sdfr		}
912184588Sdfr		mtx_lock(&gd->gd_lock);
913184588Sdfr		gd->gd_state = RPCSEC_GSS_START;
914184588Sdfr		wakeup(gd);
915184588Sdfr		mtx_unlock(&gd->gd_lock);
916184588Sdfr		return (FALSE);
917184588Sdfr	}
918184588Sdfr
919184588Sdfr	mtx_lock(&gd->gd_lock);
920184588Sdfr	gd->gd_state = RPCSEC_GSS_ESTABLISHED;
921184588Sdfr	wakeup(gd);
922184588Sdfr	mtx_unlock(&gd->gd_lock);
923184588Sdfr
924184588Sdfr	return (TRUE);
925184588Sdfr}
926184588Sdfr
927184588Sdfrstatic bool_t
928184588Sdfrrpc_gss_refresh(AUTH *auth, void *msg)
929184588Sdfr{
930184588Sdfr	struct rpc_msg *reply = (struct rpc_msg *) msg;
931184588Sdfr	rpc_gss_options_ret_t options;
932195246Srmacklem	struct rpc_gss_data *gd;
933184588Sdfr
934195246Srmacklem	gd = AUTH_PRIVATE(auth);
935195246Srmacklem
936184588Sdfr	/*
937195246Srmacklem	 * If the context is in DESTROYING state, then just return, since
938195246Srmacklem	 * there is no point in refreshing the credentials.
939195246Srmacklem	 */
940195246Srmacklem	mtx_lock(&gd->gd_lock);
941195246Srmacklem	if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
942195246Srmacklem		mtx_unlock(&gd->gd_lock);
943195246Srmacklem		return (FALSE);
944195246Srmacklem	}
945195246Srmacklem	mtx_unlock(&gd->gd_lock);
946195246Srmacklem
947195246Srmacklem	/*
948184588Sdfr	 * If the error was RPCSEC_GSS_CREDPROBLEM of
949184588Sdfr	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
950184588Sdfr	 * other errors are fatal.
951184588Sdfr	 */
952184588Sdfr	if (reply->rm_reply.rp_stat == MSG_DENIED
953184588Sdfr	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
954184588Sdfr	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
955184588Sdfr		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
956184588Sdfr		rpc_gss_destroy_context(auth, FALSE);
957184588Sdfr		memset(&options, 0, sizeof(options));
958184588Sdfr		return (rpc_gss_init(auth, &options));
959184588Sdfr	}
960184588Sdfr
961184588Sdfr	return (FALSE);
962184588Sdfr}
963184588Sdfr
964184588Sdfrstatic void
965184588Sdfrrpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
966184588Sdfr{
967184588Sdfr	struct rpc_gss_data	*gd;
968184588Sdfr	struct rpc_pending_request *pr;
969184588Sdfr	OM_uint32		 min_stat;
970184588Sdfr	struct rpc_callextra	 ext;
971184588Sdfr
972184588Sdfr	rpc_gss_log_debug("in rpc_gss_destroy_context()");
973184588Sdfr
974184588Sdfr	gd = AUTH_PRIVATE(auth);
975184588Sdfr
976184588Sdfr	mtx_lock(&gd->gd_lock);
977184588Sdfr	/*
978184588Sdfr	 * If the context isn't in ESTABISHED state, someone else is
979184588Sdfr	 * destroying/refreshing - we wait till they are done.
980184588Sdfr	 */
981184588Sdfr	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
982184588Sdfr		while (gd->gd_state != RPCSEC_GSS_START
983184588Sdfr		    && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
984184588Sdfr			msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
985184588Sdfr		mtx_unlock(&gd->gd_lock);
986184588Sdfr		return;
987184588Sdfr	}
988184588Sdfr	gd->gd_state = RPCSEC_GSS_DESTROYING;
989184588Sdfr	mtx_unlock(&gd->gd_lock);
990184588Sdfr
991184588Sdfr	if (send_destroy) {
992184588Sdfr		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
993184588Sdfr		bzero(&ext, sizeof(ext));
994184588Sdfr		ext.rc_auth = auth;
995184588Sdfr		CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
996184588Sdfr		    (xdrproc_t)xdr_void, NULL,
997184588Sdfr		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
998184588Sdfr	}
999184588Sdfr
1000184588Sdfr	while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1001184588Sdfr		LIST_REMOVE(pr, pr_link);
1002184588Sdfr		mem_free(pr, sizeof(*pr));
1003184588Sdfr	}
1004184588Sdfr
1005184588Sdfr	/*
1006184588Sdfr	 * Free the context token. Remember that this was
1007184588Sdfr	 * allocated by XDR, not GSS-API.
1008184588Sdfr	 */
1009184588Sdfr	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1010184588Sdfr	    (char *) &gd->gd_cred.gc_handle);
1011184588Sdfr	gd->gd_cred.gc_handle.length = 0;
1012184588Sdfr
1013184588Sdfr	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1014184588Sdfr		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1015184588Sdfr
1016184588Sdfr	mtx_lock(&gd->gd_lock);
1017184588Sdfr	gd->gd_state = RPCSEC_GSS_START;
1018184588Sdfr	wakeup(gd);
1019184588Sdfr	mtx_unlock(&gd->gd_lock);
1020184588Sdfr}
1021184588Sdfr
1022184588Sdfrstatic void
1023184588Sdfrrpc_gss_destroy(AUTH *auth)
1024184588Sdfr{
1025184588Sdfr	struct rpc_gss_data	*gd;
1026184588Sdfr
1027184588Sdfr	rpc_gss_log_debug("in rpc_gss_destroy()");
1028184588Sdfr
1029184588Sdfr	gd = AUTH_PRIVATE(auth);
1030184588Sdfr
1031184588Sdfr	if (!refcount_release(&gd->gd_refs))
1032184588Sdfr		return;
1033184588Sdfr
1034184588Sdfr	rpc_gss_destroy_context(auth, TRUE);
1035184588Sdfr
1036184588Sdfr	CLNT_RELEASE(gd->gd_clnt);
1037184588Sdfr	crfree(gd->gd_ucred);
1038184588Sdfr	free(gd->gd_principal, M_RPC);
1039184588Sdfr	if (gd->gd_verf.value)
1040184588Sdfr		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1041184588Sdfr		    (char *) &gd->gd_verf);
1042184588Sdfr	mtx_destroy(&gd->gd_lock);
1043184588Sdfr
1044184588Sdfr	mem_free(gd, sizeof(*gd));
1045184588Sdfr	mem_free(auth, sizeof(*auth));
1046184588Sdfr}
1047184588Sdfr
1048184588Sdfrint
1049184588Sdfrrpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1050184588Sdfr{
1051184588Sdfr	struct rpc_gss_data	*gd;
1052184588Sdfr	int			want_conf;
1053184588Sdfr	OM_uint32		max;
1054184588Sdfr	OM_uint32		maj_stat, min_stat;
1055184588Sdfr	int			result;
1056184588Sdfr
1057184588Sdfr	gd = AUTH_PRIVATE(auth);
1058184588Sdfr
1059184588Sdfr	switch (gd->gd_cred.gc_svc) {
1060184588Sdfr	case rpc_gss_svc_none:
1061184588Sdfr		return (max_tp_unit_len);
1062184588Sdfr		break;
1063184588Sdfr
1064184588Sdfr	case rpc_gss_svc_default:
1065184588Sdfr	case rpc_gss_svc_integrity:
1066184588Sdfr		want_conf = FALSE;
1067184588Sdfr		break;
1068184588Sdfr
1069184588Sdfr	case rpc_gss_svc_privacy:
1070184588Sdfr		want_conf = TRUE;
1071184588Sdfr		break;
1072184588Sdfr
1073184588Sdfr	default:
1074184588Sdfr		return (0);
1075184588Sdfr	}
1076184588Sdfr
1077184588Sdfr	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1078184588Sdfr	    gd->gd_qop, max_tp_unit_len, &max);
1079184588Sdfr
1080184588Sdfr	if (maj_stat == GSS_S_COMPLETE) {
1081184588Sdfr		result = (int) max;
1082184588Sdfr		if (result < 0)
1083184588Sdfr			result = 0;
1084184588Sdfr		return (result);
1085184588Sdfr	} else {
1086184588Sdfr		rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1087184588Sdfr		    maj_stat, min_stat);
1088184588Sdfr		return (0);
1089184588Sdfr	}
1090184588Sdfr}
1091