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