xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision c408f06b5e3187a2b17e2e4b5b4f34216af9936a)
1a9148abdSDoug Rabson /*-
22e92ac56SJamie Gritton  * Copyright (c) 2008 Doug Rabson
32e92ac56SJamie Gritton  * All rights reserved.
4a9148abdSDoug Rabson  *
5a9148abdSDoug Rabson  * Redistribution and use in source and binary forms, with or without
6a9148abdSDoug Rabson  * modification, are permitted provided that the following conditions
7a9148abdSDoug Rabson  * are met:
8a9148abdSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
9a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
10a9148abdSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
11a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
12a9148abdSDoug Rabson  *    documentation and/or other materials provided with the distribution.
13a9148abdSDoug Rabson  *
142e92ac56SJamie Gritton  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a9148abdSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a9148abdSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172e92ac56SJamie Gritton  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a9148abdSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a9148abdSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a9148abdSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a9148abdSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a9148abdSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a9148abdSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a9148abdSDoug Rabson  * SUCH DAMAGE.
252e92ac56SJamie Gritton  */
262e92ac56SJamie Gritton /*
272e92ac56SJamie Gritton   svc_rpcsec_gss.c
282e92ac56SJamie Gritton 
292e92ac56SJamie Gritton   Copyright (c) 2000 The Regents of the University of Michigan.
302e92ac56SJamie Gritton   All rights reserved.
312e92ac56SJamie Gritton 
322e92ac56SJamie Gritton   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
332e92ac56SJamie Gritton   All rights reserved, all wrongs reversed.
342e92ac56SJamie Gritton 
352e92ac56SJamie Gritton   Redistribution and use in source and binary forms, with or without
362e92ac56SJamie Gritton   modification, are permitted provided that the following conditions
372e92ac56SJamie Gritton   are met:
382e92ac56SJamie Gritton 
392e92ac56SJamie Gritton   1. Redistributions of source code must retain the above copyright
402e92ac56SJamie Gritton      notice, this list of conditions and the following disclaimer.
412e92ac56SJamie Gritton   2. Redistributions in binary form must reproduce the above copyright
422e92ac56SJamie Gritton      notice, this list of conditions and the following disclaimer in the
432e92ac56SJamie Gritton      documentation and/or other materials provided with the distribution.
442e92ac56SJamie Gritton   3. Neither the name of the University nor the names of its
452e92ac56SJamie Gritton      contributors may be used to endorse or promote products derived
462e92ac56SJamie Gritton      from this software without specific prior written permission.
472e92ac56SJamie Gritton 
482e92ac56SJamie Gritton   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
492e92ac56SJamie Gritton   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
502e92ac56SJamie Gritton   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
512e92ac56SJamie Gritton   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
522e92ac56SJamie Gritton   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
532e92ac56SJamie Gritton   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
542e92ac56SJamie Gritton   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
552e92ac56SJamie Gritton   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
562e92ac56SJamie Gritton   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
572e92ac56SJamie Gritton   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
582e92ac56SJamie Gritton   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
592e92ac56SJamie Gritton 
602e92ac56SJamie Gritton   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
61a9148abdSDoug Rabson  */
62a9148abdSDoug Rabson 
63a9148abdSDoug Rabson #include <sys/cdefs.h>
64a9148abdSDoug Rabson __FBSDID("$FreeBSD$");
65a9148abdSDoug Rabson 
66a9148abdSDoug Rabson #include <sys/param.h>
672e92ac56SJamie Gritton #include <sys/systm.h>
68ae883d55SRick Macklem #include <sys/jail.h>
69a9148abdSDoug Rabson #include <sys/kernel.h>
702e92ac56SJamie Gritton #include <sys/kobj.h>
71a9148abdSDoug Rabson #include <sys/lock.h>
72a9148abdSDoug Rabson #include <sys/malloc.h>
73a9148abdSDoug Rabson #include <sys/mbuf.h>
74a9148abdSDoug Rabson #include <sys/mutex.h>
752e92ac56SJamie Gritton #include <sys/proc.h>
762e92ac56SJamie Gritton #include <sys/sx.h>
772e92ac56SJamie Gritton #include <sys/ucred.h>
78a9148abdSDoug Rabson 
792e92ac56SJamie Gritton #include <rpc/rpc.h>
802e92ac56SJamie Gritton #include <rpc/rpcsec_gss.h>
81a9148abdSDoug Rabson 
822e92ac56SJamie Gritton #include "rpcsec_gss_int.h"
83a9148abdSDoug Rabson 
842e92ac56SJamie Gritton static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
852e92ac56SJamie Gritton static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
862e92ac56SJamie Gritton static void     svc_rpc_gss_release(SVCAUTH *);
872e92ac56SJamie Gritton static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
882e92ac56SJamie Gritton static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
89a9148abdSDoug Rabson 
902e92ac56SJamie Gritton static struct svc_auth_ops svc_auth_gss_ops = {
912e92ac56SJamie Gritton 	svc_rpc_gss_wrap,
922e92ac56SJamie Gritton 	svc_rpc_gss_unwrap,
932e92ac56SJamie Gritton 	svc_rpc_gss_release,
94a9148abdSDoug Rabson };
95a9148abdSDoug Rabson 
962e92ac56SJamie Gritton struct sx svc_rpc_gss_lock;
972e92ac56SJamie Gritton 
982e92ac56SJamie Gritton struct svc_rpc_gss_callback {
992e92ac56SJamie Gritton 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
1002e92ac56SJamie Gritton 	rpc_gss_callback_t	cb_callback;
1012e92ac56SJamie Gritton };
1022e92ac56SJamie Gritton static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
1032e92ac56SJamie Gritton 	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_callbacks);
1042e92ac56SJamie Gritton 
1052e92ac56SJamie Gritton struct svc_rpc_gss_svc_name {
1062e92ac56SJamie Gritton 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
1072e92ac56SJamie Gritton 	char			*sn_principal;
1082e92ac56SJamie Gritton 	gss_OID			sn_mech;
1092e92ac56SJamie Gritton 	u_int			sn_req_time;
1102e92ac56SJamie Gritton 	gss_cred_id_t		sn_cred;
1112e92ac56SJamie Gritton 	u_int			sn_program;
1122e92ac56SJamie Gritton 	u_int			sn_version;
1132e92ac56SJamie Gritton };
1142e92ac56SJamie Gritton static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
1152e92ac56SJamie Gritton 	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_svc_names);
1162e92ac56SJamie Gritton 
1172e92ac56SJamie Gritton enum svc_rpc_gss_client_state {
1182e92ac56SJamie Gritton 	CLIENT_NEW,				/* still authenticating */
1192e92ac56SJamie Gritton 	CLIENT_ESTABLISHED,			/* context established */
1202e92ac56SJamie Gritton 	CLIENT_STALE				/* garbage to collect */
121d4468577SJamie Gritton };
122a9148abdSDoug Rabson 
1232e92ac56SJamie Gritton #define SVC_RPC_GSS_SEQWINDOW	128
1242e92ac56SJamie Gritton 
1252e92ac56SJamie Gritton struct svc_rpc_gss_clientid {
1262e92ac56SJamie Gritton 	unsigned long		ci_hostid;
1272e92ac56SJamie Gritton 	uint32_t		ci_boottime;
1282e92ac56SJamie Gritton 	uint32_t		ci_id;
1292e92ac56SJamie Gritton };
1302e92ac56SJamie Gritton 
1312e92ac56SJamie Gritton struct svc_rpc_gss_client {
1322e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
1332e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
1342e92ac56SJamie Gritton 	volatile u_int		cl_refs;
1352e92ac56SJamie Gritton 	struct sx		cl_lock;
1362e92ac56SJamie Gritton 	struct svc_rpc_gss_clientid cl_id;
1372e92ac56SJamie Gritton 	time_t			cl_expiration;	/* when to gc */
1382e92ac56SJamie Gritton 	enum svc_rpc_gss_client_state cl_state;	/* client state */
1392e92ac56SJamie Gritton 	bool_t			cl_locked;	/* fixed service+qop */
1402e92ac56SJamie Gritton 	gss_ctx_id_t		cl_ctx;		/* context id */
1412e92ac56SJamie Gritton 	gss_cred_id_t		cl_creds;	/* delegated creds */
1422e92ac56SJamie Gritton 	gss_name_t		cl_cname;	/* client name */
1432e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
1442e92ac56SJamie Gritton 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
1452e92ac56SJamie Gritton 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
1462e92ac56SJamie Gritton 	struct ucred		*cl_cred;	/* kernel-style credentials */
1472e92ac56SJamie Gritton 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
1482e92ac56SJamie Gritton 	bool_t			cl_done_callback; /* TRUE after call */
1492e92ac56SJamie Gritton 	void			*cl_cookie;	/* user cookie from callback */
1502e92ac56SJamie Gritton 	gid_t			cl_gid_storage[NGROUPS];
1512e92ac56SJamie Gritton 	gss_OID			cl_mech;	/* mechanism */
1522e92ac56SJamie Gritton 	gss_qop_t		cl_qop;		/* quality of protection */
1532e92ac56SJamie Gritton 	uint32_t		cl_seqlast;	/* sequence window origin */
1542e92ac56SJamie Gritton 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
1552e92ac56SJamie Gritton };
1562e92ac56SJamie Gritton TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
1572e92ac56SJamie Gritton 
158a9148abdSDoug Rabson /*
1592e92ac56SJamie Gritton  * This structure holds enough information to unwrap arguments or wrap
1602e92ac56SJamie Gritton  * results for a given request. We use the rq_clntcred area for this
1612e92ac56SJamie Gritton  * (which is a per-request buffer).
1622e92ac56SJamie Gritton  */
1632e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred {
1642e92ac56SJamie Gritton 	struct svc_rpc_gss_client *cc_client;
1652e92ac56SJamie Gritton 	rpc_gss_service_t	cc_service;
1662e92ac56SJamie Gritton 	uint32_t		cc_seq;
1672e92ac56SJamie Gritton };
1682e92ac56SJamie Gritton 
1692e92ac56SJamie Gritton #define CLIENT_HASH_SIZE	256
1702e92ac56SJamie Gritton #define CLIENT_MAX		128
1712e92ac56SJamie Gritton struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
1722e92ac56SJamie Gritton struct svc_rpc_gss_client_list svc_rpc_gss_clients;
1732e92ac56SJamie Gritton static size_t svc_rpc_gss_client_count;
1742e92ac56SJamie Gritton static uint32_t svc_rpc_gss_next_clientid = 1;
1752e92ac56SJamie Gritton 
1762e92ac56SJamie Gritton static void
1772e92ac56SJamie Gritton svc_rpc_gss_init(void *arg)
1782e92ac56SJamie Gritton {
1792e92ac56SJamie Gritton 	int i;
1802e92ac56SJamie Gritton 
1812e92ac56SJamie Gritton 	for (i = 0; i < CLIENT_HASH_SIZE; i++)
1822e92ac56SJamie Gritton 		TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
1832e92ac56SJamie Gritton 	TAILQ_INIT(&svc_rpc_gss_clients);
1842e92ac56SJamie Gritton 	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
1852e92ac56SJamie Gritton 	sx_init(&svc_rpc_gss_lock, "gsslock");
1862e92ac56SJamie Gritton }
1872e92ac56SJamie Gritton SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
1882e92ac56SJamie Gritton 
1892e92ac56SJamie Gritton bool_t
1902e92ac56SJamie Gritton rpc_gss_set_callback(rpc_gss_callback_t *cb)
1912e92ac56SJamie Gritton {
1922e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
1932e92ac56SJamie Gritton 
1942e92ac56SJamie Gritton 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
1952e92ac56SJamie Gritton 	if (!scb) {
1962e92ac56SJamie Gritton 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
1972e92ac56SJamie Gritton 		return (FALSE);
1982e92ac56SJamie Gritton 	}
1992e92ac56SJamie Gritton 	scb->cb_callback = *cb;
2002e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2012e92ac56SJamie Gritton 	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
2022e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2032e92ac56SJamie Gritton 
2042e92ac56SJamie Gritton 	return (TRUE);
2052e92ac56SJamie Gritton }
2062e92ac56SJamie Gritton 
2072e92ac56SJamie Gritton void
2082e92ac56SJamie Gritton rpc_gss_clear_callback(rpc_gss_callback_t *cb)
2092e92ac56SJamie Gritton {
2102e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
2112e92ac56SJamie Gritton 
2122e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2132e92ac56SJamie Gritton 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
2142e92ac56SJamie Gritton 		if (scb->cb_callback.program == cb->program
2152e92ac56SJamie Gritton 		    && scb->cb_callback.version == cb->version
2162e92ac56SJamie Gritton 		    && scb->cb_callback.callback == cb->callback) {
2172e92ac56SJamie Gritton 			SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
2182e92ac56SJamie Gritton 			    svc_rpc_gss_callback, cb_link);
2192e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
2202e92ac56SJamie Gritton 			mem_free(scb, sizeof(*scb));
2212e92ac56SJamie Gritton 			return;
2222e92ac56SJamie Gritton 		}
2232e92ac56SJamie Gritton 	}
2242e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2252e92ac56SJamie Gritton }
2262e92ac56SJamie Gritton 
2272e92ac56SJamie Gritton static bool_t
2282e92ac56SJamie Gritton rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
2292e92ac56SJamie Gritton {
2302e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
2312e92ac56SJamie Gritton 	gss_buffer_desc		namebuf;
2322e92ac56SJamie Gritton 	gss_name_t		name;
2332e92ac56SJamie Gritton 	gss_OID_set_desc	oid_set;
2342e92ac56SJamie Gritton 
2352e92ac56SJamie Gritton 	oid_set.count = 1;
2362e92ac56SJamie Gritton 	oid_set.elements = sname->sn_mech;
2372e92ac56SJamie Gritton 
2382e92ac56SJamie Gritton 	namebuf.value = (void *) sname->sn_principal;
2392e92ac56SJamie Gritton 	namebuf.length = strlen(sname->sn_principal);
2402e92ac56SJamie Gritton 
2412e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &namebuf,
2422e92ac56SJamie Gritton 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
2432e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE)
2442e92ac56SJamie Gritton 		return (FALSE);
2452e92ac56SJamie Gritton 
2462e92ac56SJamie Gritton 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
2472e92ac56SJamie Gritton 		gss_release_cred(&min_stat, &sname->sn_cred);
2482e92ac56SJamie Gritton 
2492e92ac56SJamie Gritton 	maj_stat = gss_acquire_cred(&min_stat, name,
2502e92ac56SJamie Gritton 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
2512e92ac56SJamie Gritton 	    NULL, NULL);
2522e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
2532e92ac56SJamie Gritton 		gss_release_name(&min_stat, &name);
2542e92ac56SJamie Gritton 		return (FALSE);
2552e92ac56SJamie Gritton 	}
2562e92ac56SJamie Gritton 	gss_release_name(&min_stat, &name);
2572e92ac56SJamie Gritton 
2582e92ac56SJamie Gritton 	return (TRUE);
2592e92ac56SJamie Gritton }
2602e92ac56SJamie Gritton 
2612e92ac56SJamie Gritton bool_t
2622e92ac56SJamie Gritton rpc_gss_set_svc_name(const char *principal, const char *mechanism,
2632e92ac56SJamie Gritton     u_int req_time, u_int program, u_int version)
2642e92ac56SJamie Gritton {
2652e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
2662e92ac56SJamie Gritton 	gss_OID			mech_oid;
2672e92ac56SJamie Gritton 
2682e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
2692e92ac56SJamie Gritton 		return (FALSE);
2702e92ac56SJamie Gritton 
2712e92ac56SJamie Gritton 	sname = mem_alloc(sizeof(*sname));
2722e92ac56SJamie Gritton 	if (!sname)
2732e92ac56SJamie Gritton 		return (FALSE);
2742e92ac56SJamie Gritton 	sname->sn_principal = strdup(principal, M_RPC);
2752e92ac56SJamie Gritton 	sname->sn_mech = mech_oid;
2762e92ac56SJamie Gritton 	sname->sn_req_time = req_time;
2772e92ac56SJamie Gritton 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
2782e92ac56SJamie Gritton 	sname->sn_program = program;
2792e92ac56SJamie Gritton 	sname->sn_version = version;
2802e92ac56SJamie Gritton 
2812e92ac56SJamie Gritton 	if (!rpc_gss_acquire_svc_cred(sname)) {
2822e92ac56SJamie Gritton 		free(sname->sn_principal, M_RPC);
2832e92ac56SJamie Gritton 		mem_free(sname, sizeof(*sname));
2842e92ac56SJamie Gritton 		return (FALSE);
2852e92ac56SJamie Gritton 	}
2862e92ac56SJamie Gritton 
2872e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2882e92ac56SJamie Gritton 	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
2892e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2902e92ac56SJamie Gritton 
2912e92ac56SJamie Gritton 	return (TRUE);
2922e92ac56SJamie Gritton }
2932e92ac56SJamie Gritton 
2942e92ac56SJamie Gritton void
2952e92ac56SJamie Gritton rpc_gss_clear_svc_name(u_int program, u_int version)
2962e92ac56SJamie Gritton {
2972e92ac56SJamie Gritton 	OM_uint32		min_stat;
2982e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
2992e92ac56SJamie Gritton 
3002e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
3012e92ac56SJamie Gritton 	SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
3022e92ac56SJamie Gritton 		if (sname->sn_program == program
3032e92ac56SJamie Gritton 		    && sname->sn_version == version) {
3042e92ac56SJamie Gritton 			SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
3052e92ac56SJamie Gritton 			    svc_rpc_gss_svc_name, sn_link);
3062e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
3072e92ac56SJamie Gritton 			gss_release_cred(&min_stat, &sname->sn_cred);
3082e92ac56SJamie Gritton 			free(sname->sn_principal, M_RPC);
3092e92ac56SJamie Gritton 			mem_free(sname, sizeof(*sname));
3102e92ac56SJamie Gritton 			return;
3112e92ac56SJamie Gritton 		}
3122e92ac56SJamie Gritton 	}
3132e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
3142e92ac56SJamie Gritton }
3152e92ac56SJamie Gritton 
3162e92ac56SJamie Gritton bool_t
3172e92ac56SJamie Gritton rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
3182e92ac56SJamie Gritton     const char *mech, const char *name, const char *node, const char *domain)
3192e92ac56SJamie Gritton {
3202e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
3212e92ac56SJamie Gritton 	gss_OID			mech_oid;
3222e92ac56SJamie Gritton 	size_t			namelen;
3232e92ac56SJamie Gritton 	gss_buffer_desc		buf;
3242e92ac56SJamie Gritton 	gss_name_t		gss_name, gss_mech_name;
3252e92ac56SJamie Gritton 	rpc_gss_principal_t	result;
3262e92ac56SJamie Gritton 
3272e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
3282e92ac56SJamie Gritton 		return (FALSE);
3292e92ac56SJamie Gritton 
3302e92ac56SJamie Gritton 	/*
3312e92ac56SJamie Gritton 	 * Construct a gss_buffer containing the full name formatted
3322e92ac56SJamie Gritton 	 * as "name/node@domain" where node and domain are optional.
3332e92ac56SJamie Gritton 	 */
3342e92ac56SJamie Gritton 	namelen = strlen(name);
3352e92ac56SJamie Gritton 	if (node) {
3362e92ac56SJamie Gritton 		namelen += strlen(node) + 1;
3372e92ac56SJamie Gritton 	}
3382e92ac56SJamie Gritton 	if (domain) {
3392e92ac56SJamie Gritton 		namelen += strlen(domain) + 1;
3402e92ac56SJamie Gritton 	}
3412e92ac56SJamie Gritton 
3422e92ac56SJamie Gritton 	buf.value = mem_alloc(namelen);
3432e92ac56SJamie Gritton 	buf.length = namelen;
3442e92ac56SJamie Gritton 	strcpy((char *) buf.value, name);
3452e92ac56SJamie Gritton 	if (node) {
3462e92ac56SJamie Gritton 		strcat((char *) buf.value, "/");
3472e92ac56SJamie Gritton 		strcat((char *) buf.value, node);
3482e92ac56SJamie Gritton 	}
3492e92ac56SJamie Gritton 	if (domain) {
3502e92ac56SJamie Gritton 		strcat((char *) buf.value, "@");
3512e92ac56SJamie Gritton 		strcat((char *) buf.value, domain);
3522e92ac56SJamie Gritton 	}
3532e92ac56SJamie Gritton 
3542e92ac56SJamie Gritton 	/*
3552e92ac56SJamie Gritton 	 * Convert that to a gss_name_t and then convert that to a
3562e92ac56SJamie Gritton 	 * mechanism name in the selected mechanism.
3572e92ac56SJamie Gritton 	 */
3582e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &buf,
3592e92ac56SJamie Gritton 	    GSS_C_NT_USER_NAME, &gss_name);
3602e92ac56SJamie Gritton 	mem_free(buf.value, buf.length);
3612e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
3622e92ac56SJamie Gritton 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
3632e92ac56SJamie Gritton 		return (FALSE);
3642e92ac56SJamie Gritton 	}
3652e92ac56SJamie Gritton 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
3662e92ac56SJamie Gritton 	    &gss_mech_name);
3672e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
3682e92ac56SJamie Gritton 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
3692e92ac56SJamie Gritton 		    min_stat);
3702e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_name);
3712e92ac56SJamie Gritton 		return (FALSE);
3722e92ac56SJamie Gritton 	}
3732e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_name);
3742e92ac56SJamie Gritton 
3752e92ac56SJamie Gritton 	/*
3762e92ac56SJamie Gritton 	 * Export the mechanism name and use that to construct the
3772e92ac56SJamie Gritton 	 * rpc_gss_principal_t result.
3782e92ac56SJamie Gritton 	 */
3792e92ac56SJamie Gritton 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
3802e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
3812e92ac56SJamie Gritton 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
3822e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_mech_name);
3832e92ac56SJamie Gritton 		return (FALSE);
3842e92ac56SJamie Gritton 	}
3852e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_mech_name);
3862e92ac56SJamie Gritton 
3872e92ac56SJamie Gritton 	result = mem_alloc(sizeof(int) + buf.length);
3882e92ac56SJamie Gritton 	if (!result) {
3892e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &buf);
3902e92ac56SJamie Gritton 		return (FALSE);
3912e92ac56SJamie Gritton 	}
3922e92ac56SJamie Gritton 	result->len = buf.length;
3932e92ac56SJamie Gritton 	memcpy(result->name, buf.value, buf.length);
3942e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &buf);
3952e92ac56SJamie Gritton 
3962e92ac56SJamie Gritton 	*principal = result;
3972e92ac56SJamie Gritton 	return (TRUE);
3982e92ac56SJamie Gritton }
3992e92ac56SJamie Gritton 
4002e92ac56SJamie Gritton bool_t
4012e92ac56SJamie Gritton rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
4022e92ac56SJamie Gritton     rpc_gss_ucred_t **ucred, void **cookie)
4032e92ac56SJamie Gritton {
4042e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
4052e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
4062e92ac56SJamie Gritton 
4072e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
4082e92ac56SJamie Gritton 		return (FALSE);
4092e92ac56SJamie Gritton 
4102e92ac56SJamie Gritton 	cc = req->rq_clntcred;
4112e92ac56SJamie Gritton 	client = cc->cc_client;
4122e92ac56SJamie Gritton 	if (rcred)
4132e92ac56SJamie Gritton 		*rcred = &client->cl_rawcred;
4142e92ac56SJamie Gritton 	if (ucred)
4152e92ac56SJamie Gritton 		*ucred = &client->cl_ucred;
4162e92ac56SJamie Gritton 	if (cookie)
4172e92ac56SJamie Gritton 		*cookie = client->cl_cookie;
4182e92ac56SJamie Gritton 	return (TRUE);
4192e92ac56SJamie Gritton }
4202e92ac56SJamie Gritton 
4212e92ac56SJamie Gritton /*
4222e92ac56SJamie Gritton  * This simpler interface is used by svc_getcred to copy the cred data
4232e92ac56SJamie Gritton  * into a kernel cred structure.
424a9148abdSDoug Rabson  */
425a9148abdSDoug Rabson static int
4262e92ac56SJamie Gritton rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
427a9148abdSDoug Rabson {
4282e92ac56SJamie Gritton 	struct ucred *cr;
4292e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
4302e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
4312e92ac56SJamie Gritton 	rpc_gss_ucred_t *uc;
432a9148abdSDoug Rabson 
4332e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
4342e92ac56SJamie Gritton 		return (FALSE);
4352e92ac56SJamie Gritton 
4362e92ac56SJamie Gritton 	cc = req->rq_clntcred;
4372e92ac56SJamie Gritton 	client = cc->cc_client;
4382e92ac56SJamie Gritton 
4392e92ac56SJamie Gritton 	if (flavorp)
4402e92ac56SJamie Gritton 		*flavorp = client->cl_rpcflavor;
4412e92ac56SJamie Gritton 
4422e92ac56SJamie Gritton 	if (client->cl_cred) {
4432e92ac56SJamie Gritton 		*crp = crhold(client->cl_cred);
4442e92ac56SJamie Gritton 		return (TRUE);
445a9148abdSDoug Rabson 	}
446a9148abdSDoug Rabson 
4472e92ac56SJamie Gritton 	uc = &client->cl_ucred;
4482e92ac56SJamie Gritton 	cr = client->cl_cred = crget();
4492e92ac56SJamie Gritton 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
4502e92ac56SJamie Gritton 	cr->cr_rgid = cr->cr_svgid = uc->gid;
4512e92ac56SJamie Gritton 	crsetgroups(cr, uc->gidlen, uc->gidlist);
452c408f06bSJamie Gritton 	cr->cr_prison = &prison0;
453c408f06bSJamie Gritton 	prison_hold(cr->cr_prison);
4542e92ac56SJamie Gritton 	*crp = crhold(cr);
4552e92ac56SJamie Gritton 
4562e92ac56SJamie Gritton 	return (TRUE);
457a9148abdSDoug Rabson }
4582e92ac56SJamie Gritton 
4592e92ac56SJamie Gritton int
4602e92ac56SJamie Gritton rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
4612e92ac56SJamie Gritton {
4622e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
4632e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client = cc->cc_client;
4642e92ac56SJamie Gritton 	int			want_conf;
4652e92ac56SJamie Gritton 	OM_uint32		max;
4662e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
4672e92ac56SJamie Gritton 	int			result;
4682e92ac56SJamie Gritton 
4692e92ac56SJamie Gritton 	switch (client->cl_rawcred.service) {
4702e92ac56SJamie Gritton 	case rpc_gss_svc_none:
4712e92ac56SJamie Gritton 		return (max_tp_unit_len);
4722e92ac56SJamie Gritton 		break;
4732e92ac56SJamie Gritton 
4742e92ac56SJamie Gritton 	case rpc_gss_svc_default:
4752e92ac56SJamie Gritton 	case rpc_gss_svc_integrity:
4762e92ac56SJamie Gritton 		want_conf = FALSE;
4772e92ac56SJamie Gritton 		break;
4782e92ac56SJamie Gritton 
4792e92ac56SJamie Gritton 	case rpc_gss_svc_privacy:
4802e92ac56SJamie Gritton 		want_conf = TRUE;
4812e92ac56SJamie Gritton 		break;
4822e92ac56SJamie Gritton 
4832e92ac56SJamie Gritton 	default:
484a9148abdSDoug Rabson 		return (0);
485a9148abdSDoug Rabson 	}
486a9148abdSDoug Rabson 
4872e92ac56SJamie Gritton 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
4882e92ac56SJamie Gritton 	    client->cl_qop, max_tp_unit_len, &max);
4892e92ac56SJamie Gritton 
4902e92ac56SJamie Gritton 	if (maj_stat == GSS_S_COMPLETE) {
4912e92ac56SJamie Gritton 		result = (int) max;
4922e92ac56SJamie Gritton 		if (result < 0)
4932e92ac56SJamie Gritton 			result = 0;
4942e92ac56SJamie Gritton 		return (result);
4952e92ac56SJamie Gritton 	} else {
4962e92ac56SJamie Gritton 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
4972e92ac56SJamie Gritton 		    maj_stat, min_stat);
4982e92ac56SJamie Gritton 		return (0);
4992e92ac56SJamie Gritton 	}
5002e92ac56SJamie Gritton }
5012e92ac56SJamie Gritton 
5022e92ac56SJamie Gritton static struct svc_rpc_gss_client *
5032e92ac56SJamie Gritton svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
5042e92ac56SJamie Gritton {
5052e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5062e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
5072e92ac56SJamie Gritton 	unsigned long hostid;
5082e92ac56SJamie Gritton 
5092e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
5102e92ac56SJamie Gritton 
5112e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
5122e92ac56SJamie Gritton 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
5132e92ac56SJamie Gritton 		return (NULL);
5142e92ac56SJamie Gritton 
5152e92ac56SJamie Gritton 	list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
5162e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
5172e92ac56SJamie Gritton 	TAILQ_FOREACH(client, list, cl_link) {
5182e92ac56SJamie Gritton 		if (client->cl_id.ci_id == id->ci_id) {
5192e92ac56SJamie Gritton 			/*
5202e92ac56SJamie Gritton 			 * Move this client to the front of the LRU
5212e92ac56SJamie Gritton 			 * list.
5222e92ac56SJamie Gritton 			 */
5232e92ac56SJamie Gritton 			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
5242e92ac56SJamie Gritton 			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
5252e92ac56SJamie Gritton 			    cl_alllink);
5262e92ac56SJamie Gritton 			refcount_acquire(&client->cl_refs);
5272e92ac56SJamie Gritton 			break;
5282e92ac56SJamie Gritton 		}
5292e92ac56SJamie Gritton 	}
5302e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
5312e92ac56SJamie Gritton 
5322e92ac56SJamie Gritton 	return (client);
5332e92ac56SJamie Gritton }
5342e92ac56SJamie Gritton 
5352e92ac56SJamie Gritton static struct svc_rpc_gss_client *
5362e92ac56SJamie Gritton svc_rpc_gss_create_client(void)
5372e92ac56SJamie Gritton {
5382e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5392e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
5402e92ac56SJamie Gritton 	unsigned long hostid;
5412e92ac56SJamie Gritton 
5422e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
5432e92ac56SJamie Gritton 
5442e92ac56SJamie Gritton 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
5452e92ac56SJamie Gritton 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
5462e92ac56SJamie Gritton 	refcount_init(&client->cl_refs, 1);
5472e92ac56SJamie Gritton 	sx_init(&client->cl_lock, "GSS-client");
5482e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
5492e92ac56SJamie Gritton 	client->cl_id.ci_hostid = hostid;
5502e92ac56SJamie Gritton 	client->cl_id.ci_boottime = boottime.tv_sec;
5512e92ac56SJamie Gritton 	client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
5522e92ac56SJamie Gritton 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
5532e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
5542e92ac56SJamie Gritton 	TAILQ_INSERT_HEAD(list, client, cl_link);
5552e92ac56SJamie Gritton 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
5562e92ac56SJamie Gritton 	svc_rpc_gss_client_count++;
5572e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
5582e92ac56SJamie Gritton 
5592e92ac56SJamie Gritton 	/*
5602e92ac56SJamie Gritton 	 * Start the client off with a short expiration time. We will
5612e92ac56SJamie Gritton 	 * try to get a saner value from the client creds later.
5622e92ac56SJamie Gritton 	 */
5632e92ac56SJamie Gritton 	client->cl_state = CLIENT_NEW;
5642e92ac56SJamie Gritton 	client->cl_locked = FALSE;
5652e92ac56SJamie Gritton 	client->cl_expiration = time_uptime + 5*60;
5662e92ac56SJamie Gritton 
5672e92ac56SJamie Gritton 	return (client);
5682e92ac56SJamie Gritton }
5692e92ac56SJamie Gritton 
5702e92ac56SJamie Gritton static void
5712e92ac56SJamie Gritton svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
5722e92ac56SJamie Gritton {
5732e92ac56SJamie Gritton 	OM_uint32 min_stat;
5742e92ac56SJamie Gritton 
5752e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
5762e92ac56SJamie Gritton 
5772e92ac56SJamie Gritton 	if (client->cl_ctx)
5782e92ac56SJamie Gritton 		gss_delete_sec_context(&min_stat,
5792e92ac56SJamie Gritton 		    &client->cl_ctx, GSS_C_NO_BUFFER);
5802e92ac56SJamie Gritton 
5812e92ac56SJamie Gritton 	if (client->cl_cname)
5822e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
5832e92ac56SJamie Gritton 
5842e92ac56SJamie Gritton 	if (client->cl_rawcred.client_principal)
5852e92ac56SJamie Gritton 		mem_free(client->cl_rawcred.client_principal,
5862e92ac56SJamie Gritton 		    sizeof(*client->cl_rawcred.client_principal)
5872e92ac56SJamie Gritton 		    + client->cl_rawcred.client_principal->len);
5882e92ac56SJamie Gritton 
5892e92ac56SJamie Gritton 	if (client->cl_cred)
5902e92ac56SJamie Gritton 		crfree(client->cl_cred);
5912e92ac56SJamie Gritton 
5922e92ac56SJamie Gritton 	sx_destroy(&client->cl_lock);
5932e92ac56SJamie Gritton 	mem_free(client, sizeof(*client));
5942e92ac56SJamie Gritton }
5952e92ac56SJamie Gritton 
5962e92ac56SJamie Gritton /*
5972e92ac56SJamie Gritton  * Drop a reference to a client and free it if that was the last reference.
5982e92ac56SJamie Gritton  */
5992e92ac56SJamie Gritton static void
6002e92ac56SJamie Gritton svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
6012e92ac56SJamie Gritton {
6022e92ac56SJamie Gritton 
6032e92ac56SJamie Gritton 	if (!refcount_release(&client->cl_refs))
6042e92ac56SJamie Gritton 		return;
6052e92ac56SJamie Gritton 	svc_rpc_gss_destroy_client(client);
6062e92ac56SJamie Gritton }
6072e92ac56SJamie Gritton 
6082e92ac56SJamie Gritton /*
6092e92ac56SJamie Gritton  * Remove a client from our global lists and free it if we can.
6102e92ac56SJamie Gritton  */
6112e92ac56SJamie Gritton static void
6122e92ac56SJamie Gritton svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
6132e92ac56SJamie Gritton {
6142e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
6152e92ac56SJamie Gritton 
6162e92ac56SJamie Gritton 	list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
6172e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
6182e92ac56SJamie Gritton 	TAILQ_REMOVE(list, client, cl_link);
6192e92ac56SJamie Gritton 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
6202e92ac56SJamie Gritton 	svc_rpc_gss_client_count--;
6212e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
6222e92ac56SJamie Gritton 	svc_rpc_gss_release_client(client);
6232e92ac56SJamie Gritton }
6242e92ac56SJamie Gritton 
6252e92ac56SJamie Gritton static void
6262e92ac56SJamie Gritton svc_rpc_gss_timeout_clients(void)
6272e92ac56SJamie Gritton {
6282e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
6292e92ac56SJamie Gritton 	struct svc_rpc_gss_client *nclient;
6302e92ac56SJamie Gritton 	time_t now = time_uptime;
6312e92ac56SJamie Gritton 
6322e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
6332e92ac56SJamie Gritton 
6342e92ac56SJamie Gritton 	/*
6352e92ac56SJamie Gritton 	 * First enforce the max client limit. We keep
6362e92ac56SJamie Gritton 	 * svc_rpc_gss_clients in LRU order.
6372e92ac56SJamie Gritton 	 */
6382e92ac56SJamie Gritton 	while (svc_rpc_gss_client_count > CLIENT_MAX)
6392e92ac56SJamie Gritton 		svc_rpc_gss_forget_client(TAILQ_LAST(&svc_rpc_gss_clients,
6402e92ac56SJamie Gritton 			    svc_rpc_gss_client_list));
6412e92ac56SJamie Gritton 	TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
6422e92ac56SJamie Gritton 		if (client->cl_state == CLIENT_STALE
6432e92ac56SJamie Gritton 		    || now > client->cl_expiration) {
6442e92ac56SJamie Gritton 			rpc_gss_log_debug("expiring client %p", client);
6452e92ac56SJamie Gritton 			svc_rpc_gss_forget_client(client);
6462e92ac56SJamie Gritton 		}
6472e92ac56SJamie Gritton 	}
6482e92ac56SJamie Gritton }
6492e92ac56SJamie Gritton 
6502e92ac56SJamie Gritton #ifdef DEBUG
6512e92ac56SJamie Gritton /*
6522e92ac56SJamie Gritton  * OID<->string routines.  These are uuuuugly.
6532e92ac56SJamie Gritton  */
6542e92ac56SJamie Gritton static OM_uint32
6552e92ac56SJamie Gritton gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
6562e92ac56SJamie Gritton {
6572e92ac56SJamie Gritton 	char		numstr[128];
6582e92ac56SJamie Gritton 	unsigned long	number;
6592e92ac56SJamie Gritton 	int		numshift;
6602e92ac56SJamie Gritton 	size_t		string_length;
6612e92ac56SJamie Gritton 	size_t		i;
6622e92ac56SJamie Gritton 	unsigned char	*cp;
6632e92ac56SJamie Gritton 	char		*bp;
6642e92ac56SJamie Gritton 
6652e92ac56SJamie Gritton 	/* Decoded according to krb5/gssapi_krb5.c */
6662e92ac56SJamie Gritton 
6672e92ac56SJamie Gritton 	/* First determine the size of the string */
6682e92ac56SJamie Gritton 	string_length = 0;
6692e92ac56SJamie Gritton 	number = 0;
6702e92ac56SJamie Gritton 	numshift = 0;
6712e92ac56SJamie Gritton 	cp = (unsigned char *) oid->elements;
6722e92ac56SJamie Gritton 	number = (unsigned long) cp[0];
6732e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number/40);
6742e92ac56SJamie Gritton 	string_length += strlen(numstr);
6752e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number%40);
6762e92ac56SJamie Gritton 	string_length += strlen(numstr);
6772e92ac56SJamie Gritton 	for (i=1; i<oid->length; i++) {
6782e92ac56SJamie Gritton 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
6792e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
6802e92ac56SJamie Gritton 			numshift += 7;
6812e92ac56SJamie Gritton 		}
6822e92ac56SJamie Gritton 		else {
6832e92ac56SJamie Gritton 			*minor_status = 0;
6842e92ac56SJamie Gritton 			return(GSS_S_FAILURE);
6852e92ac56SJamie Gritton 		}
6862e92ac56SJamie Gritton 		if ((cp[i] & 0x80) == 0) {
6872e92ac56SJamie Gritton 			sprintf(numstr, "%ld ", number);
6882e92ac56SJamie Gritton 			string_length += strlen(numstr);
6892e92ac56SJamie Gritton 			number = 0;
6902e92ac56SJamie Gritton 			numshift = 0;
6912e92ac56SJamie Gritton 		}
6922e92ac56SJamie Gritton 	}
6932e92ac56SJamie Gritton 	/*
6942e92ac56SJamie Gritton 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
6952e92ac56SJamie Gritton 	 * here for "{ " and "}\0".
6962e92ac56SJamie Gritton 	 */
6972e92ac56SJamie Gritton 	string_length += 4;
6982e92ac56SJamie Gritton 	if ((bp = (char *) mem_alloc(string_length))) {
6992e92ac56SJamie Gritton 		strcpy(bp, "{ ");
7002e92ac56SJamie Gritton 		number = (unsigned long) cp[0];
7012e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number/40);
7022e92ac56SJamie Gritton 		strcat(bp, numstr);
7032e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number%40);
7042e92ac56SJamie Gritton 		strcat(bp, numstr);
7052e92ac56SJamie Gritton 		number = 0;
7062e92ac56SJamie Gritton 		cp = (unsigned char *) oid->elements;
7072e92ac56SJamie Gritton 		for (i=1; i<oid->length; i++) {
7082e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
7092e92ac56SJamie Gritton 			if ((cp[i] & 0x80) == 0) {
7102e92ac56SJamie Gritton 				sprintf(numstr, "%ld ", number);
7112e92ac56SJamie Gritton 				strcat(bp, numstr);
7122e92ac56SJamie Gritton 				number = 0;
7132e92ac56SJamie Gritton 			}
7142e92ac56SJamie Gritton 		}
7152e92ac56SJamie Gritton 		strcat(bp, "}");
7162e92ac56SJamie Gritton 		oid_str->length = strlen(bp)+1;
7172e92ac56SJamie Gritton 		oid_str->value = (void *) bp;
7182e92ac56SJamie Gritton 		*minor_status = 0;
7192e92ac56SJamie Gritton 		return(GSS_S_COMPLETE);
7202e92ac56SJamie Gritton 	}
7212e92ac56SJamie Gritton 	*minor_status = 0;
7222e92ac56SJamie Gritton 	return(GSS_S_FAILURE);
723a9148abdSDoug Rabson }
724a9148abdSDoug Rabson #endif
725a9148abdSDoug Rabson 
726a9148abdSDoug Rabson static void
7272e92ac56SJamie Gritton svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
7282e92ac56SJamie Gritton     const gss_name_t name)
729a9148abdSDoug Rabson {
7302e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
7312e92ac56SJamie Gritton 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
7322e92ac56SJamie Gritton 	int			numgroups;
733a9148abdSDoug Rabson 
7342e92ac56SJamie Gritton 	uc->uid = 65534;
7352e92ac56SJamie Gritton 	uc->gid = 65534;
7362e92ac56SJamie Gritton 	uc->gidlist = client->cl_gid_storage;
737d4468577SJamie Gritton 
7382e92ac56SJamie Gritton 	numgroups = NGROUPS;
7392e92ac56SJamie Gritton 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
7402e92ac56SJamie Gritton 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
7412e92ac56SJamie Gritton 	if (GSS_ERROR(maj_stat))
7422e92ac56SJamie Gritton 		uc->gidlen = 0;
7432e92ac56SJamie Gritton 	else
7442e92ac56SJamie Gritton 		uc->gidlen = numgroups;
745d4468577SJamie Gritton }
746d4468577SJamie Gritton 
7472e92ac56SJamie Gritton static void
7482e92ac56SJamie Gritton svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
749a9148abdSDoug Rabson {
7502e92ac56SJamie Gritton 	static gss_OID_desc krb5_mech_oid =
7512e92ac56SJamie Gritton 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
752a9148abdSDoug Rabson 
753d4468577SJamie Gritton 	/*
7542e92ac56SJamie Gritton 	 * Attempt to translate mech type and service into a
7552e92ac56SJamie Gritton 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
756d4468577SJamie Gritton 	 */
7572e92ac56SJamie Gritton 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
7582e92ac56SJamie Gritton 		switch (client->cl_rawcred.service) {
7592e92ac56SJamie Gritton 		case rpc_gss_svc_default:
7602e92ac56SJamie Gritton 		case rpc_gss_svc_none:
7612e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
7622e92ac56SJamie Gritton 			break;
7632e92ac56SJamie Gritton 		case rpc_gss_svc_integrity:
7642e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
7652e92ac56SJamie Gritton 			break;
7662e92ac56SJamie Gritton 		case rpc_gss_svc_privacy:
7672e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
7682e92ac56SJamie Gritton 			break;
7692e92ac56SJamie Gritton 		}
7702e92ac56SJamie Gritton 	} else {
7712e92ac56SJamie Gritton 		client->cl_rpcflavor = RPCSEC_GSS;
7722e92ac56SJamie Gritton 	}
7732e92ac56SJamie Gritton }
7742e92ac56SJamie Gritton 
7752e92ac56SJamie Gritton static bool_t
7762e92ac56SJamie Gritton svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
7772e92ac56SJamie Gritton 			       struct svc_req *rqst,
7782e92ac56SJamie Gritton 			       struct rpc_gss_init_res *gr,
7792e92ac56SJamie Gritton 			       struct rpc_gss_cred *gc)
780a9148abdSDoug Rabson {
7812e92ac56SJamie Gritton 	gss_buffer_desc		recv_tok;
7822e92ac56SJamie Gritton 	gss_OID			mech;
7832e92ac56SJamie Gritton 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
7842e92ac56SJamie Gritton 	OM_uint32		cred_lifetime;
7852e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
786a9148abdSDoug Rabson 
7872e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
7882e92ac56SJamie Gritton 
7892e92ac56SJamie Gritton 	/* Deserialize arguments. */
7902e92ac56SJamie Gritton 	memset(&recv_tok, 0, sizeof(recv_tok));
7912e92ac56SJamie Gritton 
7922e92ac56SJamie Gritton 	if (!svc_getargs(rqst,
7932e92ac56SJamie Gritton 		(xdrproc_t) xdr_gss_buffer_desc,
7942e92ac56SJamie Gritton 		(caddr_t) &recv_tok)) {
7952e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
7962e92ac56SJamie Gritton 		return (FALSE);
797a9148abdSDoug Rabson 	}
798a9148abdSDoug Rabson 
799d4468577SJamie Gritton 	/*
8002e92ac56SJamie Gritton 	 * First time round, try all the server names we have until
8012e92ac56SJamie Gritton 	 * one matches. Afterwards, stick with that one.
802d4468577SJamie Gritton 	 */
8032e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
8042e92ac56SJamie Gritton 	if (!client->cl_sname) {
8052e92ac56SJamie Gritton 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
8062e92ac56SJamie Gritton 			if (sname->sn_program == rqst->rq_prog
8072e92ac56SJamie Gritton 			    && sname->sn_version == rqst->rq_vers) {
8082e92ac56SJamie Gritton 			retry:
8092e92ac56SJamie Gritton 				gr->gr_major = gss_accept_sec_context(
8102e92ac56SJamie Gritton 					&gr->gr_minor,
8112e92ac56SJamie Gritton 					&client->cl_ctx,
8122e92ac56SJamie Gritton 					sname->sn_cred,
8132e92ac56SJamie Gritton 					&recv_tok,
8142e92ac56SJamie Gritton 					GSS_C_NO_CHANNEL_BINDINGS,
8152e92ac56SJamie Gritton 					&client->cl_cname,
8162e92ac56SJamie Gritton 					&mech,
8172e92ac56SJamie Gritton 					&gr->gr_token,
8182e92ac56SJamie Gritton 					&ret_flags,
8192e92ac56SJamie Gritton 					&cred_lifetime,
8202e92ac56SJamie Gritton 					&client->cl_creds);
8212e92ac56SJamie Gritton 				if (gr->gr_major ==
8222e92ac56SJamie Gritton 				    GSS_S_CREDENTIALS_EXPIRED) {
823d4468577SJamie Gritton 					/*
8242e92ac56SJamie Gritton 					 * Either our creds really did
8252e92ac56SJamie Gritton 					 * expire or gssd was
8262e92ac56SJamie Gritton 					 * restarted.
827d4468577SJamie Gritton 					 */
8282e92ac56SJamie Gritton 					if (rpc_gss_acquire_svc_cred(sname))
8292e92ac56SJamie Gritton 						goto retry;
8302e92ac56SJamie Gritton 				}
8312e92ac56SJamie Gritton 				client->cl_sname = sname;
832d4468577SJamie Gritton 				break;
833d4468577SJamie Gritton 			}
834d4468577SJamie Gritton 		}
8352e92ac56SJamie Gritton 		if (!sname) {
8362e92ac56SJamie Gritton 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
8372e92ac56SJamie Gritton 			    (char *) &recv_tok);
8382e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
8392e92ac56SJamie Gritton 			return (FALSE);
840d4468577SJamie Gritton 		}
8412e92ac56SJamie Gritton 	} else {
8422e92ac56SJamie Gritton 		gr->gr_major = gss_accept_sec_context(
8432e92ac56SJamie Gritton 			&gr->gr_minor,
8442e92ac56SJamie Gritton 			&client->cl_ctx,
8452e92ac56SJamie Gritton 			client->cl_sname->sn_cred,
8462e92ac56SJamie Gritton 			&recv_tok,
8472e92ac56SJamie Gritton 			GSS_C_NO_CHANNEL_BINDINGS,
8482e92ac56SJamie Gritton 			&client->cl_cname,
8492e92ac56SJamie Gritton 			&mech,
8502e92ac56SJamie Gritton 			&gr->gr_token,
8512e92ac56SJamie Gritton 			&ret_flags,
8522e92ac56SJamie Gritton 			&cred_lifetime,
8532e92ac56SJamie Gritton 			NULL);
854d4468577SJamie Gritton 	}
8552e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
856a9148abdSDoug Rabson 
8572e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
858d4468577SJamie Gritton 
859d4468577SJamie Gritton 	/*
8602e92ac56SJamie Gritton 	 * If we get an error from gss_accept_sec_context, send the
8612e92ac56SJamie Gritton 	 * reply anyway so that the client gets a chance to see what
8622e92ac56SJamie Gritton 	 * is wrong.
863d4468577SJamie Gritton 	 */
8642e92ac56SJamie Gritton 	if (gr->gr_major != GSS_S_COMPLETE &&
8652e92ac56SJamie Gritton 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
8662e92ac56SJamie Gritton 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
8672e92ac56SJamie Gritton 		    gr->gr_major, gr->gr_minor);
8682e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
8692e92ac56SJamie Gritton 		return (TRUE);
8702e92ac56SJamie Gritton 	}
8712e92ac56SJamie Gritton 
8722e92ac56SJamie Gritton 	gr->gr_handle.value = &client->cl_id;
8732e92ac56SJamie Gritton 	gr->gr_handle.length = sizeof(client->cl_id);
8742e92ac56SJamie Gritton 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
8752e92ac56SJamie Gritton 
8762e92ac56SJamie Gritton 	/* Save client info. */
8772e92ac56SJamie Gritton 	client->cl_mech = mech;
8782e92ac56SJamie Gritton 	client->cl_qop = GSS_C_QOP_DEFAULT;
8792e92ac56SJamie Gritton 	client->cl_done_callback = FALSE;
8802e92ac56SJamie Gritton 
8812e92ac56SJamie Gritton 	if (gr->gr_major == GSS_S_COMPLETE) {
8822e92ac56SJamie Gritton 		gss_buffer_desc	export_name;
8832e92ac56SJamie Gritton 
8842e92ac56SJamie Gritton 		/*
8852e92ac56SJamie Gritton 		 * Change client expiration time to be near when the
8862e92ac56SJamie Gritton 		 * client creds expire (or 24 hours if we can't figure
8872e92ac56SJamie Gritton 		 * that out).
8882e92ac56SJamie Gritton 		 */
8892e92ac56SJamie Gritton 		if (cred_lifetime == GSS_C_INDEFINITE)
8902e92ac56SJamie Gritton 			cred_lifetime = time_uptime + 24*60*60;
8912e92ac56SJamie Gritton 
8922e92ac56SJamie Gritton 		client->cl_expiration = time_uptime + cred_lifetime;
8932e92ac56SJamie Gritton 
8942e92ac56SJamie Gritton 		/*
8952e92ac56SJamie Gritton 		 * Fill in cred details in the rawcred structure.
8962e92ac56SJamie Gritton 		 */
8972e92ac56SJamie Gritton 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
8982e92ac56SJamie Gritton 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
8992e92ac56SJamie Gritton 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
9002e92ac56SJamie Gritton 		    &export_name);
9012e92ac56SJamie Gritton 		if (maj_stat != GSS_S_COMPLETE) {
9022e92ac56SJamie Gritton 			rpc_gss_log_status("gss_export_name", client->cl_mech,
9032e92ac56SJamie Gritton 			    maj_stat, min_stat);
9042e92ac56SJamie Gritton 			return (FALSE);
9052e92ac56SJamie Gritton 		}
9062e92ac56SJamie Gritton 		client->cl_rawcred.client_principal =
9072e92ac56SJamie Gritton 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
9082e92ac56SJamie Gritton 			    + export_name.length);
9092e92ac56SJamie Gritton 		client->cl_rawcred.client_principal->len = export_name.length;
9102e92ac56SJamie Gritton 		memcpy(client->cl_rawcred.client_principal->name,
9112e92ac56SJamie Gritton 		    export_name.value, export_name.length);
9122e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &export_name);
9132e92ac56SJamie Gritton 		client->cl_rawcred.svc_principal =
9142e92ac56SJamie Gritton 			client->cl_sname->sn_principal;
9152e92ac56SJamie Gritton 		client->cl_rawcred.service = gc->gc_svc;
9162e92ac56SJamie Gritton 
9172e92ac56SJamie Gritton 		/*
9182e92ac56SJamie Gritton 		 * Use gss_pname_to_uid to map to unix creds. For
9192e92ac56SJamie Gritton 		 * kerberos5, this uses krb5_aname_to_localname.
9202e92ac56SJamie Gritton 		 */
9212e92ac56SJamie Gritton 		svc_rpc_gss_build_ucred(client, client->cl_cname);
9222e92ac56SJamie Gritton 		svc_rpc_gss_set_flavor(client);
9232e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
9242e92ac56SJamie Gritton 
9252e92ac56SJamie Gritton #ifdef DEBUG
926a9148abdSDoug Rabson 		{
9272e92ac56SJamie Gritton 			gss_buffer_desc mechname;
928a9148abdSDoug Rabson 
9292e92ac56SJamie Gritton 			gss_oid_to_str(&min_stat, mech, &mechname);
9302e92ac56SJamie Gritton 
9312e92ac56SJamie Gritton 			rpc_gss_log_debug("accepted context for %s with "
9322e92ac56SJamie Gritton 			    "<mech %.*s, qop %d, svc %d>",
9332e92ac56SJamie Gritton 			    client->cl_rawcred.client_principal->name,
9342e92ac56SJamie Gritton 			    mechname.length, (char *)mechname.value,
9352e92ac56SJamie Gritton 			    client->cl_qop, client->rawcred.service);
9362e92ac56SJamie Gritton 
9372e92ac56SJamie Gritton 			gss_release_buffer(&min_stat, &mechname);
938d4468577SJamie Gritton 		}
9392e92ac56SJamie Gritton #endif /* DEBUG */
940d4468577SJamie Gritton 	}
9412e92ac56SJamie Gritton 	return (TRUE);
942a9148abdSDoug Rabson }
943a9148abdSDoug Rabson 
9442e92ac56SJamie Gritton static bool_t
9452e92ac56SJamie Gritton svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
9462e92ac56SJamie Gritton     gss_qop_t *qop)
947a9148abdSDoug Rabson {
9482e92ac56SJamie Gritton 	struct opaque_auth	*oa;
9492e92ac56SJamie Gritton 	gss_buffer_desc		 rpcbuf, checksum;
9502e92ac56SJamie Gritton 	OM_uint32		 maj_stat, min_stat;
9512e92ac56SJamie Gritton 	gss_qop_t		 qop_state;
9522e92ac56SJamie Gritton 	int32_t			 rpchdr[128 / sizeof(int32_t)];
9532e92ac56SJamie Gritton 	int32_t			*buf;
954a9148abdSDoug Rabson 
9552e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
9562e92ac56SJamie Gritton 
9572e92ac56SJamie Gritton 	memset(rpchdr, 0, sizeof(rpchdr));
9582e92ac56SJamie Gritton 
9592e92ac56SJamie Gritton 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
9602e92ac56SJamie Gritton 	buf = rpchdr;
9612e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_xid);
9622e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, msg->rm_direction);
9632e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
9642e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
9652e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
9662e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
9672e92ac56SJamie Gritton 	oa = &msg->rm_call.cb_cred;
9682e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
9692e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, oa->oa_length);
9702e92ac56SJamie Gritton 	if (oa->oa_length) {
9712e92ac56SJamie Gritton 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
9722e92ac56SJamie Gritton 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
973a9148abdSDoug Rabson 	}
9742e92ac56SJamie Gritton 	rpcbuf.value = rpchdr;
9752e92ac56SJamie Gritton 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
9762e92ac56SJamie Gritton 
9772e92ac56SJamie Gritton 	checksum.value = msg->rm_call.cb_verf.oa_base;
9782e92ac56SJamie Gritton 	checksum.length = msg->rm_call.cb_verf.oa_length;
9792e92ac56SJamie Gritton 
9802e92ac56SJamie Gritton 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
9812e92ac56SJamie Gritton 				  &qop_state);
9822e92ac56SJamie Gritton 
9832e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
9842e92ac56SJamie Gritton 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
9852e92ac56SJamie Gritton 		    maj_stat, min_stat);
9862e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
9872e92ac56SJamie Gritton 		return (FALSE);
988d4468577SJamie Gritton 	}
989d4468577SJamie Gritton 
9902e92ac56SJamie Gritton 	*qop = qop_state;
9912e92ac56SJamie Gritton 	return (TRUE);
9922e92ac56SJamie Gritton }
9932e92ac56SJamie Gritton 
9942e92ac56SJamie Gritton static bool_t
9952e92ac56SJamie Gritton svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
9962e92ac56SJamie Gritton     struct svc_req *rqst, u_int seq)
9972e92ac56SJamie Gritton {
9982e92ac56SJamie Gritton 	gss_buffer_desc		signbuf;
9992e92ac56SJamie Gritton 	gss_buffer_desc		mic;
10002e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
10012e92ac56SJamie Gritton 	uint32_t		nseq;
10022e92ac56SJamie Gritton 
10032e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
10042e92ac56SJamie Gritton 
10052e92ac56SJamie Gritton 	nseq = htonl(seq);
10062e92ac56SJamie Gritton 	signbuf.value = &nseq;
10072e92ac56SJamie Gritton 	signbuf.length = sizeof(nseq);
10082e92ac56SJamie Gritton 
10092e92ac56SJamie Gritton 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
10102e92ac56SJamie Gritton 	    &signbuf, &mic);
10112e92ac56SJamie Gritton 
10122e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
10132e92ac56SJamie Gritton 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
10142e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
10152e92ac56SJamie Gritton 		return (FALSE);
10162e92ac56SJamie Gritton 	}
10172e92ac56SJamie Gritton 
10182e92ac56SJamie Gritton 	KASSERT(mic.length <= MAX_AUTH_BYTES,
10192e92ac56SJamie Gritton 	    ("MIC too large for RPCSEC_GSS"));
10202e92ac56SJamie Gritton 
10212e92ac56SJamie Gritton 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
10222e92ac56SJamie Gritton 	rqst->rq_verf.oa_length = mic.length;
10232e92ac56SJamie Gritton 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
10242e92ac56SJamie Gritton 
10252e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &mic);
10262e92ac56SJamie Gritton 
10272e92ac56SJamie Gritton 	return (TRUE);
10282e92ac56SJamie Gritton }
10292e92ac56SJamie Gritton 
10302e92ac56SJamie Gritton static bool_t
10312e92ac56SJamie Gritton svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
10322e92ac56SJamie Gritton {
10332e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
10342e92ac56SJamie Gritton 	rpc_gss_lock_t	lock;
10352e92ac56SJamie Gritton 	void		*cookie;
10362e92ac56SJamie Gritton 	bool_t		cb_res;
10372e92ac56SJamie Gritton 	bool_t		result;
10382e92ac56SJamie Gritton 
10392e92ac56SJamie Gritton 	/*
10402e92ac56SJamie Gritton 	 * See if we have a callback for this guy.
10412e92ac56SJamie Gritton 	 */
10422e92ac56SJamie Gritton 	result = TRUE;
10432e92ac56SJamie Gritton 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
10442e92ac56SJamie Gritton 		if (scb->cb_callback.program == rqst->rq_prog
10452e92ac56SJamie Gritton 		    && scb->cb_callback.version == rqst->rq_vers) {
10462e92ac56SJamie Gritton 			/*
10472e92ac56SJamie Gritton 			 * This one matches. Call the callback and see
10482e92ac56SJamie Gritton 			 * if it wants to veto or something.
10492e92ac56SJamie Gritton 			 */
10502e92ac56SJamie Gritton 			lock.locked = FALSE;
10512e92ac56SJamie Gritton 			lock.raw_cred = &client->cl_rawcred;
10522e92ac56SJamie Gritton 			cb_res = scb->cb_callback.callback(rqst,
10532e92ac56SJamie Gritton 			    client->cl_creds,
10542e92ac56SJamie Gritton 			    client->cl_ctx,
10552e92ac56SJamie Gritton 			    &lock,
10562e92ac56SJamie Gritton 			    &cookie);
10572e92ac56SJamie Gritton 
10582e92ac56SJamie Gritton 			if (!cb_res) {
10592e92ac56SJamie Gritton 				client->cl_state = CLIENT_STALE;
10602e92ac56SJamie Gritton 				result = FALSE;
10612e92ac56SJamie Gritton 				break;
10622e92ac56SJamie Gritton 			}
10632e92ac56SJamie Gritton 
10642e92ac56SJamie Gritton 			/*
10652e92ac56SJamie Gritton 			 * The callback accepted the connection - it
10662e92ac56SJamie Gritton 			 * is responsible for freeing client->cl_creds
10672e92ac56SJamie Gritton 			 * now.
10682e92ac56SJamie Gritton 			 */
10692e92ac56SJamie Gritton 			client->cl_creds = GSS_C_NO_CREDENTIAL;
10702e92ac56SJamie Gritton 			client->cl_locked = lock.locked;
10712e92ac56SJamie Gritton 			client->cl_cookie = cookie;
10722e92ac56SJamie Gritton 			return (TRUE);
10732e92ac56SJamie Gritton 		}
10742e92ac56SJamie Gritton 	}
10752e92ac56SJamie Gritton 
10762e92ac56SJamie Gritton 	/*
10772e92ac56SJamie Gritton 	 * Either no callback exists for this program/version or one
10782e92ac56SJamie Gritton 	 * of the callbacks rejected the connection. We just need to
10792e92ac56SJamie Gritton 	 * clean up the delegated client creds, if any.
10802e92ac56SJamie Gritton 	 */
10812e92ac56SJamie Gritton 	if (client->cl_creds) {
10822e92ac56SJamie Gritton 		OM_uint32 min_ver;
10832e92ac56SJamie Gritton 		gss_release_cred(&min_ver, &client->cl_creds);
10842e92ac56SJamie Gritton 	}
10852e92ac56SJamie Gritton 	return (result);
10862e92ac56SJamie Gritton }
10872e92ac56SJamie Gritton 
10882e92ac56SJamie Gritton static bool_t
10892e92ac56SJamie Gritton svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
10902e92ac56SJamie Gritton {
10912e92ac56SJamie Gritton 	u_int32_t offset;
10922e92ac56SJamie Gritton 	int word, bit;
10932e92ac56SJamie Gritton 	bool_t result;
10942e92ac56SJamie Gritton 
10952e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
10962e92ac56SJamie Gritton 	if (seq <= client->cl_seqlast) {
10972e92ac56SJamie Gritton 		/*
10982e92ac56SJamie Gritton 		 * The request sequence number is less than
10992e92ac56SJamie Gritton 		 * the largest we have seen so far. If it is
11002e92ac56SJamie Gritton 		 * outside the window or if we have seen a
11012e92ac56SJamie Gritton 		 * request with this sequence before, silently
11022e92ac56SJamie Gritton 		 * discard it.
11032e92ac56SJamie Gritton 		 */
11042e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
11052e92ac56SJamie Gritton 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
11062e92ac56SJamie Gritton 			result = FALSE;
11072e92ac56SJamie Gritton 			goto out;
11082e92ac56SJamie Gritton 		}
11092e92ac56SJamie Gritton 		word = offset / 32;
11102e92ac56SJamie Gritton 		bit = offset % 32;
11112e92ac56SJamie Gritton 		if (client->cl_seqmask[word] & (1 << bit)) {
11122e92ac56SJamie Gritton 			result = FALSE;
11132e92ac56SJamie Gritton 			goto out;
11142e92ac56SJamie Gritton 		}
11152e92ac56SJamie Gritton 	}
11162e92ac56SJamie Gritton 
11172e92ac56SJamie Gritton 	result = TRUE;
11182e92ac56SJamie Gritton out:
11192e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
11202e92ac56SJamie Gritton 	return (result);
11212e92ac56SJamie Gritton }
11222e92ac56SJamie Gritton 
11232e92ac56SJamie Gritton static void
11242e92ac56SJamie Gritton svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
11252e92ac56SJamie Gritton {
11262e92ac56SJamie Gritton 	int offset, i, word, bit;
11272e92ac56SJamie Gritton 	uint32_t carry, newcarry;
11282e92ac56SJamie Gritton 
11292e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
11302e92ac56SJamie Gritton 	if (seq > client->cl_seqlast) {
11312e92ac56SJamie Gritton 		/*
11322e92ac56SJamie Gritton 		 * This request has a sequence number greater
11332e92ac56SJamie Gritton 		 * than any we have seen so far. Advance the
11342e92ac56SJamie Gritton 		 * seq window and set bit zero of the window
11352e92ac56SJamie Gritton 		 * (which corresponds to the new sequence
11362e92ac56SJamie Gritton 		 * number)
11372e92ac56SJamie Gritton 		 */
11382e92ac56SJamie Gritton 		offset = seq - client->cl_seqlast;
11392e92ac56SJamie Gritton 		while (offset > 32) {
11402e92ac56SJamie Gritton 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
11412e92ac56SJamie Gritton 			     i > 0; i--) {
11422e92ac56SJamie Gritton 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
11432e92ac56SJamie Gritton 			}
11442e92ac56SJamie Gritton 			client->cl_seqmask[0] = 0;
11452e92ac56SJamie Gritton 			offset -= 32;
11462e92ac56SJamie Gritton 		}
11472e92ac56SJamie Gritton 		carry = 0;
11482e92ac56SJamie Gritton 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
11492e92ac56SJamie Gritton 			newcarry = client->cl_seqmask[i] >> (32 - offset);
11502e92ac56SJamie Gritton 			client->cl_seqmask[i] =
11512e92ac56SJamie Gritton 				(client->cl_seqmask[i] << offset) | carry;
11522e92ac56SJamie Gritton 			carry = newcarry;
11532e92ac56SJamie Gritton 		}
11542e92ac56SJamie Gritton 		client->cl_seqmask[0] |= 1;
11552e92ac56SJamie Gritton 		client->cl_seqlast = seq;
11562e92ac56SJamie Gritton 	} else {
11572e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
11582e92ac56SJamie Gritton 		word = offset / 32;
11592e92ac56SJamie Gritton 		bit = offset % 32;
11602e92ac56SJamie Gritton 		client->cl_seqmask[word] |= (1 << bit);
11612e92ac56SJamie Gritton 	}
11622e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
11632e92ac56SJamie Gritton }
11642e92ac56SJamie Gritton 
11652e92ac56SJamie Gritton enum auth_stat
11662e92ac56SJamie Gritton svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
11672e92ac56SJamie Gritton 
11682e92ac56SJamie Gritton {
11692e92ac56SJamie Gritton 	OM_uint32		 min_stat;
11702e92ac56SJamie Gritton 	XDR	 		 xdrs;
11712e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
11722e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
11732e92ac56SJamie Gritton 	struct rpc_gss_cred	 gc;
11742e92ac56SJamie Gritton 	struct rpc_gss_init_res	 gr;
11752e92ac56SJamie Gritton 	gss_qop_t		 qop;
11762e92ac56SJamie Gritton 	int			 call_stat;
11772e92ac56SJamie Gritton 	enum auth_stat		 result;
11782e92ac56SJamie Gritton 
11792e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss()");
11802e92ac56SJamie Gritton 
11812e92ac56SJamie Gritton 	/* Garbage collect old clients. */
11822e92ac56SJamie Gritton 	svc_rpc_gss_timeout_clients();
11832e92ac56SJamie Gritton 
11842e92ac56SJamie Gritton 	/* Initialize reply. */
11852e92ac56SJamie Gritton 	rqst->rq_verf = _null_auth;
11862e92ac56SJamie Gritton 
11872e92ac56SJamie Gritton 	/* Deserialize client credentials. */
11882e92ac56SJamie Gritton 	if (rqst->rq_cred.oa_length <= 0)
11892e92ac56SJamie Gritton 		return (AUTH_BADCRED);
11902e92ac56SJamie Gritton 
11912e92ac56SJamie Gritton 	memset(&gc, 0, sizeof(gc));
11922e92ac56SJamie Gritton 
11932e92ac56SJamie Gritton 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
11942e92ac56SJamie Gritton 	    rqst->rq_cred.oa_length, XDR_DECODE);
11952e92ac56SJamie Gritton 
11962e92ac56SJamie Gritton 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
11972e92ac56SJamie Gritton 		XDR_DESTROY(&xdrs);
11982e92ac56SJamie Gritton 		return (AUTH_BADCRED);
11992e92ac56SJamie Gritton 	}
12002e92ac56SJamie Gritton 	XDR_DESTROY(&xdrs);
12012e92ac56SJamie Gritton 
12022e92ac56SJamie Gritton 	client = NULL;
12032e92ac56SJamie Gritton 
12042e92ac56SJamie Gritton 	/* Check version. */
12052e92ac56SJamie Gritton 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
12062e92ac56SJamie Gritton 		result = AUTH_BADCRED;
12072e92ac56SJamie Gritton 		goto out;
12082e92ac56SJamie Gritton 	}
12092e92ac56SJamie Gritton 
12102e92ac56SJamie Gritton 	/* Check the proc and find the client (or create it) */
12112e92ac56SJamie Gritton 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
12122e92ac56SJamie Gritton 		if (gc.gc_handle.length != 0) {
12132e92ac56SJamie Gritton 			result = AUTH_BADCRED;
12142e92ac56SJamie Gritton 			goto out;
12152e92ac56SJamie Gritton 		}
12162e92ac56SJamie Gritton 		client = svc_rpc_gss_create_client();
12172e92ac56SJamie Gritton 		refcount_acquire(&client->cl_refs);
12182e92ac56SJamie Gritton 	} else {
12192e92ac56SJamie Gritton 		struct svc_rpc_gss_clientid *p;
12202e92ac56SJamie Gritton 		if (gc.gc_handle.length != sizeof(*p)) {
12212e92ac56SJamie Gritton 			result = AUTH_BADCRED;
12222e92ac56SJamie Gritton 			goto out;
12232e92ac56SJamie Gritton 		}
12242e92ac56SJamie Gritton 		p = gc.gc_handle.value;
12252e92ac56SJamie Gritton 		client = svc_rpc_gss_find_client(p);
12262e92ac56SJamie Gritton 		if (!client) {
12272e92ac56SJamie Gritton 			/*
12282e92ac56SJamie Gritton 			 * Can't find the client - we may have
12292e92ac56SJamie Gritton 			 * destroyed it - tell the other side to
12302e92ac56SJamie Gritton 			 * re-authenticate.
12312e92ac56SJamie Gritton 			 */
12322e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
12332e92ac56SJamie Gritton 			goto out;
12342e92ac56SJamie Gritton 		}
12352e92ac56SJamie Gritton 	}
12362e92ac56SJamie Gritton 	cc = rqst->rq_clntcred;
12372e92ac56SJamie Gritton 	cc->cc_client = client;
12382e92ac56SJamie Gritton 	cc->cc_service = gc.gc_svc;
12392e92ac56SJamie Gritton 	cc->cc_seq = gc.gc_seq;
12402e92ac56SJamie Gritton 
12412e92ac56SJamie Gritton 	/*
12422e92ac56SJamie Gritton 	 * The service and sequence number must be ignored for
12432e92ac56SJamie Gritton 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
12442e92ac56SJamie Gritton 	 */
12452e92ac56SJamie Gritton 	if (gc.gc_proc != RPCSEC_GSS_INIT
12462e92ac56SJamie Gritton 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
12472e92ac56SJamie Gritton 		/*
12482e92ac56SJamie Gritton 		 * Check for sequence number overflow.
12492e92ac56SJamie Gritton 		 */
12502e92ac56SJamie Gritton 		if (gc.gc_seq >= MAXSEQ) {
12512e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
12522e92ac56SJamie Gritton 			goto out;
12532e92ac56SJamie Gritton 		}
12542e92ac56SJamie Gritton 
12552e92ac56SJamie Gritton 		/*
12562e92ac56SJamie Gritton 		 * Check for valid service.
12572e92ac56SJamie Gritton 		 */
12582e92ac56SJamie Gritton 		if (gc.gc_svc != rpc_gss_svc_none &&
12592e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_integrity &&
12602e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_privacy) {
12612e92ac56SJamie Gritton 			result = AUTH_BADCRED;
12622e92ac56SJamie Gritton 			goto out;
12632e92ac56SJamie Gritton 		}
12642e92ac56SJamie Gritton 	}
12652e92ac56SJamie Gritton 
12662e92ac56SJamie Gritton 	/* Handle RPCSEC_GSS control procedure. */
12672e92ac56SJamie Gritton 	switch (gc.gc_proc) {
12682e92ac56SJamie Gritton 
12692e92ac56SJamie Gritton 	case RPCSEC_GSS_INIT:
12702e92ac56SJamie Gritton 	case RPCSEC_GSS_CONTINUE_INIT:
12712e92ac56SJamie Gritton 		if (rqst->rq_proc != NULLPROC) {
12722e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
12732e92ac56SJamie Gritton 			break;
12742e92ac56SJamie Gritton 		}
12752e92ac56SJamie Gritton 
12762e92ac56SJamie Gritton 		memset(&gr, 0, sizeof(gr));
12772e92ac56SJamie Gritton 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
12782e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
12792e92ac56SJamie Gritton 			break;
12802e92ac56SJamie Gritton 		}
12812e92ac56SJamie Gritton 
12822e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE) {
12832e92ac56SJamie Gritton 			/*
12842e92ac56SJamie Gritton 			 * We borrow the space for the call verf to
12852e92ac56SJamie Gritton 			 * pack our reply verf.
12862e92ac56SJamie Gritton 			 */
12872e92ac56SJamie Gritton 			rqst->rq_verf = msg->rm_call.cb_verf;
12882e92ac56SJamie Gritton 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
12892e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
12902e92ac56SJamie Gritton 				break;
12912e92ac56SJamie Gritton 			}
12922e92ac56SJamie Gritton 		} else {
12932e92ac56SJamie Gritton 			rqst->rq_verf = _null_auth;
12942e92ac56SJamie Gritton 		}
12952e92ac56SJamie Gritton 
12962e92ac56SJamie Gritton 		call_stat = svc_sendreply(rqst,
12972e92ac56SJamie Gritton 		    (xdrproc_t) xdr_rpc_gss_init_res,
12982e92ac56SJamie Gritton 		    (caddr_t) &gr);
12992e92ac56SJamie Gritton 
13002e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &gr.gr_token);
13012e92ac56SJamie Gritton 
13022e92ac56SJamie Gritton 		if (!call_stat) {
13032e92ac56SJamie Gritton 			result = AUTH_FAILED;
13042e92ac56SJamie Gritton 			break;
13052e92ac56SJamie Gritton 		}
13062e92ac56SJamie Gritton 
13072e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE)
13082e92ac56SJamie Gritton 			client->cl_state = CLIENT_ESTABLISHED;
13092e92ac56SJamie Gritton 
13102e92ac56SJamie Gritton 		result = RPCSEC_GSS_NODISPATCH;
13112e92ac56SJamie Gritton 		break;
13122e92ac56SJamie Gritton 
13132e92ac56SJamie Gritton 	case RPCSEC_GSS_DATA:
13142e92ac56SJamie Gritton 	case RPCSEC_GSS_DESTROY:
13152e92ac56SJamie Gritton 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
13162e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
13172e92ac56SJamie Gritton 			break;
13182e92ac56SJamie Gritton 		}
13192e92ac56SJamie Gritton 
13202e92ac56SJamie Gritton 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
13212e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
13222e92ac56SJamie Gritton 			break;
13232e92ac56SJamie Gritton 		}
13242e92ac56SJamie Gritton 
13252e92ac56SJamie Gritton 		/*
13262e92ac56SJamie Gritton 		 * We borrow the space for the call verf to pack our
13272e92ac56SJamie Gritton 		 * reply verf.
13282e92ac56SJamie Gritton 		 */
13292e92ac56SJamie Gritton 		rqst->rq_verf = msg->rm_call.cb_verf;
13302e92ac56SJamie Gritton 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
13312e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
13322e92ac56SJamie Gritton 			break;
13332e92ac56SJamie Gritton 		}
13342e92ac56SJamie Gritton 
13352e92ac56SJamie Gritton 		svc_rpc_gss_update_seq(client, gc.gc_seq);
13362e92ac56SJamie Gritton 
13372e92ac56SJamie Gritton 		/*
13382e92ac56SJamie Gritton 		 * Change the SVCAUTH ops on the request to point at
13392e92ac56SJamie Gritton 		 * our own code so that we can unwrap the arguments
13402e92ac56SJamie Gritton 		 * and wrap the result. The caller will re-set this on
13412e92ac56SJamie Gritton 		 * every request to point to a set of null wrap/unwrap
13422e92ac56SJamie Gritton 		 * methods. Acquire an extra reference to the client
13432e92ac56SJamie Gritton 		 * which will be released by svc_rpc_gss_release()
13442e92ac56SJamie Gritton 		 * after the request has finished processing.
13452e92ac56SJamie Gritton 		 */
13462e92ac56SJamie Gritton 		refcount_acquire(&client->cl_refs);
13472e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
13482e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_private = cc;
13492e92ac56SJamie Gritton 
13502e92ac56SJamie Gritton 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
13512e92ac56SJamie Gritton 			/*
13522e92ac56SJamie Gritton 			 * We might be ready to do a callback to the server to
13532e92ac56SJamie Gritton 			 * see if it wants to accept/reject the connection.
13542e92ac56SJamie Gritton 			 */
13552e92ac56SJamie Gritton 			sx_xlock(&client->cl_lock);
13562e92ac56SJamie Gritton 			if (!client->cl_done_callback) {
13572e92ac56SJamie Gritton 				client->cl_done_callback = TRUE;
13582e92ac56SJamie Gritton 				client->cl_qop = qop;
13592e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
13602e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
13612e92ac56SJamie Gritton 				if (!svc_rpc_gss_callback(client, rqst)) {
13622e92ac56SJamie Gritton 					result = AUTH_REJECTEDCRED;
13632e92ac56SJamie Gritton 					sx_xunlock(&client->cl_lock);
13642e92ac56SJamie Gritton 					break;
13652e92ac56SJamie Gritton 				}
13662e92ac56SJamie Gritton 			}
13672e92ac56SJamie Gritton 			sx_xunlock(&client->cl_lock);
13682e92ac56SJamie Gritton 
13692e92ac56SJamie Gritton 			/*
13702e92ac56SJamie Gritton 			 * If the server has locked this client to a
13712e92ac56SJamie Gritton 			 * particular service+qop pair, enforce that
13722e92ac56SJamie Gritton 			 * restriction now.
13732e92ac56SJamie Gritton 			 */
13742e92ac56SJamie Gritton 			if (client->cl_locked) {
13752e92ac56SJamie Gritton 				if (client->cl_rawcred.service != gc.gc_svc) {
13762e92ac56SJamie Gritton 					result = AUTH_FAILED;
13772e92ac56SJamie Gritton 					break;
13782e92ac56SJamie Gritton 				} else if (client->cl_qop != qop) {
13792e92ac56SJamie Gritton 					result = AUTH_BADVERF;
13802e92ac56SJamie Gritton 					break;
13812e92ac56SJamie Gritton 				}
13822e92ac56SJamie Gritton 			}
13832e92ac56SJamie Gritton 
13842e92ac56SJamie Gritton 			/*
13852e92ac56SJamie Gritton 			 * If the qop changed, look up the new qop
13862e92ac56SJamie Gritton 			 * name for rawcred.
13872e92ac56SJamie Gritton 			 */
13882e92ac56SJamie Gritton 			if (client->cl_qop != qop) {
13892e92ac56SJamie Gritton 				client->cl_qop = qop;
13902e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
13912e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
13922e92ac56SJamie Gritton 			}
13932e92ac56SJamie Gritton 
13942e92ac56SJamie Gritton 			/*
13952e92ac56SJamie Gritton 			 * Make sure we use the right service value
13962e92ac56SJamie Gritton 			 * for unwrap/wrap.
13972e92ac56SJamie Gritton 			 */
13982e92ac56SJamie Gritton 			if (client->cl_rawcred.service != gc.gc_svc) {
13992e92ac56SJamie Gritton 				client->cl_rawcred.service = gc.gc_svc;
14002e92ac56SJamie Gritton 				svc_rpc_gss_set_flavor(client);
14012e92ac56SJamie Gritton 			}
14022e92ac56SJamie Gritton 
14032e92ac56SJamie Gritton 			result = AUTH_OK;
14042e92ac56SJamie Gritton 		} else {
14052e92ac56SJamie Gritton 			if (rqst->rq_proc != NULLPROC) {
14062e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
14072e92ac56SJamie Gritton 				break;
14082e92ac56SJamie Gritton 			}
14092e92ac56SJamie Gritton 
14102e92ac56SJamie Gritton 			call_stat = svc_sendreply(rqst,
14112e92ac56SJamie Gritton 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
14122e92ac56SJamie Gritton 
14132e92ac56SJamie Gritton 			if (!call_stat) {
14142e92ac56SJamie Gritton 				result = AUTH_FAILED;
14152e92ac56SJamie Gritton 				break;
14162e92ac56SJamie Gritton 			}
14172e92ac56SJamie Gritton 
14182e92ac56SJamie Gritton 			svc_rpc_gss_forget_client(client);
14192e92ac56SJamie Gritton 
14202e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
14212e92ac56SJamie Gritton 			break;
14222e92ac56SJamie Gritton 		}
14232e92ac56SJamie Gritton 		break;
14242e92ac56SJamie Gritton 
14252e92ac56SJamie Gritton 	default:
14262e92ac56SJamie Gritton 		result = AUTH_BADCRED;
14272e92ac56SJamie Gritton 		break;
14282e92ac56SJamie Gritton 	}
14292e92ac56SJamie Gritton out:
14302e92ac56SJamie Gritton 	if (client)
14312e92ac56SJamie Gritton 		svc_rpc_gss_release_client(client);
14322e92ac56SJamie Gritton 
14332e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
14342e92ac56SJamie Gritton 	return (result);
14352e92ac56SJamie Gritton }
14362e92ac56SJamie Gritton 
14372e92ac56SJamie Gritton static bool_t
14382e92ac56SJamie Gritton svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
14392e92ac56SJamie Gritton {
14402e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
14412e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
14422e92ac56SJamie Gritton 
14432e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
14442e92ac56SJamie Gritton 
14452e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
14462e92ac56SJamie Gritton 	client = cc->cc_client;
14472e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
14482e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
14492e92ac56SJamie Gritton 		return (TRUE);
14502e92ac56SJamie Gritton 	}
14512e92ac56SJamie Gritton 
14522e92ac56SJamie Gritton 	return (xdr_rpc_gss_wrap_data(mp,
14532e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
14542e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
14552e92ac56SJamie Gritton }
14562e92ac56SJamie Gritton 
14572e92ac56SJamie Gritton static bool_t
14582e92ac56SJamie Gritton svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
14592e92ac56SJamie Gritton {
14602e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
14612e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
14622e92ac56SJamie Gritton 
14632e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
14642e92ac56SJamie Gritton 
14652e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
14662e92ac56SJamie Gritton 	client = cc->cc_client;
14672e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
14682e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none) {
14692e92ac56SJamie Gritton 		return (TRUE);
14702e92ac56SJamie Gritton 	}
14712e92ac56SJamie Gritton 
14722e92ac56SJamie Gritton 	return (xdr_rpc_gss_unwrap_data(mp,
14732e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
14742e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
14752e92ac56SJamie Gritton }
14762e92ac56SJamie Gritton 
14772e92ac56SJamie Gritton static void
14782e92ac56SJamie Gritton svc_rpc_gss_release(SVCAUTH *auth)
14792e92ac56SJamie Gritton {
14802e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
14812e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
14822e92ac56SJamie Gritton 
14832e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_release()");
14842e92ac56SJamie Gritton 
14852e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
14862e92ac56SJamie Gritton 	client = cc->cc_client;
14872e92ac56SJamie Gritton 	svc_rpc_gss_release_client(client);
14882e92ac56SJamie Gritton }
1489