xref: /freebsd-src/sys/rpc/rpcsec_gss/rpcsec_gss.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1a9148abdSDoug Rabson /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4a9148abdSDoug Rabson  * Copyright (c) 2008 Doug Rabson
5a9148abdSDoug Rabson  * All rights reserved.
6a9148abdSDoug Rabson  *
7a9148abdSDoug Rabson  * Redistribution and use in source and binary forms, with or without
8a9148abdSDoug Rabson  * modification, are permitted provided that the following conditions
9a9148abdSDoug Rabson  * are met:
10a9148abdSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12a9148abdSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
13a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
14a9148abdSDoug Rabson  *    documentation and/or other materials provided with the distribution.
15a9148abdSDoug Rabson  *
16a9148abdSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17a9148abdSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9148abdSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19a9148abdSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20a9148abdSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21a9148abdSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22a9148abdSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23a9148abdSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a9148abdSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25a9148abdSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26a9148abdSDoug Rabson  * SUCH DAMAGE.
27a9148abdSDoug Rabson  */
28a9148abdSDoug Rabson /*
29a9148abdSDoug Rabson   auth_gss.c
30a9148abdSDoug Rabson 
31a9148abdSDoug Rabson   RPCSEC_GSS client routines.
32a9148abdSDoug Rabson 
33a9148abdSDoug Rabson   Copyright (c) 2000 The Regents of the University of Michigan.
34a9148abdSDoug Rabson   All rights reserved.
35a9148abdSDoug Rabson 
36a9148abdSDoug Rabson   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37a9148abdSDoug Rabson   All rights reserved, all wrongs reversed.
38a9148abdSDoug Rabson 
39a9148abdSDoug Rabson   Redistribution and use in source and binary forms, with or without
40a9148abdSDoug Rabson   modification, are permitted provided that the following conditions
41a9148abdSDoug Rabson   are met:
42a9148abdSDoug Rabson 
43a9148abdSDoug Rabson   1. Redistributions of source code must retain the above copyright
44a9148abdSDoug Rabson      notice, this list of conditions and the following disclaimer.
45a9148abdSDoug Rabson   2. Redistributions in binary form must reproduce the above copyright
46a9148abdSDoug Rabson      notice, this list of conditions and the following disclaimer in the
47a9148abdSDoug Rabson      documentation and/or other materials provided with the distribution.
48a9148abdSDoug Rabson   3. Neither the name of the University nor the names of its
49a9148abdSDoug Rabson      contributors may be used to endorse or promote products derived
50a9148abdSDoug Rabson      from this software without specific prior written permission.
51a9148abdSDoug Rabson 
52a9148abdSDoug Rabson   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53a9148abdSDoug Rabson   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54a9148abdSDoug Rabson   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55a9148abdSDoug Rabson   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56a9148abdSDoug Rabson   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57a9148abdSDoug Rabson   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58a9148abdSDoug Rabson   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59a9148abdSDoug Rabson   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60a9148abdSDoug Rabson   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61a9148abdSDoug Rabson   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62a9148abdSDoug Rabson   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63a9148abdSDoug Rabson 
64a9148abdSDoug Rabson   $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65a9148abdSDoug Rabson */
66a9148abdSDoug Rabson 
67a9148abdSDoug Rabson #include <sys/param.h>
68a9148abdSDoug Rabson #include <sys/systm.h>
69a9148abdSDoug Rabson #include <sys/hash.h>
70a9148abdSDoug Rabson #include <sys/kernel.h>
71a9148abdSDoug Rabson #include <sys/kobj.h>
72a9148abdSDoug Rabson #include <sys/lock.h>
73a9148abdSDoug Rabson #include <sys/malloc.h>
74a9148abdSDoug Rabson #include <sys/mbuf.h>
75a9148abdSDoug Rabson #include <sys/mutex.h>
76a9148abdSDoug Rabson #include <sys/proc.h>
77a9148abdSDoug Rabson #include <sys/refcount.h>
78a9148abdSDoug Rabson #include <sys/sx.h>
79a9148abdSDoug Rabson #include <sys/ucred.h>
80a9148abdSDoug Rabson 
81a9148abdSDoug Rabson #include <rpc/rpc.h>
82a9148abdSDoug Rabson #include <rpc/rpcsec_gss.h>
83a9148abdSDoug Rabson 
8488a2437aSRick Macklem #include <kgssapi/krb5/kcrypto.h>
8588a2437aSRick Macklem 
86a9148abdSDoug Rabson #include "rpcsec_gss_int.h"
87a9148abdSDoug Rabson 
88a9148abdSDoug Rabson static void	rpc_gss_nextverf(AUTH*);
89a9148abdSDoug Rabson static bool_t	rpc_gss_marshal(AUTH *, uint32_t, XDR *, struct mbuf *);
90a9148abdSDoug Rabson static bool_t	rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
91a9148abdSDoug Rabson static bool_t	rpc_gss_refresh(AUTH *, void *);
92a9148abdSDoug Rabson static bool_t	rpc_gss_validate(AUTH *, uint32_t, struct opaque_auth *,
93a9148abdSDoug Rabson     struct mbuf **);
94a9148abdSDoug Rabson static void	rpc_gss_destroy(AUTH *);
95a9148abdSDoug Rabson static void	rpc_gss_destroy_context(AUTH *, bool_t);
96a9148abdSDoug Rabson 
97*20d728b5SMark Johnston static const struct auth_ops rpc_gss_ops = {
98ba5bc6e8SMark Johnston 	.ah_nextverf =	rpc_gss_nextverf,
99ba5bc6e8SMark Johnston 	.ah_marshal =	rpc_gss_marshal,
100ba5bc6e8SMark Johnston 	.ah_validate =	rpc_gss_validate,
101ba5bc6e8SMark Johnston 	.ah_refresh =	rpc_gss_refresh,
102ba5bc6e8SMark Johnston 	.ah_destroy =	rpc_gss_destroy,
103a9148abdSDoug Rabson };
104a9148abdSDoug Rabson 
105a9148abdSDoug Rabson enum rpcsec_gss_state {
106a9148abdSDoug Rabson 	RPCSEC_GSS_START,
107a9148abdSDoug Rabson 	RPCSEC_GSS_CONTEXT,
108a9148abdSDoug Rabson 	RPCSEC_GSS_ESTABLISHED,
109a9148abdSDoug Rabson 	RPCSEC_GSS_DESTROYING
110a9148abdSDoug Rabson };
111a9148abdSDoug Rabson 
112a9148abdSDoug Rabson struct rpc_pending_request {
113a9148abdSDoug Rabson 	uint32_t		pr_xid;		/* XID of rpc */
114a9148abdSDoug Rabson 	uint32_t		pr_seq;		/* matching GSS seq */
115a9148abdSDoug Rabson 	LIST_ENTRY(rpc_pending_request) pr_link;
116a9148abdSDoug Rabson };
117a9148abdSDoug Rabson LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
118a9148abdSDoug Rabson 
119a9148abdSDoug Rabson struct rpc_gss_data {
120a9148abdSDoug Rabson 	volatile u_int		gd_refs;	/* number of current users */
121a9148abdSDoug Rabson 	struct mtx		gd_lock;
122a9148abdSDoug Rabson 	uint32_t		gd_hash;
123a9148abdSDoug Rabson 	AUTH			*gd_auth;	/* link back to AUTH */
124a9148abdSDoug Rabson 	struct ucred		*gd_ucred;	/* matching local cred */
125a9148abdSDoug Rabson 	char			*gd_principal;	/* server principal name */
12688a2437aSRick Macklem 	char			*gd_clntprincipal; /* client principal name */
127a9148abdSDoug Rabson 	rpc_gss_options_req_t	gd_options;	/* GSS context options */
128a9148abdSDoug Rabson 	enum rpcsec_gss_state	gd_state;	/* connection state */
129a9148abdSDoug Rabson 	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
130a9148abdSDoug Rabson 						 * NULL RPC verfier to
131a9148abdSDoug Rabson 						 * process at end of
132a9148abdSDoug Rabson 						 * context negotiation */
133a9148abdSDoug Rabson 	CLIENT			*gd_clnt;	/* client handle */
134a9148abdSDoug Rabson 	gss_OID			gd_mech;	/* mechanism to use */
135a9148abdSDoug Rabson 	gss_qop_t		gd_qop;		/* quality of protection */
136a9148abdSDoug Rabson 	gss_ctx_id_t		gd_ctx;		/* context id */
137a9148abdSDoug Rabson 	struct rpc_gss_cred	gd_cred;	/* client credentials */
138a9148abdSDoug Rabson 	uint32_t		gd_seq;		/* next sequence number */
139a9148abdSDoug Rabson 	u_int			gd_win;		/* sequence window */
140a9148abdSDoug Rabson 	struct rpc_pending_request_list gd_reqs;
141a9148abdSDoug Rabson 	TAILQ_ENTRY(rpc_gss_data) gd_link;
142a9148abdSDoug Rabson 	TAILQ_ENTRY(rpc_gss_data) gd_alllink;
143a9148abdSDoug Rabson };
144a9148abdSDoug Rabson TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
145a9148abdSDoug Rabson 
146a9148abdSDoug Rabson #define	AUTH_PRIVATE(auth)	((struct rpc_gss_data *)auth->ah_private)
147a9148abdSDoug Rabson 
148a9148abdSDoug Rabson static struct timeval AUTH_TIMEOUT = { 25, 0 };
149a9148abdSDoug Rabson 
150a9148abdSDoug Rabson #define RPC_GSS_HASH_SIZE	11
151a9148abdSDoug Rabson #define RPC_GSS_MAX		256
152a9148abdSDoug Rabson static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
153a9148abdSDoug Rabson static struct rpc_gss_data_list rpc_gss_all;
154a9148abdSDoug Rabson static struct sx rpc_gss_lock;
155a9148abdSDoug Rabson static int rpc_gss_count;
156a9148abdSDoug Rabson 
157a9148abdSDoug Rabson static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
15888a2437aSRick Macklem     const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
159a9148abdSDoug Rabson     rpc_gss_options_ret_t *);
160a9148abdSDoug Rabson 
161a9148abdSDoug Rabson static void
rpc_gss_hashinit(void * dummy)162a9148abdSDoug Rabson rpc_gss_hashinit(void *dummy)
163a9148abdSDoug Rabson {
164a9148abdSDoug Rabson 	int i;
165a9148abdSDoug Rabson 
166a9148abdSDoug Rabson 	for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
167a9148abdSDoug Rabson 		TAILQ_INIT(&rpc_gss_cache[i]);
168a9148abdSDoug Rabson 	TAILQ_INIT(&rpc_gss_all);
169a9148abdSDoug Rabson 	sx_init(&rpc_gss_lock, "rpc_gss_lock");
170a9148abdSDoug Rabson }
171a9148abdSDoug Rabson SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
172a9148abdSDoug Rabson 
173a9148abdSDoug Rabson static uint32_t
rpc_gss_hash(const char * principal,gss_OID mech,struct ucred * cred,rpc_gss_service_t service)174a9148abdSDoug Rabson rpc_gss_hash(const char *principal, gss_OID mech,
175a9148abdSDoug Rabson     struct ucred *cred, rpc_gss_service_t service)
176a9148abdSDoug Rabson {
177a9148abdSDoug Rabson 	uint32_t h;
178a9148abdSDoug Rabson 
179a9148abdSDoug Rabson 	h = HASHSTEP(HASHINIT, cred->cr_uid);
180a9148abdSDoug Rabson 	h = hash32_str(principal, h);
181a9148abdSDoug Rabson 	h = hash32_buf(mech->elements, mech->length, h);
182a9148abdSDoug Rabson 	h = HASHSTEP(h, (int) service);
183a9148abdSDoug Rabson 
184a9148abdSDoug Rabson 	return (h % RPC_GSS_HASH_SIZE);
185a9148abdSDoug Rabson }
186a9148abdSDoug Rabson 
187a9148abdSDoug Rabson /*
188a9148abdSDoug Rabson  * Simplified interface to create a security association for the
189a9148abdSDoug Rabson  * current thread's * ucred.
190a9148abdSDoug Rabson  */
191a9148abdSDoug Rabson AUTH *
rpc_gss_secfind(CLIENT * clnt,struct ucred * cred,const char * principal,gss_OID mech_oid,rpc_gss_service_t service)192a9148abdSDoug Rabson rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
193a9148abdSDoug Rabson     gss_OID mech_oid, rpc_gss_service_t service)
194a9148abdSDoug Rabson {
195a9148abdSDoug Rabson 	uint32_t		h, th;
196a9148abdSDoug Rabson 	AUTH			*auth;
197a9148abdSDoug Rabson 	struct rpc_gss_data	*gd, *tgd;
198b2115885SRick Macklem 	rpc_gss_options_ret_t	options;
199a9148abdSDoug Rabson 
200a9148abdSDoug Rabson 	if (rpc_gss_count > RPC_GSS_MAX) {
201a9148abdSDoug Rabson 		while (rpc_gss_count > RPC_GSS_MAX) {
202a9148abdSDoug Rabson 			sx_xlock(&rpc_gss_lock);
203a9148abdSDoug Rabson 			tgd = TAILQ_FIRST(&rpc_gss_all);
204a9148abdSDoug Rabson 			th = tgd->gd_hash;
205a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
206a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
207a9148abdSDoug Rabson 			rpc_gss_count--;
208a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
209a9148abdSDoug Rabson 			AUTH_DESTROY(tgd->gd_auth);
210a9148abdSDoug Rabson 		}
211a9148abdSDoug Rabson 	}
212a9148abdSDoug Rabson 
213a9148abdSDoug Rabson 	/*
214a9148abdSDoug Rabson 	 * See if we already have an AUTH which matches.
215a9148abdSDoug Rabson 	 */
216a9148abdSDoug Rabson 	h = rpc_gss_hash(principal, mech_oid, cred, service);
217a9148abdSDoug Rabson 
218a9148abdSDoug Rabson again:
219a9148abdSDoug Rabson 	sx_slock(&rpc_gss_lock);
220a9148abdSDoug Rabson 	TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
221a9148abdSDoug Rabson 		if (gd->gd_ucred->cr_uid == cred->cr_uid
222a9148abdSDoug Rabson 		    && !strcmp(gd->gd_principal, principal)
223a9148abdSDoug Rabson 		    && gd->gd_mech == mech_oid
224a9148abdSDoug Rabson 		    && gd->gd_cred.gc_svc == service) {
225a9148abdSDoug Rabson 			refcount_acquire(&gd->gd_refs);
226a9148abdSDoug Rabson 			if (sx_try_upgrade(&rpc_gss_lock)) {
227a9148abdSDoug Rabson 				/*
228a9148abdSDoug Rabson 				 * Keep rpc_gss_all LRU sorted.
229a9148abdSDoug Rabson 				 */
230a9148abdSDoug Rabson 				TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
231a9148abdSDoug Rabson 				TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
232a9148abdSDoug Rabson 				    gd_alllink);
233a9148abdSDoug Rabson 				sx_xunlock(&rpc_gss_lock);
234a9148abdSDoug Rabson 			} else {
235a9148abdSDoug Rabson 				sx_sunlock(&rpc_gss_lock);
236a9148abdSDoug Rabson 			}
237b2115885SRick Macklem 
238b2115885SRick Macklem 			/*
239b2115885SRick Macklem 			 * If the state != ESTABLISHED, try and initialize
240b2115885SRick Macklem 			 * the authenticator again. This will happen if the
241b2115885SRick Macklem 			 * user's credentials have expired. It may succeed now,
242b2115885SRick Macklem 			 * if they have done a kinit or similar.
243b2115885SRick Macklem 			 */
244b2115885SRick Macklem 			if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
245b2115885SRick Macklem 				memset(&options, 0, sizeof (options));
246b2115885SRick Macklem 				(void) rpc_gss_init(gd->gd_auth, &options);
247b2115885SRick Macklem 			}
248a9148abdSDoug Rabson 			return (gd->gd_auth);
249a9148abdSDoug Rabson 		}
250a9148abdSDoug Rabson 	}
251a9148abdSDoug Rabson 	sx_sunlock(&rpc_gss_lock);
252a9148abdSDoug Rabson 
253a9148abdSDoug Rabson 	/*
254a9148abdSDoug Rabson 	 * We missed in the cache - create a new association.
255a9148abdSDoug Rabson 	 */
25688a2437aSRick Macklem 	auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid,
25788a2437aSRick Macklem 	    service, GSS_C_QOP_DEFAULT, NULL, NULL);
258a9148abdSDoug Rabson 	if (!auth)
259a9148abdSDoug Rabson 		return (NULL);
260a9148abdSDoug Rabson 
261a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
262a9148abdSDoug Rabson 	gd->gd_hash = h;
263a9148abdSDoug Rabson 
264a9148abdSDoug Rabson 	sx_xlock(&rpc_gss_lock);
265a9148abdSDoug Rabson 	TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
266a9148abdSDoug Rabson 		if (tgd->gd_ucred->cr_uid == cred->cr_uid
267a9148abdSDoug Rabson 		    && !strcmp(tgd->gd_principal, principal)
268a9148abdSDoug Rabson 		    && tgd->gd_mech == mech_oid
269a9148abdSDoug Rabson 		    && tgd->gd_cred.gc_svc == service) {
270a9148abdSDoug Rabson 			/*
271a9148abdSDoug Rabson 			 * We lost a race to create the AUTH that
272a9148abdSDoug Rabson 			 * matches this cred.
273a9148abdSDoug Rabson 			 */
274a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
275a9148abdSDoug Rabson 			AUTH_DESTROY(auth);
276a9148abdSDoug Rabson 			goto again;
277a9148abdSDoug Rabson 		}
278a9148abdSDoug Rabson 	}
279a9148abdSDoug Rabson 
280a9148abdSDoug Rabson 	rpc_gss_count++;
281a9148abdSDoug Rabson 	TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
282a9148abdSDoug Rabson 	TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
283a9148abdSDoug Rabson 	refcount_acquire(&gd->gd_refs);	/* one for the cache, one for user */
284a9148abdSDoug Rabson 	sx_xunlock(&rpc_gss_lock);
285a9148abdSDoug Rabson 
286a9148abdSDoug Rabson 	return (auth);
287a9148abdSDoug Rabson }
288a9148abdSDoug Rabson 
289a9148abdSDoug Rabson void
rpc_gss_secpurge(CLIENT * clnt)290a9148abdSDoug Rabson rpc_gss_secpurge(CLIENT *clnt)
291a9148abdSDoug Rabson {
292a9148abdSDoug Rabson 	uint32_t		h;
293a9148abdSDoug Rabson 	struct rpc_gss_data	*gd, *tgd;
294a9148abdSDoug Rabson 
295a9148abdSDoug Rabson 	TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
296a9148abdSDoug Rabson 		if (gd->gd_clnt == clnt) {
297a9148abdSDoug Rabson 			sx_xlock(&rpc_gss_lock);
298a9148abdSDoug Rabson 			h = gd->gd_hash;
299a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
300a9148abdSDoug Rabson 			TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
301a9148abdSDoug Rabson 			rpc_gss_count--;
302a9148abdSDoug Rabson 			sx_xunlock(&rpc_gss_lock);
303a9148abdSDoug Rabson 			AUTH_DESTROY(gd->gd_auth);
304a9148abdSDoug Rabson 		}
305a9148abdSDoug Rabson 	}
306a9148abdSDoug Rabson }
307a9148abdSDoug Rabson 
308a9148abdSDoug Rabson AUTH *
rpc_gss_seccreate(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,const char * mechanism,rpc_gss_service_t service,const char * qop,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)30988a2437aSRick Macklem rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal,
31088a2437aSRick Macklem     const char *principal, const char *mechanism, rpc_gss_service_t service,
31188a2437aSRick Macklem     const char *qop, rpc_gss_options_req_t *options_req,
31288a2437aSRick Macklem     rpc_gss_options_ret_t *options_ret)
313a9148abdSDoug Rabson {
314a9148abdSDoug Rabson 	gss_OID			oid;
315a9148abdSDoug Rabson 	u_int			qop_num;
316a9148abdSDoug Rabson 
317a9148abdSDoug Rabson 	/*
318a9148abdSDoug Rabson 	 * Bail out now if we don't know this mechanism.
319a9148abdSDoug Rabson 	 */
320a9148abdSDoug Rabson 	if (!rpc_gss_mech_to_oid(mechanism, &oid))
321a9148abdSDoug Rabson 		return (NULL);
322a9148abdSDoug Rabson 
323a9148abdSDoug Rabson 	if (qop) {
324a9148abdSDoug Rabson 		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
325a9148abdSDoug Rabson 			return (NULL);
326a9148abdSDoug Rabson 	} else {
327a9148abdSDoug Rabson 		qop_num = GSS_C_QOP_DEFAULT;
328a9148abdSDoug Rabson 	}
329a9148abdSDoug Rabson 
33088a2437aSRick Macklem 	return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal,
33188a2437aSRick Macklem 		oid, service, qop_num, options_req, options_ret));
33288a2437aSRick Macklem }
33388a2437aSRick Macklem 
33488a2437aSRick Macklem void
rpc_gss_refresh_auth(AUTH * auth)33588a2437aSRick Macklem rpc_gss_refresh_auth(AUTH *auth)
33688a2437aSRick Macklem {
33788a2437aSRick Macklem 	struct rpc_gss_data	*gd;
33888a2437aSRick Macklem 	rpc_gss_options_ret_t	options;
33988a2437aSRick Macklem 
34088a2437aSRick Macklem 	gd = AUTH_PRIVATE(auth);
34188a2437aSRick Macklem 	/*
34288a2437aSRick Macklem 	 * If the state != ESTABLISHED, try and initialize
34388a2437aSRick Macklem 	 * the authenticator again. This will happen if the
34488a2437aSRick Macklem 	 * user's credentials have expired. It may succeed now,
34588a2437aSRick Macklem 	 * if they have done a kinit or similar.
34688a2437aSRick Macklem 	 */
34788a2437aSRick Macklem 	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
34888a2437aSRick Macklem 		memset(&options, 0, sizeof (options));
34988a2437aSRick Macklem 		(void) rpc_gss_init(auth, &options);
35088a2437aSRick Macklem 	}
351a9148abdSDoug Rabson }
352a9148abdSDoug Rabson 
353a9148abdSDoug Rabson static AUTH *
rpc_gss_seccreate_int(CLIENT * clnt,struct ucred * cred,const char * clnt_principal,const char * principal,gss_OID mech_oid,rpc_gss_service_t service,u_int qop_num,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)35488a2437aSRick Macklem rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred,
35588a2437aSRick Macklem     const char *clnt_principal, const char *principal, gss_OID mech_oid,
35688a2437aSRick Macklem     rpc_gss_service_t service, u_int qop_num,
357a9148abdSDoug Rabson     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
358a9148abdSDoug Rabson {
359a9148abdSDoug Rabson 	AUTH			*auth;
360a9148abdSDoug Rabson 	rpc_gss_options_ret_t	options;
361a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
362a9148abdSDoug Rabson 
363a9148abdSDoug Rabson 	/*
364a9148abdSDoug Rabson 	 * If the caller doesn't want the options, point at local
365a9148abdSDoug Rabson 	 * storage to simplify the code below.
366a9148abdSDoug Rabson 	 */
367a9148abdSDoug Rabson 	if (!options_ret)
368a9148abdSDoug Rabson 		options_ret = &options;
369a9148abdSDoug Rabson 
370a9148abdSDoug Rabson 	/*
371a9148abdSDoug Rabson 	 * Default service is integrity.
372a9148abdSDoug Rabson 	 */
373a9148abdSDoug Rabson 	if (service == rpc_gss_svc_default)
374a9148abdSDoug Rabson 		service = rpc_gss_svc_integrity;
375a9148abdSDoug Rabson 
376a9148abdSDoug Rabson 	memset(options_ret, 0, sizeof(*options_ret));
377a9148abdSDoug Rabson 
378a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_seccreate()");
379a9148abdSDoug Rabson 
380a9148abdSDoug Rabson 	memset(&rpc_createerr, 0, sizeof(rpc_createerr));
381a9148abdSDoug Rabson 
382a9148abdSDoug Rabson 	auth = mem_alloc(sizeof(*auth));
383a9148abdSDoug Rabson 	if (auth == NULL) {
384a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
385a9148abdSDoug Rabson 		rpc_createerr.cf_error.re_errno = ENOMEM;
386a9148abdSDoug Rabson 		return (NULL);
387a9148abdSDoug Rabson 	}
388a9148abdSDoug Rabson 	gd = mem_alloc(sizeof(*gd));
389a9148abdSDoug Rabson 	if (gd == NULL) {
390a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
391a9148abdSDoug Rabson 		rpc_createerr.cf_error.re_errno = ENOMEM;
392a9148abdSDoug Rabson 		mem_free(auth, sizeof(*auth));
393a9148abdSDoug Rabson 		return (NULL);
394a9148abdSDoug Rabson 	}
395a9148abdSDoug Rabson 
396a9148abdSDoug Rabson 	auth->ah_ops = &rpc_gss_ops;
397a9148abdSDoug Rabson 	auth->ah_private = (caddr_t) gd;
398a9148abdSDoug Rabson 	auth->ah_cred.oa_flavor = RPCSEC_GSS;
399a9148abdSDoug Rabson 
400a9148abdSDoug Rabson 	refcount_init(&gd->gd_refs, 1);
401a9148abdSDoug Rabson 	mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
402a9148abdSDoug Rabson 	gd->gd_auth = auth;
403a9148abdSDoug Rabson 	gd->gd_ucred = crdup(cred);
404a9148abdSDoug Rabson 	gd->gd_principal = strdup(principal, M_RPC);
40588a2437aSRick Macklem 	if (clnt_principal != NULL)
40688a2437aSRick Macklem 		gd->gd_clntprincipal = strdup(clnt_principal, M_RPC);
40788a2437aSRick Macklem 	else
40888a2437aSRick Macklem 		gd->gd_clntprincipal = NULL;
409a9148abdSDoug Rabson 
410a9148abdSDoug Rabson 
411a9148abdSDoug Rabson 	if (options_req) {
412a9148abdSDoug Rabson 		gd->gd_options = *options_req;
413a9148abdSDoug Rabson 	} else {
414a9148abdSDoug Rabson 		gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
415a9148abdSDoug Rabson 		gd->gd_options.time_req = 0;
416a9148abdSDoug Rabson 		gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
417a9148abdSDoug Rabson 		gd->gd_options.input_channel_bindings = NULL;
418a9148abdSDoug Rabson 	}
419a9148abdSDoug Rabson 	CLNT_ACQUIRE(clnt);
420a9148abdSDoug Rabson 	gd->gd_clnt = clnt;
421a9148abdSDoug Rabson 	gd->gd_ctx = GSS_C_NO_CONTEXT;
422a9148abdSDoug Rabson 	gd->gd_mech = mech_oid;
423a9148abdSDoug Rabson 	gd->gd_qop = qop_num;
424a9148abdSDoug Rabson 
425a9148abdSDoug Rabson 	gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
426a9148abdSDoug Rabson 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
427a9148abdSDoug Rabson 	gd->gd_cred.gc_seq = 0;
428a9148abdSDoug Rabson 	gd->gd_cred.gc_svc = service;
429a9148abdSDoug Rabson 	LIST_INIT(&gd->gd_reqs);
430a9148abdSDoug Rabson 
431a9148abdSDoug Rabson 	if (!rpc_gss_init(auth, options_ret)) {
432a9148abdSDoug Rabson 		goto bad;
433a9148abdSDoug Rabson 	}
434a9148abdSDoug Rabson 
435a9148abdSDoug Rabson 	return (auth);
436a9148abdSDoug Rabson 
437a9148abdSDoug Rabson  bad:
438a9148abdSDoug Rabson 	AUTH_DESTROY(auth);
439a9148abdSDoug Rabson 	return (NULL);
440a9148abdSDoug Rabson }
441a9148abdSDoug Rabson 
442a9148abdSDoug Rabson bool_t
rpc_gss_set_defaults(AUTH * auth,rpc_gss_service_t service,const char * qop)443a9148abdSDoug Rabson rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
444a9148abdSDoug Rabson {
445a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
446a9148abdSDoug Rabson 	u_int			qop_num;
447a9148abdSDoug Rabson 	const char		*mechanism;
448a9148abdSDoug Rabson 
449a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
450a9148abdSDoug Rabson 	if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
451a9148abdSDoug Rabson 		return (FALSE);
452a9148abdSDoug Rabson 	}
453a9148abdSDoug Rabson 
454a9148abdSDoug Rabson 	if (qop) {
455a9148abdSDoug Rabson 		if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
456a9148abdSDoug Rabson 			return (FALSE);
457a9148abdSDoug Rabson 		}
458a9148abdSDoug Rabson 	} else {
459a9148abdSDoug Rabson 		qop_num = GSS_C_QOP_DEFAULT;
460a9148abdSDoug Rabson 	}
461a9148abdSDoug Rabson 
462a9148abdSDoug Rabson 	gd->gd_cred.gc_svc = service;
463a9148abdSDoug Rabson 	gd->gd_qop = qop_num;
464a9148abdSDoug Rabson 	return (TRUE);
465a9148abdSDoug Rabson }
466a9148abdSDoug Rabson 
467a9148abdSDoug Rabson static void
rpc_gss_purge_xid(struct rpc_gss_data * gd,uint32_t xid)468a9148abdSDoug Rabson rpc_gss_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
469a9148abdSDoug Rabson {
470a9148abdSDoug Rabson 	struct rpc_pending_request *pr, *npr;
471a9148abdSDoug Rabson 	struct rpc_pending_request_list reqs;
472a9148abdSDoug Rabson 
473a9148abdSDoug Rabson 	LIST_INIT(&reqs);
474a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
475a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
476a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
477a9148abdSDoug Rabson 			LIST_REMOVE(pr, pr_link);
478a9148abdSDoug Rabson 			LIST_INSERT_HEAD(&reqs, pr, pr_link);
479a9148abdSDoug Rabson 		}
480a9148abdSDoug Rabson 	}
481a9148abdSDoug Rabson 
482a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
483a9148abdSDoug Rabson 
484a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
485a9148abdSDoug Rabson 		mem_free(pr, sizeof(*pr));
486a9148abdSDoug Rabson 	}
487a9148abdSDoug Rabson }
488a9148abdSDoug Rabson 
489a9148abdSDoug Rabson static uint32_t
rpc_gss_alloc_seq(struct rpc_gss_data * gd)490a9148abdSDoug Rabson rpc_gss_alloc_seq(struct rpc_gss_data *gd)
491a9148abdSDoug Rabson {
492a9148abdSDoug Rabson 	uint32_t seq;
493a9148abdSDoug Rabson 
494a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
495a9148abdSDoug Rabson 	seq = gd->gd_seq;
496a9148abdSDoug Rabson 	gd->gd_seq++;
497a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
498a9148abdSDoug Rabson 
499a9148abdSDoug Rabson 	return (seq);
500a9148abdSDoug Rabson }
501a9148abdSDoug Rabson 
502a9148abdSDoug Rabson static void
rpc_gss_nextverf(__unused AUTH * auth)503a9148abdSDoug Rabson rpc_gss_nextverf(__unused AUTH *auth)
504a9148abdSDoug Rabson {
505a9148abdSDoug Rabson 
506a9148abdSDoug Rabson 	/* not used */
507a9148abdSDoug Rabson }
508a9148abdSDoug Rabson 
509a9148abdSDoug Rabson static bool_t
rpc_gss_marshal(AUTH * auth,uint32_t xid,XDR * xdrs,struct mbuf * args)510a9148abdSDoug Rabson rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
511a9148abdSDoug Rabson {
512a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
513a9148abdSDoug Rabson 	struct rpc_pending_request *pr;
514a9148abdSDoug Rabson 	uint32_t		 seq;
515a9148abdSDoug Rabson 	XDR			 tmpxdrs;
516a9148abdSDoug Rabson 	struct rpc_gss_cred	 gsscred;
517a9148abdSDoug Rabson 	char			 credbuf[MAX_AUTH_BYTES];
518a9148abdSDoug Rabson 	struct opaque_auth	 creds, verf;
519a9148abdSDoug Rabson 	gss_buffer_desc		 rpcbuf, checksum;
520a9148abdSDoug Rabson 	OM_uint32		 maj_stat, min_stat;
521a9148abdSDoug Rabson 	bool_t			 xdr_stat;
522a9148abdSDoug Rabson 
523a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_marshal()");
524a9148abdSDoug Rabson 
525a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
526a9148abdSDoug Rabson 
527a9148abdSDoug Rabson 	gsscred = gd->gd_cred;
528a9148abdSDoug Rabson 	seq = rpc_gss_alloc_seq(gd);
529a9148abdSDoug Rabson 	gsscred.gc_seq = seq;
530a9148abdSDoug Rabson 
531a9148abdSDoug Rabson 	xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
532a9148abdSDoug Rabson 	if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
533a9148abdSDoug Rabson 		XDR_DESTROY(&tmpxdrs);
534a9148abdSDoug Rabson 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
535a9148abdSDoug Rabson 		return (FALSE);
536a9148abdSDoug Rabson 	}
537a9148abdSDoug Rabson 	creds.oa_flavor = RPCSEC_GSS;
538a9148abdSDoug Rabson 	creds.oa_base = credbuf;
539a9148abdSDoug Rabson 	creds.oa_length = XDR_GETPOS(&tmpxdrs);
540a9148abdSDoug Rabson 	XDR_DESTROY(&tmpxdrs);
541a9148abdSDoug Rabson 
542a9148abdSDoug Rabson 	xdr_opaque_auth(xdrs, &creds);
543a9148abdSDoug Rabson 
544a9148abdSDoug Rabson 	if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
545a9148abdSDoug Rabson 	    gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
546a9148abdSDoug Rabson 		if (!xdr_opaque_auth(xdrs, &_null_auth)) {
547a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
548a9148abdSDoug Rabson 			return (FALSE);
549a9148abdSDoug Rabson 		}
550a9148abdSDoug Rabson 		xdrmbuf_append(xdrs, args);
551a9148abdSDoug Rabson 		return (TRUE);
552a9148abdSDoug Rabson 	} else {
553a9148abdSDoug Rabson 		/*
554a9148abdSDoug Rabson 		 * Keep track of this XID + seq pair so that we can do
555a9148abdSDoug Rabson 		 * the matching gss_verify_mic in AUTH_VALIDATE.
556a9148abdSDoug Rabson 		 */
557a9148abdSDoug Rabson 		pr = mem_alloc(sizeof(struct rpc_pending_request));
558a9148abdSDoug Rabson 		mtx_lock(&gd->gd_lock);
559a9148abdSDoug Rabson 		pr->pr_xid = xid;
560a9148abdSDoug Rabson 		pr->pr_seq = seq;
561a9148abdSDoug Rabson 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
562a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
563a9148abdSDoug Rabson 
564a9148abdSDoug Rabson 		/*
565a9148abdSDoug Rabson 		 * Checksum serialized RPC header, up to and including
566a9148abdSDoug Rabson 		 * credential. For the in-kernel environment, we
567a9148abdSDoug Rabson 		 * assume that our XDR stream is on a contiguous
568a9148abdSDoug Rabson 		 * memory buffer (e.g. an mbuf).
569a9148abdSDoug Rabson 		 */
570a9148abdSDoug Rabson 		rpcbuf.length = XDR_GETPOS(xdrs);
571a9148abdSDoug Rabson 		XDR_SETPOS(xdrs, 0);
572a9148abdSDoug Rabson 		rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
573a9148abdSDoug Rabson 
574a9148abdSDoug Rabson 		maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
575a9148abdSDoug Rabson 		    &rpcbuf, &checksum);
576a9148abdSDoug Rabson 
577a9148abdSDoug Rabson 		if (maj_stat != GSS_S_COMPLETE) {
578a9148abdSDoug Rabson 			rpc_gss_log_status("gss_get_mic", gd->gd_mech,
579a9148abdSDoug Rabson 			    maj_stat, min_stat);
580a9148abdSDoug Rabson 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
581a9148abdSDoug Rabson 				rpc_gss_destroy_context(auth, TRUE);
582a9148abdSDoug Rabson 			}
583a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
584a9148abdSDoug Rabson 			return (FALSE);
585a9148abdSDoug Rabson 		}
586a9148abdSDoug Rabson 
587a9148abdSDoug Rabson 		verf.oa_flavor = RPCSEC_GSS;
588a9148abdSDoug Rabson 		verf.oa_base = checksum.value;
589a9148abdSDoug Rabson 		verf.oa_length = checksum.length;
590a9148abdSDoug Rabson 
591a9148abdSDoug Rabson 		xdr_stat = xdr_opaque_auth(xdrs, &verf);
592a9148abdSDoug Rabson 		gss_release_buffer(&min_stat, &checksum);
593a9148abdSDoug Rabson 		if (!xdr_stat) {
594a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
595a9148abdSDoug Rabson 			return (FALSE);
596a9148abdSDoug Rabson 		}
597a9148abdSDoug Rabson 		if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
598a9148abdSDoug Rabson 		    gd->gd_cred.gc_svc == rpc_gss_svc_none) {
599a9148abdSDoug Rabson 			xdrmbuf_append(xdrs, args);
600a9148abdSDoug Rabson 			return (TRUE);
601a9148abdSDoug Rabson 		} else {
602a9148abdSDoug Rabson 			if (!xdr_rpc_gss_wrap_data(&args,
603a9148abdSDoug Rabson 				gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
604a9148abdSDoug Rabson 				seq))
605a9148abdSDoug Rabson 				return (FALSE);
606a9148abdSDoug Rabson 			xdrmbuf_append(xdrs, args);
607a9148abdSDoug Rabson 			return (TRUE);
608a9148abdSDoug Rabson 		}
609a9148abdSDoug Rabson 	}
610a9148abdSDoug Rabson 
611a9148abdSDoug Rabson 	return (TRUE);
612a9148abdSDoug Rabson }
613a9148abdSDoug Rabson 
614a9148abdSDoug Rabson static bool_t
rpc_gss_validate(AUTH * auth,uint32_t xid,struct opaque_auth * verf,struct mbuf ** resultsp)615a9148abdSDoug Rabson rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
616a9148abdSDoug Rabson     struct mbuf **resultsp)
617a9148abdSDoug Rabson {
618a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
619a9148abdSDoug Rabson 	struct rpc_pending_request *pr, *npr;
620a9148abdSDoug Rabson 	struct rpc_pending_request_list reqs;
621a9148abdSDoug Rabson 	gss_qop_t		qop_state;
622a9148abdSDoug Rabson 	uint32_t		num, seq;
623a9148abdSDoug Rabson 	gss_buffer_desc		signbuf, checksum;
624a9148abdSDoug Rabson 	OM_uint32		maj_stat, min_stat;
625a9148abdSDoug Rabson 
626a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_validate()");
627a9148abdSDoug Rabson 
628a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
629a9148abdSDoug Rabson 
630a9148abdSDoug Rabson 	/*
631a9148abdSDoug Rabson 	 * The client will call us with a NULL verf when it gives up
632a9148abdSDoug Rabson 	 * on an XID.
633a9148abdSDoug Rabson 	 */
634a9148abdSDoug Rabson 	if (!verf) {
635a9148abdSDoug Rabson 		rpc_gss_purge_xid(gd, xid);
636a9148abdSDoug Rabson 		return (TRUE);
637a9148abdSDoug Rabson 	}
638a9148abdSDoug Rabson 
639a9148abdSDoug Rabson 	if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
640a9148abdSDoug Rabson 		/*
641a9148abdSDoug Rabson 		 * Save the on the wire verifier to validate last INIT
642a9148abdSDoug Rabson 		 * phase packet after decode if the major status is
643a9148abdSDoug Rabson 		 * GSS_S_COMPLETE.
644a9148abdSDoug Rabson 		 */
645a9148abdSDoug Rabson 		if (gd->gd_verf.value)
646a9148abdSDoug Rabson 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
647a9148abdSDoug Rabson 			    (char *) &gd->gd_verf);
648a9148abdSDoug Rabson 		gd->gd_verf.value = mem_alloc(verf->oa_length);
649a9148abdSDoug Rabson 		if (gd->gd_verf.value == NULL) {
650a9148abdSDoug Rabson 			printf("gss_validate: out of memory\n");
651a9148abdSDoug Rabson 			_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
652a9148abdSDoug Rabson 			m_freem(*resultsp);
653a9148abdSDoug Rabson 			*resultsp = NULL;
654a9148abdSDoug Rabson 			return (FALSE);
655a9148abdSDoug Rabson 		}
656a9148abdSDoug Rabson 		memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
657a9148abdSDoug Rabson 		gd->gd_verf.length = verf->oa_length;
658a9148abdSDoug Rabson 
659a9148abdSDoug Rabson 		return (TRUE);
660a9148abdSDoug Rabson 	}
661a9148abdSDoug Rabson 
662a9148abdSDoug Rabson 	/*
663a9148abdSDoug Rabson 	 * We need to check the verifier against all the requests
664a9148abdSDoug Rabson 	 * we've send for this XID - for unreliable protocols, we
665a9148abdSDoug Rabson 	 * retransmit with the same XID but different sequence
666a9148abdSDoug Rabson 	 * number. We temporarily take this set of requests out of the
667a9148abdSDoug Rabson 	 * list so that we can work through the list without having to
668a9148abdSDoug Rabson 	 * hold the lock.
669a9148abdSDoug Rabson 	 */
670a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
671a9148abdSDoug Rabson 	LIST_INIT(&reqs);
672a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
673a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
674a9148abdSDoug Rabson 			LIST_REMOVE(pr, pr_link);
675a9148abdSDoug Rabson 			LIST_INSERT_HEAD(&reqs, pr, pr_link);
676a9148abdSDoug Rabson 		}
677a9148abdSDoug Rabson 	}
678a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
679a9148abdSDoug Rabson 	LIST_FOREACH(pr, &reqs, pr_link) {
680a9148abdSDoug Rabson 		if (pr->pr_xid == xid) {
681a9148abdSDoug Rabson 			seq = pr->pr_seq;
682a9148abdSDoug Rabson 			num = htonl(seq);
683a9148abdSDoug Rabson 			signbuf.value = &num;
684a9148abdSDoug Rabson 			signbuf.length = sizeof(num);
685a9148abdSDoug Rabson 
686a9148abdSDoug Rabson 			checksum.value = verf->oa_base;
687a9148abdSDoug Rabson 			checksum.length = verf->oa_length;
688a9148abdSDoug Rabson 
689a9148abdSDoug Rabson 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
690a9148abdSDoug Rabson 			    &signbuf, &checksum, &qop_state);
691a9148abdSDoug Rabson 			if (maj_stat != GSS_S_COMPLETE
692a9148abdSDoug Rabson 			    || qop_state != gd->gd_qop) {
693a9148abdSDoug Rabson 				continue;
694a9148abdSDoug Rabson 			}
695a9148abdSDoug Rabson 			if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
696a9148abdSDoug Rabson 				rpc_gss_destroy_context(auth, TRUE);
697a9148abdSDoug Rabson 				break;
698a9148abdSDoug Rabson 			}
699a9148abdSDoug Rabson 			//rpc_gss_purge_reqs(gd, seq);
700a9148abdSDoug Rabson 			LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
701a9148abdSDoug Rabson 				mem_free(pr, sizeof(*pr));
702a9148abdSDoug Rabson 
703a9148abdSDoug Rabson 			if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
704a9148abdSDoug Rabson 				return (TRUE);
705a9148abdSDoug Rabson 			} else {
706a9148abdSDoug Rabson 				if (!xdr_rpc_gss_unwrap_data(resultsp,
707a9148abdSDoug Rabson 					gd->gd_ctx, gd->gd_qop,
708a9148abdSDoug Rabson 					gd->gd_cred.gc_svc, seq)) {
709a9148abdSDoug Rabson 					return (FALSE);
710a9148abdSDoug Rabson 				}
711a9148abdSDoug Rabson 			}
712a9148abdSDoug Rabson 			return (TRUE);
713a9148abdSDoug Rabson 		}
714a9148abdSDoug Rabson 	}
715a9148abdSDoug Rabson 
716a9148abdSDoug Rabson 	/*
717a9148abdSDoug Rabson 	 * We didn't match - put back any entries for this XID so that
718a9148abdSDoug Rabson 	 * a future call to validate can retry.
719a9148abdSDoug Rabson 	 */
720a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
721a9148abdSDoug Rabson 	LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
722a9148abdSDoug Rabson 		LIST_REMOVE(pr, pr_link);
723a9148abdSDoug Rabson 		LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
724a9148abdSDoug Rabson 	}
725a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
726a9148abdSDoug Rabson 
727a9148abdSDoug Rabson 	/*
728a9148abdSDoug Rabson 	 * Nothing matches - give up.
729a9148abdSDoug Rabson 	 */
730a9148abdSDoug Rabson 	_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
731a9148abdSDoug Rabson 	m_freem(*resultsp);
732a9148abdSDoug Rabson 	*resultsp = NULL;
733a9148abdSDoug Rabson 	return (FALSE);
734a9148abdSDoug Rabson }
735a9148abdSDoug Rabson 
736a9148abdSDoug Rabson static bool_t
rpc_gss_init(AUTH * auth,rpc_gss_options_ret_t * options_ret)737a9148abdSDoug Rabson rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
738a9148abdSDoug Rabson {
739a9148abdSDoug Rabson 	struct thread		*td = curthread;
740a9148abdSDoug Rabson 	struct ucred		*crsave;
741a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
742a9148abdSDoug Rabson 	struct rpc_gss_init_res	 gr;
743a9148abdSDoug Rabson 	gss_buffer_desc		principal_desc;
744a9148abdSDoug Rabson 	gss_buffer_desc		*recv_tokenp, recv_token, send_token;
745a9148abdSDoug Rabson 	gss_name_t		name;
746a9148abdSDoug Rabson 	OM_uint32		 maj_stat, min_stat, call_stat;
747a9148abdSDoug Rabson 	const char		*mech;
748a9148abdSDoug Rabson 	struct rpc_callextra	 ext;
74988a2437aSRick Macklem 	gss_OID			mech_oid;
75088a2437aSRick Macklem 	gss_OID_set		mechlist;
751a9148abdSDoug Rabson 
752a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_refresh()");
753a9148abdSDoug Rabson 
754a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
755a9148abdSDoug Rabson 
756a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
757a9148abdSDoug Rabson 	/*
758a9148abdSDoug Rabson 	 * If the context isn't in START state, someone else is
759a9148abdSDoug Rabson 	 * refreshing - we wait till they are done. If they fail, they
760a9148abdSDoug Rabson 	 * will put the state back to START and we can try (most
761a9148abdSDoug Rabson 	 * likely to also fail).
762a9148abdSDoug Rabson 	 */
763a9148abdSDoug Rabson 	while (gd->gd_state != RPCSEC_GSS_START
764a9148abdSDoug Rabson 	    && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
765a9148abdSDoug Rabson 		msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
766a9148abdSDoug Rabson 	}
767a9148abdSDoug Rabson 	if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
768a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
769a9148abdSDoug Rabson 		return (TRUE);
770a9148abdSDoug Rabson 	}
771a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_CONTEXT;
772a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
773a9148abdSDoug Rabson 
774b2115885SRick Macklem 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
775b2115885SRick Macklem 	gd->gd_cred.gc_seq = 0;
776b2115885SRick Macklem 
77788a2437aSRick Macklem 	/*
77888a2437aSRick Macklem 	 * For KerberosV, if there is a client principal name, that implies
77988a2437aSRick Macklem 	 * that this is a host based initiator credential in the default
78088a2437aSRick Macklem 	 * keytab file. For this case, it is necessary to do a
78188a2437aSRick Macklem 	 * gss_acquire_cred(). When this is done, the gssd daemon will
78288a2437aSRick Macklem 	 * do the equivalent of "kinit -k" to put a TGT for the name in
78388a2437aSRick Macklem 	 * the credential cache file for the gssd daemon.
78488a2437aSRick Macklem 	 */
78588a2437aSRick Macklem 	if (gd->gd_clntprincipal != NULL &&
78688a2437aSRick Macklem 	    rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
78788a2437aSRick Macklem 	    gd->gd_mech == mech_oid) {
78888a2437aSRick Macklem 		/* Get rid of any old credential. */
78988a2437aSRick Macklem 		if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
79088a2437aSRick Macklem 			gss_release_cred(&min_stat, &gd->gd_options.my_cred);
79188a2437aSRick Macklem 			gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
79288a2437aSRick Macklem 		}
79388a2437aSRick Macklem 
79488a2437aSRick Macklem 		/*
79588a2437aSRick Macklem 		 * The mechanism must be set to KerberosV for acquisition
79688a2437aSRick Macklem 		 * of credentials to work reliably.
79788a2437aSRick Macklem 		 */
79888a2437aSRick Macklem 		maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
79988a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
80088a2437aSRick Macklem 			options_ret->major_status = maj_stat;
80188a2437aSRick Macklem 			options_ret->minor_status = min_stat;
80288a2437aSRick Macklem 			goto out;
80388a2437aSRick Macklem 		}
80488a2437aSRick Macklem 		maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
80588a2437aSRick Macklem 		    &mechlist);
80688a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
80788a2437aSRick Macklem 			options_ret->major_status = maj_stat;
80888a2437aSRick Macklem 			options_ret->minor_status = min_stat;
80988a2437aSRick Macklem 			gss_release_oid_set(&min_stat, &mechlist);
81088a2437aSRick Macklem 			goto out;
81188a2437aSRick Macklem 		}
81288a2437aSRick Macklem 
81388a2437aSRick Macklem 		principal_desc.value = (void *)gd->gd_clntprincipal;
81488a2437aSRick Macklem 		principal_desc.length = strlen(gd->gd_clntprincipal);
81588a2437aSRick Macklem 		maj_stat = gss_import_name(&min_stat, &principal_desc,
81688a2437aSRick Macklem 		    GSS_C_NT_HOSTBASED_SERVICE, &name);
81788a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
81888a2437aSRick Macklem 			options_ret->major_status = maj_stat;
81988a2437aSRick Macklem 			options_ret->minor_status = min_stat;
82088a2437aSRick Macklem 			gss_release_oid_set(&min_stat, &mechlist);
82188a2437aSRick Macklem 			goto out;
82288a2437aSRick Macklem 		}
82388a2437aSRick Macklem 		/* Acquire the credentials. */
82488a2437aSRick Macklem 		maj_stat = gss_acquire_cred(&min_stat, name, 0,
82588a2437aSRick Macklem 		    mechlist, GSS_C_INITIATE,
82688a2437aSRick Macklem 		    &gd->gd_options.my_cred, NULL, NULL);
82788a2437aSRick Macklem 		gss_release_name(&min_stat, &name);
82888a2437aSRick Macklem 		gss_release_oid_set(&min_stat, &mechlist);
82988a2437aSRick Macklem 		if (maj_stat != GSS_S_COMPLETE) {
83088a2437aSRick Macklem 			options_ret->major_status = maj_stat;
83188a2437aSRick Macklem 			options_ret->minor_status = min_stat;
83288a2437aSRick Macklem 			goto out;
83388a2437aSRick Macklem 		}
83488a2437aSRick Macklem 	}
83588a2437aSRick Macklem 
836a9148abdSDoug Rabson 	principal_desc.value = (void *)gd->gd_principal;
837a9148abdSDoug Rabson 	principal_desc.length = strlen(gd->gd_principal);
838a9148abdSDoug Rabson 	maj_stat = gss_import_name(&min_stat, &principal_desc,
839a9148abdSDoug Rabson 	    GSS_C_NT_HOSTBASED_SERVICE, &name);
840a9148abdSDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
841a9148abdSDoug Rabson 		options_ret->major_status = maj_stat;
842a9148abdSDoug Rabson 		options_ret->minor_status = min_stat;
843a9148abdSDoug Rabson 		goto out;
844a9148abdSDoug Rabson 	}
845a9148abdSDoug Rabson 
846a9148abdSDoug Rabson 	/* GSS context establishment loop. */
847a9148abdSDoug Rabson 	memset(&recv_token, 0, sizeof(recv_token));
848a9148abdSDoug Rabson 	memset(&gr, 0, sizeof(gr));
849a9148abdSDoug Rabson 	memset(options_ret, 0, sizeof(*options_ret));
850a9148abdSDoug Rabson 	options_ret->major_status = GSS_S_FAILURE;
851a9148abdSDoug Rabson 	recv_tokenp = GSS_C_NO_BUFFER;
852a9148abdSDoug Rabson 
853a9148abdSDoug Rabson 	for (;;) {
854a9148abdSDoug Rabson 		crsave = td->td_ucred;
855a9148abdSDoug Rabson 		td->td_ucred = gd->gd_ucred;
856a9148abdSDoug Rabson 		maj_stat = gss_init_sec_context(&min_stat,
857a9148abdSDoug Rabson 		    gd->gd_options.my_cred,
858a9148abdSDoug Rabson 		    &gd->gd_ctx,
859a9148abdSDoug Rabson 		    name,
860a9148abdSDoug Rabson 		    gd->gd_mech,
861a9148abdSDoug Rabson 		    gd->gd_options.req_flags,
862a9148abdSDoug Rabson 		    gd->gd_options.time_req,
863a9148abdSDoug Rabson 		    gd->gd_options.input_channel_bindings,
864a9148abdSDoug Rabson 		    recv_tokenp,
865a9148abdSDoug Rabson 		    &gd->gd_mech,	/* used mech */
866a9148abdSDoug Rabson 		    &send_token,
867a9148abdSDoug Rabson 		    &options_ret->ret_flags,
868a9148abdSDoug Rabson 		    &options_ret->time_req);
869a9148abdSDoug Rabson 		td->td_ucred = crsave;
870a9148abdSDoug Rabson 
871a9148abdSDoug Rabson 		/*
872a9148abdSDoug Rabson 		 * Free the token which we got from the server (if
873a9148abdSDoug Rabson 		 * any).  Remember that this was allocated by XDR, not
874a9148abdSDoug Rabson 		 * GSS-API.
875a9148abdSDoug Rabson 		 */
876a9148abdSDoug Rabson 		if (recv_tokenp != GSS_C_NO_BUFFER) {
877a9148abdSDoug Rabson 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
878a9148abdSDoug Rabson 			    (char *) &recv_token);
879a9148abdSDoug Rabson 			recv_tokenp = GSS_C_NO_BUFFER;
880a9148abdSDoug Rabson 		}
881a9148abdSDoug Rabson 		if (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
882a9148abdSDoug Rabson 			strlcpy(options_ret->actual_mechanism,
883a9148abdSDoug Rabson 			    mech,
884a9148abdSDoug Rabson 			    sizeof(options_ret->actual_mechanism));
885a9148abdSDoug Rabson 		}
886a9148abdSDoug Rabson 		if (maj_stat != GSS_S_COMPLETE &&
887a9148abdSDoug Rabson 		    maj_stat != GSS_S_CONTINUE_NEEDED) {
888a9148abdSDoug Rabson 			rpc_gss_log_status("gss_init_sec_context", gd->gd_mech,
889a9148abdSDoug Rabson 			    maj_stat, min_stat);
890a9148abdSDoug Rabson 			options_ret->major_status = maj_stat;
891a9148abdSDoug Rabson 			options_ret->minor_status = min_stat;
892a9148abdSDoug Rabson 			break;
893a9148abdSDoug Rabson 		}
894a9148abdSDoug Rabson 		if (send_token.length != 0) {
895a9148abdSDoug Rabson 			memset(&gr, 0, sizeof(gr));
896a9148abdSDoug Rabson 
897a9148abdSDoug Rabson 			bzero(&ext, sizeof(ext));
898a9148abdSDoug Rabson 			ext.rc_auth = auth;
899a9148abdSDoug Rabson 			call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
900a9148abdSDoug Rabson 			    (xdrproc_t)xdr_gss_buffer_desc,
901a9148abdSDoug Rabson 			    &send_token,
902a9148abdSDoug Rabson 			    (xdrproc_t)xdr_rpc_gss_init_res,
903a9148abdSDoug Rabson 			    (caddr_t)&gr, AUTH_TIMEOUT);
904a9148abdSDoug Rabson 
905a9148abdSDoug Rabson 			gss_release_buffer(&min_stat, &send_token);
906a9148abdSDoug Rabson 
907a9148abdSDoug Rabson 			if (call_stat != RPC_SUCCESS)
908a9148abdSDoug Rabson 				break;
909a9148abdSDoug Rabson 
910a9148abdSDoug Rabson 			if (gr.gr_major != GSS_S_COMPLETE &&
911a9148abdSDoug Rabson 			    gr.gr_major != GSS_S_CONTINUE_NEEDED) {
912a9148abdSDoug Rabson 				rpc_gss_log_status("server reply", gd->gd_mech,
913a9148abdSDoug Rabson 				    gr.gr_major, gr.gr_minor);
914a9148abdSDoug Rabson 				options_ret->major_status = gr.gr_major;
915a9148abdSDoug Rabson 				options_ret->minor_status = gr.gr_minor;
916a9148abdSDoug Rabson 				break;
917a9148abdSDoug Rabson 			}
918a9148abdSDoug Rabson 
919a9148abdSDoug Rabson 			/*
920a9148abdSDoug Rabson 			 * Save the server's gr_handle value, freeing
921a9148abdSDoug Rabson 			 * what we have already (remember that this
922a9148abdSDoug Rabson 			 * was allocated by XDR, not GSS-API).
923a9148abdSDoug Rabson 			 */
924a9148abdSDoug Rabson 			if (gr.gr_handle.length != 0) {
925a9148abdSDoug Rabson 				xdr_free((xdrproc_t) xdr_gss_buffer_desc,
926a9148abdSDoug Rabson 				    (char *) &gd->gd_cred.gc_handle);
927a9148abdSDoug Rabson 				gd->gd_cred.gc_handle = gr.gr_handle;
928a9148abdSDoug Rabson 			}
929a9148abdSDoug Rabson 
930a9148abdSDoug Rabson 			/*
931a9148abdSDoug Rabson 			 * Save the server's token as well.
932a9148abdSDoug Rabson 			 */
933a9148abdSDoug Rabson 			if (gr.gr_token.length != 0) {
934a9148abdSDoug Rabson 				recv_token = gr.gr_token;
935a9148abdSDoug Rabson 				recv_tokenp = &recv_token;
936a9148abdSDoug Rabson 			}
937a9148abdSDoug Rabson 
938a9148abdSDoug Rabson 			/*
939a9148abdSDoug Rabson 			 * Since we have copied out all the bits of gr
940a9148abdSDoug Rabson 			 * which XDR allocated for us, we don't need
941a9148abdSDoug Rabson 			 * to free it.
942a9148abdSDoug Rabson 			 */
943a9148abdSDoug Rabson 			gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
944a9148abdSDoug Rabson 		}
945a9148abdSDoug Rabson 
946a9148abdSDoug Rabson 		if (maj_stat == GSS_S_COMPLETE) {
947a9148abdSDoug Rabson 			gss_buffer_desc   bufin;
948a9148abdSDoug Rabson 			u_int seq, qop_state = 0;
949a9148abdSDoug Rabson 
950a9148abdSDoug Rabson 			/*
951a9148abdSDoug Rabson 			 * gss header verifier,
952a9148abdSDoug Rabson 			 * usually checked in gss_validate
953a9148abdSDoug Rabson 			 */
954a9148abdSDoug Rabson 			seq = htonl(gr.gr_win);
955a9148abdSDoug Rabson 			bufin.value = (unsigned char *)&seq;
956a9148abdSDoug Rabson 			bufin.length = sizeof(seq);
957a9148abdSDoug Rabson 
958a9148abdSDoug Rabson 			maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
959a9148abdSDoug Rabson 			    &bufin, &gd->gd_verf, &qop_state);
960a9148abdSDoug Rabson 
961a9148abdSDoug Rabson 			if (maj_stat != GSS_S_COMPLETE ||
962a9148abdSDoug Rabson 			    qop_state != gd->gd_qop) {
963a9148abdSDoug Rabson 				rpc_gss_log_status("gss_verify_mic", gd->gd_mech,
964a9148abdSDoug Rabson 				    maj_stat, min_stat);
965a9148abdSDoug Rabson 				if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
966a9148abdSDoug Rabson 					rpc_gss_destroy_context(auth, TRUE);
967a9148abdSDoug Rabson 				}
968a9148abdSDoug Rabson 				_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
969a9148abdSDoug Rabson 				    EPERM);
970a9148abdSDoug Rabson 				options_ret->major_status = maj_stat;
971a9148abdSDoug Rabson 				options_ret->minor_status = min_stat;
972a9148abdSDoug Rabson 				break;
973a9148abdSDoug Rabson 			}
974a9148abdSDoug Rabson 
975a9148abdSDoug Rabson 			options_ret->major_status = GSS_S_COMPLETE;
976a9148abdSDoug Rabson 			options_ret->minor_status = 0;
977a9148abdSDoug Rabson 			options_ret->rpcsec_version = gd->gd_cred.gc_version;
978a9148abdSDoug Rabson 			options_ret->gss_context = gd->gd_ctx;
979a9148abdSDoug Rabson 
980a9148abdSDoug Rabson 			gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
981a9148abdSDoug Rabson 			gd->gd_seq = 1;
982a9148abdSDoug Rabson 			gd->gd_win = gr.gr_win;
983a9148abdSDoug Rabson 			break;
984a9148abdSDoug Rabson 		}
985a9148abdSDoug Rabson 	}
986a9148abdSDoug Rabson 
987a9148abdSDoug Rabson 	gss_release_name(&min_stat, &name);
988a9148abdSDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
989a9148abdSDoug Rabson 	    (char *) &gd->gd_verf);
990a9148abdSDoug Rabson 
991a9148abdSDoug Rabson out:
992a9148abdSDoug Rabson 	/* End context negotiation loop. */
993a9148abdSDoug Rabson 	if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
994a9148abdSDoug Rabson 		rpc_createerr.cf_stat = RPC_AUTHERROR;
995a9148abdSDoug Rabson 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
996a9148abdSDoug Rabson 		if (gd->gd_ctx) {
997a9148abdSDoug Rabson 			gss_delete_sec_context(&min_stat, &gd->gd_ctx,
998a9148abdSDoug Rabson 				GSS_C_NO_BUFFER);
999a9148abdSDoug Rabson 		}
1000a9148abdSDoug Rabson 		mtx_lock(&gd->gd_lock);
1001a9148abdSDoug Rabson 		gd->gd_state = RPCSEC_GSS_START;
1002a9148abdSDoug Rabson 		wakeup(gd);
1003a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
1004a9148abdSDoug Rabson 		return (FALSE);
1005a9148abdSDoug Rabson 	}
1006a9148abdSDoug Rabson 
1007a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1008a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_ESTABLISHED;
1009a9148abdSDoug Rabson 	wakeup(gd);
1010a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1011a9148abdSDoug Rabson 
1012a9148abdSDoug Rabson 	return (TRUE);
1013a9148abdSDoug Rabson }
1014a9148abdSDoug Rabson 
1015a9148abdSDoug Rabson static bool_t
rpc_gss_refresh(AUTH * auth,void * msg)1016a9148abdSDoug Rabson rpc_gss_refresh(AUTH *auth, void *msg)
1017a9148abdSDoug Rabson {
1018a9148abdSDoug Rabson 	struct rpc_msg *reply = (struct rpc_msg *) msg;
1019a9148abdSDoug Rabson 	rpc_gss_options_ret_t options;
1020a4c5a1c3SRick Macklem 	struct rpc_gss_data *gd;
1021a4c5a1c3SRick Macklem 
1022a4c5a1c3SRick Macklem 	gd = AUTH_PRIVATE(auth);
1023a4c5a1c3SRick Macklem 
1024a4c5a1c3SRick Macklem 	/*
1025a4c5a1c3SRick Macklem 	 * If the context is in DESTROYING state, then just return, since
1026a4c5a1c3SRick Macklem 	 * there is no point in refreshing the credentials.
1027a4c5a1c3SRick Macklem 	 */
1028a4c5a1c3SRick Macklem 	mtx_lock(&gd->gd_lock);
1029a4c5a1c3SRick Macklem 	if (gd->gd_state == RPCSEC_GSS_DESTROYING) {
1030a4c5a1c3SRick Macklem 		mtx_unlock(&gd->gd_lock);
1031a4c5a1c3SRick Macklem 		return (FALSE);
1032a4c5a1c3SRick Macklem 	}
1033a4c5a1c3SRick Macklem 	mtx_unlock(&gd->gd_lock);
1034a9148abdSDoug Rabson 
1035a9148abdSDoug Rabson 	/*
1036a9148abdSDoug Rabson 	 * If the error was RPCSEC_GSS_CREDPROBLEM of
1037a9148abdSDoug Rabson 	 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
1038a9148abdSDoug Rabson 	 * other errors are fatal.
1039a9148abdSDoug Rabson 	 */
1040a9148abdSDoug Rabson 	if (reply->rm_reply.rp_stat == MSG_DENIED
1041a9148abdSDoug Rabson 	    && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
1042a9148abdSDoug Rabson 	    && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
1043a9148abdSDoug Rabson 		|| reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
1044a9148abdSDoug Rabson 		rpc_gss_destroy_context(auth, FALSE);
1045a9148abdSDoug Rabson 		memset(&options, 0, sizeof(options));
1046a9148abdSDoug Rabson 		return (rpc_gss_init(auth, &options));
1047a9148abdSDoug Rabson 	}
1048a9148abdSDoug Rabson 
1049a9148abdSDoug Rabson 	return (FALSE);
1050a9148abdSDoug Rabson }
1051a9148abdSDoug Rabson 
1052a9148abdSDoug Rabson static void
rpc_gss_destroy_context(AUTH * auth,bool_t send_destroy)1053a9148abdSDoug Rabson rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
1054a9148abdSDoug Rabson {
1055a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1056a9148abdSDoug Rabson 	struct rpc_pending_request *pr;
1057a9148abdSDoug Rabson 	OM_uint32		 min_stat;
1058a9148abdSDoug Rabson 	struct rpc_callextra	 ext;
1059a9148abdSDoug Rabson 
1060a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_destroy_context()");
1061a9148abdSDoug Rabson 
1062a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1063a9148abdSDoug Rabson 
1064a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1065a9148abdSDoug Rabson 	/*
1066a9148abdSDoug Rabson 	 * If the context isn't in ESTABISHED state, someone else is
1067a9148abdSDoug Rabson 	 * destroying/refreshing - we wait till they are done.
1068a9148abdSDoug Rabson 	 */
1069a9148abdSDoug Rabson 	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
1070a9148abdSDoug Rabson 		while (gd->gd_state != RPCSEC_GSS_START
1071a9148abdSDoug Rabson 		    && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
1072a9148abdSDoug Rabson 			msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
1073a9148abdSDoug Rabson 		mtx_unlock(&gd->gd_lock);
1074a9148abdSDoug Rabson 		return;
1075a9148abdSDoug Rabson 	}
1076a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_DESTROYING;
1077a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1078a9148abdSDoug Rabson 
1079a9148abdSDoug Rabson 	if (send_destroy) {
1080a9148abdSDoug Rabson 		gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
1081a9148abdSDoug Rabson 		bzero(&ext, sizeof(ext));
1082a9148abdSDoug Rabson 		ext.rc_auth = auth;
1083a9148abdSDoug Rabson 		CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
1084a9148abdSDoug Rabson 		    (xdrproc_t)xdr_void, NULL,
1085a9148abdSDoug Rabson 		    (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
1086a9148abdSDoug Rabson 	}
1087a9148abdSDoug Rabson 
1088a9148abdSDoug Rabson 	while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
1089a9148abdSDoug Rabson 		LIST_REMOVE(pr, pr_link);
1090a9148abdSDoug Rabson 		mem_free(pr, sizeof(*pr));
1091a9148abdSDoug Rabson 	}
1092a9148abdSDoug Rabson 
1093a9148abdSDoug Rabson 	/*
1094a9148abdSDoug Rabson 	 * Free the context token. Remember that this was
1095a9148abdSDoug Rabson 	 * allocated by XDR, not GSS-API.
1096a9148abdSDoug Rabson 	 */
1097a9148abdSDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1098a9148abdSDoug Rabson 	    (char *) &gd->gd_cred.gc_handle);
1099a9148abdSDoug Rabson 	gd->gd_cred.gc_handle.length = 0;
1100a9148abdSDoug Rabson 
1101a9148abdSDoug Rabson 	if (gd->gd_ctx != GSS_C_NO_CONTEXT)
1102a9148abdSDoug Rabson 		gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
1103a9148abdSDoug Rabson 
1104a9148abdSDoug Rabson 	mtx_lock(&gd->gd_lock);
1105a9148abdSDoug Rabson 	gd->gd_state = RPCSEC_GSS_START;
1106a9148abdSDoug Rabson 	wakeup(gd);
1107a9148abdSDoug Rabson 	mtx_unlock(&gd->gd_lock);
1108a9148abdSDoug Rabson }
1109a9148abdSDoug Rabson 
1110a9148abdSDoug Rabson static void
rpc_gss_destroy(AUTH * auth)1111a9148abdSDoug Rabson rpc_gss_destroy(AUTH *auth)
1112a9148abdSDoug Rabson {
1113a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1114a9148abdSDoug Rabson 
1115a9148abdSDoug Rabson 	rpc_gss_log_debug("in rpc_gss_destroy()");
1116a9148abdSDoug Rabson 
1117a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1118a9148abdSDoug Rabson 
1119a9148abdSDoug Rabson 	if (!refcount_release(&gd->gd_refs))
1120a9148abdSDoug Rabson 		return;
1121a9148abdSDoug Rabson 
1122a9148abdSDoug Rabson 	rpc_gss_destroy_context(auth, TRUE);
1123a9148abdSDoug Rabson 
1124a9148abdSDoug Rabson 	CLNT_RELEASE(gd->gd_clnt);
1125a9148abdSDoug Rabson 	crfree(gd->gd_ucred);
1126a9148abdSDoug Rabson 	free(gd->gd_principal, M_RPC);
112788a2437aSRick Macklem 	if (gd->gd_clntprincipal != NULL)
112888a2437aSRick Macklem 		free(gd->gd_clntprincipal, M_RPC);
1129a9148abdSDoug Rabson 	if (gd->gd_verf.value)
1130a9148abdSDoug Rabson 		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
1131a9148abdSDoug Rabson 		    (char *) &gd->gd_verf);
1132a9148abdSDoug Rabson 	mtx_destroy(&gd->gd_lock);
1133a9148abdSDoug Rabson 
1134a9148abdSDoug Rabson 	mem_free(gd, sizeof(*gd));
1135a9148abdSDoug Rabson 	mem_free(auth, sizeof(*auth));
1136a9148abdSDoug Rabson }
1137a9148abdSDoug Rabson 
1138a9148abdSDoug Rabson int
rpc_gss_max_data_length(AUTH * auth,int max_tp_unit_len)1139a9148abdSDoug Rabson rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
1140a9148abdSDoug Rabson {
1141a9148abdSDoug Rabson 	struct rpc_gss_data	*gd;
1142a9148abdSDoug Rabson 	int			want_conf;
1143a9148abdSDoug Rabson 	OM_uint32		max;
1144a9148abdSDoug Rabson 	OM_uint32		maj_stat, min_stat;
1145a9148abdSDoug Rabson 	int			result;
1146a9148abdSDoug Rabson 
1147a9148abdSDoug Rabson 	gd = AUTH_PRIVATE(auth);
1148a9148abdSDoug Rabson 
1149a9148abdSDoug Rabson 	switch (gd->gd_cred.gc_svc) {
1150a9148abdSDoug Rabson 	case rpc_gss_svc_none:
1151a9148abdSDoug Rabson 		return (max_tp_unit_len);
1152a9148abdSDoug Rabson 		break;
1153a9148abdSDoug Rabson 
1154a9148abdSDoug Rabson 	case rpc_gss_svc_default:
1155a9148abdSDoug Rabson 	case rpc_gss_svc_integrity:
1156a9148abdSDoug Rabson 		want_conf = FALSE;
1157a9148abdSDoug Rabson 		break;
1158a9148abdSDoug Rabson 
1159a9148abdSDoug Rabson 	case rpc_gss_svc_privacy:
1160a9148abdSDoug Rabson 		want_conf = TRUE;
1161a9148abdSDoug Rabson 		break;
1162a9148abdSDoug Rabson 
1163a9148abdSDoug Rabson 	default:
1164a9148abdSDoug Rabson 		return (0);
1165a9148abdSDoug Rabson 	}
1166a9148abdSDoug Rabson 
1167a9148abdSDoug Rabson 	maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
1168a9148abdSDoug Rabson 	    gd->gd_qop, max_tp_unit_len, &max);
1169a9148abdSDoug Rabson 
1170a9148abdSDoug Rabson 	if (maj_stat == GSS_S_COMPLETE) {
1171a9148abdSDoug Rabson 		result = (int) max;
1172a9148abdSDoug Rabson 		if (result < 0)
1173a9148abdSDoug Rabson 			result = 0;
1174a9148abdSDoug Rabson 		return (result);
1175a9148abdSDoug Rabson 	} else {
1176a9148abdSDoug Rabson 		rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
1177a9148abdSDoug Rabson 		    maj_stat, min_stat);
1178a9148abdSDoug Rabson 		return (0);
1179a9148abdSDoug Rabson 	}
1180a9148abdSDoug Rabson }
1181