xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision ef6fcc5e2b0714c859d2e4ba23a55b1fd12f8a4e)
1a9148abdSDoug Rabson /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  * Copyright (c) 1990 The Regents of the University of California.
451369649SPedro F. Giffuni  *
52e92ac56SJamie Gritton  * Copyright (c) 2008 Doug Rabson
62e92ac56SJamie Gritton  * All rights reserved.
7a9148abdSDoug Rabson  *
8a9148abdSDoug Rabson  * Redistribution and use in source and binary forms, with or without
9a9148abdSDoug Rabson  * modification, are permitted provided that the following conditions
10a9148abdSDoug Rabson  * are met:
11a9148abdSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
12a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
13a9148abdSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
14a9148abdSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
15a9148abdSDoug Rabson  *    documentation and/or other materials provided with the distribution.
16a9148abdSDoug Rabson  *
172e92ac56SJamie Gritton  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a9148abdSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a9148abdSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202e92ac56SJamie Gritton  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a9148abdSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a9148abdSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a9148abdSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a9148abdSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a9148abdSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a9148abdSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a9148abdSDoug Rabson  * SUCH DAMAGE.
282e92ac56SJamie Gritton  */
292e92ac56SJamie Gritton /*
302e92ac56SJamie Gritton   svc_rpcsec_gss.c
312e92ac56SJamie Gritton 
322e92ac56SJamie Gritton   Copyright (c) 2000 The Regents of the University of Michigan.
332e92ac56SJamie Gritton   All rights reserved.
342e92ac56SJamie Gritton 
352e92ac56SJamie Gritton   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
362e92ac56SJamie Gritton   All rights reserved, all wrongs reversed.
372e92ac56SJamie Gritton 
382e92ac56SJamie Gritton   Redistribution and use in source and binary forms, with or without
392e92ac56SJamie Gritton   modification, are permitted provided that the following conditions
402e92ac56SJamie Gritton   are met:
412e92ac56SJamie Gritton 
422e92ac56SJamie Gritton   1. Redistributions of source code must retain the above copyright
432e92ac56SJamie Gritton      notice, this list of conditions and the following disclaimer.
442e92ac56SJamie Gritton   2. Redistributions in binary form must reproduce the above copyright
452e92ac56SJamie Gritton      notice, this list of conditions and the following disclaimer in the
462e92ac56SJamie Gritton      documentation and/or other materials provided with the distribution.
472e92ac56SJamie Gritton   3. Neither the name of the University nor the names of its
482e92ac56SJamie Gritton      contributors may be used to endorse or promote products derived
492e92ac56SJamie Gritton      from this software without specific prior written permission.
502e92ac56SJamie Gritton 
512e92ac56SJamie Gritton   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
522e92ac56SJamie Gritton   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
532e92ac56SJamie Gritton   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
542e92ac56SJamie Gritton   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
552e92ac56SJamie Gritton   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
562e92ac56SJamie Gritton   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
572e92ac56SJamie Gritton   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
582e92ac56SJamie Gritton   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
592e92ac56SJamie Gritton   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
602e92ac56SJamie Gritton   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
612e92ac56SJamie Gritton   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
622e92ac56SJamie Gritton 
632e92ac56SJamie Gritton   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
64a9148abdSDoug Rabson  */
65a9148abdSDoug Rabson 
66a9148abdSDoug Rabson #include <sys/cdefs.h>
67a9148abdSDoug Rabson __FBSDID("$FreeBSD$");
68a9148abdSDoug Rabson 
69a9148abdSDoug Rabson #include <sys/param.h>
702e92ac56SJamie Gritton #include <sys/systm.h>
71ae883d55SRick Macklem #include <sys/jail.h>
72a9148abdSDoug Rabson #include <sys/kernel.h>
732e92ac56SJamie Gritton #include <sys/kobj.h>
74a9148abdSDoug Rabson #include <sys/lock.h>
75a9148abdSDoug Rabson #include <sys/malloc.h>
76a9148abdSDoug Rabson #include <sys/mbuf.h>
77a9148abdSDoug Rabson #include <sys/mutex.h>
782e92ac56SJamie Gritton #include <sys/proc.h>
792e92ac56SJamie Gritton #include <sys/sx.h>
802e92ac56SJamie Gritton #include <sys/ucred.h>
81a9148abdSDoug Rabson 
822e92ac56SJamie Gritton #include <rpc/rpc.h>
832e92ac56SJamie Gritton #include <rpc/rpcsec_gss.h>
84a9148abdSDoug Rabson 
852e92ac56SJamie Gritton #include "rpcsec_gss_int.h"
86a9148abdSDoug Rabson 
872e92ac56SJamie Gritton static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
882e92ac56SJamie Gritton static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
892e92ac56SJamie Gritton static void     svc_rpc_gss_release(SVCAUTH *);
902e92ac56SJamie Gritton static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
912e92ac56SJamie Gritton static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
92a9148abdSDoug Rabson 
9320d728b5SMark Johnston static const struct svc_auth_ops svc_auth_gss_ops = {
9420d728b5SMark Johnston 	.svc_ah_wrap =		svc_rpc_gss_wrap,
9520d728b5SMark Johnston 	.svc_ah_unwrap =	svc_rpc_gss_unwrap,
9620d728b5SMark Johnston 	.svc_ah_release =	svc_rpc_gss_release,
97a9148abdSDoug Rabson };
98a9148abdSDoug Rabson 
992e92ac56SJamie Gritton struct sx svc_rpc_gss_lock;
1002e92ac56SJamie Gritton 
1012e92ac56SJamie Gritton struct svc_rpc_gss_callback {
1022e92ac56SJamie Gritton 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
1032e92ac56SJamie Gritton 	rpc_gss_callback_t	cb_callback;
1042e92ac56SJamie Gritton };
1052894c8c9SRick Macklem SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback);
1062894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list,
1072894c8c9SRick Macklem     svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
1082e92ac56SJamie Gritton 
1092e92ac56SJamie Gritton struct svc_rpc_gss_svc_name {
1102e92ac56SJamie Gritton 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
1112e92ac56SJamie Gritton 	char			*sn_principal;
1122e92ac56SJamie Gritton 	gss_OID			sn_mech;
1132e92ac56SJamie Gritton 	u_int			sn_req_time;
1142e92ac56SJamie Gritton 	gss_cred_id_t		sn_cred;
1152e92ac56SJamie Gritton 	u_int			sn_program;
1162e92ac56SJamie Gritton 	u_int			sn_version;
1172e92ac56SJamie Gritton };
1182894c8c9SRick Macklem SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name);
1192894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list,
1202894c8c9SRick Macklem     svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
1212e92ac56SJamie Gritton 
1222e92ac56SJamie Gritton enum svc_rpc_gss_client_state {
1232e92ac56SJamie Gritton 	CLIENT_NEW,				/* still authenticating */
1242e92ac56SJamie Gritton 	CLIENT_ESTABLISHED,			/* context established */
1252e92ac56SJamie Gritton 	CLIENT_STALE				/* garbage to collect */
126d4468577SJamie Gritton };
127a9148abdSDoug Rabson 
1282e92ac56SJamie Gritton #define SVC_RPC_GSS_SEQWINDOW	128
1292e92ac56SJamie Gritton 
1302e92ac56SJamie Gritton struct svc_rpc_gss_clientid {
1312e92ac56SJamie Gritton 	unsigned long		ci_hostid;
1322e92ac56SJamie Gritton 	uint32_t		ci_boottime;
1332e92ac56SJamie Gritton 	uint32_t		ci_id;
1342e92ac56SJamie Gritton };
1352e92ac56SJamie Gritton 
1362e92ac56SJamie Gritton struct svc_rpc_gss_client {
1372e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
1382e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
1392e92ac56SJamie Gritton 	volatile u_int		cl_refs;
1402e92ac56SJamie Gritton 	struct sx		cl_lock;
1412e92ac56SJamie Gritton 	struct svc_rpc_gss_clientid cl_id;
1422e92ac56SJamie Gritton 	time_t			cl_expiration;	/* when to gc */
1432e92ac56SJamie Gritton 	enum svc_rpc_gss_client_state cl_state;	/* client state */
1442e92ac56SJamie Gritton 	bool_t			cl_locked;	/* fixed service+qop */
1452e92ac56SJamie Gritton 	gss_ctx_id_t		cl_ctx;		/* context id */
1462e92ac56SJamie Gritton 	gss_cred_id_t		cl_creds;	/* delegated creds */
1472e92ac56SJamie Gritton 	gss_name_t		cl_cname;	/* client name */
1482e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
1492e92ac56SJamie Gritton 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
1502e92ac56SJamie Gritton 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
1512e92ac56SJamie Gritton 	struct ucred		*cl_cred;	/* kernel-style credentials */
1522e92ac56SJamie Gritton 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
1532e92ac56SJamie Gritton 	bool_t			cl_done_callback; /* TRUE after call */
1542e92ac56SJamie Gritton 	void			*cl_cookie;	/* user cookie from callback */
1555eff3ec6SJosh Paetzel 	gid_t			cl_gid_storage[NGROUPS];
1562e92ac56SJamie Gritton 	gss_OID			cl_mech;	/* mechanism */
1572e92ac56SJamie Gritton 	gss_qop_t		cl_qop;		/* quality of protection */
1582e92ac56SJamie Gritton 	uint32_t		cl_seqlast;	/* sequence window origin */
1592e92ac56SJamie Gritton 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
1602e92ac56SJamie Gritton };
1612e92ac56SJamie Gritton TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
1622e92ac56SJamie Gritton 
163a9148abdSDoug Rabson /*
1642e92ac56SJamie Gritton  * This structure holds enough information to unwrap arguments or wrap
1652e92ac56SJamie Gritton  * results for a given request. We use the rq_clntcred area for this
1662e92ac56SJamie Gritton  * (which is a per-request buffer).
1672e92ac56SJamie Gritton  */
1682e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred {
1692e92ac56SJamie Gritton 	struct svc_rpc_gss_client *cc_client;
1702e92ac56SJamie Gritton 	rpc_gss_service_t	cc_service;
1712e92ac56SJamie Gritton 	uint32_t		cc_seq;
1722e92ac56SJamie Gritton };
1732e92ac56SJamie Gritton 
1742e92ac56SJamie Gritton #define CLIENT_HASH_SIZE	256
175e998861bSEdward Tomasz Napierala #define CLIENT_MAX		1024
176b329fb28SEdward Tomasz Napierala u_int svc_rpc_gss_client_max = CLIENT_MAX;
17752eb4995SEdward Tomasz Napierala u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
178b329fb28SEdward Tomasz Napierala 
1797029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1807029da5cSPawel Biernacki     "RPC");
1817029da5cSPawel Biernacki SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1827029da5cSPawel Biernacki     "GSS");
183b329fb28SEdward Tomasz Napierala 
184b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
185b329fb28SEdward Tomasz Napierala     &svc_rpc_gss_client_max, 0,
186b329fb28SEdward Tomasz Napierala     "Max number of rpc-gss clients");
187b329fb28SEdward Tomasz Napierala 
18852eb4995SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
18952eb4995SEdward Tomasz Napierala     &svc_rpc_gss_client_hash_size, 0,
19052eb4995SEdward Tomasz Napierala     "Size of rpc-gss client hash table");
19152eb4995SEdward Tomasz Napierala 
192841c3621SRick Macklem static u_int svc_rpc_gss_lifetime_max = 0;
19304cb0c38SRick Macklem SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
19404cb0c38SRick Macklem     &svc_rpc_gss_lifetime_max, 0,
19504cb0c38SRick Macklem     "Maximum lifetime (seconds) of rpc-gss clients");
19604cb0c38SRick Macklem 
197b329fb28SEdward Tomasz Napierala static u_int svc_rpc_gss_client_count;
198b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
199b329fb28SEdward Tomasz Napierala     &svc_rpc_gss_client_count, 0,
200b329fb28SEdward Tomasz Napierala     "Number of rpc-gss clients");
201b329fb28SEdward Tomasz Napierala 
2022894c8c9SRick Macklem KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
2032894c8c9SRick Macklem KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
2042894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1;
2052e92ac56SJamie Gritton 
2062e92ac56SJamie Gritton static void
207*ef6fcc5eSRick Macklem svc_rpc_gss_init(void *unused __unused)
2082e92ac56SJamie Gritton {
2092e92ac56SJamie Gritton 	int i;
2102e92ac56SJamie Gritton 
2112894c8c9SRick Macklem 	KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc(
2122894c8c9SRick Macklem 	    sizeof(struct svc_rpc_gss_client_list) *
2132894c8c9SRick Macklem 	    svc_rpc_gss_client_hash_size);
21452eb4995SEdward Tomasz Napierala 	for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
2152894c8c9SRick Macklem 		TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
2162894c8c9SRick Macklem 	TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients));
2172894c8c9SRick Macklem 	if (IS_DEFAULT_VNET(curvnet)) {
2182e92ac56SJamie Gritton 		svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
2192e92ac56SJamie Gritton 		sx_init(&svc_rpc_gss_lock, "gsslock");
2202e92ac56SJamie Gritton 	}
2212894c8c9SRick Macklem }
222ed03776cSRick Macklem VNET_SYSINIT(svc_rpc_gss_init, SI_SUB_VNET_DONE, SI_ORDER_ANY,
2232894c8c9SRick Macklem     svc_rpc_gss_init, NULL);
2242e92ac56SJamie Gritton 
225*ef6fcc5eSRick Macklem static void
226*ef6fcc5eSRick Macklem svc_rpc_gss_cleanup(void *unused __unused)
227*ef6fcc5eSRick Macklem {
228*ef6fcc5eSRick Macklem 
229*ef6fcc5eSRick Macklem 	mem_free(KGSS_VNET(svc_rpc_gss_client_hash),
230*ef6fcc5eSRick Macklem 	    sizeof(struct svc_rpc_gss_client_list) *
231*ef6fcc5eSRick Macklem 	    svc_rpc_gss_client_hash_size);
232*ef6fcc5eSRick Macklem 	if (IS_DEFAULT_VNET(curvnet))
233*ef6fcc5eSRick Macklem 		sx_destroy(&svc_rpc_gss_lock);
234*ef6fcc5eSRick Macklem }
235*ef6fcc5eSRick Macklem VNET_SYSUNINIT(svc_rpc_gss_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY,
236*ef6fcc5eSRick Macklem     svc_rpc_gss_cleanup, NULL);
237*ef6fcc5eSRick Macklem 
2382e92ac56SJamie Gritton bool_t
2392e92ac56SJamie Gritton rpc_gss_set_callback(rpc_gss_callback_t *cb)
2402e92ac56SJamie Gritton {
2412e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
2422e92ac56SJamie Gritton 
2432e92ac56SJamie Gritton 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
2442e92ac56SJamie Gritton 	if (!scb) {
2452e92ac56SJamie Gritton 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
2462e92ac56SJamie Gritton 		return (FALSE);
2472e92ac56SJamie Gritton 	}
2482e92ac56SJamie Gritton 	scb->cb_callback = *cb;
2492e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2502894c8c9SRick Macklem 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link);
2512e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2522e92ac56SJamie Gritton 
2532e92ac56SJamie Gritton 	return (TRUE);
2542e92ac56SJamie Gritton }
2552e92ac56SJamie Gritton 
2562e92ac56SJamie Gritton void
2572e92ac56SJamie Gritton rpc_gss_clear_callback(rpc_gss_callback_t *cb)
2582e92ac56SJamie Gritton {
2592e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
2602e92ac56SJamie Gritton 
2612e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2622894c8c9SRick Macklem 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
2632e92ac56SJamie Gritton 		if (scb->cb_callback.program == cb->program
2642e92ac56SJamie Gritton 		    && scb->cb_callback.version == cb->version
2652e92ac56SJamie Gritton 		    && scb->cb_callback.callback == cb->callback) {
2662894c8c9SRick Macklem 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb,
2672e92ac56SJamie Gritton 			    svc_rpc_gss_callback, cb_link);
2682e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
2692e92ac56SJamie Gritton 			mem_free(scb, sizeof(*scb));
2702e92ac56SJamie Gritton 			return;
2712e92ac56SJamie Gritton 		}
2722e92ac56SJamie Gritton 	}
2732e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2742e92ac56SJamie Gritton }
2752e92ac56SJamie Gritton 
2762e92ac56SJamie Gritton static bool_t
2772e92ac56SJamie Gritton rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
2782e92ac56SJamie Gritton {
2792e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
2802e92ac56SJamie Gritton 	gss_buffer_desc		namebuf;
2812e92ac56SJamie Gritton 	gss_name_t		name;
2822e92ac56SJamie Gritton 	gss_OID_set_desc	oid_set;
2832e92ac56SJamie Gritton 
2842e92ac56SJamie Gritton 	oid_set.count = 1;
2852e92ac56SJamie Gritton 	oid_set.elements = sname->sn_mech;
2862e92ac56SJamie Gritton 
2872e92ac56SJamie Gritton 	namebuf.value = (void *) sname->sn_principal;
2882e92ac56SJamie Gritton 	namebuf.length = strlen(sname->sn_principal);
2892e92ac56SJamie Gritton 
2902e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &namebuf,
2912e92ac56SJamie Gritton 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
2922e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE)
2932e92ac56SJamie Gritton 		return (FALSE);
2942e92ac56SJamie Gritton 
2952e92ac56SJamie Gritton 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
2962e92ac56SJamie Gritton 		gss_release_cred(&min_stat, &sname->sn_cred);
2972e92ac56SJamie Gritton 
2982e92ac56SJamie Gritton 	maj_stat = gss_acquire_cred(&min_stat, name,
2992e92ac56SJamie Gritton 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
3002e92ac56SJamie Gritton 	    NULL, NULL);
3012e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
3022e92ac56SJamie Gritton 		gss_release_name(&min_stat, &name);
3032e92ac56SJamie Gritton 		return (FALSE);
3042e92ac56SJamie Gritton 	}
3052e92ac56SJamie Gritton 	gss_release_name(&min_stat, &name);
3062e92ac56SJamie Gritton 
3072e92ac56SJamie Gritton 	return (TRUE);
3082e92ac56SJamie Gritton }
3092e92ac56SJamie Gritton 
3102e92ac56SJamie Gritton bool_t
3112e92ac56SJamie Gritton rpc_gss_set_svc_name(const char *principal, const char *mechanism,
3122e92ac56SJamie Gritton     u_int req_time, u_int program, u_int version)
3132e92ac56SJamie Gritton {
3142e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
3152e92ac56SJamie Gritton 	gss_OID			mech_oid;
3162e92ac56SJamie Gritton 
3172e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
3182e92ac56SJamie Gritton 		return (FALSE);
3192e92ac56SJamie Gritton 
3202e92ac56SJamie Gritton 	sname = mem_alloc(sizeof(*sname));
3212e92ac56SJamie Gritton 	if (!sname)
3222e92ac56SJamie Gritton 		return (FALSE);
3232e92ac56SJamie Gritton 	sname->sn_principal = strdup(principal, M_RPC);
3242e92ac56SJamie Gritton 	sname->sn_mech = mech_oid;
3252e92ac56SJamie Gritton 	sname->sn_req_time = req_time;
3262e92ac56SJamie Gritton 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
3272e92ac56SJamie Gritton 	sname->sn_program = program;
3282e92ac56SJamie Gritton 	sname->sn_version = version;
3292e92ac56SJamie Gritton 
3302e92ac56SJamie Gritton 	if (!rpc_gss_acquire_svc_cred(sname)) {
3312e92ac56SJamie Gritton 		free(sname->sn_principal, M_RPC);
3322e92ac56SJamie Gritton 		mem_free(sname, sizeof(*sname));
3332e92ac56SJamie Gritton 		return (FALSE);
3342e92ac56SJamie Gritton 	}
3352e92ac56SJamie Gritton 
3362e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
3372894c8c9SRick Macklem 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link);
3382e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
3392e92ac56SJamie Gritton 
3402e92ac56SJamie Gritton 	return (TRUE);
3412e92ac56SJamie Gritton }
3422e92ac56SJamie Gritton 
3432e92ac56SJamie Gritton void
3442e92ac56SJamie Gritton rpc_gss_clear_svc_name(u_int program, u_int version)
3452e92ac56SJamie Gritton {
3462e92ac56SJamie Gritton 	OM_uint32		min_stat;
3472e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
3482e92ac56SJamie Gritton 
3492e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
3502894c8c9SRick Macklem 	SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
3512e92ac56SJamie Gritton 		if (sname->sn_program == program
3522e92ac56SJamie Gritton 		    && sname->sn_version == version) {
3532894c8c9SRick Macklem 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname,
3542e92ac56SJamie Gritton 			    svc_rpc_gss_svc_name, sn_link);
3552e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
3562e92ac56SJamie Gritton 			gss_release_cred(&min_stat, &sname->sn_cred);
3572e92ac56SJamie Gritton 			free(sname->sn_principal, M_RPC);
3582e92ac56SJamie Gritton 			mem_free(sname, sizeof(*sname));
3592e92ac56SJamie Gritton 			return;
3602e92ac56SJamie Gritton 		}
3612e92ac56SJamie Gritton 	}
3622e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
3632e92ac56SJamie Gritton }
3642e92ac56SJamie Gritton 
3652e92ac56SJamie Gritton bool_t
3662e92ac56SJamie Gritton rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
3672e92ac56SJamie Gritton     const char *mech, const char *name, const char *node, const char *domain)
3682e92ac56SJamie Gritton {
3692e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
3702e92ac56SJamie Gritton 	gss_OID			mech_oid;
3712e92ac56SJamie Gritton 	size_t			namelen;
3722e92ac56SJamie Gritton 	gss_buffer_desc		buf;
3732e92ac56SJamie Gritton 	gss_name_t		gss_name, gss_mech_name;
3742e92ac56SJamie Gritton 	rpc_gss_principal_t	result;
3752e92ac56SJamie Gritton 
3762e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
3772e92ac56SJamie Gritton 		return (FALSE);
3782e92ac56SJamie Gritton 
3792e92ac56SJamie Gritton 	/*
3802e92ac56SJamie Gritton 	 * Construct a gss_buffer containing the full name formatted
3812e92ac56SJamie Gritton 	 * as "name/node@domain" where node and domain are optional.
3822e92ac56SJamie Gritton 	 */
383e3081f7eSConrad Meyer 	namelen = strlen(name) + 1;
3842e92ac56SJamie Gritton 	if (node) {
3852e92ac56SJamie Gritton 		namelen += strlen(node) + 1;
3862e92ac56SJamie Gritton 	}
3872e92ac56SJamie Gritton 	if (domain) {
3882e92ac56SJamie Gritton 		namelen += strlen(domain) + 1;
3892e92ac56SJamie Gritton 	}
3902e92ac56SJamie Gritton 
3912e92ac56SJamie Gritton 	buf.value = mem_alloc(namelen);
3922e92ac56SJamie Gritton 	buf.length = namelen;
3932e92ac56SJamie Gritton 	strcpy((char *) buf.value, name);
3942e92ac56SJamie Gritton 	if (node) {
3952e92ac56SJamie Gritton 		strcat((char *) buf.value, "/");
3962e92ac56SJamie Gritton 		strcat((char *) buf.value, node);
3972e92ac56SJamie Gritton 	}
3982e92ac56SJamie Gritton 	if (domain) {
3992e92ac56SJamie Gritton 		strcat((char *) buf.value, "@");
4002e92ac56SJamie Gritton 		strcat((char *) buf.value, domain);
4012e92ac56SJamie Gritton 	}
4022e92ac56SJamie Gritton 
4032e92ac56SJamie Gritton 	/*
4042e92ac56SJamie Gritton 	 * Convert that to a gss_name_t and then convert that to a
4052e92ac56SJamie Gritton 	 * mechanism name in the selected mechanism.
4062e92ac56SJamie Gritton 	 */
4072e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &buf,
4082e92ac56SJamie Gritton 	    GSS_C_NT_USER_NAME, &gss_name);
4092e92ac56SJamie Gritton 	mem_free(buf.value, buf.length);
4102e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4112e92ac56SJamie Gritton 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
4122e92ac56SJamie Gritton 		return (FALSE);
4132e92ac56SJamie Gritton 	}
4142e92ac56SJamie Gritton 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
4152e92ac56SJamie Gritton 	    &gss_mech_name);
4162e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4172e92ac56SJamie Gritton 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
4182e92ac56SJamie Gritton 		    min_stat);
4192e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_name);
4202e92ac56SJamie Gritton 		return (FALSE);
4212e92ac56SJamie Gritton 	}
4222e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_name);
4232e92ac56SJamie Gritton 
4242e92ac56SJamie Gritton 	/*
4252e92ac56SJamie Gritton 	 * Export the mechanism name and use that to construct the
4262e92ac56SJamie Gritton 	 * rpc_gss_principal_t result.
4272e92ac56SJamie Gritton 	 */
4282e92ac56SJamie Gritton 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
4292e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4302e92ac56SJamie Gritton 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
4312e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_mech_name);
4322e92ac56SJamie Gritton 		return (FALSE);
4332e92ac56SJamie Gritton 	}
4342e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_mech_name);
4352e92ac56SJamie Gritton 
4362e92ac56SJamie Gritton 	result = mem_alloc(sizeof(int) + buf.length);
4372e92ac56SJamie Gritton 	if (!result) {
4382e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &buf);
4392e92ac56SJamie Gritton 		return (FALSE);
4402e92ac56SJamie Gritton 	}
4412e92ac56SJamie Gritton 	result->len = buf.length;
4422e92ac56SJamie Gritton 	memcpy(result->name, buf.value, buf.length);
4432e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &buf);
4442e92ac56SJamie Gritton 
4452e92ac56SJamie Gritton 	*principal = result;
4462e92ac56SJamie Gritton 	return (TRUE);
4472e92ac56SJamie Gritton }
4482e92ac56SJamie Gritton 
4492e92ac56SJamie Gritton bool_t
4502e92ac56SJamie Gritton rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
4512e92ac56SJamie Gritton     rpc_gss_ucred_t **ucred, void **cookie)
4522e92ac56SJamie Gritton {
4532e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
4542e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
4552e92ac56SJamie Gritton 
4562e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
4572e92ac56SJamie Gritton 		return (FALSE);
4582e92ac56SJamie Gritton 
4592e92ac56SJamie Gritton 	cc = req->rq_clntcred;
4602e92ac56SJamie Gritton 	client = cc->cc_client;
4612e92ac56SJamie Gritton 	if (rcred)
4622e92ac56SJamie Gritton 		*rcred = &client->cl_rawcred;
4632e92ac56SJamie Gritton 	if (ucred)
4642e92ac56SJamie Gritton 		*ucred = &client->cl_ucred;
4652e92ac56SJamie Gritton 	if (cookie)
4662e92ac56SJamie Gritton 		*cookie = client->cl_cookie;
4672e92ac56SJamie Gritton 	return (TRUE);
4682e92ac56SJamie Gritton }
4692e92ac56SJamie Gritton 
4702e92ac56SJamie Gritton /*
4712e92ac56SJamie Gritton  * This simpler interface is used by svc_getcred to copy the cred data
4722e92ac56SJamie Gritton  * into a kernel cred structure.
473a9148abdSDoug Rabson  */
474a9148abdSDoug Rabson static int
4752e92ac56SJamie Gritton rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
476a9148abdSDoug Rabson {
4772e92ac56SJamie Gritton 	struct ucred *cr;
4782e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
4792e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
4802e92ac56SJamie Gritton 	rpc_gss_ucred_t *uc;
481a9148abdSDoug Rabson 
4822e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
4832e92ac56SJamie Gritton 		return (FALSE);
4842e92ac56SJamie Gritton 
4852e92ac56SJamie Gritton 	cc = req->rq_clntcred;
4862e92ac56SJamie Gritton 	client = cc->cc_client;
4872e92ac56SJamie Gritton 
4882e92ac56SJamie Gritton 	if (flavorp)
4892e92ac56SJamie Gritton 		*flavorp = client->cl_rpcflavor;
4902e92ac56SJamie Gritton 
4912e92ac56SJamie Gritton 	if (client->cl_cred) {
4922e92ac56SJamie Gritton 		*crp = crhold(client->cl_cred);
4932e92ac56SJamie Gritton 		return (TRUE);
494a9148abdSDoug Rabson 	}
495a9148abdSDoug Rabson 
4962e92ac56SJamie Gritton 	uc = &client->cl_ucred;
4972e92ac56SJamie Gritton 	cr = client->cl_cred = crget();
4982e92ac56SJamie Gritton 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
4992e92ac56SJamie Gritton 	cr->cr_rgid = cr->cr_svgid = uc->gid;
5002e92ac56SJamie Gritton 	crsetgroups(cr, uc->gidlen, uc->gidlist);
5016a76d35cSRick Macklem 	cr->cr_prison = curthread->td_ucred->cr_prison;
502c408f06bSJamie Gritton 	prison_hold(cr->cr_prison);
5032e92ac56SJamie Gritton 	*crp = crhold(cr);
5042e92ac56SJamie Gritton 
5052e92ac56SJamie Gritton 	return (TRUE);
506a9148abdSDoug Rabson }
5072e92ac56SJamie Gritton 
5082e92ac56SJamie Gritton int
5092e92ac56SJamie Gritton rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
5102e92ac56SJamie Gritton {
5112e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
5122e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client = cc->cc_client;
5132e92ac56SJamie Gritton 	int			want_conf;
5142e92ac56SJamie Gritton 	OM_uint32		max;
5152e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
5162e92ac56SJamie Gritton 	int			result;
5172e92ac56SJamie Gritton 
5182e92ac56SJamie Gritton 	switch (client->cl_rawcred.service) {
5192e92ac56SJamie Gritton 	case rpc_gss_svc_none:
5202e92ac56SJamie Gritton 		return (max_tp_unit_len);
5212e92ac56SJamie Gritton 		break;
5222e92ac56SJamie Gritton 
5232e92ac56SJamie Gritton 	case rpc_gss_svc_default:
5242e92ac56SJamie Gritton 	case rpc_gss_svc_integrity:
5252e92ac56SJamie Gritton 		want_conf = FALSE;
5262e92ac56SJamie Gritton 		break;
5272e92ac56SJamie Gritton 
5282e92ac56SJamie Gritton 	case rpc_gss_svc_privacy:
5292e92ac56SJamie Gritton 		want_conf = TRUE;
5302e92ac56SJamie Gritton 		break;
5312e92ac56SJamie Gritton 
5322e92ac56SJamie Gritton 	default:
533a9148abdSDoug Rabson 		return (0);
534a9148abdSDoug Rabson 	}
535a9148abdSDoug Rabson 
5362e92ac56SJamie Gritton 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
5372e92ac56SJamie Gritton 	    client->cl_qop, max_tp_unit_len, &max);
5382e92ac56SJamie Gritton 
5392e92ac56SJamie Gritton 	if (maj_stat == GSS_S_COMPLETE) {
5402e92ac56SJamie Gritton 		result = (int) max;
5412e92ac56SJamie Gritton 		if (result < 0)
5422e92ac56SJamie Gritton 			result = 0;
5432e92ac56SJamie Gritton 		return (result);
5442e92ac56SJamie Gritton 	} else {
5452e92ac56SJamie Gritton 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
5462e92ac56SJamie Gritton 		    maj_stat, min_stat);
5472e92ac56SJamie Gritton 		return (0);
5482e92ac56SJamie Gritton 	}
5492e92ac56SJamie Gritton }
5502e92ac56SJamie Gritton 
5512e92ac56SJamie Gritton static struct svc_rpc_gss_client *
5522e92ac56SJamie Gritton svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
5532e92ac56SJamie Gritton {
5542e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5552e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
556584b675eSKonstantin Belousov 	struct timeval boottime;
5572e92ac56SJamie Gritton 	unsigned long hostid;
5582e92ac56SJamie Gritton 
5592e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
5602e92ac56SJamie Gritton 
5612e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
562584b675eSKonstantin Belousov 	getboottime(&boottime);
5632e92ac56SJamie Gritton 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
5642e92ac56SJamie Gritton 		return (NULL);
5652e92ac56SJamie Gritton 
5662894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
5672894c8c9SRick Macklem 	    [id->ci_id % svc_rpc_gss_client_hash_size];
5682e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
5692e92ac56SJamie Gritton 	TAILQ_FOREACH(client, list, cl_link) {
5702e92ac56SJamie Gritton 		if (client->cl_id.ci_id == id->ci_id) {
5712e92ac56SJamie Gritton 			/*
5722e92ac56SJamie Gritton 			 * Move this client to the front of the LRU
5732e92ac56SJamie Gritton 			 * list.
5742e92ac56SJamie Gritton 			 */
5752894c8c9SRick Macklem 			TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client,
5762e92ac56SJamie Gritton 			    cl_alllink);
5772894c8c9SRick Macklem 			TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients),
5782894c8c9SRick Macklem 			    client, cl_alllink);
5792e92ac56SJamie Gritton 			refcount_acquire(&client->cl_refs);
5802e92ac56SJamie Gritton 			break;
5812e92ac56SJamie Gritton 		}
5822e92ac56SJamie Gritton 	}
5832e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
5842e92ac56SJamie Gritton 
5852e92ac56SJamie Gritton 	return (client);
5862e92ac56SJamie Gritton }
5872e92ac56SJamie Gritton 
5882e92ac56SJamie Gritton static struct svc_rpc_gss_client *
5892e92ac56SJamie Gritton svc_rpc_gss_create_client(void)
5902e92ac56SJamie Gritton {
5912e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5922e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
593584b675eSKonstantin Belousov 	struct timeval boottime;
5942e92ac56SJamie Gritton 	unsigned long hostid;
5952e92ac56SJamie Gritton 
5962e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
5972e92ac56SJamie Gritton 
5982e92ac56SJamie Gritton 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
5992e92ac56SJamie Gritton 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
60014068959SRick Macklem 
60114068959SRick Macklem 	/*
60214068959SRick Macklem 	 * Set the initial value of cl_refs to two.  One for the caller
60314068959SRick Macklem 	 * and the other to hold onto the client structure until it expires.
60414068959SRick Macklem 	 */
605b0e14530SRick Macklem 	refcount_init(&client->cl_refs, 2);
6062e92ac56SJamie Gritton 	sx_init(&client->cl_lock, "GSS-client");
6072e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
6082e92ac56SJamie Gritton 	client->cl_id.ci_hostid = hostid;
609584b675eSKonstantin Belousov 	getboottime(&boottime);
6102e92ac56SJamie Gritton 	client->cl_id.ci_boottime = boottime.tv_sec;
6112894c8c9SRick Macklem 	client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++;
6122e92ac56SJamie Gritton 
6132e92ac56SJamie Gritton 	/*
6142e92ac56SJamie Gritton 	 * Start the client off with a short expiration time. We will
6152e92ac56SJamie Gritton 	 * try to get a saner value from the client creds later.
6162e92ac56SJamie Gritton 	 */
6172e92ac56SJamie Gritton 	client->cl_state = CLIENT_NEW;
6182e92ac56SJamie Gritton 	client->cl_locked = FALSE;
6192e92ac56SJamie Gritton 	client->cl_expiration = time_uptime + 5*60;
6202e92ac56SJamie Gritton 
6212894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
6222894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
623b0e14530SRick Macklem 	sx_xlock(&svc_rpc_gss_lock);
624b0e14530SRick Macklem 	TAILQ_INSERT_HEAD(list, client, cl_link);
6252894c8c9SRick Macklem 	TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
626b0e14530SRick Macklem 	svc_rpc_gss_client_count++;
627b0e14530SRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
6282e92ac56SJamie Gritton 	return (client);
6292e92ac56SJamie Gritton }
6302e92ac56SJamie Gritton 
6312e92ac56SJamie Gritton static void
6322e92ac56SJamie Gritton svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
6332e92ac56SJamie Gritton {
6342e92ac56SJamie Gritton 	OM_uint32 min_stat;
6352e92ac56SJamie Gritton 
6362e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
6372e92ac56SJamie Gritton 
6382e92ac56SJamie Gritton 	if (client->cl_ctx)
6392e92ac56SJamie Gritton 		gss_delete_sec_context(&min_stat,
6402e92ac56SJamie Gritton 		    &client->cl_ctx, GSS_C_NO_BUFFER);
6412e92ac56SJamie Gritton 
6422e92ac56SJamie Gritton 	if (client->cl_cname)
6432e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
6442e92ac56SJamie Gritton 
6452e92ac56SJamie Gritton 	if (client->cl_rawcred.client_principal)
6462e92ac56SJamie Gritton 		mem_free(client->cl_rawcred.client_principal,
6472e92ac56SJamie Gritton 		    sizeof(*client->cl_rawcred.client_principal)
6482e92ac56SJamie Gritton 		    + client->cl_rawcred.client_principal->len);
6492e92ac56SJamie Gritton 
6502e92ac56SJamie Gritton 	if (client->cl_cred)
6512e92ac56SJamie Gritton 		crfree(client->cl_cred);
6522e92ac56SJamie Gritton 
6532e92ac56SJamie Gritton 	sx_destroy(&client->cl_lock);
6542e92ac56SJamie Gritton 	mem_free(client, sizeof(*client));
6552e92ac56SJamie Gritton }
6562e92ac56SJamie Gritton 
6572e92ac56SJamie Gritton /*
6582e92ac56SJamie Gritton  * Drop a reference to a client and free it if that was the last reference.
6592e92ac56SJamie Gritton  */
6602e92ac56SJamie Gritton static void
6612e92ac56SJamie Gritton svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
6622e92ac56SJamie Gritton {
6632e92ac56SJamie Gritton 
6642e92ac56SJamie Gritton 	if (!refcount_release(&client->cl_refs))
6652e92ac56SJamie Gritton 		return;
6662e92ac56SJamie Gritton 	svc_rpc_gss_destroy_client(client);
6672e92ac56SJamie Gritton }
6682e92ac56SJamie Gritton 
6692e92ac56SJamie Gritton /*
6705328a32eSRick Macklem  * Remove a client from our global lists.
6715328a32eSRick Macklem  * Must be called with svc_rpc_gss_lock held.
6725328a32eSRick Macklem  */
6735328a32eSRick Macklem static void
6745328a32eSRick Macklem svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
6755328a32eSRick Macklem {
6765328a32eSRick Macklem 	struct svc_rpc_gss_client_list *list;
6775328a32eSRick Macklem 
6785328a32eSRick Macklem 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
6792894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
6802894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
6815328a32eSRick Macklem 	TAILQ_REMOVE(list, client, cl_link);
6822894c8c9SRick Macklem 	TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
6835328a32eSRick Macklem 	svc_rpc_gss_client_count--;
6845328a32eSRick Macklem }
6855328a32eSRick Macklem 
6865328a32eSRick Macklem /*
6872e92ac56SJamie Gritton  * Remove a client from our global lists and free it if we can.
6882e92ac56SJamie Gritton  */
6892e92ac56SJamie Gritton static void
6902e92ac56SJamie Gritton svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
6912e92ac56SJamie Gritton {
6922e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
6935328a32eSRick Macklem 	struct svc_rpc_gss_client *tclient;
6942e92ac56SJamie Gritton 
6952894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
6962894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
6972e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
6985328a32eSRick Macklem 	TAILQ_FOREACH(tclient, list, cl_link) {
6995328a32eSRick Macklem 		/*
7005328a32eSRick Macklem 		 * Make sure this client has not already been removed
7015328a32eSRick Macklem 		 * from the lists by svc_rpc_gss_forget_client() or
702cbf06947SRick Macklem 		 * svc_rpc_gss_forget_client_locked().
7035328a32eSRick Macklem 		 */
7045328a32eSRick Macklem 		if (client == tclient) {
7055328a32eSRick Macklem 			svc_rpc_gss_forget_client_locked(client);
7062e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
7072e92ac56SJamie Gritton 			svc_rpc_gss_release_client(client);
7085328a32eSRick Macklem 			return;
7095328a32eSRick Macklem 		}
7105328a32eSRick Macklem 	}
7115328a32eSRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
7122e92ac56SJamie Gritton }
7132e92ac56SJamie Gritton 
7142e92ac56SJamie Gritton static void
7152e92ac56SJamie Gritton svc_rpc_gss_timeout_clients(void)
7162e92ac56SJamie Gritton {
7172e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
7182e92ac56SJamie Gritton 	time_t now = time_uptime;
7192e92ac56SJamie Gritton 
7202e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
7212e92ac56SJamie Gritton 
7222e92ac56SJamie Gritton 	/*
7232e92ac56SJamie Gritton 	 * First enforce the max client limit. We keep
7242e92ac56SJamie Gritton 	 * svc_rpc_gss_clients in LRU order.
7252e92ac56SJamie Gritton 	 */
7265328a32eSRick Macklem 	sx_xlock(&svc_rpc_gss_lock);
7272894c8c9SRick Macklem 	client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
7282894c8c9SRick Macklem 	    svc_rpc_gss_client_list);
729b329fb28SEdward Tomasz Napierala 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
7305328a32eSRick Macklem 		svc_rpc_gss_forget_client_locked(client);
7315328a32eSRick Macklem 		sx_xunlock(&svc_rpc_gss_lock);
7325328a32eSRick Macklem 		svc_rpc_gss_release_client(client);
7335328a32eSRick Macklem 		sx_xlock(&svc_rpc_gss_lock);
7342894c8c9SRick Macklem 		client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
7355328a32eSRick Macklem 		    svc_rpc_gss_client_list);
7365328a32eSRick Macklem 	}
7375328a32eSRick Macklem again:
7382894c8c9SRick Macklem 	TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) {
7392e92ac56SJamie Gritton 		if (client->cl_state == CLIENT_STALE
7402e92ac56SJamie Gritton 		    || now > client->cl_expiration) {
7415328a32eSRick Macklem 			svc_rpc_gss_forget_client_locked(client);
7425328a32eSRick Macklem 			sx_xunlock(&svc_rpc_gss_lock);
7432e92ac56SJamie Gritton 			rpc_gss_log_debug("expiring client %p", client);
7445328a32eSRick Macklem 			svc_rpc_gss_release_client(client);
7455328a32eSRick Macklem 			sx_xlock(&svc_rpc_gss_lock);
7465328a32eSRick Macklem 			goto again;
7472e92ac56SJamie Gritton 		}
7482e92ac56SJamie Gritton 	}
7495328a32eSRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
7502e92ac56SJamie Gritton }
7512e92ac56SJamie Gritton 
7522e92ac56SJamie Gritton #ifdef DEBUG
7532e92ac56SJamie Gritton /*
7542e92ac56SJamie Gritton  * OID<->string routines.  These are uuuuugly.
7552e92ac56SJamie Gritton  */
7562e92ac56SJamie Gritton static OM_uint32
7572e92ac56SJamie Gritton gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
7582e92ac56SJamie Gritton {
7592e92ac56SJamie Gritton 	char		numstr[128];
7602e92ac56SJamie Gritton 	unsigned long	number;
7612e92ac56SJamie Gritton 	int		numshift;
7622e92ac56SJamie Gritton 	size_t		string_length;
7632e92ac56SJamie Gritton 	size_t		i;
7642e92ac56SJamie Gritton 	unsigned char	*cp;
7652e92ac56SJamie Gritton 	char		*bp;
7662e92ac56SJamie Gritton 
7672e92ac56SJamie Gritton 	/* Decoded according to krb5/gssapi_krb5.c */
7682e92ac56SJamie Gritton 
7692e92ac56SJamie Gritton 	/* First determine the size of the string */
7702e92ac56SJamie Gritton 	string_length = 0;
7712e92ac56SJamie Gritton 	number = 0;
7722e92ac56SJamie Gritton 	numshift = 0;
7732e92ac56SJamie Gritton 	cp = (unsigned char *) oid->elements;
7742e92ac56SJamie Gritton 	number = (unsigned long) cp[0];
7752e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number/40);
7762e92ac56SJamie Gritton 	string_length += strlen(numstr);
7772e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number%40);
7782e92ac56SJamie Gritton 	string_length += strlen(numstr);
7792e92ac56SJamie Gritton 	for (i=1; i<oid->length; i++) {
7802e92ac56SJamie Gritton 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
7812e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
7822e92ac56SJamie Gritton 			numshift += 7;
7832e92ac56SJamie Gritton 		}
7842e92ac56SJamie Gritton 		else {
7852e92ac56SJamie Gritton 			*minor_status = 0;
7862e92ac56SJamie Gritton 			return(GSS_S_FAILURE);
7872e92ac56SJamie Gritton 		}
7882e92ac56SJamie Gritton 		if ((cp[i] & 0x80) == 0) {
7892e92ac56SJamie Gritton 			sprintf(numstr, "%ld ", number);
7902e92ac56SJamie Gritton 			string_length += strlen(numstr);
7912e92ac56SJamie Gritton 			number = 0;
7922e92ac56SJamie Gritton 			numshift = 0;
7932e92ac56SJamie Gritton 		}
7942e92ac56SJamie Gritton 	}
7952e92ac56SJamie Gritton 	/*
7962e92ac56SJamie Gritton 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
7972e92ac56SJamie Gritton 	 * here for "{ " and "}\0".
7982e92ac56SJamie Gritton 	 */
7992e92ac56SJamie Gritton 	string_length += 4;
80052cab12cSRick Macklem 	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
8012e92ac56SJamie Gritton 		strcpy(bp, "{ ");
8022e92ac56SJamie Gritton 		number = (unsigned long) cp[0];
8032e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number/40);
8042e92ac56SJamie Gritton 		strcat(bp, numstr);
8052e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number%40);
8062e92ac56SJamie Gritton 		strcat(bp, numstr);
8072e92ac56SJamie Gritton 		number = 0;
8082e92ac56SJamie Gritton 		cp = (unsigned char *) oid->elements;
8092e92ac56SJamie Gritton 		for (i=1; i<oid->length; i++) {
8102e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
8112e92ac56SJamie Gritton 			if ((cp[i] & 0x80) == 0) {
8122e92ac56SJamie Gritton 				sprintf(numstr, "%ld ", number);
8132e92ac56SJamie Gritton 				strcat(bp, numstr);
8142e92ac56SJamie Gritton 				number = 0;
8152e92ac56SJamie Gritton 			}
8162e92ac56SJamie Gritton 		}
8172e92ac56SJamie Gritton 		strcat(bp, "}");
8182e92ac56SJamie Gritton 		oid_str->length = strlen(bp)+1;
8192e92ac56SJamie Gritton 		oid_str->value = (void *) bp;
8202e92ac56SJamie Gritton 		*minor_status = 0;
8212e92ac56SJamie Gritton 		return(GSS_S_COMPLETE);
8222e92ac56SJamie Gritton 	}
8232e92ac56SJamie Gritton 	*minor_status = 0;
8242e92ac56SJamie Gritton 	return(GSS_S_FAILURE);
825a9148abdSDoug Rabson }
826a9148abdSDoug Rabson #endif
827a9148abdSDoug Rabson 
828a9148abdSDoug Rabson static void
8292e92ac56SJamie Gritton svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
8302e92ac56SJamie Gritton     const gss_name_t name)
831a9148abdSDoug Rabson {
8322e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
8332e92ac56SJamie Gritton 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
8342e92ac56SJamie Gritton 	int			numgroups;
835a9148abdSDoug Rabson 
8362e92ac56SJamie Gritton 	uc->uid = 65534;
8372e92ac56SJamie Gritton 	uc->gid = 65534;
8382e92ac56SJamie Gritton 	uc->gidlist = client->cl_gid_storage;
839d4468577SJamie Gritton 
8405eff3ec6SJosh Paetzel 	numgroups = NGROUPS;
8412e92ac56SJamie Gritton 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
8422e92ac56SJamie Gritton 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
8432e92ac56SJamie Gritton 	if (GSS_ERROR(maj_stat))
8442e92ac56SJamie Gritton 		uc->gidlen = 0;
8452e92ac56SJamie Gritton 	else
8462e92ac56SJamie Gritton 		uc->gidlen = numgroups;
847d4468577SJamie Gritton }
848d4468577SJamie Gritton 
8492e92ac56SJamie Gritton static void
8502e92ac56SJamie Gritton svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
851a9148abdSDoug Rabson {
8522e92ac56SJamie Gritton 	static gss_OID_desc krb5_mech_oid =
8532e92ac56SJamie Gritton 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
854a9148abdSDoug Rabson 
855d4468577SJamie Gritton 	/*
8562e92ac56SJamie Gritton 	 * Attempt to translate mech type and service into a
8572e92ac56SJamie Gritton 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
858d4468577SJamie Gritton 	 */
8592e92ac56SJamie Gritton 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
8602e92ac56SJamie Gritton 		switch (client->cl_rawcred.service) {
8612e92ac56SJamie Gritton 		case rpc_gss_svc_default:
8622e92ac56SJamie Gritton 		case rpc_gss_svc_none:
8632e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
8642e92ac56SJamie Gritton 			break;
8652e92ac56SJamie Gritton 		case rpc_gss_svc_integrity:
8662e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
8672e92ac56SJamie Gritton 			break;
8682e92ac56SJamie Gritton 		case rpc_gss_svc_privacy:
8692e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
8702e92ac56SJamie Gritton 			break;
8712e92ac56SJamie Gritton 		}
8722e92ac56SJamie Gritton 	} else {
8732e92ac56SJamie Gritton 		client->cl_rpcflavor = RPCSEC_GSS;
8742e92ac56SJamie Gritton 	}
8752e92ac56SJamie Gritton }
8762e92ac56SJamie Gritton 
8772e92ac56SJamie Gritton static bool_t
8782e92ac56SJamie Gritton svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
8792e92ac56SJamie Gritton 			       struct svc_req *rqst,
8802e92ac56SJamie Gritton 			       struct rpc_gss_init_res *gr,
8812e92ac56SJamie Gritton 			       struct rpc_gss_cred *gc)
882a9148abdSDoug Rabson {
8832e92ac56SJamie Gritton 	gss_buffer_desc		recv_tok;
8842e92ac56SJamie Gritton 	gss_OID			mech;
8852e92ac56SJamie Gritton 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
8862e92ac56SJamie Gritton 	OM_uint32		cred_lifetime;
8872e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
888a9148abdSDoug Rabson 
8892e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
8902e92ac56SJamie Gritton 
8912e92ac56SJamie Gritton 	/* Deserialize arguments. */
8922e92ac56SJamie Gritton 	memset(&recv_tok, 0, sizeof(recv_tok));
8932e92ac56SJamie Gritton 
8942e92ac56SJamie Gritton 	if (!svc_getargs(rqst,
8952e92ac56SJamie Gritton 		(xdrproc_t) xdr_gss_buffer_desc,
8962e92ac56SJamie Gritton 		(caddr_t) &recv_tok)) {
8972e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
8982e92ac56SJamie Gritton 		return (FALSE);
899a9148abdSDoug Rabson 	}
900a9148abdSDoug Rabson 
901d4468577SJamie Gritton 	/*
9022e92ac56SJamie Gritton 	 * First time round, try all the server names we have until
9032e92ac56SJamie Gritton 	 * one matches. Afterwards, stick with that one.
904d4468577SJamie Gritton 	 */
9052e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
9062e92ac56SJamie Gritton 	if (!client->cl_sname) {
9072894c8c9SRick Macklem 		SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
9082894c8c9SRick Macklem 		    sn_link) {
9092e92ac56SJamie Gritton 			if (sname->sn_program == rqst->rq_prog
9102e92ac56SJamie Gritton 			    && sname->sn_version == rqst->rq_vers) {
9112e92ac56SJamie Gritton 			retry:
9122e92ac56SJamie Gritton 				gr->gr_major = gss_accept_sec_context(
9132e92ac56SJamie Gritton 					&gr->gr_minor,
9142e92ac56SJamie Gritton 					&client->cl_ctx,
9152e92ac56SJamie Gritton 					sname->sn_cred,
9162e92ac56SJamie Gritton 					&recv_tok,
9172e92ac56SJamie Gritton 					GSS_C_NO_CHANNEL_BINDINGS,
9182e92ac56SJamie Gritton 					&client->cl_cname,
9192e92ac56SJamie Gritton 					&mech,
9202e92ac56SJamie Gritton 					&gr->gr_token,
9212e92ac56SJamie Gritton 					&ret_flags,
9222e92ac56SJamie Gritton 					&cred_lifetime,
9232e92ac56SJamie Gritton 					&client->cl_creds);
9242e92ac56SJamie Gritton 				if (gr->gr_major ==
9252e92ac56SJamie Gritton 				    GSS_S_CREDENTIALS_EXPIRED) {
926d4468577SJamie Gritton 					/*
9272e92ac56SJamie Gritton 					 * Either our creds really did
9282e92ac56SJamie Gritton 					 * expire or gssd was
9292e92ac56SJamie Gritton 					 * restarted.
930d4468577SJamie Gritton 					 */
9312e92ac56SJamie Gritton 					if (rpc_gss_acquire_svc_cred(sname))
9322e92ac56SJamie Gritton 						goto retry;
9332e92ac56SJamie Gritton 				}
9342e92ac56SJamie Gritton 				client->cl_sname = sname;
935d4468577SJamie Gritton 				break;
936d4468577SJamie Gritton 			}
937d4468577SJamie Gritton 		}
9382e92ac56SJamie Gritton 		if (!sname) {
9392e92ac56SJamie Gritton 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
9402e92ac56SJamie Gritton 			    (char *) &recv_tok);
9412e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
9422e92ac56SJamie Gritton 			return (FALSE);
943d4468577SJamie Gritton 		}
9442e92ac56SJamie Gritton 	} else {
9452e92ac56SJamie Gritton 		gr->gr_major = gss_accept_sec_context(
9462e92ac56SJamie Gritton 			&gr->gr_minor,
9472e92ac56SJamie Gritton 			&client->cl_ctx,
9482e92ac56SJamie Gritton 			client->cl_sname->sn_cred,
9492e92ac56SJamie Gritton 			&recv_tok,
9502e92ac56SJamie Gritton 			GSS_C_NO_CHANNEL_BINDINGS,
9512e92ac56SJamie Gritton 			&client->cl_cname,
9522e92ac56SJamie Gritton 			&mech,
9532e92ac56SJamie Gritton 			&gr->gr_token,
9542e92ac56SJamie Gritton 			&ret_flags,
9552e92ac56SJamie Gritton 			&cred_lifetime,
9562e92ac56SJamie Gritton 			NULL);
957d4468577SJamie Gritton 	}
9582e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
959a9148abdSDoug Rabson 
9602e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
961d4468577SJamie Gritton 
962d4468577SJamie Gritton 	/*
9632e92ac56SJamie Gritton 	 * If we get an error from gss_accept_sec_context, send the
9642e92ac56SJamie Gritton 	 * reply anyway so that the client gets a chance to see what
9652e92ac56SJamie Gritton 	 * is wrong.
966d4468577SJamie Gritton 	 */
9672e92ac56SJamie Gritton 	if (gr->gr_major != GSS_S_COMPLETE &&
9682e92ac56SJamie Gritton 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
9692e92ac56SJamie Gritton 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
9702e92ac56SJamie Gritton 		    gr->gr_major, gr->gr_minor);
9712e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
9722e92ac56SJamie Gritton 		return (TRUE);
9732e92ac56SJamie Gritton 	}
9742e92ac56SJamie Gritton 
9752e92ac56SJamie Gritton 	gr->gr_handle.value = &client->cl_id;
9762e92ac56SJamie Gritton 	gr->gr_handle.length = sizeof(client->cl_id);
9772e92ac56SJamie Gritton 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
9782e92ac56SJamie Gritton 
9792e92ac56SJamie Gritton 	/* Save client info. */
9802e92ac56SJamie Gritton 	client->cl_mech = mech;
9812e92ac56SJamie Gritton 	client->cl_qop = GSS_C_QOP_DEFAULT;
9822e92ac56SJamie Gritton 	client->cl_done_callback = FALSE;
9832e92ac56SJamie Gritton 
9842e92ac56SJamie Gritton 	if (gr->gr_major == GSS_S_COMPLETE) {
9852e92ac56SJamie Gritton 		gss_buffer_desc	export_name;
9862e92ac56SJamie Gritton 
9872e92ac56SJamie Gritton 		/*
9882e92ac56SJamie Gritton 		 * Change client expiration time to be near when the
9892e92ac56SJamie Gritton 		 * client creds expire (or 24 hours if we can't figure
9902e92ac56SJamie Gritton 		 * that out).
9912e92ac56SJamie Gritton 		 */
9922e92ac56SJamie Gritton 		if (cred_lifetime == GSS_C_INDEFINITE)
99304cb0c38SRick Macklem 			cred_lifetime = 24*60*60;
99404cb0c38SRick Macklem 
99504cb0c38SRick Macklem 		/*
99604cb0c38SRick Macklem 		 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
99704cb0c38SRick Macklem 		 */
99804cb0c38SRick Macklem 		if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
99904cb0c38SRick Macklem 		    svc_rpc_gss_lifetime_max)
100004cb0c38SRick Macklem 			cred_lifetime = svc_rpc_gss_lifetime_max;
10012e92ac56SJamie Gritton 
10022e92ac56SJamie Gritton 		client->cl_expiration = time_uptime + cred_lifetime;
10032e92ac56SJamie Gritton 
10042e92ac56SJamie Gritton 		/*
10052e92ac56SJamie Gritton 		 * Fill in cred details in the rawcred structure.
10062e92ac56SJamie Gritton 		 */
10072e92ac56SJamie Gritton 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
10082e92ac56SJamie Gritton 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
10092e92ac56SJamie Gritton 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
10102e92ac56SJamie Gritton 		    &export_name);
10112e92ac56SJamie Gritton 		if (maj_stat != GSS_S_COMPLETE) {
10122e92ac56SJamie Gritton 			rpc_gss_log_status("gss_export_name", client->cl_mech,
10132e92ac56SJamie Gritton 			    maj_stat, min_stat);
10142e92ac56SJamie Gritton 			return (FALSE);
10152e92ac56SJamie Gritton 		}
10162e92ac56SJamie Gritton 		client->cl_rawcred.client_principal =
10172e92ac56SJamie Gritton 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
10182e92ac56SJamie Gritton 			    + export_name.length);
10192e92ac56SJamie Gritton 		client->cl_rawcred.client_principal->len = export_name.length;
10202e92ac56SJamie Gritton 		memcpy(client->cl_rawcred.client_principal->name,
10212e92ac56SJamie Gritton 		    export_name.value, export_name.length);
10222e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &export_name);
10232e92ac56SJamie Gritton 		client->cl_rawcred.svc_principal =
10242e92ac56SJamie Gritton 			client->cl_sname->sn_principal;
10252e92ac56SJamie Gritton 		client->cl_rawcred.service = gc->gc_svc;
10262e92ac56SJamie Gritton 
10272e92ac56SJamie Gritton 		/*
10282e92ac56SJamie Gritton 		 * Use gss_pname_to_uid to map to unix creds. For
10292e92ac56SJamie Gritton 		 * kerberos5, this uses krb5_aname_to_localname.
10302e92ac56SJamie Gritton 		 */
10312e92ac56SJamie Gritton 		svc_rpc_gss_build_ucred(client, client->cl_cname);
10322e92ac56SJamie Gritton 		svc_rpc_gss_set_flavor(client);
10332e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
10342e92ac56SJamie Gritton 
10352e92ac56SJamie Gritton #ifdef DEBUG
1036a9148abdSDoug Rabson 		{
10372e92ac56SJamie Gritton 			gss_buffer_desc mechname;
1038a9148abdSDoug Rabson 
10392e92ac56SJamie Gritton 			gss_oid_to_str(&min_stat, mech, &mechname);
10402e92ac56SJamie Gritton 
10412e92ac56SJamie Gritton 			rpc_gss_log_debug("accepted context for %s with "
10422e92ac56SJamie Gritton 			    "<mech %.*s, qop %d, svc %d>",
10432e92ac56SJamie Gritton 			    client->cl_rawcred.client_principal->name,
10442e92ac56SJamie Gritton 			    mechname.length, (char *)mechname.value,
10453d26cd60SBrooks Davis 			    client->cl_qop, client->cl_rawcred.service);
10462e92ac56SJamie Gritton 
10472e92ac56SJamie Gritton 			gss_release_buffer(&min_stat, &mechname);
1048d4468577SJamie Gritton 		}
10492e92ac56SJamie Gritton #endif /* DEBUG */
1050d4468577SJamie Gritton 	}
10512e92ac56SJamie Gritton 	return (TRUE);
1052a9148abdSDoug Rabson }
1053a9148abdSDoug Rabson 
10542e92ac56SJamie Gritton static bool_t
10552e92ac56SJamie Gritton svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
105605496254SRick Macklem     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1057a9148abdSDoug Rabson {
10582e92ac56SJamie Gritton 	struct opaque_auth	*oa;
10592e92ac56SJamie Gritton 	gss_buffer_desc		 rpcbuf, checksum;
10602e92ac56SJamie Gritton 	OM_uint32		 maj_stat, min_stat;
10612e92ac56SJamie Gritton 	gss_qop_t		 qop_state;
10622e92ac56SJamie Gritton 	int32_t			 rpchdr[128 / sizeof(int32_t)];
10632e92ac56SJamie Gritton 	int32_t			*buf;
1064a9148abdSDoug Rabson 
10652e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
10662e92ac56SJamie Gritton 
10672e92ac56SJamie Gritton 	memset(rpchdr, 0, sizeof(rpchdr));
10682e92ac56SJamie Gritton 
10692e92ac56SJamie Gritton 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
10702e92ac56SJamie Gritton 	buf = rpchdr;
10712e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_xid);
10722e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, msg->rm_direction);
10732e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
10742e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
10752e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
10762e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
10772e92ac56SJamie Gritton 	oa = &msg->rm_call.cb_cred;
10782e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
10792e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, oa->oa_length);
10802e92ac56SJamie Gritton 	if (oa->oa_length) {
10812e92ac56SJamie Gritton 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
10822e92ac56SJamie Gritton 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1083a9148abdSDoug Rabson 	}
10842e92ac56SJamie Gritton 	rpcbuf.value = rpchdr;
10852e92ac56SJamie Gritton 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
10862e92ac56SJamie Gritton 
10872e92ac56SJamie Gritton 	checksum.value = msg->rm_call.cb_verf.oa_base;
10882e92ac56SJamie Gritton 	checksum.length = msg->rm_call.cb_verf.oa_length;
10892e92ac56SJamie Gritton 
10902e92ac56SJamie Gritton 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
10912e92ac56SJamie Gritton 				  &qop_state);
10922e92ac56SJamie Gritton 
10932e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
10942e92ac56SJamie Gritton 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
10952e92ac56SJamie Gritton 		    maj_stat, min_stat);
1096798a34feSRick Macklem 		/*
10971e0706fdSRick Macklem 		 * A bug in some versions of the Linux client generates a
10981e0706fdSRick Macklem 		 * Destroy operation with a bogus encrypted checksum. Deleting
10991e0706fdSRick Macklem 		 * the credential handle for that case causes the mount to fail.
1100798a34feSRick Macklem 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1101798a34feSRick Macklem 		 * doesn't make sense to destroy the handle and not doing so
1102798a34feSRick Macklem 		 * fixes the Linux mount.
1103798a34feSRick Macklem 		 */
110405496254SRick Macklem 		if (gcproc != RPCSEC_GSS_DESTROY)
11052e92ac56SJamie Gritton 			client->cl_state = CLIENT_STALE;
11062e92ac56SJamie Gritton 		return (FALSE);
1107d4468577SJamie Gritton 	}
1108d4468577SJamie Gritton 
11092e92ac56SJamie Gritton 	*qop = qop_state;
11102e92ac56SJamie Gritton 	return (TRUE);
11112e92ac56SJamie Gritton }
11122e92ac56SJamie Gritton 
11132e92ac56SJamie Gritton static bool_t
11142e92ac56SJamie Gritton svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
11152e92ac56SJamie Gritton     struct svc_req *rqst, u_int seq)
11162e92ac56SJamie Gritton {
11172e92ac56SJamie Gritton 	gss_buffer_desc		signbuf;
11182e92ac56SJamie Gritton 	gss_buffer_desc		mic;
11192e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
11202e92ac56SJamie Gritton 	uint32_t		nseq;
11212e92ac56SJamie Gritton 
11222e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
11232e92ac56SJamie Gritton 
11242e92ac56SJamie Gritton 	nseq = htonl(seq);
11252e92ac56SJamie Gritton 	signbuf.value = &nseq;
11262e92ac56SJamie Gritton 	signbuf.length = sizeof(nseq);
11272e92ac56SJamie Gritton 
11282e92ac56SJamie Gritton 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
11292e92ac56SJamie Gritton 	    &signbuf, &mic);
11302e92ac56SJamie Gritton 
11312e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
11322e92ac56SJamie Gritton 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
11332e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
11342e92ac56SJamie Gritton 		return (FALSE);
11352e92ac56SJamie Gritton 	}
11362e92ac56SJamie Gritton 
11372e92ac56SJamie Gritton 	KASSERT(mic.length <= MAX_AUTH_BYTES,
11382e92ac56SJamie Gritton 	    ("MIC too large for RPCSEC_GSS"));
11392e92ac56SJamie Gritton 
11402e92ac56SJamie Gritton 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
11412e92ac56SJamie Gritton 	rqst->rq_verf.oa_length = mic.length;
11422e92ac56SJamie Gritton 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
11432e92ac56SJamie Gritton 
11442e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &mic);
11452e92ac56SJamie Gritton 
11462e92ac56SJamie Gritton 	return (TRUE);
11472e92ac56SJamie Gritton }
11482e92ac56SJamie Gritton 
11492e92ac56SJamie Gritton static bool_t
11502e92ac56SJamie Gritton svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
11512e92ac56SJamie Gritton {
11522e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
11532e92ac56SJamie Gritton 	rpc_gss_lock_t	lock;
11542e92ac56SJamie Gritton 	void		*cookie;
11552e92ac56SJamie Gritton 	bool_t		cb_res;
11562e92ac56SJamie Gritton 	bool_t		result;
11572e92ac56SJamie Gritton 
11582e92ac56SJamie Gritton 	/*
11592e92ac56SJamie Gritton 	 * See if we have a callback for this guy.
11602e92ac56SJamie Gritton 	 */
11612e92ac56SJamie Gritton 	result = TRUE;
11622894c8c9SRick Macklem 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
11632e92ac56SJamie Gritton 		if (scb->cb_callback.program == rqst->rq_prog
11642e92ac56SJamie Gritton 		    && scb->cb_callback.version == rqst->rq_vers) {
11652e92ac56SJamie Gritton 			/*
11662e92ac56SJamie Gritton 			 * This one matches. Call the callback and see
11672e92ac56SJamie Gritton 			 * if it wants to veto or something.
11682e92ac56SJamie Gritton 			 */
11692e92ac56SJamie Gritton 			lock.locked = FALSE;
11702e92ac56SJamie Gritton 			lock.raw_cred = &client->cl_rawcred;
11712e92ac56SJamie Gritton 			cb_res = scb->cb_callback.callback(rqst,
11722e92ac56SJamie Gritton 			    client->cl_creds,
11732e92ac56SJamie Gritton 			    client->cl_ctx,
11742e92ac56SJamie Gritton 			    &lock,
11752e92ac56SJamie Gritton 			    &cookie);
11762e92ac56SJamie Gritton 
11772e92ac56SJamie Gritton 			if (!cb_res) {
11782e92ac56SJamie Gritton 				client->cl_state = CLIENT_STALE;
11792e92ac56SJamie Gritton 				result = FALSE;
11802e92ac56SJamie Gritton 				break;
11812e92ac56SJamie Gritton 			}
11822e92ac56SJamie Gritton 
11832e92ac56SJamie Gritton 			/*
11842e92ac56SJamie Gritton 			 * The callback accepted the connection - it
11852e92ac56SJamie Gritton 			 * is responsible for freeing client->cl_creds
11862e92ac56SJamie Gritton 			 * now.
11872e92ac56SJamie Gritton 			 */
11882e92ac56SJamie Gritton 			client->cl_creds = GSS_C_NO_CREDENTIAL;
11892e92ac56SJamie Gritton 			client->cl_locked = lock.locked;
11902e92ac56SJamie Gritton 			client->cl_cookie = cookie;
11912e92ac56SJamie Gritton 			return (TRUE);
11922e92ac56SJamie Gritton 		}
11932e92ac56SJamie Gritton 	}
11942e92ac56SJamie Gritton 
11952e92ac56SJamie Gritton 	/*
11962e92ac56SJamie Gritton 	 * Either no callback exists for this program/version or one
11972e92ac56SJamie Gritton 	 * of the callbacks rejected the connection. We just need to
11982e92ac56SJamie Gritton 	 * clean up the delegated client creds, if any.
11992e92ac56SJamie Gritton 	 */
12002e92ac56SJamie Gritton 	if (client->cl_creds) {
12012e92ac56SJamie Gritton 		OM_uint32 min_ver;
12022e92ac56SJamie Gritton 		gss_release_cred(&min_ver, &client->cl_creds);
12032e92ac56SJamie Gritton 	}
12042e92ac56SJamie Gritton 	return (result);
12052e92ac56SJamie Gritton }
12062e92ac56SJamie Gritton 
12072e92ac56SJamie Gritton static bool_t
12082e92ac56SJamie Gritton svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
12092e92ac56SJamie Gritton {
1210bcd0e31dSJohn Baldwin 	uint32_t offset;
12112e92ac56SJamie Gritton 	int word, bit;
12122e92ac56SJamie Gritton 	bool_t result;
12132e92ac56SJamie Gritton 
12142e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
12152e92ac56SJamie Gritton 	if (seq <= client->cl_seqlast) {
12162e92ac56SJamie Gritton 		/*
12172e92ac56SJamie Gritton 		 * The request sequence number is less than
12182e92ac56SJamie Gritton 		 * the largest we have seen so far. If it is
12192e92ac56SJamie Gritton 		 * outside the window or if we have seen a
12202e92ac56SJamie Gritton 		 * request with this sequence before, silently
12212e92ac56SJamie Gritton 		 * discard it.
12222e92ac56SJamie Gritton 		 */
12232e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
12242e92ac56SJamie Gritton 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
12252e92ac56SJamie Gritton 			result = FALSE;
12262e92ac56SJamie Gritton 			goto out;
12272e92ac56SJamie Gritton 		}
12282e92ac56SJamie Gritton 		word = offset / 32;
12292e92ac56SJamie Gritton 		bit = offset % 32;
12302e92ac56SJamie Gritton 		if (client->cl_seqmask[word] & (1 << bit)) {
12312e92ac56SJamie Gritton 			result = FALSE;
12322e92ac56SJamie Gritton 			goto out;
12332e92ac56SJamie Gritton 		}
12342e92ac56SJamie Gritton 	}
12352e92ac56SJamie Gritton 
12362e92ac56SJamie Gritton 	result = TRUE;
12372e92ac56SJamie Gritton out:
12382e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
12392e92ac56SJamie Gritton 	return (result);
12402e92ac56SJamie Gritton }
12412e92ac56SJamie Gritton 
12422e92ac56SJamie Gritton static void
12432e92ac56SJamie Gritton svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
12442e92ac56SJamie Gritton {
12452e92ac56SJamie Gritton 	int offset, i, word, bit;
12462e92ac56SJamie Gritton 	uint32_t carry, newcarry;
12472e92ac56SJamie Gritton 
12482e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
12492e92ac56SJamie Gritton 	if (seq > client->cl_seqlast) {
12502e92ac56SJamie Gritton 		/*
12512e92ac56SJamie Gritton 		 * This request has a sequence number greater
12522e92ac56SJamie Gritton 		 * than any we have seen so far. Advance the
12532e92ac56SJamie Gritton 		 * seq window and set bit zero of the window
12542e92ac56SJamie Gritton 		 * (which corresponds to the new sequence
12552e92ac56SJamie Gritton 		 * number)
12562e92ac56SJamie Gritton 		 */
12572e92ac56SJamie Gritton 		offset = seq - client->cl_seqlast;
12582e92ac56SJamie Gritton 		while (offset > 32) {
12592e92ac56SJamie Gritton 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
12602e92ac56SJamie Gritton 			     i > 0; i--) {
12612e92ac56SJamie Gritton 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
12622e92ac56SJamie Gritton 			}
12632e92ac56SJamie Gritton 			client->cl_seqmask[0] = 0;
12642e92ac56SJamie Gritton 			offset -= 32;
12652e92ac56SJamie Gritton 		}
12662e92ac56SJamie Gritton 		carry = 0;
12672e92ac56SJamie Gritton 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
12682e92ac56SJamie Gritton 			newcarry = client->cl_seqmask[i] >> (32 - offset);
12692e92ac56SJamie Gritton 			client->cl_seqmask[i] =
12702e92ac56SJamie Gritton 				(client->cl_seqmask[i] << offset) | carry;
12712e92ac56SJamie Gritton 			carry = newcarry;
12722e92ac56SJamie Gritton 		}
12732e92ac56SJamie Gritton 		client->cl_seqmask[0] |= 1;
12742e92ac56SJamie Gritton 		client->cl_seqlast = seq;
12752e92ac56SJamie Gritton 	} else {
12762e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
12772e92ac56SJamie Gritton 		word = offset / 32;
12782e92ac56SJamie Gritton 		bit = offset % 32;
12792e92ac56SJamie Gritton 		client->cl_seqmask[word] |= (1 << bit);
12802e92ac56SJamie Gritton 	}
12812e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
12822e92ac56SJamie Gritton }
12832e92ac56SJamie Gritton 
12842e92ac56SJamie Gritton enum auth_stat
12852e92ac56SJamie Gritton svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
12862e92ac56SJamie Gritton 
12872e92ac56SJamie Gritton {
12882e92ac56SJamie Gritton 	OM_uint32		 min_stat;
12892e92ac56SJamie Gritton 	XDR	 		 xdrs;
12902e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
12912e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
12922e92ac56SJamie Gritton 	struct rpc_gss_cred	 gc;
12932e92ac56SJamie Gritton 	struct rpc_gss_init_res	 gr;
12942e92ac56SJamie Gritton 	gss_qop_t		 qop;
12952e92ac56SJamie Gritton 	int			 call_stat;
12962e92ac56SJamie Gritton 	enum auth_stat		 result;
12972e92ac56SJamie Gritton 
12982894c8c9SRick Macklem 	KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
12992e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss()");
13002e92ac56SJamie Gritton 
13012e92ac56SJamie Gritton 	/* Garbage collect old clients. */
13022e92ac56SJamie Gritton 	svc_rpc_gss_timeout_clients();
13032e92ac56SJamie Gritton 
13042e92ac56SJamie Gritton 	/* Initialize reply. */
13052e92ac56SJamie Gritton 	rqst->rq_verf = _null_auth;
13062e92ac56SJamie Gritton 
13072e92ac56SJamie Gritton 	/* Deserialize client credentials. */
13082894c8c9SRick Macklem 	if (rqst->rq_cred.oa_length <= 0) {
13092894c8c9SRick Macklem 		KGSS_CURVNET_RESTORE();
13102e92ac56SJamie Gritton 		return (AUTH_BADCRED);
13112894c8c9SRick Macklem 	}
13122e92ac56SJamie Gritton 
13132e92ac56SJamie Gritton 	memset(&gc, 0, sizeof(gc));
13142e92ac56SJamie Gritton 
13152e92ac56SJamie Gritton 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
13162e92ac56SJamie Gritton 	    rqst->rq_cred.oa_length, XDR_DECODE);
13172e92ac56SJamie Gritton 
13182e92ac56SJamie Gritton 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
13192e92ac56SJamie Gritton 		XDR_DESTROY(&xdrs);
13202894c8c9SRick Macklem 		KGSS_CURVNET_RESTORE();
13212e92ac56SJamie Gritton 		return (AUTH_BADCRED);
13222e92ac56SJamie Gritton 	}
13232e92ac56SJamie Gritton 	XDR_DESTROY(&xdrs);
13242e92ac56SJamie Gritton 
13252e92ac56SJamie Gritton 	client = NULL;
13262e92ac56SJamie Gritton 
13272e92ac56SJamie Gritton 	/* Check version. */
13282e92ac56SJamie Gritton 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
13292e92ac56SJamie Gritton 		result = AUTH_BADCRED;
13302e92ac56SJamie Gritton 		goto out;
13312e92ac56SJamie Gritton 	}
13322e92ac56SJamie Gritton 
13332e92ac56SJamie Gritton 	/* Check the proc and find the client (or create it) */
13342e92ac56SJamie Gritton 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
13352e92ac56SJamie Gritton 		if (gc.gc_handle.length != 0) {
13362e92ac56SJamie Gritton 			result = AUTH_BADCRED;
13372e92ac56SJamie Gritton 			goto out;
13382e92ac56SJamie Gritton 		}
13392e92ac56SJamie Gritton 		client = svc_rpc_gss_create_client();
13402e92ac56SJamie Gritton 	} else {
13412e92ac56SJamie Gritton 		struct svc_rpc_gss_clientid *p;
13422e92ac56SJamie Gritton 		if (gc.gc_handle.length != sizeof(*p)) {
13432e92ac56SJamie Gritton 			result = AUTH_BADCRED;
13442e92ac56SJamie Gritton 			goto out;
13452e92ac56SJamie Gritton 		}
13462e92ac56SJamie Gritton 		p = gc.gc_handle.value;
13472e92ac56SJamie Gritton 		client = svc_rpc_gss_find_client(p);
13482e92ac56SJamie Gritton 		if (!client) {
13492e92ac56SJamie Gritton 			/*
13502e92ac56SJamie Gritton 			 * Can't find the client - we may have
13512e92ac56SJamie Gritton 			 * destroyed it - tell the other side to
13522e92ac56SJamie Gritton 			 * re-authenticate.
13532e92ac56SJamie Gritton 			 */
13542e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
13552e92ac56SJamie Gritton 			goto out;
13562e92ac56SJamie Gritton 		}
13572e92ac56SJamie Gritton 	}
13582e92ac56SJamie Gritton 	cc = rqst->rq_clntcred;
13592e92ac56SJamie Gritton 	cc->cc_client = client;
13602e92ac56SJamie Gritton 	cc->cc_service = gc.gc_svc;
13612e92ac56SJamie Gritton 	cc->cc_seq = gc.gc_seq;
13622e92ac56SJamie Gritton 
13632e92ac56SJamie Gritton 	/*
13642e92ac56SJamie Gritton 	 * The service and sequence number must be ignored for
13652e92ac56SJamie Gritton 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
13662e92ac56SJamie Gritton 	 */
13672e92ac56SJamie Gritton 	if (gc.gc_proc != RPCSEC_GSS_INIT
13682e92ac56SJamie Gritton 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
13692e92ac56SJamie Gritton 		/*
13702e92ac56SJamie Gritton 		 * Check for sequence number overflow.
13712e92ac56SJamie Gritton 		 */
13722e92ac56SJamie Gritton 		if (gc.gc_seq >= MAXSEQ) {
13732e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
13742e92ac56SJamie Gritton 			goto out;
13752e92ac56SJamie Gritton 		}
13762e92ac56SJamie Gritton 
13772e92ac56SJamie Gritton 		/*
13782e92ac56SJamie Gritton 		 * Check for valid service.
13792e92ac56SJamie Gritton 		 */
13802e92ac56SJamie Gritton 		if (gc.gc_svc != rpc_gss_svc_none &&
13812e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_integrity &&
13822e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_privacy) {
13832e92ac56SJamie Gritton 			result = AUTH_BADCRED;
13842e92ac56SJamie Gritton 			goto out;
13852e92ac56SJamie Gritton 		}
13862e92ac56SJamie Gritton 	}
13872e92ac56SJamie Gritton 
13882e92ac56SJamie Gritton 	/* Handle RPCSEC_GSS control procedure. */
13892e92ac56SJamie Gritton 	switch (gc.gc_proc) {
13902e92ac56SJamie Gritton 
13912e92ac56SJamie Gritton 	case RPCSEC_GSS_INIT:
13922e92ac56SJamie Gritton 	case RPCSEC_GSS_CONTINUE_INIT:
13932e92ac56SJamie Gritton 		if (rqst->rq_proc != NULLPROC) {
13942e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
13952e92ac56SJamie Gritton 			break;
13962e92ac56SJamie Gritton 		}
13972e92ac56SJamie Gritton 
13982e92ac56SJamie Gritton 		memset(&gr, 0, sizeof(gr));
13992e92ac56SJamie Gritton 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
14002e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
14012e92ac56SJamie Gritton 			break;
14022e92ac56SJamie Gritton 		}
14032e92ac56SJamie Gritton 
14042e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE) {
14052e92ac56SJamie Gritton 			/*
14062e92ac56SJamie Gritton 			 * We borrow the space for the call verf to
14072e92ac56SJamie Gritton 			 * pack our reply verf.
14082e92ac56SJamie Gritton 			 */
14092e92ac56SJamie Gritton 			rqst->rq_verf = msg->rm_call.cb_verf;
14102e92ac56SJamie Gritton 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
14112e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
14122e92ac56SJamie Gritton 				break;
14132e92ac56SJamie Gritton 			}
14142e92ac56SJamie Gritton 		} else {
14152e92ac56SJamie Gritton 			rqst->rq_verf = _null_auth;
14162e92ac56SJamie Gritton 		}
14172e92ac56SJamie Gritton 
14182e92ac56SJamie Gritton 		call_stat = svc_sendreply(rqst,
14192e92ac56SJamie Gritton 		    (xdrproc_t) xdr_rpc_gss_init_res,
14202e92ac56SJamie Gritton 		    (caddr_t) &gr);
14212e92ac56SJamie Gritton 
14222e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &gr.gr_token);
14232e92ac56SJamie Gritton 
14242e92ac56SJamie Gritton 		if (!call_stat) {
14252e92ac56SJamie Gritton 			result = AUTH_FAILED;
14262e92ac56SJamie Gritton 			break;
14272e92ac56SJamie Gritton 		}
14282e92ac56SJamie Gritton 
14292e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE)
14302e92ac56SJamie Gritton 			client->cl_state = CLIENT_ESTABLISHED;
14312e92ac56SJamie Gritton 
14322e92ac56SJamie Gritton 		result = RPCSEC_GSS_NODISPATCH;
14332e92ac56SJamie Gritton 		break;
14342e92ac56SJamie Gritton 
14352e92ac56SJamie Gritton 	case RPCSEC_GSS_DATA:
14362e92ac56SJamie Gritton 	case RPCSEC_GSS_DESTROY:
14372e92ac56SJamie Gritton 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
14382e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
14392e92ac56SJamie Gritton 			break;
14402e92ac56SJamie Gritton 		}
14412e92ac56SJamie Gritton 
144205496254SRick Macklem 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
14432e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
14442e92ac56SJamie Gritton 			break;
14452e92ac56SJamie Gritton 		}
14462e92ac56SJamie Gritton 
14472e92ac56SJamie Gritton 		/*
14482e92ac56SJamie Gritton 		 * We borrow the space for the call verf to pack our
14492e92ac56SJamie Gritton 		 * reply verf.
14502e92ac56SJamie Gritton 		 */
14512e92ac56SJamie Gritton 		rqst->rq_verf = msg->rm_call.cb_verf;
14522e92ac56SJamie Gritton 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
14532e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
14542e92ac56SJamie Gritton 			break;
14552e92ac56SJamie Gritton 		}
14562e92ac56SJamie Gritton 
14572e92ac56SJamie Gritton 		svc_rpc_gss_update_seq(client, gc.gc_seq);
14582e92ac56SJamie Gritton 
14592e92ac56SJamie Gritton 		/*
14602e92ac56SJamie Gritton 		 * Change the SVCAUTH ops on the request to point at
14612e92ac56SJamie Gritton 		 * our own code so that we can unwrap the arguments
14622e92ac56SJamie Gritton 		 * and wrap the result. The caller will re-set this on
14632e92ac56SJamie Gritton 		 * every request to point to a set of null wrap/unwrap
14642e92ac56SJamie Gritton 		 * methods. Acquire an extra reference to the client
14652e92ac56SJamie Gritton 		 * which will be released by svc_rpc_gss_release()
14662e92ac56SJamie Gritton 		 * after the request has finished processing.
14672e92ac56SJamie Gritton 		 */
14682e92ac56SJamie Gritton 		refcount_acquire(&client->cl_refs);
14692e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
14702e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_private = cc;
14712e92ac56SJamie Gritton 
14722e92ac56SJamie Gritton 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
14732e92ac56SJamie Gritton 			/*
14742e92ac56SJamie Gritton 			 * We might be ready to do a callback to the server to
14752e92ac56SJamie Gritton 			 * see if it wants to accept/reject the connection.
14762e92ac56SJamie Gritton 			 */
14772e92ac56SJamie Gritton 			sx_xlock(&client->cl_lock);
14782e92ac56SJamie Gritton 			if (!client->cl_done_callback) {
14792e92ac56SJamie Gritton 				client->cl_done_callback = TRUE;
14802e92ac56SJamie Gritton 				client->cl_qop = qop;
14812e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
14822e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
14832e92ac56SJamie Gritton 				if (!svc_rpc_gss_callback(client, rqst)) {
14842e92ac56SJamie Gritton 					result = AUTH_REJECTEDCRED;
14852e92ac56SJamie Gritton 					sx_xunlock(&client->cl_lock);
14862e92ac56SJamie Gritton 					break;
14872e92ac56SJamie Gritton 				}
14882e92ac56SJamie Gritton 			}
14892e92ac56SJamie Gritton 			sx_xunlock(&client->cl_lock);
14902e92ac56SJamie Gritton 
14912e92ac56SJamie Gritton 			/*
14922e92ac56SJamie Gritton 			 * If the server has locked this client to a
14932e92ac56SJamie Gritton 			 * particular service+qop pair, enforce that
14942e92ac56SJamie Gritton 			 * restriction now.
14952e92ac56SJamie Gritton 			 */
14962e92ac56SJamie Gritton 			if (client->cl_locked) {
14972e92ac56SJamie Gritton 				if (client->cl_rawcred.service != gc.gc_svc) {
14982e92ac56SJamie Gritton 					result = AUTH_FAILED;
14992e92ac56SJamie Gritton 					break;
15002e92ac56SJamie Gritton 				} else if (client->cl_qop != qop) {
15012e92ac56SJamie Gritton 					result = AUTH_BADVERF;
15022e92ac56SJamie Gritton 					break;
15032e92ac56SJamie Gritton 				}
15042e92ac56SJamie Gritton 			}
15052e92ac56SJamie Gritton 
15062e92ac56SJamie Gritton 			/*
15072e92ac56SJamie Gritton 			 * If the qop changed, look up the new qop
15082e92ac56SJamie Gritton 			 * name for rawcred.
15092e92ac56SJamie Gritton 			 */
15102e92ac56SJamie Gritton 			if (client->cl_qop != qop) {
15112e92ac56SJamie Gritton 				client->cl_qop = qop;
15122e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
15132e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
15142e92ac56SJamie Gritton 			}
15152e92ac56SJamie Gritton 
15162e92ac56SJamie Gritton 			/*
15172e92ac56SJamie Gritton 			 * Make sure we use the right service value
15182e92ac56SJamie Gritton 			 * for unwrap/wrap.
15192e92ac56SJamie Gritton 			 */
15202e92ac56SJamie Gritton 			if (client->cl_rawcred.service != gc.gc_svc) {
15212e92ac56SJamie Gritton 				client->cl_rawcred.service = gc.gc_svc;
15222e92ac56SJamie Gritton 				svc_rpc_gss_set_flavor(client);
15232e92ac56SJamie Gritton 			}
15242e92ac56SJamie Gritton 
15252e92ac56SJamie Gritton 			result = AUTH_OK;
15262e92ac56SJamie Gritton 		} else {
15272e92ac56SJamie Gritton 			if (rqst->rq_proc != NULLPROC) {
15282e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
15292e92ac56SJamie Gritton 				break;
15302e92ac56SJamie Gritton 			}
15312e92ac56SJamie Gritton 
15322e92ac56SJamie Gritton 			call_stat = svc_sendreply(rqst,
15332e92ac56SJamie Gritton 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
15342e92ac56SJamie Gritton 
15352e92ac56SJamie Gritton 			if (!call_stat) {
15362e92ac56SJamie Gritton 				result = AUTH_FAILED;
15372e92ac56SJamie Gritton 				break;
15382e92ac56SJamie Gritton 			}
15392e92ac56SJamie Gritton 
15402e92ac56SJamie Gritton 			svc_rpc_gss_forget_client(client);
15412e92ac56SJamie Gritton 
15422e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
15432e92ac56SJamie Gritton 			break;
15442e92ac56SJamie Gritton 		}
15452e92ac56SJamie Gritton 		break;
15462e92ac56SJamie Gritton 
15472e92ac56SJamie Gritton 	default:
15482e92ac56SJamie Gritton 		result = AUTH_BADCRED;
15492e92ac56SJamie Gritton 		break;
15502e92ac56SJamie Gritton 	}
15512e92ac56SJamie Gritton out:
15522e92ac56SJamie Gritton 	if (client)
15532e92ac56SJamie Gritton 		svc_rpc_gss_release_client(client);
15542e92ac56SJamie Gritton 
15552e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
15562894c8c9SRick Macklem 	KGSS_CURVNET_RESTORE();
15572e92ac56SJamie Gritton 	return (result);
15582e92ac56SJamie Gritton }
15592e92ac56SJamie Gritton 
15602e92ac56SJamie Gritton static bool_t
15612e92ac56SJamie Gritton svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
15622e92ac56SJamie Gritton {
15632e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
15642e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
15652e92ac56SJamie Gritton 
15662e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_wrap()");
15672e92ac56SJamie Gritton 
15682e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
15692e92ac56SJamie Gritton 	client = cc->cc_client;
15702e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
15712e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
15722e92ac56SJamie Gritton 		return (TRUE);
15732e92ac56SJamie Gritton 	}
15742e92ac56SJamie Gritton 
15752e92ac56SJamie Gritton 	return (xdr_rpc_gss_wrap_data(mp,
15762e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
15772e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
15782e92ac56SJamie Gritton }
15792e92ac56SJamie Gritton 
15802e92ac56SJamie Gritton static bool_t
15812e92ac56SJamie Gritton svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
15822e92ac56SJamie Gritton {
15832e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
15842e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
15852e92ac56SJamie Gritton 
15862e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
15872e92ac56SJamie Gritton 
15882e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
15892e92ac56SJamie Gritton 	client = cc->cc_client;
15902e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
15912e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none) {
15922e92ac56SJamie Gritton 		return (TRUE);
15932e92ac56SJamie Gritton 	}
15942e92ac56SJamie Gritton 
15952e92ac56SJamie Gritton 	return (xdr_rpc_gss_unwrap_data(mp,
15962e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
15972e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
15982e92ac56SJamie Gritton }
15992e92ac56SJamie Gritton 
16002e92ac56SJamie Gritton static void
16012e92ac56SJamie Gritton svc_rpc_gss_release(SVCAUTH *auth)
16022e92ac56SJamie Gritton {
16032e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
16042e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
16052e92ac56SJamie Gritton 
16062e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_release()");
16072e92ac56SJamie Gritton 
16082e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
16092e92ac56SJamie Gritton 	client = cc->cc_client;
16102e92ac56SJamie Gritton 	svc_rpc_gss_release_client(client);
16112e92ac56SJamie Gritton }
1612