xref: /freebsd/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision cfbe7a62dc62e8a5d7520cb5eb8ad7c4a9418e26)
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/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 
9020d728b5SMark Johnston static const struct svc_auth_ops svc_auth_gss_ops = {
9120d728b5SMark Johnston 	.svc_ah_wrap =		svc_rpc_gss_wrap,
9220d728b5SMark Johnston 	.svc_ah_unwrap =	svc_rpc_gss_unwrap,
9320d728b5SMark Johnston 	.svc_ah_release =	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 };
1022894c8c9SRick Macklem SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback);
1032894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list,
1042894c8c9SRick Macklem     svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
1052e92ac56SJamie Gritton 
1062e92ac56SJamie Gritton struct svc_rpc_gss_svc_name {
1072e92ac56SJamie Gritton 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
1082e92ac56SJamie Gritton 	char			*sn_principal;
1092e92ac56SJamie Gritton 	gss_OID			sn_mech;
1102e92ac56SJamie Gritton 	u_int			sn_req_time;
1112e92ac56SJamie Gritton 	gss_cred_id_t		sn_cred;
1122e92ac56SJamie Gritton 	u_int			sn_program;
1132e92ac56SJamie Gritton 	u_int			sn_version;
1142e92ac56SJamie Gritton };
1152894c8c9SRick Macklem SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name);
1162894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list,
1172894c8c9SRick Macklem     svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
1182e92ac56SJamie Gritton 
1192e92ac56SJamie Gritton enum svc_rpc_gss_client_state {
1202e92ac56SJamie Gritton 	CLIENT_NEW,				/* still authenticating */
1212e92ac56SJamie Gritton 	CLIENT_ESTABLISHED,			/* context established */
1222e92ac56SJamie Gritton 	CLIENT_STALE				/* garbage to collect */
123d4468577SJamie Gritton };
124a9148abdSDoug Rabson 
1252e92ac56SJamie Gritton #define SVC_RPC_GSS_SEQWINDOW	128
1262e92ac56SJamie Gritton 
1272e92ac56SJamie Gritton struct svc_rpc_gss_clientid {
1282e92ac56SJamie Gritton 	unsigned long		ci_hostid;
1292e92ac56SJamie Gritton 	uint32_t		ci_boottime;
1302e92ac56SJamie Gritton 	uint32_t		ci_id;
1312e92ac56SJamie Gritton };
1322e92ac56SJamie Gritton 
1332e92ac56SJamie Gritton struct svc_rpc_gss_client {
1342e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
1352e92ac56SJamie Gritton 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
1362e92ac56SJamie Gritton 	volatile u_int		cl_refs;
1372e92ac56SJamie Gritton 	struct sx		cl_lock;
1382e92ac56SJamie Gritton 	struct svc_rpc_gss_clientid cl_id;
1392e92ac56SJamie Gritton 	time_t			cl_expiration;	/* when to gc */
1402e92ac56SJamie Gritton 	enum svc_rpc_gss_client_state cl_state;	/* client state */
1412e92ac56SJamie Gritton 	bool_t			cl_locked;	/* fixed service+qop */
1422e92ac56SJamie Gritton 	gss_ctx_id_t		cl_ctx;		/* context id */
1432e92ac56SJamie Gritton 	gss_cred_id_t		cl_creds;	/* delegated creds */
1442e92ac56SJamie Gritton 	gss_name_t		cl_cname;	/* client name */
1452e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
1462e92ac56SJamie Gritton 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
1472e92ac56SJamie Gritton 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
1482e92ac56SJamie Gritton 	struct ucred		*cl_cred;	/* kernel-style credentials */
1492e92ac56SJamie Gritton 	int			cl_rpcflavor;	/* RPC pseudo sec flavor */
1502e92ac56SJamie Gritton 	bool_t			cl_done_callback; /* TRUE after call */
1512e92ac56SJamie Gritton 	void			*cl_cookie;	/* user cookie from callback */
1525eff3ec6SJosh Paetzel 	gid_t			cl_gid_storage[NGROUPS];
1532e92ac56SJamie Gritton 	gss_OID			cl_mech;	/* mechanism */
1542e92ac56SJamie Gritton 	gss_qop_t		cl_qop;		/* quality of protection */
1552e92ac56SJamie Gritton 	uint32_t		cl_seqlast;	/* sequence window origin */
1562e92ac56SJamie Gritton 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
1572e92ac56SJamie Gritton };
1582e92ac56SJamie Gritton TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
1592e92ac56SJamie Gritton 
160a9148abdSDoug Rabson /*
1612e92ac56SJamie Gritton  * This structure holds enough information to unwrap arguments or wrap
1622e92ac56SJamie Gritton  * results for a given request. We use the rq_clntcred area for this
1632e92ac56SJamie Gritton  * (which is a per-request buffer).
1642e92ac56SJamie Gritton  */
1652e92ac56SJamie Gritton struct svc_rpc_gss_cookedcred {
1662e92ac56SJamie Gritton 	struct svc_rpc_gss_client *cc_client;
1672e92ac56SJamie Gritton 	rpc_gss_service_t	cc_service;
1682e92ac56SJamie Gritton 	uint32_t		cc_seq;
1692e92ac56SJamie Gritton };
1702e92ac56SJamie Gritton 
1712e92ac56SJamie Gritton #define CLIENT_HASH_SIZE	256
172e998861bSEdward Tomasz Napierala #define CLIENT_MAX		1024
173b329fb28SEdward Tomasz Napierala u_int svc_rpc_gss_client_max = CLIENT_MAX;
17452eb4995SEdward Tomasz Napierala u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE;
175b329fb28SEdward Tomasz Napierala 
1761a878807SRick Macklem SYSCTL_DECL(_kern_rpc);
1777029da5cSPawel Biernacki SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1787029da5cSPawel Biernacki     "GSS");
179b329fb28SEdward Tomasz Napierala 
180b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW,
181b329fb28SEdward Tomasz Napierala     &svc_rpc_gss_client_max, 0,
182b329fb28SEdward Tomasz Napierala     "Max number of rpc-gss clients");
183b329fb28SEdward Tomasz Napierala 
18452eb4995SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN,
18552eb4995SEdward Tomasz Napierala     &svc_rpc_gss_client_hash_size, 0,
18652eb4995SEdward Tomasz Napierala     "Size of rpc-gss client hash table");
18752eb4995SEdward Tomasz Napierala 
188841c3621SRick Macklem static u_int svc_rpc_gss_lifetime_max = 0;
18904cb0c38SRick Macklem SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW,
19004cb0c38SRick Macklem     &svc_rpc_gss_lifetime_max, 0,
19104cb0c38SRick Macklem     "Maximum lifetime (seconds) of rpc-gss clients");
19204cb0c38SRick Macklem 
193b329fb28SEdward Tomasz Napierala static u_int svc_rpc_gss_client_count;
194b329fb28SEdward Tomasz Napierala SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD,
195b329fb28SEdward Tomasz Napierala     &svc_rpc_gss_client_count, 0,
196b329fb28SEdward Tomasz Napierala     "Number of rpc-gss clients");
197b329fb28SEdward Tomasz Napierala 
1982894c8c9SRick Macklem KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash);
1992894c8c9SRick Macklem KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients);
2002894c8c9SRick Macklem KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1;
2012e92ac56SJamie Gritton 
2022e92ac56SJamie Gritton static void
svc_rpc_gss_init(void * unused __unused)203ef6fcc5eSRick Macklem svc_rpc_gss_init(void *unused __unused)
2042e92ac56SJamie Gritton {
20557ff3488SRick Macklem 
20657ff3488SRick Macklem 	svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
20757ff3488SRick Macklem 	sx_init(&svc_rpc_gss_lock, "gsslock");
20857ff3488SRick Macklem }
20957ff3488SRick Macklem SYSINIT(svc_rpc_gss_init, SI_SUB_VFS, SI_ORDER_ANY,
21057ff3488SRick Macklem     svc_rpc_gss_init, NULL);
21157ff3488SRick Macklem 
21257ff3488SRick Macklem static void
svc_rpc_gss_cleanup(void * unused __unused)21357ff3488SRick Macklem svc_rpc_gss_cleanup(void *unused __unused)
21457ff3488SRick Macklem {
21557ff3488SRick Macklem 
21657ff3488SRick Macklem 	sx_destroy(&svc_rpc_gss_lock);
21757ff3488SRick Macklem }
21857ff3488SRick Macklem SYSUNINIT(svc_rpc_gss_cleanup, SI_SUB_VFS, SI_ORDER_ANY,
21957ff3488SRick Macklem     svc_rpc_gss_cleanup, NULL);
22057ff3488SRick Macklem 
22157ff3488SRick Macklem static void
svc_rpc_gss_vnetinit(void * unused __unused)22257ff3488SRick Macklem svc_rpc_gss_vnetinit(void *unused __unused)
22357ff3488SRick Macklem {
2242e92ac56SJamie Gritton 	int i;
2252e92ac56SJamie Gritton 
2262894c8c9SRick Macklem 	KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc(
2272894c8c9SRick Macklem 	    sizeof(struct svc_rpc_gss_client_list) *
2282894c8c9SRick Macklem 	    svc_rpc_gss_client_hash_size);
22952eb4995SEdward Tomasz Napierala 	for (i = 0; i < svc_rpc_gss_client_hash_size; i++)
2302894c8c9SRick Macklem 		TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]);
2312894c8c9SRick Macklem 	TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients));
2322e92ac56SJamie Gritton }
23357ff3488SRick Macklem VNET_SYSINIT(svc_rpc_gss_vnetinit, SI_SUB_VNET_DONE, SI_ORDER_ANY,
23457ff3488SRick Macklem     svc_rpc_gss_vnetinit, NULL);
2352e92ac56SJamie Gritton 
236ef6fcc5eSRick Macklem static void
svc_rpc_gss_vnet_cleanup(void * unused __unused)23757ff3488SRick Macklem svc_rpc_gss_vnet_cleanup(void *unused __unused)
238ef6fcc5eSRick Macklem {
239ef6fcc5eSRick Macklem 
240ef6fcc5eSRick Macklem 	mem_free(KGSS_VNET(svc_rpc_gss_client_hash),
241ef6fcc5eSRick Macklem 	    sizeof(struct svc_rpc_gss_client_list) *
242ef6fcc5eSRick Macklem 	    svc_rpc_gss_client_hash_size);
243ef6fcc5eSRick Macklem }
24457ff3488SRick Macklem VNET_SYSUNINIT(svc_rpc_gss_vnet_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY,
24557ff3488SRick Macklem     svc_rpc_gss_vnet_cleanup, NULL);
246ef6fcc5eSRick Macklem 
2472e92ac56SJamie Gritton bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)2482e92ac56SJamie Gritton rpc_gss_set_callback(rpc_gss_callback_t *cb)
2492e92ac56SJamie Gritton {
2502e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
2512e92ac56SJamie Gritton 
2522e92ac56SJamie Gritton 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
2532e92ac56SJamie Gritton 	if (!scb) {
2542e92ac56SJamie Gritton 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
2552e92ac56SJamie Gritton 		return (FALSE);
2562e92ac56SJamie Gritton 	}
2572e92ac56SJamie Gritton 	scb->cb_callback = *cb;
2582e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2592894c8c9SRick Macklem 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link);
2602e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2612e92ac56SJamie Gritton 
2622e92ac56SJamie Gritton 	return (TRUE);
2632e92ac56SJamie Gritton }
2642e92ac56SJamie Gritton 
2652e92ac56SJamie Gritton void
rpc_gss_clear_callback(rpc_gss_callback_t * cb)2662e92ac56SJamie Gritton rpc_gss_clear_callback(rpc_gss_callback_t *cb)
2672e92ac56SJamie Gritton {
2682e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
2692e92ac56SJamie Gritton 
2702e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
2712894c8c9SRick Macklem 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
2722e92ac56SJamie Gritton 		if (scb->cb_callback.program == cb->program
2732e92ac56SJamie Gritton 		    && scb->cb_callback.version == cb->version
2742e92ac56SJamie Gritton 		    && scb->cb_callback.callback == cb->callback) {
2752894c8c9SRick Macklem 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb,
2762e92ac56SJamie Gritton 			    svc_rpc_gss_callback, cb_link);
2772e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
2782e92ac56SJamie Gritton 			mem_free(scb, sizeof(*scb));
2792e92ac56SJamie Gritton 			return;
2802e92ac56SJamie Gritton 		}
2812e92ac56SJamie Gritton 	}
2822e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
2832e92ac56SJamie Gritton }
2842e92ac56SJamie Gritton 
2852e92ac56SJamie Gritton static bool_t
rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name * sname)2862e92ac56SJamie Gritton rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
2872e92ac56SJamie Gritton {
2882e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
2892e92ac56SJamie Gritton 	gss_buffer_desc		namebuf;
2902e92ac56SJamie Gritton 	gss_name_t		name;
2912e92ac56SJamie Gritton 	gss_OID_set_desc	oid_set;
2922e92ac56SJamie Gritton 
2932e92ac56SJamie Gritton 	oid_set.count = 1;
2942e92ac56SJamie Gritton 	oid_set.elements = sname->sn_mech;
2952e92ac56SJamie Gritton 
2962e92ac56SJamie Gritton 	namebuf.value = (void *) sname->sn_principal;
2972e92ac56SJamie Gritton 	namebuf.length = strlen(sname->sn_principal);
2982e92ac56SJamie Gritton 
2992e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &namebuf,
3002e92ac56SJamie Gritton 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
3012e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE)
3022e92ac56SJamie Gritton 		return (FALSE);
3032e92ac56SJamie Gritton 
3042e92ac56SJamie Gritton 	if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
3052e92ac56SJamie Gritton 		gss_release_cred(&min_stat, &sname->sn_cred);
3062e92ac56SJamie Gritton 
3072e92ac56SJamie Gritton 	maj_stat = gss_acquire_cred(&min_stat, name,
3082e92ac56SJamie Gritton 	    sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
3092e92ac56SJamie Gritton 	    NULL, NULL);
3102e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
3112e92ac56SJamie Gritton 		gss_release_name(&min_stat, &name);
3122e92ac56SJamie Gritton 		return (FALSE);
3132e92ac56SJamie Gritton 	}
3142e92ac56SJamie Gritton 	gss_release_name(&min_stat, &name);
3152e92ac56SJamie Gritton 
3162e92ac56SJamie Gritton 	return (TRUE);
3172e92ac56SJamie Gritton }
3182e92ac56SJamie Gritton 
3192e92ac56SJamie Gritton bool_t
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)3202e92ac56SJamie Gritton rpc_gss_set_svc_name(const char *principal, const char *mechanism,
3212e92ac56SJamie Gritton     u_int req_time, u_int program, u_int version)
3222e92ac56SJamie Gritton {
3232e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
3242e92ac56SJamie Gritton 	gss_OID			mech_oid;
3252e92ac56SJamie Gritton 
3262e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
3272e92ac56SJamie Gritton 		return (FALSE);
3282e92ac56SJamie Gritton 
3292e92ac56SJamie Gritton 	sname = mem_alloc(sizeof(*sname));
3302e92ac56SJamie Gritton 	if (!sname)
3312e92ac56SJamie Gritton 		return (FALSE);
3322e92ac56SJamie Gritton 	sname->sn_principal = strdup(principal, M_RPC);
3332e92ac56SJamie Gritton 	sname->sn_mech = mech_oid;
3342e92ac56SJamie Gritton 	sname->sn_req_time = req_time;
3352e92ac56SJamie Gritton 	sname->sn_cred = GSS_C_NO_CREDENTIAL;
3362e92ac56SJamie Gritton 	sname->sn_program = program;
3372e92ac56SJamie Gritton 	sname->sn_version = version;
3382e92ac56SJamie Gritton 
3392e92ac56SJamie Gritton 	if (!rpc_gss_acquire_svc_cred(sname)) {
3402e92ac56SJamie Gritton 		free(sname->sn_principal, M_RPC);
3412e92ac56SJamie Gritton 		mem_free(sname, sizeof(*sname));
3422e92ac56SJamie Gritton 		return (FALSE);
3432e92ac56SJamie Gritton 	}
3442e92ac56SJamie Gritton 
3452e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
3462894c8c9SRick Macklem 	SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link);
3472e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
3482e92ac56SJamie Gritton 
3492e92ac56SJamie Gritton 	return (TRUE);
3502e92ac56SJamie Gritton }
3512e92ac56SJamie Gritton 
3522e92ac56SJamie Gritton void
rpc_gss_clear_svc_name(u_int program,u_int version)3532e92ac56SJamie Gritton rpc_gss_clear_svc_name(u_int program, u_int version)
3542e92ac56SJamie Gritton {
3552e92ac56SJamie Gritton 	OM_uint32		min_stat;
3562e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
3572e92ac56SJamie Gritton 
3582e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
3592894c8c9SRick Macklem 	SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) {
3602e92ac56SJamie Gritton 		if (sname->sn_program == program
3612e92ac56SJamie Gritton 		    && sname->sn_version == version) {
3622894c8c9SRick Macklem 			SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname,
3632e92ac56SJamie Gritton 			    svc_rpc_gss_svc_name, sn_link);
3642e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
3652e92ac56SJamie Gritton 			gss_release_cred(&min_stat, &sname->sn_cred);
3662e92ac56SJamie Gritton 			free(sname->sn_principal, M_RPC);
3672e92ac56SJamie Gritton 			mem_free(sname, sizeof(*sname));
3682e92ac56SJamie Gritton 			return;
3692e92ac56SJamie Gritton 		}
3702e92ac56SJamie Gritton 	}
3712e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
3722e92ac56SJamie Gritton }
3732e92ac56SJamie Gritton 
3742e92ac56SJamie Gritton bool_t
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)3752e92ac56SJamie Gritton rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
3762e92ac56SJamie Gritton     const char *mech, const char *name, const char *node, const char *domain)
3772e92ac56SJamie Gritton {
3782e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
3792e92ac56SJamie Gritton 	gss_OID			mech_oid;
3802e92ac56SJamie Gritton 	size_t			namelen;
3812e92ac56SJamie Gritton 	gss_buffer_desc		buf;
3822e92ac56SJamie Gritton 	gss_name_t		gss_name, gss_mech_name;
3832e92ac56SJamie Gritton 	rpc_gss_principal_t	result;
3842e92ac56SJamie Gritton 
3852e92ac56SJamie Gritton 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
3862e92ac56SJamie Gritton 		return (FALSE);
3872e92ac56SJamie Gritton 
3882e92ac56SJamie Gritton 	/*
3892e92ac56SJamie Gritton 	 * Construct a gss_buffer containing the full name formatted
3902e92ac56SJamie Gritton 	 * as "name/node@domain" where node and domain are optional.
3912e92ac56SJamie Gritton 	 */
392e3081f7eSConrad Meyer 	namelen = strlen(name) + 1;
3932e92ac56SJamie Gritton 	if (node) {
3942e92ac56SJamie Gritton 		namelen += strlen(node) + 1;
3952e92ac56SJamie Gritton 	}
3962e92ac56SJamie Gritton 	if (domain) {
3972e92ac56SJamie Gritton 		namelen += strlen(domain) + 1;
3982e92ac56SJamie Gritton 	}
3992e92ac56SJamie Gritton 
4002e92ac56SJamie Gritton 	buf.value = mem_alloc(namelen);
4012e92ac56SJamie Gritton 	buf.length = namelen;
4022e92ac56SJamie Gritton 	strcpy((char *) buf.value, name);
4032e92ac56SJamie Gritton 	if (node) {
4042e92ac56SJamie Gritton 		strcat((char *) buf.value, "/");
4052e92ac56SJamie Gritton 		strcat((char *) buf.value, node);
4062e92ac56SJamie Gritton 	}
4072e92ac56SJamie Gritton 	if (domain) {
4082e92ac56SJamie Gritton 		strcat((char *) buf.value, "@");
4092e92ac56SJamie Gritton 		strcat((char *) buf.value, domain);
4102e92ac56SJamie Gritton 	}
4112e92ac56SJamie Gritton 
4122e92ac56SJamie Gritton 	/*
4132e92ac56SJamie Gritton 	 * Convert that to a gss_name_t and then convert that to a
4142e92ac56SJamie Gritton 	 * mechanism name in the selected mechanism.
4152e92ac56SJamie Gritton 	 */
4162e92ac56SJamie Gritton 	maj_stat = gss_import_name(&min_stat, &buf,
4172e92ac56SJamie Gritton 	    GSS_C_NT_USER_NAME, &gss_name);
4182e92ac56SJamie Gritton 	mem_free(buf.value, buf.length);
4192e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4202e92ac56SJamie Gritton 		rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
4212e92ac56SJamie Gritton 		return (FALSE);
4222e92ac56SJamie Gritton 	}
4232e92ac56SJamie Gritton 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
4242e92ac56SJamie Gritton 	    &gss_mech_name);
4252e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4262e92ac56SJamie Gritton 		rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
4272e92ac56SJamie Gritton 		    min_stat);
4282e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_name);
4292e92ac56SJamie Gritton 		return (FALSE);
4302e92ac56SJamie Gritton 	}
4312e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_name);
4322e92ac56SJamie Gritton 
4332e92ac56SJamie Gritton 	/*
4342e92ac56SJamie Gritton 	 * Export the mechanism name and use that to construct the
4352e92ac56SJamie Gritton 	 * rpc_gss_principal_t result.
4362e92ac56SJamie Gritton 	 */
4372e92ac56SJamie Gritton 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
4382e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
4392e92ac56SJamie Gritton 		rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
4402e92ac56SJamie Gritton 		gss_release_name(&min_stat, &gss_mech_name);
4412e92ac56SJamie Gritton 		return (FALSE);
4422e92ac56SJamie Gritton 	}
4432e92ac56SJamie Gritton 	gss_release_name(&min_stat, &gss_mech_name);
4442e92ac56SJamie Gritton 
4452e92ac56SJamie Gritton 	result = mem_alloc(sizeof(int) + buf.length);
4462e92ac56SJamie Gritton 	if (!result) {
4472e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &buf);
4482e92ac56SJamie Gritton 		return (FALSE);
4492e92ac56SJamie Gritton 	}
4502e92ac56SJamie Gritton 	result->len = buf.length;
4512e92ac56SJamie Gritton 	memcpy(result->name, buf.value, buf.length);
4522e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &buf);
4532e92ac56SJamie Gritton 
4542e92ac56SJamie Gritton 	*principal = result;
4552e92ac56SJamie Gritton 	return (TRUE);
4562e92ac56SJamie Gritton }
4572e92ac56SJamie Gritton 
458dd7d42a1SRick Macklem /*
459dd7d42a1SRick Macklem  * Note that the ip_addr and srv_principal pointers can point to the same
460dd7d42a1SRick Macklem  * buffer, so long as ip_addr is at least strlen(srv_name) + 1 > srv_principal.
461dd7d42a1SRick Macklem  */
462dd7d42a1SRick Macklem bool_t
rpc_gss_ip_to_srv_principal(char * ip_addr,const char * srv_name,char * srv_principal)463dd7d42a1SRick Macklem rpc_gss_ip_to_srv_principal(char *ip_addr, const char *srv_name,
464dd7d42a1SRick Macklem     char *srv_principal)
465dd7d42a1SRick Macklem {
466dd7d42a1SRick Macklem 	OM_uint32		maj_stat, min_stat;
467dd7d42a1SRick Macklem 	size_t			len;
468dd7d42a1SRick Macklem 
469dd7d42a1SRick Macklem 	/*
470dd7d42a1SRick Macklem 	 * First fill in the service name and '@'.
471dd7d42a1SRick Macklem 	 */
472dd7d42a1SRick Macklem 	len = strlen(srv_name);
473dd7d42a1SRick Macklem 	if (len > NI_MAXSERV)
474dd7d42a1SRick Macklem 		return (FALSE);
475dd7d42a1SRick Macklem 	memcpy(srv_principal, srv_name, len);
476dd7d42a1SRick Macklem 	srv_principal[len] = '@';
477dd7d42a1SRick Macklem 
478dd7d42a1SRick Macklem 	/*
479dd7d42a1SRick Macklem 	 * Do reverse DNS to get the DNS name for the ip_addr.
480dd7d42a1SRick Macklem 	 */
481dd7d42a1SRick Macklem 	maj_stat = gss_ip_to_dns(&min_stat, ip_addr, &srv_principal[len + 1]);
482dd7d42a1SRick Macklem 	if (maj_stat != GSS_S_COMPLETE) {
483dd7d42a1SRick Macklem 		rpc_gss_log_status("gss_ip_to_dns", NULL, maj_stat, min_stat);
484dd7d42a1SRick Macklem 		return (FALSE);
485dd7d42a1SRick Macklem 	}
486dd7d42a1SRick Macklem 	return (TRUE);
487dd7d42a1SRick Macklem }
488dd7d42a1SRick Macklem 
4892e92ac56SJamie Gritton bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)4902e92ac56SJamie Gritton rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
4912e92ac56SJamie Gritton     rpc_gss_ucred_t **ucred, void **cookie)
4922e92ac56SJamie Gritton {
4932e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
4942e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
4952e92ac56SJamie Gritton 
4962e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
4972e92ac56SJamie Gritton 		return (FALSE);
4982e92ac56SJamie Gritton 
4992e92ac56SJamie Gritton 	cc = req->rq_clntcred;
5002e92ac56SJamie Gritton 	client = cc->cc_client;
5012e92ac56SJamie Gritton 	if (rcred)
5022e92ac56SJamie Gritton 		*rcred = &client->cl_rawcred;
5032e92ac56SJamie Gritton 	if (ucred)
5042e92ac56SJamie Gritton 		*ucred = &client->cl_ucred;
5052e92ac56SJamie Gritton 	if (cookie)
5062e92ac56SJamie Gritton 		*cookie = client->cl_cookie;
5072e92ac56SJamie Gritton 	return (TRUE);
5082e92ac56SJamie Gritton }
5092e92ac56SJamie Gritton 
5102e92ac56SJamie Gritton /*
5112e92ac56SJamie Gritton  * This simpler interface is used by svc_getcred to copy the cred data
5122e92ac56SJamie Gritton  * into a kernel cred structure.
513a9148abdSDoug Rabson  */
514a9148abdSDoug Rabson static int
rpc_gss_svc_getcred(struct svc_req * req,struct ucred ** crp,int * flavorp)5152e92ac56SJamie Gritton rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
516a9148abdSDoug Rabson {
5172e92ac56SJamie Gritton 	struct ucred *cr;
5182e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
5192e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5202e92ac56SJamie Gritton 	rpc_gss_ucred_t *uc;
521a9148abdSDoug Rabson 
5222e92ac56SJamie Gritton 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
5232e92ac56SJamie Gritton 		return (FALSE);
5242e92ac56SJamie Gritton 
5252e92ac56SJamie Gritton 	cc = req->rq_clntcred;
5262e92ac56SJamie Gritton 	client = cc->cc_client;
5272e92ac56SJamie Gritton 
5282e92ac56SJamie Gritton 	if (flavorp)
5292e92ac56SJamie Gritton 		*flavorp = client->cl_rpcflavor;
5302e92ac56SJamie Gritton 
5312e92ac56SJamie Gritton 	if (client->cl_cred) {
5322e92ac56SJamie Gritton 		*crp = crhold(client->cl_cred);
5332e92ac56SJamie Gritton 		return (TRUE);
534a9148abdSDoug Rabson 	}
535a9148abdSDoug Rabson 
5362e92ac56SJamie Gritton 	uc = &client->cl_ucred;
5372e92ac56SJamie Gritton 	cr = client->cl_cred = crget();
5382e92ac56SJamie Gritton 	cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
5392e92ac56SJamie Gritton 	cr->cr_rgid = cr->cr_svgid = uc->gid;
540*cfbe7a62SOlivier Certner 	crsetgroups_fallback(cr, uc->gidlen, uc->gidlist, uc->gid);
5416a76d35cSRick Macklem 	cr->cr_prison = curthread->td_ucred->cr_prison;
542c408f06bSJamie Gritton 	prison_hold(cr->cr_prison);
5432e92ac56SJamie Gritton 	*crp = crhold(cr);
5442e92ac56SJamie Gritton 
5452e92ac56SJamie Gritton 	return (TRUE);
546a9148abdSDoug Rabson }
5472e92ac56SJamie Gritton 
5482e92ac56SJamie Gritton int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)5492e92ac56SJamie Gritton rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
5502e92ac56SJamie Gritton {
5512e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
5522e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client = cc->cc_client;
5532e92ac56SJamie Gritton 	int			want_conf;
5542e92ac56SJamie Gritton 	OM_uint32		max;
5552e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
5562e92ac56SJamie Gritton 	int			result;
5572e92ac56SJamie Gritton 
5582e92ac56SJamie Gritton 	switch (client->cl_rawcred.service) {
5592e92ac56SJamie Gritton 	case rpc_gss_svc_none:
5602e92ac56SJamie Gritton 		return (max_tp_unit_len);
5612e92ac56SJamie Gritton 		break;
5622e92ac56SJamie Gritton 
5632e92ac56SJamie Gritton 	case rpc_gss_svc_default:
5642e92ac56SJamie Gritton 	case rpc_gss_svc_integrity:
5652e92ac56SJamie Gritton 		want_conf = FALSE;
5662e92ac56SJamie Gritton 		break;
5672e92ac56SJamie Gritton 
5682e92ac56SJamie Gritton 	case rpc_gss_svc_privacy:
5692e92ac56SJamie Gritton 		want_conf = TRUE;
5702e92ac56SJamie Gritton 		break;
5712e92ac56SJamie Gritton 
5722e92ac56SJamie Gritton 	default:
573a9148abdSDoug Rabson 		return (0);
574a9148abdSDoug Rabson 	}
575a9148abdSDoug Rabson 
5762e92ac56SJamie Gritton 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
5772e92ac56SJamie Gritton 	    client->cl_qop, max_tp_unit_len, &max);
5782e92ac56SJamie Gritton 
5792e92ac56SJamie Gritton 	if (maj_stat == GSS_S_COMPLETE) {
5802e92ac56SJamie Gritton 		result = (int) max;
5812e92ac56SJamie Gritton 		if (result < 0)
5822e92ac56SJamie Gritton 			result = 0;
5832e92ac56SJamie Gritton 		return (result);
5842e92ac56SJamie Gritton 	} else {
5852e92ac56SJamie Gritton 		rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
5862e92ac56SJamie Gritton 		    maj_stat, min_stat);
5872e92ac56SJamie Gritton 		return (0);
5882e92ac56SJamie Gritton 	}
5892e92ac56SJamie Gritton }
5902e92ac56SJamie Gritton 
5912e92ac56SJamie Gritton static struct svc_rpc_gss_client *
svc_rpc_gss_find_client(struct svc_rpc_gss_clientid * id)5922e92ac56SJamie Gritton svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
5932e92ac56SJamie Gritton {
5942e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
5952e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
596584b675eSKonstantin Belousov 	struct timeval boottime;
5972e92ac56SJamie Gritton 	unsigned long hostid;
5982e92ac56SJamie Gritton 
5992e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
6002e92ac56SJamie Gritton 
6012e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
602584b675eSKonstantin Belousov 	getboottime(&boottime);
6032e92ac56SJamie Gritton 	if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
6042e92ac56SJamie Gritton 		return (NULL);
6052e92ac56SJamie Gritton 
6062894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
6072894c8c9SRick Macklem 	    [id->ci_id % svc_rpc_gss_client_hash_size];
6082e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
6092e92ac56SJamie Gritton 	TAILQ_FOREACH(client, list, cl_link) {
6102e92ac56SJamie Gritton 		if (client->cl_id.ci_id == id->ci_id) {
6112e92ac56SJamie Gritton 			/*
6122e92ac56SJamie Gritton 			 * Move this client to the front of the LRU
6132e92ac56SJamie Gritton 			 * list.
6142e92ac56SJamie Gritton 			 */
6152894c8c9SRick Macklem 			TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client,
6162e92ac56SJamie Gritton 			    cl_alllink);
6172894c8c9SRick Macklem 			TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients),
6182894c8c9SRick Macklem 			    client, cl_alllink);
6192e92ac56SJamie Gritton 			refcount_acquire(&client->cl_refs);
6202e92ac56SJamie Gritton 			break;
6212e92ac56SJamie Gritton 		}
6222e92ac56SJamie Gritton 	}
6232e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
6242e92ac56SJamie Gritton 
6252e92ac56SJamie Gritton 	return (client);
6262e92ac56SJamie Gritton }
6272e92ac56SJamie Gritton 
6282e92ac56SJamie Gritton static struct svc_rpc_gss_client *
svc_rpc_gss_create_client(void)6292e92ac56SJamie Gritton svc_rpc_gss_create_client(void)
6302e92ac56SJamie Gritton {
6312e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
6322e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
633584b675eSKonstantin Belousov 	struct timeval boottime;
6342e92ac56SJamie Gritton 	unsigned long hostid;
6352e92ac56SJamie Gritton 
6362e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_create_client()");
6372e92ac56SJamie Gritton 
6382e92ac56SJamie Gritton 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
6392e92ac56SJamie Gritton 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
64014068959SRick Macklem 
64114068959SRick Macklem 	/*
64214068959SRick Macklem 	 * Set the initial value of cl_refs to two.  One for the caller
64314068959SRick Macklem 	 * and the other to hold onto the client structure until it expires.
64414068959SRick Macklem 	 */
645b0e14530SRick Macklem 	refcount_init(&client->cl_refs, 2);
6462e92ac56SJamie Gritton 	sx_init(&client->cl_lock, "GSS-client");
6472e92ac56SJamie Gritton 	getcredhostid(curthread->td_ucred, &hostid);
6482e92ac56SJamie Gritton 	client->cl_id.ci_hostid = hostid;
649584b675eSKonstantin Belousov 	getboottime(&boottime);
6502e92ac56SJamie Gritton 	client->cl_id.ci_boottime = boottime.tv_sec;
6512894c8c9SRick Macklem 	client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++;
6522e92ac56SJamie Gritton 
6532e92ac56SJamie Gritton 	/*
6542e92ac56SJamie Gritton 	 * Start the client off with a short expiration time. We will
6552e92ac56SJamie Gritton 	 * try to get a saner value from the client creds later.
6562e92ac56SJamie Gritton 	 */
6572e92ac56SJamie Gritton 	client->cl_state = CLIENT_NEW;
6582e92ac56SJamie Gritton 	client->cl_locked = FALSE;
6592e92ac56SJamie Gritton 	client->cl_expiration = time_uptime + 5*60;
6602e92ac56SJamie Gritton 
6612894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
6622894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
663b0e14530SRick Macklem 	sx_xlock(&svc_rpc_gss_lock);
664b0e14530SRick Macklem 	TAILQ_INSERT_HEAD(list, client, cl_link);
6652894c8c9SRick Macklem 	TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
666b0e14530SRick Macklem 	svc_rpc_gss_client_count++;
667b0e14530SRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
6682e92ac56SJamie Gritton 	return (client);
6692e92ac56SJamie Gritton }
6702e92ac56SJamie Gritton 
6712e92ac56SJamie Gritton static void
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)6722e92ac56SJamie Gritton svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
6732e92ac56SJamie Gritton {
6742e92ac56SJamie Gritton 	OM_uint32 min_stat;
6752e92ac56SJamie Gritton 
6762e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
6772e92ac56SJamie Gritton 
6782e92ac56SJamie Gritton 	if (client->cl_ctx)
6792e92ac56SJamie Gritton 		gss_delete_sec_context(&min_stat,
6802e92ac56SJamie Gritton 		    &client->cl_ctx, GSS_C_NO_BUFFER);
6812e92ac56SJamie Gritton 
6822e92ac56SJamie Gritton 	if (client->cl_cname)
6832e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
6842e92ac56SJamie Gritton 
6852e92ac56SJamie Gritton 	if (client->cl_rawcred.client_principal)
6862e92ac56SJamie Gritton 		mem_free(client->cl_rawcred.client_principal,
6872e92ac56SJamie Gritton 		    sizeof(*client->cl_rawcred.client_principal)
6882e92ac56SJamie Gritton 		    + client->cl_rawcred.client_principal->len);
6892e92ac56SJamie Gritton 
6902e92ac56SJamie Gritton 	if (client->cl_cred)
6912e92ac56SJamie Gritton 		crfree(client->cl_cred);
6922e92ac56SJamie Gritton 
6932e92ac56SJamie Gritton 	sx_destroy(&client->cl_lock);
6942e92ac56SJamie Gritton 	mem_free(client, sizeof(*client));
6952e92ac56SJamie Gritton }
6962e92ac56SJamie Gritton 
6972e92ac56SJamie Gritton /*
6982e92ac56SJamie Gritton  * Drop a reference to a client and free it if that was the last reference.
6992e92ac56SJamie Gritton  */
7002e92ac56SJamie Gritton static void
svc_rpc_gss_release_client(struct svc_rpc_gss_client * client)7012e92ac56SJamie Gritton svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
7022e92ac56SJamie Gritton {
7032e92ac56SJamie Gritton 
7042e92ac56SJamie Gritton 	if (!refcount_release(&client->cl_refs))
7052e92ac56SJamie Gritton 		return;
7062e92ac56SJamie Gritton 	svc_rpc_gss_destroy_client(client);
7072e92ac56SJamie Gritton }
7082e92ac56SJamie Gritton 
7092e92ac56SJamie Gritton /*
7105328a32eSRick Macklem  * Remove a client from our global lists.
7115328a32eSRick Macklem  * Must be called with svc_rpc_gss_lock held.
7125328a32eSRick Macklem  */
7135328a32eSRick Macklem static void
svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client * client)7145328a32eSRick Macklem svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
7155328a32eSRick Macklem {
7165328a32eSRick Macklem 	struct svc_rpc_gss_client_list *list;
7175328a32eSRick Macklem 
7185328a32eSRick Macklem 	sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
7192894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
7202894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
7215328a32eSRick Macklem 	TAILQ_REMOVE(list, client, cl_link);
7222894c8c9SRick Macklem 	TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink);
7235328a32eSRick Macklem 	svc_rpc_gss_client_count--;
7245328a32eSRick Macklem }
7255328a32eSRick Macklem 
7265328a32eSRick Macklem /*
7272e92ac56SJamie Gritton  * Remove a client from our global lists and free it if we can.
7282e92ac56SJamie Gritton  */
7292e92ac56SJamie Gritton static void
svc_rpc_gss_forget_client(struct svc_rpc_gss_client * client)7302e92ac56SJamie Gritton svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
7312e92ac56SJamie Gritton {
7322e92ac56SJamie Gritton 	struct svc_rpc_gss_client_list *list;
7335328a32eSRick Macklem 	struct svc_rpc_gss_client *tclient;
7342e92ac56SJamie Gritton 
7352894c8c9SRick Macklem 	list = &KGSS_VNET(svc_rpc_gss_client_hash)
7362894c8c9SRick Macklem 	    [client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
7372e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
7385328a32eSRick Macklem 	TAILQ_FOREACH(tclient, list, cl_link) {
7395328a32eSRick Macklem 		/*
7405328a32eSRick Macklem 		 * Make sure this client has not already been removed
7415328a32eSRick Macklem 		 * from the lists by svc_rpc_gss_forget_client() or
742cbf06947SRick Macklem 		 * svc_rpc_gss_forget_client_locked().
7435328a32eSRick Macklem 		 */
7445328a32eSRick Macklem 		if (client == tclient) {
7455328a32eSRick Macklem 			svc_rpc_gss_forget_client_locked(client);
7462e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
7472e92ac56SJamie Gritton 			svc_rpc_gss_release_client(client);
7485328a32eSRick Macklem 			return;
7495328a32eSRick Macklem 		}
7505328a32eSRick Macklem 	}
7515328a32eSRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
7522e92ac56SJamie Gritton }
7532e92ac56SJamie Gritton 
7542e92ac56SJamie Gritton static void
svc_rpc_gss_timeout_clients(void)7552e92ac56SJamie Gritton svc_rpc_gss_timeout_clients(void)
7562e92ac56SJamie Gritton {
7572e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
7582e92ac56SJamie Gritton 	time_t now = time_uptime;
7592e92ac56SJamie Gritton 
7602e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
7612e92ac56SJamie Gritton 
7622e92ac56SJamie Gritton 	/*
7632e92ac56SJamie Gritton 	 * First enforce the max client limit. We keep
7642e92ac56SJamie Gritton 	 * svc_rpc_gss_clients in LRU order.
7652e92ac56SJamie Gritton 	 */
7665328a32eSRick Macklem 	sx_xlock(&svc_rpc_gss_lock);
7672894c8c9SRick Macklem 	client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
7682894c8c9SRick Macklem 	    svc_rpc_gss_client_list);
769b329fb28SEdward Tomasz Napierala 	while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
7705328a32eSRick Macklem 		svc_rpc_gss_forget_client_locked(client);
7715328a32eSRick Macklem 		sx_xunlock(&svc_rpc_gss_lock);
7725328a32eSRick Macklem 		svc_rpc_gss_release_client(client);
7735328a32eSRick Macklem 		sx_xlock(&svc_rpc_gss_lock);
7742894c8c9SRick Macklem 		client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients),
7755328a32eSRick Macklem 		    svc_rpc_gss_client_list);
7765328a32eSRick Macklem 	}
7775328a32eSRick Macklem again:
7782894c8c9SRick Macklem 	TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) {
7792e92ac56SJamie Gritton 		if (client->cl_state == CLIENT_STALE
7802e92ac56SJamie Gritton 		    || now > client->cl_expiration) {
7815328a32eSRick Macklem 			svc_rpc_gss_forget_client_locked(client);
7825328a32eSRick Macklem 			sx_xunlock(&svc_rpc_gss_lock);
7832e92ac56SJamie Gritton 			rpc_gss_log_debug("expiring client %p", client);
7845328a32eSRick Macklem 			svc_rpc_gss_release_client(client);
7855328a32eSRick Macklem 			sx_xlock(&svc_rpc_gss_lock);
7865328a32eSRick Macklem 			goto again;
7872e92ac56SJamie Gritton 		}
7882e92ac56SJamie Gritton 	}
7895328a32eSRick Macklem 	sx_xunlock(&svc_rpc_gss_lock);
7902e92ac56SJamie Gritton }
7912e92ac56SJamie Gritton 
7922e92ac56SJamie Gritton #ifdef DEBUG
7932e92ac56SJamie Gritton /*
7942e92ac56SJamie Gritton  * OID<->string routines.  These are uuuuugly.
7952e92ac56SJamie Gritton  */
7962e92ac56SJamie Gritton static OM_uint32
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)7972e92ac56SJamie Gritton gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
7982e92ac56SJamie Gritton {
7992e92ac56SJamie Gritton 	char		numstr[128];
8002e92ac56SJamie Gritton 	unsigned long	number;
8012e92ac56SJamie Gritton 	int		numshift;
8022e92ac56SJamie Gritton 	size_t		string_length;
8032e92ac56SJamie Gritton 	size_t		i;
8042e92ac56SJamie Gritton 	unsigned char	*cp;
8052e92ac56SJamie Gritton 	char		*bp;
8062e92ac56SJamie Gritton 
8072e92ac56SJamie Gritton 	/* Decoded according to krb5/gssapi_krb5.c */
8082e92ac56SJamie Gritton 
8092e92ac56SJamie Gritton 	/* First determine the size of the string */
8102e92ac56SJamie Gritton 	string_length = 0;
8112e92ac56SJamie Gritton 	number = 0;
8122e92ac56SJamie Gritton 	numshift = 0;
8132e92ac56SJamie Gritton 	cp = (unsigned char *) oid->elements;
8142e92ac56SJamie Gritton 	number = (unsigned long) cp[0];
8152e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number/40);
8162e92ac56SJamie Gritton 	string_length += strlen(numstr);
8172e92ac56SJamie Gritton 	sprintf(numstr, "%ld ", number%40);
8182e92ac56SJamie Gritton 	string_length += strlen(numstr);
8192e92ac56SJamie Gritton 	for (i=1; i<oid->length; i++) {
8202e92ac56SJamie Gritton 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
8212e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
8222e92ac56SJamie Gritton 			numshift += 7;
8232e92ac56SJamie Gritton 		}
8242e92ac56SJamie Gritton 		else {
8252e92ac56SJamie Gritton 			*minor_status = 0;
8262e92ac56SJamie Gritton 			return(GSS_S_FAILURE);
8272e92ac56SJamie Gritton 		}
8282e92ac56SJamie Gritton 		if ((cp[i] & 0x80) == 0) {
8292e92ac56SJamie Gritton 			sprintf(numstr, "%ld ", number);
8302e92ac56SJamie Gritton 			string_length += strlen(numstr);
8312e92ac56SJamie Gritton 			number = 0;
8322e92ac56SJamie Gritton 			numshift = 0;
8332e92ac56SJamie Gritton 		}
8342e92ac56SJamie Gritton 	}
8352e92ac56SJamie Gritton 	/*
8362e92ac56SJamie Gritton 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
8372e92ac56SJamie Gritton 	 * here for "{ " and "}\0".
8382e92ac56SJamie Gritton 	 */
8392e92ac56SJamie Gritton 	string_length += 4;
84052cab12cSRick Macklem 	if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
8412e92ac56SJamie Gritton 		strcpy(bp, "{ ");
8422e92ac56SJamie Gritton 		number = (unsigned long) cp[0];
8432e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number/40);
8442e92ac56SJamie Gritton 		strcat(bp, numstr);
8452e92ac56SJamie Gritton 		sprintf(numstr, "%ld ", number%40);
8462e92ac56SJamie Gritton 		strcat(bp, numstr);
8472e92ac56SJamie Gritton 		number = 0;
8482e92ac56SJamie Gritton 		cp = (unsigned char *) oid->elements;
8492e92ac56SJamie Gritton 		for (i=1; i<oid->length; i++) {
8502e92ac56SJamie Gritton 			number = (number << 7) | (cp[i] & 0x7f);
8512e92ac56SJamie Gritton 			if ((cp[i] & 0x80) == 0) {
8522e92ac56SJamie Gritton 				sprintf(numstr, "%ld ", number);
8532e92ac56SJamie Gritton 				strcat(bp, numstr);
8542e92ac56SJamie Gritton 				number = 0;
8552e92ac56SJamie Gritton 			}
8562e92ac56SJamie Gritton 		}
8572e92ac56SJamie Gritton 		strcat(bp, "}");
8582e92ac56SJamie Gritton 		oid_str->length = strlen(bp)+1;
8592e92ac56SJamie Gritton 		oid_str->value = (void *) bp;
8602e92ac56SJamie Gritton 		*minor_status = 0;
8612e92ac56SJamie Gritton 		return(GSS_S_COMPLETE);
8622e92ac56SJamie Gritton 	}
8632e92ac56SJamie Gritton 	*minor_status = 0;
8642e92ac56SJamie Gritton 	return(GSS_S_FAILURE);
865a9148abdSDoug Rabson }
866a9148abdSDoug Rabson #endif
867a9148abdSDoug Rabson 
868a9148abdSDoug Rabson static void
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)8692e92ac56SJamie Gritton svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
8702e92ac56SJamie Gritton     const gss_name_t name)
871a9148abdSDoug Rabson {
8722e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
8732e92ac56SJamie Gritton 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
8742e92ac56SJamie Gritton 	int			numgroups;
875a9148abdSDoug Rabson 
8762e92ac56SJamie Gritton 	uc->uid = 65534;
8772e92ac56SJamie Gritton 	uc->gid = 65534;
8782e92ac56SJamie Gritton 	uc->gidlist = client->cl_gid_storage;
879d4468577SJamie Gritton 
8805eff3ec6SJosh Paetzel 	numgroups = NGROUPS;
8812e92ac56SJamie Gritton 	maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
8822e92ac56SJamie Gritton 	    &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
8832e92ac56SJamie Gritton 	if (GSS_ERROR(maj_stat))
8842e92ac56SJamie Gritton 		uc->gidlen = 0;
8852e92ac56SJamie Gritton 	else
8862e92ac56SJamie Gritton 		uc->gidlen = numgroups;
887d4468577SJamie Gritton }
888d4468577SJamie Gritton 
8892e92ac56SJamie Gritton static void
svc_rpc_gss_set_flavor(struct svc_rpc_gss_client * client)8902e92ac56SJamie Gritton svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
891a9148abdSDoug Rabson {
8922e92ac56SJamie Gritton 	static gss_OID_desc krb5_mech_oid =
8932e92ac56SJamie Gritton 		{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
894a9148abdSDoug Rabson 
895d4468577SJamie Gritton 	/*
8962e92ac56SJamie Gritton 	 * Attempt to translate mech type and service into a
8972e92ac56SJamie Gritton 	 * 'pseudo flavor'. Hardwire in krb5 support for now.
898d4468577SJamie Gritton 	 */
8992e92ac56SJamie Gritton 	if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
9002e92ac56SJamie Gritton 		switch (client->cl_rawcred.service) {
9012e92ac56SJamie Gritton 		case rpc_gss_svc_default:
9022e92ac56SJamie Gritton 		case rpc_gss_svc_none:
9032e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5;
9042e92ac56SJamie Gritton 			break;
9052e92ac56SJamie Gritton 		case rpc_gss_svc_integrity:
9062e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
9072e92ac56SJamie Gritton 			break;
9082e92ac56SJamie Gritton 		case rpc_gss_svc_privacy:
9092e92ac56SJamie Gritton 			client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
9102e92ac56SJamie Gritton 			break;
9112e92ac56SJamie Gritton 		}
9122e92ac56SJamie Gritton 	} else {
9132e92ac56SJamie Gritton 		client->cl_rpcflavor = RPCSEC_GSS;
9142e92ac56SJamie Gritton 	}
9152e92ac56SJamie Gritton }
9162e92ac56SJamie Gritton 
9172e92ac56SJamie Gritton static bool_t
svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client * client,struct svc_req * rqst,struct rpc_gss_init_res * gr,struct rpc_gss_cred * gc)9182e92ac56SJamie Gritton svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
9192e92ac56SJamie Gritton 			       struct svc_req *rqst,
9202e92ac56SJamie Gritton 			       struct rpc_gss_init_res *gr,
9212e92ac56SJamie Gritton 			       struct rpc_gss_cred *gc)
922a9148abdSDoug Rabson {
9232e92ac56SJamie Gritton 	gss_buffer_desc		recv_tok;
9242e92ac56SJamie Gritton 	gss_OID			mech;
9252e92ac56SJamie Gritton 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
9262e92ac56SJamie Gritton 	OM_uint32		cred_lifetime;
9272e92ac56SJamie Gritton 	struct svc_rpc_gss_svc_name *sname;
928a9148abdSDoug Rabson 
9292e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
9302e92ac56SJamie Gritton 
9312e92ac56SJamie Gritton 	/* Deserialize arguments. */
9322e92ac56SJamie Gritton 	memset(&recv_tok, 0, sizeof(recv_tok));
9332e92ac56SJamie Gritton 
9342e92ac56SJamie Gritton 	if (!svc_getargs(rqst,
9352e92ac56SJamie Gritton 		(xdrproc_t) xdr_gss_buffer_desc,
9362e92ac56SJamie Gritton 		(caddr_t) &recv_tok)) {
9372e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
9382e92ac56SJamie Gritton 		return (FALSE);
939a9148abdSDoug Rabson 	}
940a9148abdSDoug Rabson 
941d4468577SJamie Gritton 	/*
9422e92ac56SJamie Gritton 	 * First time round, try all the server names we have until
9432e92ac56SJamie Gritton 	 * one matches. Afterwards, stick with that one.
944d4468577SJamie Gritton 	 */
9452e92ac56SJamie Gritton 	sx_xlock(&svc_rpc_gss_lock);
9462e92ac56SJamie Gritton 	if (!client->cl_sname) {
9472894c8c9SRick Macklem 		SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names),
9482894c8c9SRick Macklem 		    sn_link) {
9492e92ac56SJamie Gritton 			if (sname->sn_program == rqst->rq_prog
9502e92ac56SJamie Gritton 			    && sname->sn_version == rqst->rq_vers) {
9512e92ac56SJamie Gritton 			retry:
9522e92ac56SJamie Gritton 				gr->gr_major = gss_accept_sec_context(
9532e92ac56SJamie Gritton 					&gr->gr_minor,
9542e92ac56SJamie Gritton 					&client->cl_ctx,
9552e92ac56SJamie Gritton 					sname->sn_cred,
9562e92ac56SJamie Gritton 					&recv_tok,
9572e92ac56SJamie Gritton 					GSS_C_NO_CHANNEL_BINDINGS,
9582e92ac56SJamie Gritton 					&client->cl_cname,
9592e92ac56SJamie Gritton 					&mech,
9602e92ac56SJamie Gritton 					&gr->gr_token,
9612e92ac56SJamie Gritton 					&ret_flags,
9622e92ac56SJamie Gritton 					&cred_lifetime,
9632e92ac56SJamie Gritton 					&client->cl_creds);
9642e92ac56SJamie Gritton 				if (gr->gr_major ==
9652e92ac56SJamie Gritton 				    GSS_S_CREDENTIALS_EXPIRED) {
966d4468577SJamie Gritton 					/*
9672e92ac56SJamie Gritton 					 * Either our creds really did
9682e92ac56SJamie Gritton 					 * expire or gssd was
9692e92ac56SJamie Gritton 					 * restarted.
970d4468577SJamie Gritton 					 */
9712e92ac56SJamie Gritton 					if (rpc_gss_acquire_svc_cred(sname))
9722e92ac56SJamie Gritton 						goto retry;
9732e92ac56SJamie Gritton 				}
9742e92ac56SJamie Gritton 				client->cl_sname = sname;
975d4468577SJamie Gritton 				break;
976d4468577SJamie Gritton 			}
977d4468577SJamie Gritton 		}
9782e92ac56SJamie Gritton 		if (!sname) {
9792e92ac56SJamie Gritton 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
9802e92ac56SJamie Gritton 			    (char *) &recv_tok);
9812e92ac56SJamie Gritton 			sx_xunlock(&svc_rpc_gss_lock);
9822e92ac56SJamie Gritton 			return (FALSE);
983d4468577SJamie Gritton 		}
9842e92ac56SJamie Gritton 	} else {
9852e92ac56SJamie Gritton 		gr->gr_major = gss_accept_sec_context(
9862e92ac56SJamie Gritton 			&gr->gr_minor,
9872e92ac56SJamie Gritton 			&client->cl_ctx,
9882e92ac56SJamie Gritton 			client->cl_sname->sn_cred,
9892e92ac56SJamie Gritton 			&recv_tok,
9902e92ac56SJamie Gritton 			GSS_C_NO_CHANNEL_BINDINGS,
9912e92ac56SJamie Gritton 			&client->cl_cname,
9922e92ac56SJamie Gritton 			&mech,
9932e92ac56SJamie Gritton 			&gr->gr_token,
9942e92ac56SJamie Gritton 			&ret_flags,
9952e92ac56SJamie Gritton 			&cred_lifetime,
9962e92ac56SJamie Gritton 			NULL);
997d4468577SJamie Gritton 	}
9982e92ac56SJamie Gritton 	sx_xunlock(&svc_rpc_gss_lock);
999a9148abdSDoug Rabson 
10002e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
1001d4468577SJamie Gritton 
1002d4468577SJamie Gritton 	/*
10032e92ac56SJamie Gritton 	 * If we get an error from gss_accept_sec_context, send the
10042e92ac56SJamie Gritton 	 * reply anyway so that the client gets a chance to see what
10052e92ac56SJamie Gritton 	 * is wrong.
1006d4468577SJamie Gritton 	 */
10072e92ac56SJamie Gritton 	if (gr->gr_major != GSS_S_COMPLETE &&
10082e92ac56SJamie Gritton 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
10092e92ac56SJamie Gritton 		rpc_gss_log_status("accept_sec_context", client->cl_mech,
10102e92ac56SJamie Gritton 		    gr->gr_major, gr->gr_minor);
10112e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
10122e92ac56SJamie Gritton 		return (TRUE);
10132e92ac56SJamie Gritton 	}
10142e92ac56SJamie Gritton 
10152e92ac56SJamie Gritton 	gr->gr_handle.value = &client->cl_id;
10162e92ac56SJamie Gritton 	gr->gr_handle.length = sizeof(client->cl_id);
10172e92ac56SJamie Gritton 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
10182e92ac56SJamie Gritton 
10192e92ac56SJamie Gritton 	/* Save client info. */
10202e92ac56SJamie Gritton 	client->cl_mech = mech;
10212e92ac56SJamie Gritton 	client->cl_qop = GSS_C_QOP_DEFAULT;
10222e92ac56SJamie Gritton 	client->cl_done_callback = FALSE;
10232e92ac56SJamie Gritton 
10242e92ac56SJamie Gritton 	if (gr->gr_major == GSS_S_COMPLETE) {
10252e92ac56SJamie Gritton 		gss_buffer_desc	export_name;
10262e92ac56SJamie Gritton 
10272e92ac56SJamie Gritton 		/*
10282e92ac56SJamie Gritton 		 * Change client expiration time to be near when the
10292e92ac56SJamie Gritton 		 * client creds expire (or 24 hours if we can't figure
10302e92ac56SJamie Gritton 		 * that out).
10312e92ac56SJamie Gritton 		 */
10322e92ac56SJamie Gritton 		if (cred_lifetime == GSS_C_INDEFINITE)
103304cb0c38SRick Macklem 			cred_lifetime = 24*60*60;
103404cb0c38SRick Macklem 
103504cb0c38SRick Macklem 		/*
103604cb0c38SRick Macklem 		 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
103704cb0c38SRick Macklem 		 */
103804cb0c38SRick Macklem 		if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
103904cb0c38SRick Macklem 		    svc_rpc_gss_lifetime_max)
104004cb0c38SRick Macklem 			cred_lifetime = svc_rpc_gss_lifetime_max;
10412e92ac56SJamie Gritton 
10422e92ac56SJamie Gritton 		client->cl_expiration = time_uptime + cred_lifetime;
10432e92ac56SJamie Gritton 
10442e92ac56SJamie Gritton 		/*
10452e92ac56SJamie Gritton 		 * Fill in cred details in the rawcred structure.
10462e92ac56SJamie Gritton 		 */
10472e92ac56SJamie Gritton 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
10482e92ac56SJamie Gritton 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
10492e92ac56SJamie Gritton 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
10502e92ac56SJamie Gritton 		    &export_name);
10512e92ac56SJamie Gritton 		if (maj_stat != GSS_S_COMPLETE) {
10522e92ac56SJamie Gritton 			rpc_gss_log_status("gss_export_name", client->cl_mech,
10532e92ac56SJamie Gritton 			    maj_stat, min_stat);
10542e92ac56SJamie Gritton 			return (FALSE);
10552e92ac56SJamie Gritton 		}
10562e92ac56SJamie Gritton 		client->cl_rawcred.client_principal =
10572e92ac56SJamie Gritton 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
10582e92ac56SJamie Gritton 			    + export_name.length);
10592e92ac56SJamie Gritton 		client->cl_rawcred.client_principal->len = export_name.length;
10602e92ac56SJamie Gritton 		memcpy(client->cl_rawcred.client_principal->name,
10612e92ac56SJamie Gritton 		    export_name.value, export_name.length);
10622e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &export_name);
10632e92ac56SJamie Gritton 		client->cl_rawcred.svc_principal =
10642e92ac56SJamie Gritton 			client->cl_sname->sn_principal;
10652e92ac56SJamie Gritton 		client->cl_rawcred.service = gc->gc_svc;
10662e92ac56SJamie Gritton 
10672e92ac56SJamie Gritton 		/*
10682e92ac56SJamie Gritton 		 * Use gss_pname_to_uid to map to unix creds. For
10692e92ac56SJamie Gritton 		 * kerberos5, this uses krb5_aname_to_localname.
10702e92ac56SJamie Gritton 		 */
10712e92ac56SJamie Gritton 		svc_rpc_gss_build_ucred(client, client->cl_cname);
10722e92ac56SJamie Gritton 		svc_rpc_gss_set_flavor(client);
10732e92ac56SJamie Gritton 		gss_release_name(&min_stat, &client->cl_cname);
10742e92ac56SJamie Gritton 
10752e92ac56SJamie Gritton #ifdef DEBUG
1076a9148abdSDoug Rabson 		{
10772e92ac56SJamie Gritton 			gss_buffer_desc mechname;
1078a9148abdSDoug Rabson 
10792e92ac56SJamie Gritton 			gss_oid_to_str(&min_stat, mech, &mechname);
10802e92ac56SJamie Gritton 
10812e92ac56SJamie Gritton 			rpc_gss_log_debug("accepted context for %s with "
10822e92ac56SJamie Gritton 			    "<mech %.*s, qop %d, svc %d>",
10832e92ac56SJamie Gritton 			    client->cl_rawcred.client_principal->name,
10842e92ac56SJamie Gritton 			    mechname.length, (char *)mechname.value,
10853d26cd60SBrooks Davis 			    client->cl_qop, client->cl_rawcred.service);
10862e92ac56SJamie Gritton 
10872e92ac56SJamie Gritton 			gss_release_buffer(&min_stat, &mechname);
1088d4468577SJamie Gritton 		}
10892e92ac56SJamie Gritton #endif /* DEBUG */
1090d4468577SJamie Gritton 	}
10912e92ac56SJamie Gritton 	return (TRUE);
1092a9148abdSDoug Rabson }
1093a9148abdSDoug Rabson 
10942e92ac56SJamie Gritton static bool_t
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop,rpc_gss_proc_t gcproc)10952e92ac56SJamie Gritton svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
109605496254SRick Macklem     gss_qop_t *qop, rpc_gss_proc_t gcproc)
1097a9148abdSDoug Rabson {
10982e92ac56SJamie Gritton 	struct opaque_auth	*oa;
10992e92ac56SJamie Gritton 	gss_buffer_desc		 rpcbuf, checksum;
11002e92ac56SJamie Gritton 	OM_uint32		 maj_stat, min_stat;
11012e92ac56SJamie Gritton 	gss_qop_t		 qop_state;
11022e92ac56SJamie Gritton 	int32_t			 rpchdr[128 / sizeof(int32_t)];
11032e92ac56SJamie Gritton 	int32_t			*buf;
1104a9148abdSDoug Rabson 
11052e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_validate()");
11062e92ac56SJamie Gritton 
11072e92ac56SJamie Gritton 	memset(rpchdr, 0, sizeof(rpchdr));
11082e92ac56SJamie Gritton 
11092e92ac56SJamie Gritton 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
11102e92ac56SJamie Gritton 	buf = rpchdr;
11112e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_xid);
11122e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, msg->rm_direction);
11132e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
11142e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
11152e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
11162e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
11172e92ac56SJamie Gritton 	oa = &msg->rm_call.cb_cred;
11182e92ac56SJamie Gritton 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
11192e92ac56SJamie Gritton 	IXDR_PUT_LONG(buf, oa->oa_length);
11202e92ac56SJamie Gritton 	if (oa->oa_length) {
11212e92ac56SJamie Gritton 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
11222e92ac56SJamie Gritton 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
1123a9148abdSDoug Rabson 	}
11242e92ac56SJamie Gritton 	rpcbuf.value = rpchdr;
11252e92ac56SJamie Gritton 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
11262e92ac56SJamie Gritton 
11272e92ac56SJamie Gritton 	checksum.value = msg->rm_call.cb_verf.oa_base;
11282e92ac56SJamie Gritton 	checksum.length = msg->rm_call.cb_verf.oa_length;
11292e92ac56SJamie Gritton 
11302e92ac56SJamie Gritton 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
11312e92ac56SJamie Gritton 				  &qop_state);
11322e92ac56SJamie Gritton 
11332e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
11342e92ac56SJamie Gritton 		rpc_gss_log_status("gss_verify_mic", client->cl_mech,
11352e92ac56SJamie Gritton 		    maj_stat, min_stat);
1136798a34feSRick Macklem 		/*
11371e0706fdSRick Macklem 		 * A bug in some versions of the Linux client generates a
11381e0706fdSRick Macklem 		 * Destroy operation with a bogus encrypted checksum. Deleting
11391e0706fdSRick Macklem 		 * the credential handle for that case causes the mount to fail.
1140798a34feSRick Macklem 		 * Since the checksum is bogus (gss_verify_mic() failed), it
1141798a34feSRick Macklem 		 * doesn't make sense to destroy the handle and not doing so
1142798a34feSRick Macklem 		 * fixes the Linux mount.
1143798a34feSRick Macklem 		 */
114405496254SRick Macklem 		if (gcproc != RPCSEC_GSS_DESTROY)
11452e92ac56SJamie Gritton 			client->cl_state = CLIENT_STALE;
11462e92ac56SJamie Gritton 		return (FALSE);
1147d4468577SJamie Gritton 	}
1148d4468577SJamie Gritton 
11492e92ac56SJamie Gritton 	*qop = qop_state;
11502e92ac56SJamie Gritton 	return (TRUE);
11512e92ac56SJamie Gritton }
11522e92ac56SJamie Gritton 
11532e92ac56SJamie Gritton static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)11542e92ac56SJamie Gritton svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
11552e92ac56SJamie Gritton     struct svc_req *rqst, u_int seq)
11562e92ac56SJamie Gritton {
11572e92ac56SJamie Gritton 	gss_buffer_desc		signbuf;
11582e92ac56SJamie Gritton 	gss_buffer_desc		mic;
11592e92ac56SJamie Gritton 	OM_uint32		maj_stat, min_stat;
11602e92ac56SJamie Gritton 	uint32_t		nseq;
11612e92ac56SJamie Gritton 
11622e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
11632e92ac56SJamie Gritton 
11642e92ac56SJamie Gritton 	nseq = htonl(seq);
11652e92ac56SJamie Gritton 	signbuf.value = &nseq;
11662e92ac56SJamie Gritton 	signbuf.length = sizeof(nseq);
11672e92ac56SJamie Gritton 
11682e92ac56SJamie Gritton 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
11692e92ac56SJamie Gritton 	    &signbuf, &mic);
11702e92ac56SJamie Gritton 
11712e92ac56SJamie Gritton 	if (maj_stat != GSS_S_COMPLETE) {
11722e92ac56SJamie Gritton 		rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
11732e92ac56SJamie Gritton 		client->cl_state = CLIENT_STALE;
11742e92ac56SJamie Gritton 		return (FALSE);
11752e92ac56SJamie Gritton 	}
11762e92ac56SJamie Gritton 
11772e92ac56SJamie Gritton 	KASSERT(mic.length <= MAX_AUTH_BYTES,
11782e92ac56SJamie Gritton 	    ("MIC too large for RPCSEC_GSS"));
11792e92ac56SJamie Gritton 
11802e92ac56SJamie Gritton 	rqst->rq_verf.oa_flavor = RPCSEC_GSS;
11812e92ac56SJamie Gritton 	rqst->rq_verf.oa_length = mic.length;
11822e92ac56SJamie Gritton 	bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
11832e92ac56SJamie Gritton 
11842e92ac56SJamie Gritton 	gss_release_buffer(&min_stat, &mic);
11852e92ac56SJamie Gritton 
11862e92ac56SJamie Gritton 	return (TRUE);
11872e92ac56SJamie Gritton }
11882e92ac56SJamie Gritton 
11892e92ac56SJamie Gritton static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)11902e92ac56SJamie Gritton svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
11912e92ac56SJamie Gritton {
11922e92ac56SJamie Gritton 	struct svc_rpc_gss_callback *scb;
11932e92ac56SJamie Gritton 	rpc_gss_lock_t	lock;
11942e92ac56SJamie Gritton 	void		*cookie;
11952e92ac56SJamie Gritton 	bool_t		cb_res;
11962e92ac56SJamie Gritton 	bool_t		result;
11972e92ac56SJamie Gritton 
11982e92ac56SJamie Gritton 	/*
11992e92ac56SJamie Gritton 	 * See if we have a callback for this guy.
12002e92ac56SJamie Gritton 	 */
12012e92ac56SJamie Gritton 	result = TRUE;
12022894c8c9SRick Macklem 	SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) {
12032e92ac56SJamie Gritton 		if (scb->cb_callback.program == rqst->rq_prog
12042e92ac56SJamie Gritton 		    && scb->cb_callback.version == rqst->rq_vers) {
12052e92ac56SJamie Gritton 			/*
12062e92ac56SJamie Gritton 			 * This one matches. Call the callback and see
12072e92ac56SJamie Gritton 			 * if it wants to veto or something.
12082e92ac56SJamie Gritton 			 */
12092e92ac56SJamie Gritton 			lock.locked = FALSE;
12102e92ac56SJamie Gritton 			lock.raw_cred = &client->cl_rawcred;
12112e92ac56SJamie Gritton 			cb_res = scb->cb_callback.callback(rqst,
12122e92ac56SJamie Gritton 			    client->cl_creds,
12132e92ac56SJamie Gritton 			    client->cl_ctx,
12142e92ac56SJamie Gritton 			    &lock,
12152e92ac56SJamie Gritton 			    &cookie);
12162e92ac56SJamie Gritton 
12172e92ac56SJamie Gritton 			if (!cb_res) {
12182e92ac56SJamie Gritton 				client->cl_state = CLIENT_STALE;
12192e92ac56SJamie Gritton 				result = FALSE;
12202e92ac56SJamie Gritton 				break;
12212e92ac56SJamie Gritton 			}
12222e92ac56SJamie Gritton 
12232e92ac56SJamie Gritton 			/*
12242e92ac56SJamie Gritton 			 * The callback accepted the connection - it
12252e92ac56SJamie Gritton 			 * is responsible for freeing client->cl_creds
12262e92ac56SJamie Gritton 			 * now.
12272e92ac56SJamie Gritton 			 */
12282e92ac56SJamie Gritton 			client->cl_creds = GSS_C_NO_CREDENTIAL;
12292e92ac56SJamie Gritton 			client->cl_locked = lock.locked;
12302e92ac56SJamie Gritton 			client->cl_cookie = cookie;
12312e92ac56SJamie Gritton 			return (TRUE);
12322e92ac56SJamie Gritton 		}
12332e92ac56SJamie Gritton 	}
12342e92ac56SJamie Gritton 
12352e92ac56SJamie Gritton 	/*
12362e92ac56SJamie Gritton 	 * Either no callback exists for this program/version or one
12372e92ac56SJamie Gritton 	 * of the callbacks rejected the connection. We just need to
12382e92ac56SJamie Gritton 	 * clean up the delegated client creds, if any.
12392e92ac56SJamie Gritton 	 */
12402e92ac56SJamie Gritton 	if (client->cl_creds) {
12412e92ac56SJamie Gritton 		OM_uint32 min_ver;
12422e92ac56SJamie Gritton 		gss_release_cred(&min_ver, &client->cl_creds);
12432e92ac56SJamie Gritton 	}
12442e92ac56SJamie Gritton 	return (result);
12452e92ac56SJamie Gritton }
12462e92ac56SJamie Gritton 
12472e92ac56SJamie Gritton static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)12482e92ac56SJamie Gritton svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
12492e92ac56SJamie Gritton {
1250bcd0e31dSJohn Baldwin 	uint32_t offset;
12512e92ac56SJamie Gritton 	int word, bit;
12522e92ac56SJamie Gritton 	bool_t result;
12532e92ac56SJamie Gritton 
12542e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
12552e92ac56SJamie Gritton 	if (seq <= client->cl_seqlast) {
12562e92ac56SJamie Gritton 		/*
12572e92ac56SJamie Gritton 		 * The request sequence number is less than
12582e92ac56SJamie Gritton 		 * the largest we have seen so far. If it is
12592e92ac56SJamie Gritton 		 * outside the window or if we have seen a
12602e92ac56SJamie Gritton 		 * request with this sequence before, silently
12612e92ac56SJamie Gritton 		 * discard it.
12622e92ac56SJamie Gritton 		 */
12632e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
12642e92ac56SJamie Gritton 		if (offset >= SVC_RPC_GSS_SEQWINDOW) {
12652e92ac56SJamie Gritton 			result = FALSE;
12662e92ac56SJamie Gritton 			goto out;
12672e92ac56SJamie Gritton 		}
12682e92ac56SJamie Gritton 		word = offset / 32;
12692e92ac56SJamie Gritton 		bit = offset % 32;
12702e92ac56SJamie Gritton 		if (client->cl_seqmask[word] & (1 << bit)) {
12712e92ac56SJamie Gritton 			result = FALSE;
12722e92ac56SJamie Gritton 			goto out;
12732e92ac56SJamie Gritton 		}
12742e92ac56SJamie Gritton 	}
12752e92ac56SJamie Gritton 
12762e92ac56SJamie Gritton 	result = TRUE;
12772e92ac56SJamie Gritton out:
12782e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
12792e92ac56SJamie Gritton 	return (result);
12802e92ac56SJamie Gritton }
12812e92ac56SJamie Gritton 
12822e92ac56SJamie Gritton static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)12832e92ac56SJamie Gritton svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
12842e92ac56SJamie Gritton {
12852e92ac56SJamie Gritton 	int offset, i, word, bit;
12862e92ac56SJamie Gritton 	uint32_t carry, newcarry;
12872e92ac56SJamie Gritton 
12882e92ac56SJamie Gritton 	sx_xlock(&client->cl_lock);
12892e92ac56SJamie Gritton 	if (seq > client->cl_seqlast) {
12902e92ac56SJamie Gritton 		/*
12912e92ac56SJamie Gritton 		 * This request has a sequence number greater
12922e92ac56SJamie Gritton 		 * than any we have seen so far. Advance the
12932e92ac56SJamie Gritton 		 * seq window and set bit zero of the window
12942e92ac56SJamie Gritton 		 * (which corresponds to the new sequence
12952e92ac56SJamie Gritton 		 * number)
12962e92ac56SJamie Gritton 		 */
12972e92ac56SJamie Gritton 		offset = seq - client->cl_seqlast;
12982e92ac56SJamie Gritton 		while (offset > 32) {
12992e92ac56SJamie Gritton 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
13002e92ac56SJamie Gritton 			     i > 0; i--) {
13012e92ac56SJamie Gritton 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
13022e92ac56SJamie Gritton 			}
13032e92ac56SJamie Gritton 			client->cl_seqmask[0] = 0;
13042e92ac56SJamie Gritton 			offset -= 32;
13052e92ac56SJamie Gritton 		}
13062e92ac56SJamie Gritton 		carry = 0;
13072e92ac56SJamie Gritton 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
13082e92ac56SJamie Gritton 			newcarry = client->cl_seqmask[i] >> (32 - offset);
13092e92ac56SJamie Gritton 			client->cl_seqmask[i] =
13102e92ac56SJamie Gritton 				(client->cl_seqmask[i] << offset) | carry;
13112e92ac56SJamie Gritton 			carry = newcarry;
13122e92ac56SJamie Gritton 		}
13132e92ac56SJamie Gritton 		client->cl_seqmask[0] |= 1;
13142e92ac56SJamie Gritton 		client->cl_seqlast = seq;
13152e92ac56SJamie Gritton 	} else {
13162e92ac56SJamie Gritton 		offset = client->cl_seqlast - seq;
13172e92ac56SJamie Gritton 		word = offset / 32;
13182e92ac56SJamie Gritton 		bit = offset % 32;
13192e92ac56SJamie Gritton 		client->cl_seqmask[word] |= (1 << bit);
13202e92ac56SJamie Gritton 	}
13212e92ac56SJamie Gritton 	sx_xunlock(&client->cl_lock);
13222e92ac56SJamie Gritton }
13232e92ac56SJamie Gritton 
13242e92ac56SJamie Gritton enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)13252e92ac56SJamie Gritton svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
13262e92ac56SJamie Gritton 
13272e92ac56SJamie Gritton {
13282e92ac56SJamie Gritton 	OM_uint32		 min_stat;
13292e92ac56SJamie Gritton 	XDR	 		 xdrs;
13302e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
13312e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
13322e92ac56SJamie Gritton 	struct rpc_gss_cred	 gc;
13332e92ac56SJamie Gritton 	struct rpc_gss_init_res	 gr;
13342e92ac56SJamie Gritton 	gss_qop_t		 qop;
13352e92ac56SJamie Gritton 	int			 call_stat;
13362e92ac56SJamie Gritton 	enum auth_stat		 result;
13372e92ac56SJamie Gritton 
13382894c8c9SRick Macklem 	KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread));
13392e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss()");
13402e92ac56SJamie Gritton 
13412e92ac56SJamie Gritton 	/* Garbage collect old clients. */
13422e92ac56SJamie Gritton 	svc_rpc_gss_timeout_clients();
13432e92ac56SJamie Gritton 
13442e92ac56SJamie Gritton 	/* Initialize reply. */
13452e92ac56SJamie Gritton 	rqst->rq_verf = _null_auth;
13462e92ac56SJamie Gritton 
13472e92ac56SJamie Gritton 	/* Deserialize client credentials. */
13482894c8c9SRick Macklem 	if (rqst->rq_cred.oa_length <= 0) {
13492894c8c9SRick Macklem 		KGSS_CURVNET_RESTORE();
13502e92ac56SJamie Gritton 		return (AUTH_BADCRED);
13512894c8c9SRick Macklem 	}
13522e92ac56SJamie Gritton 
13532e92ac56SJamie Gritton 	memset(&gc, 0, sizeof(gc));
13542e92ac56SJamie Gritton 
13552e92ac56SJamie Gritton 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
13562e92ac56SJamie Gritton 	    rqst->rq_cred.oa_length, XDR_DECODE);
13572e92ac56SJamie Gritton 
13582e92ac56SJamie Gritton 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
13592e92ac56SJamie Gritton 		XDR_DESTROY(&xdrs);
13602894c8c9SRick Macklem 		KGSS_CURVNET_RESTORE();
13612e92ac56SJamie Gritton 		return (AUTH_BADCRED);
13622e92ac56SJamie Gritton 	}
13632e92ac56SJamie Gritton 	XDR_DESTROY(&xdrs);
13642e92ac56SJamie Gritton 
13652e92ac56SJamie Gritton 	client = NULL;
13662e92ac56SJamie Gritton 
13672e92ac56SJamie Gritton 	/* Check version. */
13682e92ac56SJamie Gritton 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
13692e92ac56SJamie Gritton 		result = AUTH_BADCRED;
13702e92ac56SJamie Gritton 		goto out;
13712e92ac56SJamie Gritton 	}
13722e92ac56SJamie Gritton 
13732e92ac56SJamie Gritton 	/* Check the proc and find the client (or create it) */
13742e92ac56SJamie Gritton 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
13752e92ac56SJamie Gritton 		if (gc.gc_handle.length != 0) {
13762e92ac56SJamie Gritton 			result = AUTH_BADCRED;
13772e92ac56SJamie Gritton 			goto out;
13782e92ac56SJamie Gritton 		}
13792e92ac56SJamie Gritton 		client = svc_rpc_gss_create_client();
13802e92ac56SJamie Gritton 	} else {
13812e92ac56SJamie Gritton 		struct svc_rpc_gss_clientid *p;
13822e92ac56SJamie Gritton 		if (gc.gc_handle.length != sizeof(*p)) {
13832e92ac56SJamie Gritton 			result = AUTH_BADCRED;
13842e92ac56SJamie Gritton 			goto out;
13852e92ac56SJamie Gritton 		}
13862e92ac56SJamie Gritton 		p = gc.gc_handle.value;
13872e92ac56SJamie Gritton 		client = svc_rpc_gss_find_client(p);
13882e92ac56SJamie Gritton 		if (!client) {
13892e92ac56SJamie Gritton 			/*
13902e92ac56SJamie Gritton 			 * Can't find the client - we may have
13912e92ac56SJamie Gritton 			 * destroyed it - tell the other side to
13922e92ac56SJamie Gritton 			 * re-authenticate.
13932e92ac56SJamie Gritton 			 */
13942e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
13952e92ac56SJamie Gritton 			goto out;
13962e92ac56SJamie Gritton 		}
13972e92ac56SJamie Gritton 	}
13982e92ac56SJamie Gritton 	cc = rqst->rq_clntcred;
13992e92ac56SJamie Gritton 	cc->cc_client = client;
14002e92ac56SJamie Gritton 	cc->cc_service = gc.gc_svc;
14012e92ac56SJamie Gritton 	cc->cc_seq = gc.gc_seq;
14022e92ac56SJamie Gritton 
14032e92ac56SJamie Gritton 	/*
14042e92ac56SJamie Gritton 	 * The service and sequence number must be ignored for
14052e92ac56SJamie Gritton 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
14062e92ac56SJamie Gritton 	 */
14072e92ac56SJamie Gritton 	if (gc.gc_proc != RPCSEC_GSS_INIT
14082e92ac56SJamie Gritton 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
14092e92ac56SJamie Gritton 		/*
14102e92ac56SJamie Gritton 		 * Check for sequence number overflow.
14112e92ac56SJamie Gritton 		 */
14122e92ac56SJamie Gritton 		if (gc.gc_seq >= MAXSEQ) {
14132e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
14142e92ac56SJamie Gritton 			goto out;
14152e92ac56SJamie Gritton 		}
14162e92ac56SJamie Gritton 
14172e92ac56SJamie Gritton 		/*
14182e92ac56SJamie Gritton 		 * Check for valid service.
14192e92ac56SJamie Gritton 		 */
14202e92ac56SJamie Gritton 		if (gc.gc_svc != rpc_gss_svc_none &&
14212e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_integrity &&
14222e92ac56SJamie Gritton 		    gc.gc_svc != rpc_gss_svc_privacy) {
14232e92ac56SJamie Gritton 			result = AUTH_BADCRED;
14242e92ac56SJamie Gritton 			goto out;
14252e92ac56SJamie Gritton 		}
14262e92ac56SJamie Gritton 	}
14272e92ac56SJamie Gritton 
14282e92ac56SJamie Gritton 	/* Handle RPCSEC_GSS control procedure. */
14292e92ac56SJamie Gritton 	switch (gc.gc_proc) {
14302e92ac56SJamie Gritton 
14312e92ac56SJamie Gritton 	case RPCSEC_GSS_INIT:
14322e92ac56SJamie Gritton 	case RPCSEC_GSS_CONTINUE_INIT:
14332e92ac56SJamie Gritton 		if (rqst->rq_proc != NULLPROC) {
14342e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
14352e92ac56SJamie Gritton 			break;
14362e92ac56SJamie Gritton 		}
14372e92ac56SJamie Gritton 
14382e92ac56SJamie Gritton 		memset(&gr, 0, sizeof(gr));
14392e92ac56SJamie Gritton 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
14402e92ac56SJamie Gritton 			result = AUTH_REJECTEDCRED;
14412e92ac56SJamie Gritton 			break;
14422e92ac56SJamie Gritton 		}
14432e92ac56SJamie Gritton 
14442e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE) {
14452e92ac56SJamie Gritton 			/*
14462e92ac56SJamie Gritton 			 * We borrow the space for the call verf to
14472e92ac56SJamie Gritton 			 * pack our reply verf.
14482e92ac56SJamie Gritton 			 */
14492e92ac56SJamie Gritton 			rqst->rq_verf = msg->rm_call.cb_verf;
14502e92ac56SJamie Gritton 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
14512e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
14522e92ac56SJamie Gritton 				break;
14532e92ac56SJamie Gritton 			}
14542e92ac56SJamie Gritton 		} else {
14552e92ac56SJamie Gritton 			rqst->rq_verf = _null_auth;
14562e92ac56SJamie Gritton 		}
14572e92ac56SJamie Gritton 
14582e92ac56SJamie Gritton 		call_stat = svc_sendreply(rqst,
14592e92ac56SJamie Gritton 		    (xdrproc_t) xdr_rpc_gss_init_res,
14602e92ac56SJamie Gritton 		    (caddr_t) &gr);
14612e92ac56SJamie Gritton 
14622e92ac56SJamie Gritton 		gss_release_buffer(&min_stat, &gr.gr_token);
14632e92ac56SJamie Gritton 
14642e92ac56SJamie Gritton 		if (!call_stat) {
14652e92ac56SJamie Gritton 			result = AUTH_FAILED;
14662e92ac56SJamie Gritton 			break;
14672e92ac56SJamie Gritton 		}
14682e92ac56SJamie Gritton 
14692e92ac56SJamie Gritton 		if (gr.gr_major == GSS_S_COMPLETE)
14702e92ac56SJamie Gritton 			client->cl_state = CLIENT_ESTABLISHED;
14712e92ac56SJamie Gritton 
14722e92ac56SJamie Gritton 		result = RPCSEC_GSS_NODISPATCH;
14732e92ac56SJamie Gritton 		break;
14742e92ac56SJamie Gritton 
14752e92ac56SJamie Gritton 	case RPCSEC_GSS_DATA:
14762e92ac56SJamie Gritton 	case RPCSEC_GSS_DESTROY:
14772e92ac56SJamie Gritton 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
14782e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
14792e92ac56SJamie Gritton 			break;
14802e92ac56SJamie Gritton 		}
14812e92ac56SJamie Gritton 
148205496254SRick Macklem 		if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
14832e92ac56SJamie Gritton 			result = RPCSEC_GSS_CREDPROBLEM;
14842e92ac56SJamie Gritton 			break;
14852e92ac56SJamie Gritton 		}
14862e92ac56SJamie Gritton 
14872e92ac56SJamie Gritton 		/*
14882e92ac56SJamie Gritton 		 * We borrow the space for the call verf to pack our
14892e92ac56SJamie Gritton 		 * reply verf.
14902e92ac56SJamie Gritton 		 */
14912e92ac56SJamie Gritton 		rqst->rq_verf = msg->rm_call.cb_verf;
14922e92ac56SJamie Gritton 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
14932e92ac56SJamie Gritton 			result = RPCSEC_GSS_CTXPROBLEM;
14942e92ac56SJamie Gritton 			break;
14952e92ac56SJamie Gritton 		}
14962e92ac56SJamie Gritton 
14972e92ac56SJamie Gritton 		svc_rpc_gss_update_seq(client, gc.gc_seq);
14982e92ac56SJamie Gritton 
14992e92ac56SJamie Gritton 		/*
15002e92ac56SJamie Gritton 		 * Change the SVCAUTH ops on the request to point at
15012e92ac56SJamie Gritton 		 * our own code so that we can unwrap the arguments
15022e92ac56SJamie Gritton 		 * and wrap the result. The caller will re-set this on
15032e92ac56SJamie Gritton 		 * every request to point to a set of null wrap/unwrap
15042e92ac56SJamie Gritton 		 * methods. Acquire an extra reference to the client
15052e92ac56SJamie Gritton 		 * which will be released by svc_rpc_gss_release()
15062e92ac56SJamie Gritton 		 * after the request has finished processing.
15072e92ac56SJamie Gritton 		 */
15082e92ac56SJamie Gritton 		refcount_acquire(&client->cl_refs);
15092e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
15102e92ac56SJamie Gritton 		rqst->rq_auth.svc_ah_private = cc;
15112e92ac56SJamie Gritton 
15122e92ac56SJamie Gritton 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
15132e92ac56SJamie Gritton 			/*
15142e92ac56SJamie Gritton 			 * We might be ready to do a callback to the server to
15152e92ac56SJamie Gritton 			 * see if it wants to accept/reject the connection.
15162e92ac56SJamie Gritton 			 */
15172e92ac56SJamie Gritton 			sx_xlock(&client->cl_lock);
15182e92ac56SJamie Gritton 			if (!client->cl_done_callback) {
15192e92ac56SJamie Gritton 				client->cl_done_callback = TRUE;
15202e92ac56SJamie Gritton 				client->cl_qop = qop;
15212e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
15222e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
15232e92ac56SJamie Gritton 				if (!svc_rpc_gss_callback(client, rqst)) {
15242e92ac56SJamie Gritton 					result = AUTH_REJECTEDCRED;
15252e92ac56SJamie Gritton 					sx_xunlock(&client->cl_lock);
15262e92ac56SJamie Gritton 					break;
15272e92ac56SJamie Gritton 				}
15282e92ac56SJamie Gritton 			}
15292e92ac56SJamie Gritton 			sx_xunlock(&client->cl_lock);
15302e92ac56SJamie Gritton 
15312e92ac56SJamie Gritton 			/*
15322e92ac56SJamie Gritton 			 * If the server has locked this client to a
15332e92ac56SJamie Gritton 			 * particular service+qop pair, enforce that
15342e92ac56SJamie Gritton 			 * restriction now.
15352e92ac56SJamie Gritton 			 */
15362e92ac56SJamie Gritton 			if (client->cl_locked) {
15372e92ac56SJamie Gritton 				if (client->cl_rawcred.service != gc.gc_svc) {
15382e92ac56SJamie Gritton 					result = AUTH_FAILED;
15392e92ac56SJamie Gritton 					break;
15402e92ac56SJamie Gritton 				} else if (client->cl_qop != qop) {
15412e92ac56SJamie Gritton 					result = AUTH_BADVERF;
15422e92ac56SJamie Gritton 					break;
15432e92ac56SJamie Gritton 				}
15442e92ac56SJamie Gritton 			}
15452e92ac56SJamie Gritton 
15462e92ac56SJamie Gritton 			/*
15472e92ac56SJamie Gritton 			 * If the qop changed, look up the new qop
15482e92ac56SJamie Gritton 			 * name for rawcred.
15492e92ac56SJamie Gritton 			 */
15502e92ac56SJamie Gritton 			if (client->cl_qop != qop) {
15512e92ac56SJamie Gritton 				client->cl_qop = qop;
15522e92ac56SJamie Gritton 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
15532e92ac56SJamie Gritton 					client->cl_rawcred.mechanism, qop);
15542e92ac56SJamie Gritton 			}
15552e92ac56SJamie Gritton 
15562e92ac56SJamie Gritton 			/*
15572e92ac56SJamie Gritton 			 * Make sure we use the right service value
15582e92ac56SJamie Gritton 			 * for unwrap/wrap.
15592e92ac56SJamie Gritton 			 */
15602e92ac56SJamie Gritton 			if (client->cl_rawcred.service != gc.gc_svc) {
15612e92ac56SJamie Gritton 				client->cl_rawcred.service = gc.gc_svc;
15622e92ac56SJamie Gritton 				svc_rpc_gss_set_flavor(client);
15632e92ac56SJamie Gritton 			}
15642e92ac56SJamie Gritton 
15652e92ac56SJamie Gritton 			result = AUTH_OK;
15662e92ac56SJamie Gritton 		} else {
15672e92ac56SJamie Gritton 			if (rqst->rq_proc != NULLPROC) {
15682e92ac56SJamie Gritton 				result = AUTH_REJECTEDCRED;
15692e92ac56SJamie Gritton 				break;
15702e92ac56SJamie Gritton 			}
15712e92ac56SJamie Gritton 
15722e92ac56SJamie Gritton 			call_stat = svc_sendreply(rqst,
15732e92ac56SJamie Gritton 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
15742e92ac56SJamie Gritton 
15752e92ac56SJamie Gritton 			if (!call_stat) {
15762e92ac56SJamie Gritton 				result = AUTH_FAILED;
15772e92ac56SJamie Gritton 				break;
15782e92ac56SJamie Gritton 			}
15792e92ac56SJamie Gritton 
15802e92ac56SJamie Gritton 			svc_rpc_gss_forget_client(client);
15812e92ac56SJamie Gritton 
15822e92ac56SJamie Gritton 			result = RPCSEC_GSS_NODISPATCH;
15832e92ac56SJamie Gritton 			break;
15842e92ac56SJamie Gritton 		}
15852e92ac56SJamie Gritton 		break;
15862e92ac56SJamie Gritton 
15872e92ac56SJamie Gritton 	default:
15882e92ac56SJamie Gritton 		result = AUTH_BADCRED;
15892e92ac56SJamie Gritton 		break;
15902e92ac56SJamie Gritton 	}
15912e92ac56SJamie Gritton out:
15922e92ac56SJamie Gritton 	if (client)
15932e92ac56SJamie Gritton 		svc_rpc_gss_release_client(client);
15942e92ac56SJamie Gritton 
15952e92ac56SJamie Gritton 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
15962894c8c9SRick Macklem 	KGSS_CURVNET_RESTORE();
15972e92ac56SJamie Gritton 	return (result);
15982e92ac56SJamie Gritton }
15992e92ac56SJamie Gritton 
16002e92ac56SJamie Gritton static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,struct mbuf ** mp)16012e92ac56SJamie Gritton svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
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_wrap()");
16072e92ac56SJamie Gritton 
16082e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
16092e92ac56SJamie Gritton 	client = cc->cc_client;
16102e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
16112e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
16122e92ac56SJamie Gritton 		return (TRUE);
16132e92ac56SJamie Gritton 	}
16142e92ac56SJamie Gritton 
16152e92ac56SJamie Gritton 	return (xdr_rpc_gss_wrap_data(mp,
16162e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
16172e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
16182e92ac56SJamie Gritton }
16192e92ac56SJamie Gritton 
16202e92ac56SJamie Gritton static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,struct mbuf ** mp)16212e92ac56SJamie Gritton svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
16222e92ac56SJamie Gritton {
16232e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
16242e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
16252e92ac56SJamie Gritton 
16262e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
16272e92ac56SJamie Gritton 
16282e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
16292e92ac56SJamie Gritton 	client = cc->cc_client;
16302e92ac56SJamie Gritton 	if (client->cl_state != CLIENT_ESTABLISHED
16312e92ac56SJamie Gritton 	    || cc->cc_service == rpc_gss_svc_none) {
16322e92ac56SJamie Gritton 		return (TRUE);
16332e92ac56SJamie Gritton 	}
16342e92ac56SJamie Gritton 
16352e92ac56SJamie Gritton 	return (xdr_rpc_gss_unwrap_data(mp,
16362e92ac56SJamie Gritton 		client->cl_ctx, client->cl_qop,
16372e92ac56SJamie Gritton 		cc->cc_service, cc->cc_seq));
16382e92ac56SJamie Gritton }
16392e92ac56SJamie Gritton 
16402e92ac56SJamie Gritton static void
svc_rpc_gss_release(SVCAUTH * auth)16412e92ac56SJamie Gritton svc_rpc_gss_release(SVCAUTH *auth)
16422e92ac56SJamie Gritton {
16432e92ac56SJamie Gritton 	struct svc_rpc_gss_cookedcred *cc;
16442e92ac56SJamie Gritton 	struct svc_rpc_gss_client *client;
16452e92ac56SJamie Gritton 
16462e92ac56SJamie Gritton 	rpc_gss_log_debug("in svc_rpc_gss_release()");
16472e92ac56SJamie Gritton 
16482e92ac56SJamie Gritton 	cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
16492e92ac56SJamie Gritton 	client = cc->cc_client;
16502e92ac56SJamie Gritton 	svc_rpc_gss_release_client(client);
16512e92ac56SJamie Gritton }
1652