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