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