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