xref: /freebsd/lib/librpcsec_gss/svc_rpcsec_gss.c (revision 11bc2c1ca77e2c52eed4d689480bba855803c179)
18f55a568SDoug Rabson /*-
28f55a568SDoug Rabson  * Copyright (c) 2008 Doug Rabson
38f55a568SDoug Rabson  * All rights reserved.
48f55a568SDoug Rabson  *
58f55a568SDoug Rabson  * Redistribution and use in source and binary forms, with or without
68f55a568SDoug Rabson  * modification, are permitted provided that the following conditions
78f55a568SDoug Rabson  * are met:
88f55a568SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
98f55a568SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
108f55a568SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
118f55a568SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
128f55a568SDoug Rabson  *    documentation and/or other materials provided with the distribution.
138f55a568SDoug Rabson  *
148f55a568SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
158f55a568SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
168f55a568SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
178f55a568SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
188f55a568SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
198f55a568SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
208f55a568SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218f55a568SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
228f55a568SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
238f55a568SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
248f55a568SDoug Rabson  * SUCH DAMAGE.
258f55a568SDoug Rabson  *
268f55a568SDoug Rabson  *	$FreeBSD$
278f55a568SDoug Rabson  */
288f55a568SDoug Rabson /*
298f55a568SDoug Rabson   svc_rpcsec_gss.c
308f55a568SDoug Rabson 
318f55a568SDoug Rabson   Copyright (c) 2000 The Regents of the University of Michigan.
328f55a568SDoug Rabson   All rights reserved.
338f55a568SDoug Rabson 
348f55a568SDoug Rabson   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
358f55a568SDoug Rabson   All rights reserved, all wrongs reversed.
368f55a568SDoug Rabson 
378f55a568SDoug Rabson   Redistribution and use in source and binary forms, with or without
388f55a568SDoug Rabson   modification, are permitted provided that the following conditions
398f55a568SDoug Rabson   are met:
408f55a568SDoug Rabson 
418f55a568SDoug Rabson   1. Redistributions of source code must retain the above copyright
428f55a568SDoug Rabson      notice, this list of conditions and the following disclaimer.
438f55a568SDoug Rabson   2. Redistributions in binary form must reproduce the above copyright
448f55a568SDoug Rabson      notice, this list of conditions and the following disclaimer in the
458f55a568SDoug Rabson      documentation and/or other materials provided with the distribution.
468f55a568SDoug Rabson   3. Neither the name of the University nor the names of its
478f55a568SDoug Rabson      contributors may be used to endorse or promote products derived
488f55a568SDoug Rabson      from this software without specific prior written permission.
498f55a568SDoug Rabson 
508f55a568SDoug Rabson   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
518f55a568SDoug Rabson   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
528f55a568SDoug Rabson   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
538f55a568SDoug Rabson   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
548f55a568SDoug Rabson   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
558f55a568SDoug Rabson   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
568f55a568SDoug Rabson   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
578f55a568SDoug Rabson   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
588f55a568SDoug Rabson   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
598f55a568SDoug Rabson   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
608f55a568SDoug Rabson   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
618f55a568SDoug Rabson 
628f55a568SDoug Rabson   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
638f55a568SDoug Rabson  */
648f55a568SDoug Rabson 
658f55a568SDoug Rabson #include <stdio.h>
668f55a568SDoug Rabson #include <stdlib.h>
678f55a568SDoug Rabson #include <string.h>
688f55a568SDoug Rabson #include <pwd.h>
698f55a568SDoug Rabson #include <grp.h>
708f55a568SDoug Rabson #include <errno.h>
718f55a568SDoug Rabson #include <unistd.h>
728f55a568SDoug Rabson #include <sys/queue.h>
738f55a568SDoug Rabson #include <rpc/rpc.h>
748f55a568SDoug Rabson #include <rpc/rpcsec_gss.h>
758f55a568SDoug Rabson #include "rpcsec_gss_int.h"
768f55a568SDoug Rabson 
778f55a568SDoug Rabson static bool_t	svc_rpc_gss_initialised = FALSE;
788f55a568SDoug Rabson 
798f55a568SDoug Rabson static bool_t   svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
808f55a568SDoug Rabson static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
818f55a568SDoug Rabson static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
828f55a568SDoug Rabson 
838f55a568SDoug Rabson static struct svc_auth_ops svc_auth_gss_ops = {
848f55a568SDoug Rabson 	svc_rpc_gss_wrap,
858f55a568SDoug Rabson 	svc_rpc_gss_unwrap,
868f55a568SDoug Rabson };
878f55a568SDoug Rabson 
888f55a568SDoug Rabson struct svc_rpc_gss_callback {
898f55a568SDoug Rabson 	SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
908f55a568SDoug Rabson 	rpc_gss_callback_t	cb_callback;
918f55a568SDoug Rabson };
928f55a568SDoug Rabson static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
9313e403fdSAntoine Brodin 	svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
948f55a568SDoug Rabson 
958f55a568SDoug Rabson struct svc_rpc_gss_svc_name {
968f55a568SDoug Rabson 	SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
978f55a568SDoug Rabson 	char			*sn_principal;
988f55a568SDoug Rabson 	gss_OID			sn_mech;
998f55a568SDoug Rabson 	u_int			sn_req_time;
1008f55a568SDoug Rabson 	gss_cred_id_t		sn_cred;
1018f55a568SDoug Rabson 	u_int			sn_program;
1028f55a568SDoug Rabson 	u_int			sn_version;
1038f55a568SDoug Rabson };
1048f55a568SDoug Rabson static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
10513e403fdSAntoine Brodin 	svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
1068f55a568SDoug Rabson 
1078f55a568SDoug Rabson enum svc_rpc_gss_client_state {
1088f55a568SDoug Rabson 	CLIENT_NEW,				/* still authenticating */
1098f55a568SDoug Rabson 	CLIENT_ESTABLISHED,			/* context established */
1108f55a568SDoug Rabson 	CLIENT_STALE				/* garbage to collect */
1118f55a568SDoug Rabson };
1128f55a568SDoug Rabson 
1138f55a568SDoug Rabson #define SVC_RPC_GSS_SEQWINDOW	128
1148f55a568SDoug Rabson 
1158f55a568SDoug Rabson struct svc_rpc_gss_client {
1168f55a568SDoug Rabson 	TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
1178f55a568SDoug Rabson 	TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
1188f55a568SDoug Rabson 	uint32_t		cl_id;
1198f55a568SDoug Rabson 	time_t			cl_expiration;	/* when to gc */
1208f55a568SDoug Rabson 	enum svc_rpc_gss_client_state cl_state;	/* client state */
1218f55a568SDoug Rabson 	bool_t			cl_locked;	/* fixed service+qop */
1228f55a568SDoug Rabson 	gss_ctx_id_t		cl_ctx;		/* context id */
1238f55a568SDoug Rabson 	gss_cred_id_t		cl_creds;	/* delegated creds */
1248f55a568SDoug Rabson 	gss_name_t		cl_cname;	/* client name */
1258f55a568SDoug Rabson 	struct svc_rpc_gss_svc_name *cl_sname;	/* server name used */
1268f55a568SDoug Rabson 	rpc_gss_rawcred_t	cl_rawcred;	/* raw credentials */
1278f55a568SDoug Rabson 	rpc_gss_ucred_t		cl_ucred;	/* unix-style credentials */
1288f55a568SDoug Rabson 	bool_t			cl_done_callback; /* TRUE after call */
1298f55a568SDoug Rabson 	void			*cl_cookie;	/* user cookie from callback */
1308f55a568SDoug Rabson 	gid_t			cl_gid_storage[NGRPS];
1318f55a568SDoug Rabson 	gss_OID			cl_mech;	/* mechanism */
1328f55a568SDoug Rabson 	gss_qop_t		cl_qop;		/* quality of protection */
1338f55a568SDoug Rabson 	u_int			cl_seq;		/* current sequence number */
1348f55a568SDoug Rabson 	u_int			cl_win;		/* sequence window size */
1358f55a568SDoug Rabson 	u_int			cl_seqlast;	/* sequence window origin */
1368f55a568SDoug Rabson 	uint32_t		cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
1378f55a568SDoug Rabson 	gss_buffer_desc		cl_verf;	/* buffer for verf checksum */
1388f55a568SDoug Rabson };
1398f55a568SDoug Rabson TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
1408f55a568SDoug Rabson 
1418f55a568SDoug Rabson #define CLIENT_HASH_SIZE	256
1428f55a568SDoug Rabson #define CLIENT_MAX		128
143ae824d80SEd Schouten static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144ae824d80SEd Schouten static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
1458f55a568SDoug Rabson static size_t svc_rpc_gss_client_count;
1468f55a568SDoug Rabson static uint32_t svc_rpc_gss_next_clientid = 1;
1478f55a568SDoug Rabson 
1488f55a568SDoug Rabson #ifdef __GNUC__
1498f55a568SDoug Rabson static void svc_rpc_gss_init(void) __attribute__ ((constructor));
1508f55a568SDoug Rabson #endif
1518f55a568SDoug Rabson 
1528f55a568SDoug Rabson static void
1538f55a568SDoug Rabson svc_rpc_gss_init(void)
1548f55a568SDoug Rabson {
1558f55a568SDoug Rabson 	int i;
1568f55a568SDoug Rabson 
1578f55a568SDoug Rabson 	if (!svc_rpc_gss_initialised) {
1588f55a568SDoug Rabson 		for (i = 0; i < CLIENT_HASH_SIZE; i++)
1598f55a568SDoug Rabson 			TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
1608f55a568SDoug Rabson 		TAILQ_INIT(&svc_rpc_gss_clients);
1618f55a568SDoug Rabson 		svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
1628f55a568SDoug Rabson 		svc_rpc_gss_initialised = TRUE;
1638f55a568SDoug Rabson 	}
1648f55a568SDoug Rabson }
1658f55a568SDoug Rabson 
1668f55a568SDoug Rabson bool_t
1678f55a568SDoug Rabson rpc_gss_set_callback(rpc_gss_callback_t *cb)
1688f55a568SDoug Rabson {
1698f55a568SDoug Rabson 	struct svc_rpc_gss_callback *scb;
1708f55a568SDoug Rabson 
171a9148abdSDoug Rabson 	scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
1728f55a568SDoug Rabson 	if (!scb) {
1738f55a568SDoug Rabson 		_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
1748f55a568SDoug Rabson 		return (FALSE);
1758f55a568SDoug Rabson 	}
1768f55a568SDoug Rabson 	scb->cb_callback = *cb;
1778f55a568SDoug Rabson 	SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
1788f55a568SDoug Rabson 
1798f55a568SDoug Rabson 	return (TRUE);
1808f55a568SDoug Rabson }
1818f55a568SDoug Rabson 
1828f55a568SDoug Rabson bool_t
1838f55a568SDoug Rabson rpc_gss_set_svc_name(const char *principal, const char *mechanism,
1848f55a568SDoug Rabson     u_int req_time, u_int program, u_int version)
1858f55a568SDoug Rabson {
1868f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
1878f55a568SDoug Rabson 	struct svc_rpc_gss_svc_name *sname;
1888f55a568SDoug Rabson 	gss_buffer_desc		namebuf;
1898f55a568SDoug Rabson 	gss_name_t		name;
1908f55a568SDoug Rabson 	gss_OID			mech_oid;
1918f55a568SDoug Rabson 	gss_OID_set_desc	oid_set;
1928f55a568SDoug Rabson 	gss_cred_id_t		cred;
1938f55a568SDoug Rabson 
1948f55a568SDoug Rabson 	svc_rpc_gss_init();
1958f55a568SDoug Rabson 
1968f55a568SDoug Rabson 	if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
1978f55a568SDoug Rabson 		return (FALSE);
1988f55a568SDoug Rabson 	oid_set.count = 1;
1998f55a568SDoug Rabson 	oid_set.elements = mech_oid;
2008f55a568SDoug Rabson 
2018f55a568SDoug Rabson 	namebuf.value = (void *)(intptr_t) principal;
2028f55a568SDoug Rabson 	namebuf.length = strlen(principal);
2038f55a568SDoug Rabson 
2048f55a568SDoug Rabson 	maj_stat = gss_import_name(&min_stat, &namebuf,
2058f55a568SDoug Rabson 				   GSS_C_NT_HOSTBASED_SERVICE, &name);
2068f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE)
2078f55a568SDoug Rabson 		return (FALSE);
2088f55a568SDoug Rabson 
2098f55a568SDoug Rabson 	maj_stat = gss_acquire_cred(&min_stat, name,
2108f55a568SDoug Rabson 	    req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
2118f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE)
2128f55a568SDoug Rabson 		return (FALSE);
2138f55a568SDoug Rabson 
2148f55a568SDoug Rabson 	gss_release_name(&min_stat, &name);
2158f55a568SDoug Rabson 
2168f55a568SDoug Rabson 	sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
2178f55a568SDoug Rabson 	if (!sname)
2188f55a568SDoug Rabson 		return (FALSE);
2198f55a568SDoug Rabson 	sname->sn_principal = strdup(principal);
2208f55a568SDoug Rabson 	sname->sn_mech = mech_oid;
2218f55a568SDoug Rabson 	sname->sn_req_time = req_time;
2228f55a568SDoug Rabson 	sname->sn_cred = cred;
2238f55a568SDoug Rabson 	sname->sn_program = program;
2248f55a568SDoug Rabson 	sname->sn_version = version;
2258f55a568SDoug Rabson 	SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
2268f55a568SDoug Rabson 
2278f55a568SDoug Rabson 	return (TRUE);
2288f55a568SDoug Rabson }
2298f55a568SDoug Rabson 
2308f55a568SDoug Rabson bool_t
2318f55a568SDoug Rabson rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
2328f55a568SDoug Rabson     const char *mech, const char *name, const char *node, const char *domain)
2338f55a568SDoug Rabson {
2348f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
2358f55a568SDoug Rabson 	gss_OID			mech_oid;
2368f55a568SDoug Rabson 	size_t			namelen;
2378f55a568SDoug Rabson 	gss_buffer_desc		buf;
2388f55a568SDoug Rabson 	gss_name_t		gss_name, gss_mech_name;
2398f55a568SDoug Rabson 	rpc_gss_principal_t	result;
2408f55a568SDoug Rabson 
2418f55a568SDoug Rabson 	svc_rpc_gss_init();
2428f55a568SDoug Rabson 
2438f55a568SDoug Rabson 	if (!rpc_gss_mech_to_oid(mech, &mech_oid))
2448f55a568SDoug Rabson 		return (FALSE);
2458f55a568SDoug Rabson 
2468f55a568SDoug Rabson 	/*
2478f55a568SDoug Rabson 	 * Construct a gss_buffer containing the full name formatted
2488f55a568SDoug Rabson 	 * as "name/node@domain" where node and domain are optional.
2498f55a568SDoug Rabson 	 */
2508f55a568SDoug Rabson 	namelen = strlen(name);
2518f55a568SDoug Rabson 	if (node) {
2528f55a568SDoug Rabson 		namelen += strlen(node) + 1;
2538f55a568SDoug Rabson 	}
2548f55a568SDoug Rabson 	if (domain) {
2558f55a568SDoug Rabson 		namelen += strlen(domain) + 1;
2568f55a568SDoug Rabson 	}
2578f55a568SDoug Rabson 
258a9148abdSDoug Rabson 	buf.value = mem_alloc(namelen);
2598f55a568SDoug Rabson 	buf.length = namelen;
2608f55a568SDoug Rabson 	strcpy((char *) buf.value, name);
2618f55a568SDoug Rabson 	if (node) {
2628f55a568SDoug Rabson 		strcat((char *) buf.value, "/");
2638f55a568SDoug Rabson 		strcat((char *) buf.value, node);
2648f55a568SDoug Rabson 	}
2658f55a568SDoug Rabson 	if (domain) {
2668f55a568SDoug Rabson 		strcat((char *) buf.value, "@");
2678f55a568SDoug Rabson 		strcat((char *) buf.value, domain);
2688f55a568SDoug Rabson 	}
2698f55a568SDoug Rabson 
2708f55a568SDoug Rabson 	/*
2718f55a568SDoug Rabson 	 * Convert that to a gss_name_t and then convert that to a
2728f55a568SDoug Rabson 	 * mechanism name in the selected mechanism.
2738f55a568SDoug Rabson 	 */
2748f55a568SDoug Rabson 	maj_stat = gss_import_name(&min_stat, &buf,
2758f55a568SDoug Rabson 	    GSS_C_NT_USER_NAME, &gss_name);
276a9148abdSDoug Rabson 	mem_free(buf.value, buf.length);
2778f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
2788f55a568SDoug Rabson 		log_status("gss_import_name", mech_oid, maj_stat, min_stat);
2798f55a568SDoug Rabson 		return (FALSE);
2808f55a568SDoug Rabson 	}
2818f55a568SDoug Rabson 	maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
2828f55a568SDoug Rabson 	    &gss_mech_name);
2838f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
2848f55a568SDoug Rabson 		log_status("gss_canonicalize_name", mech_oid, maj_stat,
2858f55a568SDoug Rabson 		    min_stat);
2868f55a568SDoug Rabson 		gss_release_name(&min_stat, &gss_name);
2878f55a568SDoug Rabson 		return (FALSE);
2888f55a568SDoug Rabson 	}
2898f55a568SDoug Rabson 	gss_release_name(&min_stat, &gss_name);
2908f55a568SDoug Rabson 
2918f55a568SDoug Rabson 	/*
2928f55a568SDoug Rabson 	 * Export the mechanism name and use that to construct the
2938f55a568SDoug Rabson 	 * rpc_gss_principal_t result.
2948f55a568SDoug Rabson 	 */
2958f55a568SDoug Rabson 	maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
2968f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
2978f55a568SDoug Rabson 		log_status("gss_export_name", mech_oid, maj_stat, min_stat);
2988f55a568SDoug Rabson 		gss_release_name(&min_stat, &gss_mech_name);
2998f55a568SDoug Rabson 		return (FALSE);
3008f55a568SDoug Rabson 	}
3018f55a568SDoug Rabson 	gss_release_name(&min_stat, &gss_mech_name);
3028f55a568SDoug Rabson 
303a9148abdSDoug Rabson 	result = mem_alloc(sizeof(int) + buf.length);
3048f55a568SDoug Rabson 	if (!result) {
3058f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &buf);
3068f55a568SDoug Rabson 		return (FALSE);
3078f55a568SDoug Rabson 	}
3088f55a568SDoug Rabson 	result->len = buf.length;
3098f55a568SDoug Rabson 	memcpy(result->name, buf.value, buf.length);
3108f55a568SDoug Rabson 	gss_release_buffer(&min_stat, &buf);
3118f55a568SDoug Rabson 
3128f55a568SDoug Rabson 	*principal = result;
3138f55a568SDoug Rabson 	return (TRUE);
3148f55a568SDoug Rabson }
3158f55a568SDoug Rabson 
3168f55a568SDoug Rabson bool_t
3178f55a568SDoug Rabson rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
3188f55a568SDoug Rabson     rpc_gss_ucred_t **ucred, void **cookie)
3198f55a568SDoug Rabson {
3208f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
3218f55a568SDoug Rabson 
3228f55a568SDoug Rabson 	if (req->rq_cred.oa_flavor != RPCSEC_GSS)
3238f55a568SDoug Rabson 		return (FALSE);
3248f55a568SDoug Rabson 
3258f55a568SDoug Rabson 	client = req->rq_clntcred;
3268f55a568SDoug Rabson 	if (rcred)
3278f55a568SDoug Rabson 		*rcred = &client->cl_rawcred;
3288f55a568SDoug Rabson 	if (ucred)
3298f55a568SDoug Rabson 		*ucred = &client->cl_ucred;
3308f55a568SDoug Rabson 	if (cookie)
3318f55a568SDoug Rabson 		*cookie = client->cl_cookie;
3328f55a568SDoug Rabson 	return (TRUE);
3338f55a568SDoug Rabson }
3348f55a568SDoug Rabson 
3358f55a568SDoug Rabson int
3368f55a568SDoug Rabson rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
3378f55a568SDoug Rabson {
3388f55a568SDoug Rabson 	struct svc_rpc_gss_client *client = req->rq_clntcred;
3398f55a568SDoug Rabson 	int			want_conf;
3408f55a568SDoug Rabson 	OM_uint32		max;
3418f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
3428f55a568SDoug Rabson 	int			result;
3438f55a568SDoug Rabson 
3448f55a568SDoug Rabson 	switch (client->cl_rawcred.service) {
3458f55a568SDoug Rabson 	case rpc_gss_svc_none:
3468f55a568SDoug Rabson 		return (max_tp_unit_len);
3478f55a568SDoug Rabson 		break;
3488f55a568SDoug Rabson 
3498f55a568SDoug Rabson 	case rpc_gss_svc_default:
3508f55a568SDoug Rabson 	case rpc_gss_svc_integrity:
3518f55a568SDoug Rabson 		want_conf = FALSE;
3528f55a568SDoug Rabson 		break;
3538f55a568SDoug Rabson 
3548f55a568SDoug Rabson 	case rpc_gss_svc_privacy:
3558f55a568SDoug Rabson 		want_conf = TRUE;
3568f55a568SDoug Rabson 		break;
3578f55a568SDoug Rabson 
3588f55a568SDoug Rabson 	default:
3598f55a568SDoug Rabson 		return (0);
3608f55a568SDoug Rabson 	}
3618f55a568SDoug Rabson 
3628f55a568SDoug Rabson 	maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
3638f55a568SDoug Rabson 	    client->cl_qop, max_tp_unit_len, &max);
3648f55a568SDoug Rabson 
3658f55a568SDoug Rabson 	if (maj_stat == GSS_S_COMPLETE) {
3668f55a568SDoug Rabson 		result = (int) max;
3678f55a568SDoug Rabson 		if (result < 0)
3688f55a568SDoug Rabson 			result = 0;
3698f55a568SDoug Rabson 		return (result);
3708f55a568SDoug Rabson 	} else {
3718f55a568SDoug Rabson 		log_status("gss_wrap_size_limit", client->cl_mech,
3728f55a568SDoug Rabson 		    maj_stat, min_stat);
3738f55a568SDoug Rabson 		return (0);
3748f55a568SDoug Rabson 	}
3758f55a568SDoug Rabson }
3768f55a568SDoug Rabson 
3778f55a568SDoug Rabson static struct svc_rpc_gss_client *
3788f55a568SDoug Rabson svc_rpc_gss_find_client(uint32_t clientid)
3798f55a568SDoug Rabson {
3808f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
3818f55a568SDoug Rabson 	struct svc_rpc_gss_client_list *list;
3828f55a568SDoug Rabson 
3838f55a568SDoug Rabson 
3848f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_find_client(%d)", clientid);
3858f55a568SDoug Rabson 
3868f55a568SDoug Rabson 	list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
3878f55a568SDoug Rabson 	TAILQ_FOREACH(client, list, cl_link) {
3888f55a568SDoug Rabson 		if (client->cl_id == clientid) {
3898f55a568SDoug Rabson 			/*
3908f55a568SDoug Rabson 			 * Move this client to the front of the LRU
3918f55a568SDoug Rabson 			 * list.
3928f55a568SDoug Rabson 			 */
3938f55a568SDoug Rabson 			TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
3948f55a568SDoug Rabson 			TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
3958f55a568SDoug Rabson 			    cl_alllink);
3968f55a568SDoug Rabson 			return client;
3978f55a568SDoug Rabson 		}
3988f55a568SDoug Rabson 	}
3998f55a568SDoug Rabson 
4008f55a568SDoug Rabson 	return (NULL);
4018f55a568SDoug Rabson }
4028f55a568SDoug Rabson 
4038f55a568SDoug Rabson static struct svc_rpc_gss_client *
4048f55a568SDoug Rabson svc_rpc_gss_create_client(void)
4058f55a568SDoug Rabson {
4068f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
4078f55a568SDoug Rabson 	struct svc_rpc_gss_client_list *list;
4088f55a568SDoug Rabson 
4098f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_create_client()");
4108f55a568SDoug Rabson 
4118f55a568SDoug Rabson 	client = mem_alloc(sizeof(struct svc_rpc_gss_client));
4128f55a568SDoug Rabson 	memset(client, 0, sizeof(struct svc_rpc_gss_client));
4138f55a568SDoug Rabson 	client->cl_id = svc_rpc_gss_next_clientid++;
4148f55a568SDoug Rabson 	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
4158f55a568SDoug Rabson 	TAILQ_INSERT_HEAD(list, client, cl_link);
4168f55a568SDoug Rabson 	TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
4178f55a568SDoug Rabson 
4188f55a568SDoug Rabson 	/*
4198f55a568SDoug Rabson 	 * Start the client off with a short expiration time. We will
4208f55a568SDoug Rabson 	 * try to get a saner value from the client creds later.
4218f55a568SDoug Rabson 	 */
4228f55a568SDoug Rabson 	client->cl_state = CLIENT_NEW;
4238f55a568SDoug Rabson 	client->cl_locked = FALSE;
4248f55a568SDoug Rabson 	client->cl_expiration = time(0) + 5*60;
4258f55a568SDoug Rabson 	svc_rpc_gss_client_count++;
4268f55a568SDoug Rabson 
4278f55a568SDoug Rabson 	return (client);
4288f55a568SDoug Rabson }
4298f55a568SDoug Rabson 
4308f55a568SDoug Rabson static void
4318f55a568SDoug Rabson svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
4328f55a568SDoug Rabson {
4338f55a568SDoug Rabson 	struct svc_rpc_gss_client_list *list;
4348f55a568SDoug Rabson 	OM_uint32 min_stat;
4358f55a568SDoug Rabson 
4368f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_destroy_client()");
4378f55a568SDoug Rabson 
4388f55a568SDoug Rabson 	if (client->cl_ctx)
4398f55a568SDoug Rabson 		gss_delete_sec_context(&min_stat,
4408f55a568SDoug Rabson 		    &client->cl_ctx, GSS_C_NO_BUFFER);
4418f55a568SDoug Rabson 
4428f55a568SDoug Rabson 	if (client->cl_cname)
4438f55a568SDoug Rabson 		gss_release_name(&min_stat, &client->cl_cname);
4448f55a568SDoug Rabson 
4458f55a568SDoug Rabson 	if (client->cl_rawcred.client_principal)
446a9148abdSDoug Rabson 		mem_free(client->cl_rawcred.client_principal,
447a9148abdSDoug Rabson 		    sizeof(*client->cl_rawcred.client_principal)
448a9148abdSDoug Rabson 		    + client->cl_rawcred.client_principal->len);
4498f55a568SDoug Rabson 
4508f55a568SDoug Rabson 	if (client->cl_verf.value)
4518f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &client->cl_verf);
4528f55a568SDoug Rabson 
4538f55a568SDoug Rabson 	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
4548f55a568SDoug Rabson 	TAILQ_REMOVE(list, client, cl_link);
4558f55a568SDoug Rabson 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
4568f55a568SDoug Rabson 	svc_rpc_gss_client_count--;
4578f55a568SDoug Rabson 	mem_free(client, sizeof(*client));
4588f55a568SDoug Rabson }
4598f55a568SDoug Rabson 
4608f55a568SDoug Rabson static void
4618f55a568SDoug Rabson svc_rpc_gss_timeout_clients(void)
4628f55a568SDoug Rabson {
4638f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
4648f55a568SDoug Rabson 	struct svc_rpc_gss_client *nclient;
4658f55a568SDoug Rabson 	time_t now = time(0);
4668f55a568SDoug Rabson 
4678f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_timeout_clients()");
4688f55a568SDoug Rabson 	/*
4698f55a568SDoug Rabson 	 * First enforce the max client limit. We keep
4708f55a568SDoug Rabson 	 * svc_rpc_gss_clients in LRU order.
4718f55a568SDoug Rabson 	 */
4728f55a568SDoug Rabson 	while (svc_rpc_gss_client_count > CLIENT_MAX)
4738f55a568SDoug Rabson 		svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
4748f55a568SDoug Rabson 			    svc_rpc_gss_client_list));
4758f55a568SDoug Rabson 	TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
4768f55a568SDoug Rabson 		if (client->cl_state == CLIENT_STALE
4778f55a568SDoug Rabson 		    || now > client->cl_expiration) {
4788f55a568SDoug Rabson 			log_debug("expiring client %p", client);
4798f55a568SDoug Rabson 			svc_rpc_gss_destroy_client(client);
4808f55a568SDoug Rabson 		}
4818f55a568SDoug Rabson 	}
4828f55a568SDoug Rabson }
4838f55a568SDoug Rabson 
4848f55a568SDoug Rabson #ifdef DEBUG
4858f55a568SDoug Rabson /*
4868f55a568SDoug Rabson  * OID<->string routines.  These are uuuuugly.
4878f55a568SDoug Rabson  */
4888f55a568SDoug Rabson static OM_uint32
4898f55a568SDoug Rabson gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
4908f55a568SDoug Rabson {
4918f55a568SDoug Rabson 	char		numstr[128];
4928f55a568SDoug Rabson 	unsigned long	number;
4938f55a568SDoug Rabson 	int		numshift;
4948f55a568SDoug Rabson 	size_t		string_length;
4958f55a568SDoug Rabson 	size_t		i;
4968f55a568SDoug Rabson 	unsigned char	*cp;
4978f55a568SDoug Rabson 	char		*bp;
4988f55a568SDoug Rabson 
4998f55a568SDoug Rabson 	/* Decoded according to krb5/gssapi_krb5.c */
5008f55a568SDoug Rabson 
5018f55a568SDoug Rabson 	/* First determine the size of the string */
5028f55a568SDoug Rabson 	string_length = 0;
5038f55a568SDoug Rabson 	number = 0;
5048f55a568SDoug Rabson 	numshift = 0;
5058f55a568SDoug Rabson 	cp = (unsigned char *) oid->elements;
5068f55a568SDoug Rabson 	number = (unsigned long) cp[0];
5078f55a568SDoug Rabson 	sprintf(numstr, "%ld ", number/40);
5088f55a568SDoug Rabson 	string_length += strlen(numstr);
5098f55a568SDoug Rabson 	sprintf(numstr, "%ld ", number%40);
5108f55a568SDoug Rabson 	string_length += strlen(numstr);
5118f55a568SDoug Rabson 	for (i=1; i<oid->length; i++) {
5128f55a568SDoug Rabson 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
5138f55a568SDoug Rabson 			number = (number << 7) | (cp[i] & 0x7f);
5148f55a568SDoug Rabson 			numshift += 7;
5158f55a568SDoug Rabson 		}
5168f55a568SDoug Rabson 		else {
5178f55a568SDoug Rabson 			*minor_status = 0;
5188f55a568SDoug Rabson 			return(GSS_S_FAILURE);
5198f55a568SDoug Rabson 		}
5208f55a568SDoug Rabson 		if ((cp[i] & 0x80) == 0) {
5218f55a568SDoug Rabson 			sprintf(numstr, "%ld ", number);
5228f55a568SDoug Rabson 			string_length += strlen(numstr);
5238f55a568SDoug Rabson 			number = 0;
5248f55a568SDoug Rabson 			numshift = 0;
5258f55a568SDoug Rabson 		}
5268f55a568SDoug Rabson 	}
5278f55a568SDoug Rabson 	/*
5288f55a568SDoug Rabson 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
5298f55a568SDoug Rabson 	 * here for "{ " and "}\0".
5308f55a568SDoug Rabson 	 */
5318f55a568SDoug Rabson 	string_length += 4;
532a9148abdSDoug Rabson 	if ((bp = (char *) mem_alloc(string_length))) {
5338f55a568SDoug Rabson 		strcpy(bp, "{ ");
5348f55a568SDoug Rabson 		number = (unsigned long) cp[0];
5358f55a568SDoug Rabson 		sprintf(numstr, "%ld ", number/40);
5368f55a568SDoug Rabson 		strcat(bp, numstr);
5378f55a568SDoug Rabson 		sprintf(numstr, "%ld ", number%40);
5388f55a568SDoug Rabson 		strcat(bp, numstr);
5398f55a568SDoug Rabson 		number = 0;
5408f55a568SDoug Rabson 		cp = (unsigned char *) oid->elements;
5418f55a568SDoug Rabson 		for (i=1; i<oid->length; i++) {
5428f55a568SDoug Rabson 			number = (number << 7) | (cp[i] & 0x7f);
5438f55a568SDoug Rabson 			if ((cp[i] & 0x80) == 0) {
5448f55a568SDoug Rabson 				sprintf(numstr, "%ld ", number);
5458f55a568SDoug Rabson 				strcat(bp, numstr);
5468f55a568SDoug Rabson 				number = 0;
5478f55a568SDoug Rabson 			}
5488f55a568SDoug Rabson 		}
5498f55a568SDoug Rabson 		strcat(bp, "}");
5508f55a568SDoug Rabson 		oid_str->length = strlen(bp)+1;
5518f55a568SDoug Rabson 		oid_str->value = (void *) bp;
5528f55a568SDoug Rabson 		*minor_status = 0;
5538f55a568SDoug Rabson 		return(GSS_S_COMPLETE);
5548f55a568SDoug Rabson 	}
5558f55a568SDoug Rabson 	*minor_status = 0;
5568f55a568SDoug Rabson 	return(GSS_S_FAILURE);
5578f55a568SDoug Rabson }
5588f55a568SDoug Rabson #endif
5598f55a568SDoug Rabson 
5608f55a568SDoug Rabson static void
5618f55a568SDoug Rabson svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
5628f55a568SDoug Rabson     const gss_name_t name)
5638f55a568SDoug Rabson {
5648f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
5658f55a568SDoug Rabson 	char			buf[128];
5668f55a568SDoug Rabson 	uid_t			uid;
5678f55a568SDoug Rabson 	struct passwd		pwd, *pw;
5688f55a568SDoug Rabson 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
5698f55a568SDoug Rabson 
5708f55a568SDoug Rabson 	uc->uid = 65534;
5718f55a568SDoug Rabson 	uc->gid = 65534;
5728f55a568SDoug Rabson 	uc->gidlen = 0;
5738f55a568SDoug Rabson 	uc->gidlist = client->cl_gid_storage;
5748f55a568SDoug Rabson 
5758f55a568SDoug Rabson 	maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
5768f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE)
5778f55a568SDoug Rabson 		return;
5788f55a568SDoug Rabson 
5798f55a568SDoug Rabson 	getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
5808f55a568SDoug Rabson 	if (pw) {
5818f55a568SDoug Rabson 		int len = NGRPS;
5828f55a568SDoug Rabson 		uc->uid = pw->pw_uid;
5838f55a568SDoug Rabson 		uc->gid = pw->pw_gid;
5848f55a568SDoug Rabson 		uc->gidlist = client->cl_gid_storage;
5858f55a568SDoug Rabson 		getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
5868f55a568SDoug Rabson 		uc->gidlen = len;
5878f55a568SDoug Rabson 	}
5888f55a568SDoug Rabson }
5898f55a568SDoug Rabson 
5908f55a568SDoug Rabson static bool_t
5918f55a568SDoug Rabson svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
5928f55a568SDoug Rabson 			       struct svc_req *rqst,
5938f55a568SDoug Rabson 			       struct rpc_gss_init_res *gr,
5948f55a568SDoug Rabson 			       struct rpc_gss_cred *gc)
5958f55a568SDoug Rabson {
5968f55a568SDoug Rabson 	gss_buffer_desc		recv_tok;
5978f55a568SDoug Rabson 	gss_OID			mech;
5988f55a568SDoug Rabson 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
5998f55a568SDoug Rabson 	OM_uint32		cred_lifetime;
6008f55a568SDoug Rabson 	struct svc_rpc_gss_svc_name *sname;
6018f55a568SDoug Rabson 
6028f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_accept_context()");
6038f55a568SDoug Rabson 
6048f55a568SDoug Rabson 	/* Deserialize arguments. */
6058f55a568SDoug Rabson 	memset(&recv_tok, 0, sizeof(recv_tok));
6068f55a568SDoug Rabson 
6078f55a568SDoug Rabson 	if (!svc_getargs(rqst->rq_xprt,
6088f55a568SDoug Rabson 		(xdrproc_t) xdr_gss_buffer_desc,
6098f55a568SDoug Rabson 		(caddr_t) &recv_tok)) {
6108f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
6118f55a568SDoug Rabson 		return (FALSE);
6128f55a568SDoug Rabson 	}
6138f55a568SDoug Rabson 
6148f55a568SDoug Rabson 	/*
6158f55a568SDoug Rabson 	 * First time round, try all the server names we have until
6168f55a568SDoug Rabson 	 * one matches. Afterwards, stick with that one.
6178f55a568SDoug Rabson 	 */
6188f55a568SDoug Rabson 	if (!client->cl_sname) {
6198f55a568SDoug Rabson 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
6208f55a568SDoug Rabson 			if (sname->sn_program == rqst->rq_prog
6218f55a568SDoug Rabson 			    && sname->sn_version == rqst->rq_vers) {
6228f55a568SDoug Rabson 				gr->gr_major = gss_accept_sec_context(
6238f55a568SDoug Rabson 					&gr->gr_minor,
6248f55a568SDoug Rabson 					&client->cl_ctx,
6258f55a568SDoug Rabson 					sname->sn_cred,
6268f55a568SDoug Rabson 					&recv_tok,
6278f55a568SDoug Rabson 					GSS_C_NO_CHANNEL_BINDINGS,
6288f55a568SDoug Rabson 					&client->cl_cname,
6298f55a568SDoug Rabson 					&mech,
6308f55a568SDoug Rabson 					&gr->gr_token,
6318f55a568SDoug Rabson 					&ret_flags,
6328f55a568SDoug Rabson 					&cred_lifetime,
6338f55a568SDoug Rabson 					&client->cl_creds);
634a9148abdSDoug Rabson 				client->cl_sname = sname;
635a9148abdSDoug Rabson 				break;
6368f55a568SDoug Rabson 			}
6378f55a568SDoug Rabson 		}
638a9148abdSDoug Rabson 		if (!sname) {
639a9148abdSDoug Rabson 			xdr_free((xdrproc_t) xdr_gss_buffer_desc,
640a9148abdSDoug Rabson 			    (char *) &recv_tok);
641a9148abdSDoug Rabson 			return (FALSE);
642a9148abdSDoug Rabson 		}
6438f55a568SDoug Rabson 	} else {
6448f55a568SDoug Rabson 		gr->gr_major = gss_accept_sec_context(
6458f55a568SDoug Rabson 			&gr->gr_minor,
6468f55a568SDoug Rabson 			&client->cl_ctx,
6478f55a568SDoug Rabson 			client->cl_sname->sn_cred,
6488f55a568SDoug Rabson 			&recv_tok,
6498f55a568SDoug Rabson 			GSS_C_NO_CHANNEL_BINDINGS,
6508f55a568SDoug Rabson 			&client->cl_cname,
6518f55a568SDoug Rabson 			&mech,
6528f55a568SDoug Rabson 			&gr->gr_token,
6538f55a568SDoug Rabson 			&ret_flags,
6548f55a568SDoug Rabson 			&cred_lifetime,
6558f55a568SDoug Rabson 			NULL);
6568f55a568SDoug Rabson 	}
6578f55a568SDoug Rabson 
6588f55a568SDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
6598f55a568SDoug Rabson 
6608f55a568SDoug Rabson 	/*
6618f55a568SDoug Rabson 	 * If we get an error from gss_accept_sec_context, send the
6628f55a568SDoug Rabson 	 * reply anyway so that the client gets a chance to see what
6638f55a568SDoug Rabson 	 * is wrong.
6648f55a568SDoug Rabson 	 */
6658f55a568SDoug Rabson 	if (gr->gr_major != GSS_S_COMPLETE &&
6668f55a568SDoug Rabson 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
6678f55a568SDoug Rabson 		log_status("accept_sec_context", client->cl_mech,
6688f55a568SDoug Rabson 		    gr->gr_major, gr->gr_minor);
6698f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
670a9148abdSDoug Rabson 		return (TRUE);
6718f55a568SDoug Rabson 	}
6728f55a568SDoug Rabson 
6738f55a568SDoug Rabson 	gr->gr_handle.value = &client->cl_id;
674a9148abdSDoug Rabson 	gr->gr_handle.length = sizeof(client->cl_id);
6758f55a568SDoug Rabson 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
6768f55a568SDoug Rabson 
6778f55a568SDoug Rabson 	/* Save client info. */
6788f55a568SDoug Rabson 	client->cl_mech = mech;
6798f55a568SDoug Rabson 	client->cl_qop = GSS_C_QOP_DEFAULT;
6808f55a568SDoug Rabson 	client->cl_seq = gc->gc_seq;
6818f55a568SDoug Rabson 	client->cl_win = gr->gr_win;
6828f55a568SDoug Rabson 	client->cl_done_callback = FALSE;
6838f55a568SDoug Rabson 
6848f55a568SDoug Rabson 	if (gr->gr_major == GSS_S_COMPLETE) {
6858f55a568SDoug Rabson 		gss_buffer_desc	export_name;
6868f55a568SDoug Rabson 
6878f55a568SDoug Rabson 		/*
6888f55a568SDoug Rabson 		 * Change client expiration time to be near when the
6898f55a568SDoug Rabson 		 * client creds expire (or 24 hours if we can't figure
6908f55a568SDoug Rabson 		 * that out).
6918f55a568SDoug Rabson 		 */
6928f55a568SDoug Rabson 		if (cred_lifetime == GSS_C_INDEFINITE)
6938f55a568SDoug Rabson 			cred_lifetime = time(0) + 24*60*60;
6948f55a568SDoug Rabson 
6958f55a568SDoug Rabson 		client->cl_expiration = time(0) + cred_lifetime;
6968f55a568SDoug Rabson 
6978f55a568SDoug Rabson 		/*
6988f55a568SDoug Rabson 		 * Fill in cred details in the rawcred structure.
6998f55a568SDoug Rabson 		 */
7008f55a568SDoug Rabson 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
7018f55a568SDoug Rabson 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
7028f55a568SDoug Rabson 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
7038f55a568SDoug Rabson 		    &export_name);
7048f55a568SDoug Rabson 		if (maj_stat != GSS_S_COMPLETE) {
7058f55a568SDoug Rabson 			log_status("gss_export_name", client->cl_mech,
7068f55a568SDoug Rabson 			    maj_stat, min_stat);
7078f55a568SDoug Rabson 			return (FALSE);
7088f55a568SDoug Rabson 		}
7098f55a568SDoug Rabson 		client->cl_rawcred.client_principal =
710a9148abdSDoug Rabson 			mem_alloc(sizeof(*client->cl_rawcred.client_principal)
7118f55a568SDoug Rabson 			    + export_name.length);
7128f55a568SDoug Rabson 		client->cl_rawcred.client_principal->len = export_name.length;
7138f55a568SDoug Rabson 		memcpy(client->cl_rawcred.client_principal->name,
7148f55a568SDoug Rabson 		    export_name.value, export_name.length);
7158f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &export_name);
7168f55a568SDoug Rabson 		client->cl_rawcred.svc_principal =
7178f55a568SDoug Rabson 			client->cl_sname->sn_principal;
7188f55a568SDoug Rabson 		client->cl_rawcred.service = gc->gc_svc;
7198f55a568SDoug Rabson 
7208f55a568SDoug Rabson 		/*
7218f55a568SDoug Rabson 		 * Use gss_pname_to_uid to map to unix creds. For
7228f55a568SDoug Rabson 		 * kerberos5, this uses krb5_aname_to_localname.
7238f55a568SDoug Rabson 		 */
7248f55a568SDoug Rabson 		svc_rpc_gss_build_ucred(client, client->cl_cname);
725a9148abdSDoug Rabson 		gss_release_name(&min_stat, &client->cl_cname);
7268f55a568SDoug Rabson 
7278f55a568SDoug Rabson #ifdef DEBUG
7288f55a568SDoug Rabson 		{
7298f55a568SDoug Rabson 			gss_buffer_desc mechname;
7308f55a568SDoug Rabson 
7318f55a568SDoug Rabson 			gss_oid_to_str(&min_stat, mech, &mechname);
7328f55a568SDoug Rabson 
7338f55a568SDoug Rabson 			log_debug("accepted context for %s with "
7348f55a568SDoug Rabson 			    "<mech %.*s, qop %d, svc %d>",
7358f55a568SDoug Rabson 			    client->cl_rawcred.client_principal->name,
7368f55a568SDoug Rabson 			    mechname.length, (char *)mechname.value,
7378f55a568SDoug Rabson 			    client->cl_qop, client->rawcred.service);
7388f55a568SDoug Rabson 
7398f55a568SDoug Rabson 			gss_release_buffer(&min_stat, &mechname);
7408f55a568SDoug Rabson 		}
7418f55a568SDoug Rabson #endif /* DEBUG */
7428f55a568SDoug Rabson 	}
7438f55a568SDoug Rabson 	return (TRUE);
7448f55a568SDoug Rabson }
7458f55a568SDoug Rabson 
7468f55a568SDoug Rabson static bool_t
7478f55a568SDoug Rabson svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
7488f55a568SDoug Rabson 	gss_qop_t *qop)
7498f55a568SDoug Rabson {
7508f55a568SDoug Rabson 	struct opaque_auth	*oa;
7518f55a568SDoug Rabson 	gss_buffer_desc		 rpcbuf, checksum;
7528f55a568SDoug Rabson 	OM_uint32		 maj_stat, min_stat;
7538f55a568SDoug Rabson 	gss_qop_t		 qop_state;
7549b118815SDoug Rabson 	int32_t			 rpchdr[128 / sizeof(int32_t)];
7558f55a568SDoug Rabson 	int32_t			*buf;
7568f55a568SDoug Rabson 
7578f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_validate()");
7588f55a568SDoug Rabson 
7598f55a568SDoug Rabson 	memset(rpchdr, 0, sizeof(rpchdr));
7608f55a568SDoug Rabson 
7618f55a568SDoug Rabson 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
7629b118815SDoug Rabson 	buf = rpchdr;
7638f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_xid);
7648f55a568SDoug Rabson 	IXDR_PUT_ENUM(buf, msg->rm_direction);
7658f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
7668f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
7678f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
7688f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
7698f55a568SDoug Rabson 	oa = &msg->rm_call.cb_cred;
7708f55a568SDoug Rabson 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
7718f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, oa->oa_length);
7728f55a568SDoug Rabson 	if (oa->oa_length) {
7738f55a568SDoug Rabson 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
7748f55a568SDoug Rabson 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
7758f55a568SDoug Rabson 	}
7768f55a568SDoug Rabson 	rpcbuf.value = rpchdr;
7779b118815SDoug Rabson 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
7788f55a568SDoug Rabson 
7798f55a568SDoug Rabson 	checksum.value = msg->rm_call.cb_verf.oa_base;
7808f55a568SDoug Rabson 	checksum.length = msg->rm_call.cb_verf.oa_length;
7818f55a568SDoug Rabson 
7828f55a568SDoug Rabson 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
7838f55a568SDoug Rabson 				  &qop_state);
7848f55a568SDoug Rabson 
7858f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
7868f55a568SDoug Rabson 		log_status("gss_verify_mic", client->cl_mech,
7878f55a568SDoug Rabson 		    maj_stat, min_stat);
7888f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
7898f55a568SDoug Rabson 		return (FALSE);
7908f55a568SDoug Rabson 	}
7918f55a568SDoug Rabson 	*qop = qop_state;
7928f55a568SDoug Rabson 	return (TRUE);
7938f55a568SDoug Rabson }
7948f55a568SDoug Rabson 
7958f55a568SDoug Rabson static bool_t
7968f55a568SDoug Rabson svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
7978f55a568SDoug Rabson     struct svc_req *rqst, u_int seq)
7988f55a568SDoug Rabson {
7998f55a568SDoug Rabson 	gss_buffer_desc		signbuf;
8008f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
8018f55a568SDoug Rabson 	uint32_t		nseq;
8028f55a568SDoug Rabson 
8038f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_nextverf()");
8048f55a568SDoug Rabson 
8058f55a568SDoug Rabson 	nseq = htonl(seq);
8068f55a568SDoug Rabson 	signbuf.value = &nseq;
8078f55a568SDoug Rabson 	signbuf.length = sizeof(nseq);
8088f55a568SDoug Rabson 
8098f55a568SDoug Rabson 	if (client->cl_verf.value)
8108f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &client->cl_verf);
8118f55a568SDoug Rabson 
8128f55a568SDoug Rabson 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
8138f55a568SDoug Rabson 	    &signbuf, &client->cl_verf);
8148f55a568SDoug Rabson 
8158f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
8168f55a568SDoug Rabson 		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
8178f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
8188f55a568SDoug Rabson 		return (FALSE);
8198f55a568SDoug Rabson 	}
8208f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
8218f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
8228f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
8238f55a568SDoug Rabson 
8248f55a568SDoug Rabson 	return (TRUE);
8258f55a568SDoug Rabson }
8268f55a568SDoug Rabson 
8278f55a568SDoug Rabson static bool_t
8288f55a568SDoug Rabson svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
8298f55a568SDoug Rabson {
8308f55a568SDoug Rabson 	struct svc_rpc_gss_callback *scb;
8318f55a568SDoug Rabson 	rpc_gss_lock_t	lock;
8328f55a568SDoug Rabson 	void		*cookie;
8338f55a568SDoug Rabson 	bool_t		cb_res;
8348f55a568SDoug Rabson 	bool_t		result;
8358f55a568SDoug Rabson 
8368f55a568SDoug Rabson 	/*
8378f55a568SDoug Rabson 	 * See if we have a callback for this guy.
8388f55a568SDoug Rabson 	 */
8398f55a568SDoug Rabson 	result = TRUE;
8408f55a568SDoug Rabson 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
8418f55a568SDoug Rabson 		if (scb->cb_callback.program == rqst->rq_prog
8428f55a568SDoug Rabson 		    && scb->cb_callback.version == rqst->rq_vers) {
8438f55a568SDoug Rabson 			/*
8448f55a568SDoug Rabson 			 * This one matches. Call the callback and see
8458f55a568SDoug Rabson 			 * if it wants to veto or something.
8468f55a568SDoug Rabson 			 */
8478f55a568SDoug Rabson 			lock.locked = FALSE;
8488f55a568SDoug Rabson 			lock.raw_cred = &client->cl_rawcred;
8498f55a568SDoug Rabson 			cb_res = scb->cb_callback.callback(rqst,
8508f55a568SDoug Rabson 			    client->cl_creds,
8518f55a568SDoug Rabson 			    client->cl_ctx,
8528f55a568SDoug Rabson 			    &lock,
8538f55a568SDoug Rabson 			    &cookie);
8548f55a568SDoug Rabson 
8558f55a568SDoug Rabson 			if (!cb_res) {
8568f55a568SDoug Rabson 				client->cl_state = CLIENT_STALE;
8578f55a568SDoug Rabson 				result = FALSE;
8588f55a568SDoug Rabson 				break;
8598f55a568SDoug Rabson 			}
8608f55a568SDoug Rabson 
8618f55a568SDoug Rabson 			/*
8628f55a568SDoug Rabson 			 * The callback accepted the connection - it
8638f55a568SDoug Rabson 			 * is responsible for freeing client->cl_creds
8648f55a568SDoug Rabson 			 * now.
8658f55a568SDoug Rabson 			 */
8668f55a568SDoug Rabson 			client->cl_creds = GSS_C_NO_CREDENTIAL;
8678f55a568SDoug Rabson 			client->cl_locked = lock.locked;
8688f55a568SDoug Rabson 			client->cl_cookie = cookie;
8698f55a568SDoug Rabson 			return (TRUE);
8708f55a568SDoug Rabson 		}
8718f55a568SDoug Rabson 	}
8728f55a568SDoug Rabson 
8738f55a568SDoug Rabson 	/*
8748f55a568SDoug Rabson 	 * Either no callback exists for this program/version or one
8758f55a568SDoug Rabson 	 * of the callbacks rejected the connection. We just need to
8768f55a568SDoug Rabson 	 * clean up the delegated client creds, if any.
8778f55a568SDoug Rabson 	 */
8788f55a568SDoug Rabson 	if (client->cl_creds) {
8798f55a568SDoug Rabson 		OM_uint32 min_ver;
8808f55a568SDoug Rabson 		gss_release_cred(&min_ver, &client->cl_creds);
8818f55a568SDoug Rabson 	}
8828f55a568SDoug Rabson 	return (result);
8838f55a568SDoug Rabson }
8848f55a568SDoug Rabson 
8858f55a568SDoug Rabson static bool_t
8868f55a568SDoug Rabson svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
8878f55a568SDoug Rabson {
8888f55a568SDoug Rabson 	u_int32_t offset;
8898f55a568SDoug Rabson 	int word, bit;
8908f55a568SDoug Rabson 
8915f7399dcSDoug Rabson 	if (seq <= client->cl_seqlast) {
8928f55a568SDoug Rabson 		/*
8938f55a568SDoug Rabson 		 * The request sequence number is less than
8948f55a568SDoug Rabson 		 * the largest we have seen so far. If it is
8958f55a568SDoug Rabson 		 * outside the window or if we have seen a
8968f55a568SDoug Rabson 		 * request with this sequence before, silently
8978f55a568SDoug Rabson 		 * discard it.
8988f55a568SDoug Rabson 		 */
8998f55a568SDoug Rabson 		offset = client->cl_seqlast - seq;
900a9148abdSDoug Rabson 		if (offset >= SVC_RPC_GSS_SEQWINDOW)
9018f55a568SDoug Rabson 			return (FALSE);
9028f55a568SDoug Rabson 		word = offset / 32;
9038f55a568SDoug Rabson 		bit = offset % 32;
9048f55a568SDoug Rabson 		if (client->cl_seqmask[word] & (1 << bit))
9058f55a568SDoug Rabson 			return (FALSE);
9068f55a568SDoug Rabson 	}
9078f55a568SDoug Rabson 
9088f55a568SDoug Rabson 	return (TRUE);
9098f55a568SDoug Rabson }
9108f55a568SDoug Rabson 
9118f55a568SDoug Rabson static void
9128f55a568SDoug Rabson svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
9138f55a568SDoug Rabson {
914a9148abdSDoug Rabson 	int offset, i, word, bit;
9158f55a568SDoug Rabson 	uint32_t carry, newcarry;
916*11bc2c1cSDoug Rabson 	uint32_t* maskp;
9178f55a568SDoug Rabson 
918*11bc2c1cSDoug Rabson 	maskp = client->cl_seqmask;
9198f55a568SDoug Rabson 	if (seq > client->cl_seqlast) {
9208f55a568SDoug Rabson 		/*
9218f55a568SDoug Rabson 		 * This request has a sequence number greater
9228f55a568SDoug Rabson 		 * than any we have seen so far. Advance the
9238f55a568SDoug Rabson 		 * seq window and set bit zero of the window
9248f55a568SDoug Rabson 		 * (which corresponds to the new sequence
9258f55a568SDoug Rabson 		 * number)
9268f55a568SDoug Rabson 		 */
9278f55a568SDoug Rabson 		offset = seq - client->cl_seqlast;
928*11bc2c1cSDoug Rabson 		while (offset >= 32) {
9298f55a568SDoug Rabson 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
9308f55a568SDoug Rabson 			     i > 0; i--) {
931*11bc2c1cSDoug Rabson 				maskp[i] = maskp[i-1];
9328f55a568SDoug Rabson 			}
933*11bc2c1cSDoug Rabson 			maskp[0] = 0;
9348f55a568SDoug Rabson 			offset -= 32;
9358f55a568SDoug Rabson 		}
936*11bc2c1cSDoug Rabson 		if (offset > 0) {
9378f55a568SDoug Rabson 			carry = 0;
9388f55a568SDoug Rabson 			for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
939*11bc2c1cSDoug Rabson 				newcarry = maskp[i] >> (32 - offset);
940*11bc2c1cSDoug Rabson 				maskp[i] = (maskp[i] << offset) | carry;
9418f55a568SDoug Rabson 				carry = newcarry;
9428f55a568SDoug Rabson 			}
943*11bc2c1cSDoug Rabson 		}
944*11bc2c1cSDoug Rabson 		maskp[0] |= 1;
9458f55a568SDoug Rabson 		client->cl_seqlast = seq;
946a9148abdSDoug Rabson 	} else {
947a9148abdSDoug Rabson 		offset = client->cl_seqlast - seq;
948a9148abdSDoug Rabson 		word = offset / 32;
949a9148abdSDoug Rabson 		bit = offset % 32;
950*11bc2c1cSDoug Rabson 		maskp[word] |= (1 << bit);
9518f55a568SDoug Rabson 	}
952a9148abdSDoug Rabson 
9538f55a568SDoug Rabson }
9548f55a568SDoug Rabson 
9558f55a568SDoug Rabson enum auth_stat
9568f55a568SDoug Rabson svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
9578f55a568SDoug Rabson 
9588f55a568SDoug Rabson {
9598f55a568SDoug Rabson 	OM_uint32		 min_stat;
9608f55a568SDoug Rabson 	XDR	 		 xdrs;
9618f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
9628f55a568SDoug Rabson 	struct rpc_gss_cred	 gc;
9638f55a568SDoug Rabson 	struct rpc_gss_init_res	 gr;
9648f55a568SDoug Rabson 	gss_qop_t		 qop;
9658f55a568SDoug Rabson 	int			 call_stat;
9668f55a568SDoug Rabson 	enum auth_stat		 result;
9678f55a568SDoug Rabson 
9688f55a568SDoug Rabson 	log_debug("in svc_rpc_gss()");
9698f55a568SDoug Rabson 
9708f55a568SDoug Rabson 	/* Garbage collect old clients. */
9718f55a568SDoug Rabson 	svc_rpc_gss_timeout_clients();
9728f55a568SDoug Rabson 
9738f55a568SDoug Rabson 	/* Initialize reply. */
9748f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf = _null_auth;
9758f55a568SDoug Rabson 
9768f55a568SDoug Rabson 	/* Deserialize client credentials. */
9778f55a568SDoug Rabson 	if (rqst->rq_cred.oa_length <= 0)
9788f55a568SDoug Rabson 		return (AUTH_BADCRED);
9798f55a568SDoug Rabson 
9808f55a568SDoug Rabson 	memset(&gc, 0, sizeof(gc));
9818f55a568SDoug Rabson 
9828f55a568SDoug Rabson 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
9838f55a568SDoug Rabson 	    rqst->rq_cred.oa_length, XDR_DECODE);
9848f55a568SDoug Rabson 
9858f55a568SDoug Rabson 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
9868f55a568SDoug Rabson 		XDR_DESTROY(&xdrs);
9878f55a568SDoug Rabson 		return (AUTH_BADCRED);
9888f55a568SDoug Rabson 	}
9898f55a568SDoug Rabson 	XDR_DESTROY(&xdrs);
9908f55a568SDoug Rabson 
9918f55a568SDoug Rabson 	/* Check version. */
9928f55a568SDoug Rabson 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
9938f55a568SDoug Rabson 		result = AUTH_BADCRED;
9948f55a568SDoug Rabson 		goto out;
9958f55a568SDoug Rabson 	}
9968f55a568SDoug Rabson 
9978f55a568SDoug Rabson 	/* Check the proc and find the client (or create it) */
9988f55a568SDoug Rabson 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
999a9148abdSDoug Rabson 		if (gc.gc_handle.length != 0) {
1000a9148abdSDoug Rabson 			result = AUTH_BADCRED;
1001a9148abdSDoug Rabson 			goto out;
1002a9148abdSDoug Rabson 		}
10038f55a568SDoug Rabson 		client = svc_rpc_gss_create_client();
10048f55a568SDoug Rabson 	} else {
10058f55a568SDoug Rabson 		if (gc.gc_handle.length != sizeof(uint32_t)) {
10068f55a568SDoug Rabson 			result = AUTH_BADCRED;
10078f55a568SDoug Rabson 			goto out;
10088f55a568SDoug Rabson 		}
10098f55a568SDoug Rabson 		uint32_t *p = gc.gc_handle.value;
10108f55a568SDoug Rabson 		client = svc_rpc_gss_find_client(*p);
10118f55a568SDoug Rabson 		if (!client) {
10128f55a568SDoug Rabson 			/*
10138f55a568SDoug Rabson 			 * Can't find the client - we may have
10148f55a568SDoug Rabson 			 * destroyed it - tell the other side to
10158f55a568SDoug Rabson 			 * re-authenticate.
10168f55a568SDoug Rabson 			 */
10178f55a568SDoug Rabson 			result = RPCSEC_GSS_CREDPROBLEM;
10188f55a568SDoug Rabson 			goto out;
10198f55a568SDoug Rabson 		}
10208f55a568SDoug Rabson 	}
10218f55a568SDoug Rabson 	rqst->rq_clntcred = client;
10228f55a568SDoug Rabson 
10238f55a568SDoug Rabson 	/*
10248f55a568SDoug Rabson 	 * The service and sequence number must be ignored for
10258f55a568SDoug Rabson 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
10268f55a568SDoug Rabson 	 */
10278f55a568SDoug Rabson 	if (gc.gc_proc != RPCSEC_GSS_INIT
10288f55a568SDoug Rabson 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
10298f55a568SDoug Rabson 		/*
10308f55a568SDoug Rabson 		 * Check for sequence number overflow.
10318f55a568SDoug Rabson 		 */
10328f55a568SDoug Rabson 		if (gc.gc_seq >= MAXSEQ) {
10338f55a568SDoug Rabson 			result = RPCSEC_GSS_CTXPROBLEM;
10348f55a568SDoug Rabson 			goto out;
10358f55a568SDoug Rabson 		}
10368f55a568SDoug Rabson 		client->cl_seq = gc.gc_seq;
10378f55a568SDoug Rabson 
10388f55a568SDoug Rabson 		/*
10398f55a568SDoug Rabson 		 * Check for valid service.
10408f55a568SDoug Rabson 		 */
10418f55a568SDoug Rabson 		if (gc.gc_svc != rpc_gss_svc_none &&
10428f55a568SDoug Rabson 		    gc.gc_svc != rpc_gss_svc_integrity &&
10438f55a568SDoug Rabson 		    gc.gc_svc != rpc_gss_svc_privacy) {
10448f55a568SDoug Rabson 			result = AUTH_BADCRED;
10458f55a568SDoug Rabson 			goto out;
10468f55a568SDoug Rabson 		}
10478f55a568SDoug Rabson 	}
10488f55a568SDoug Rabson 
10498f55a568SDoug Rabson 	/* Handle RPCSEC_GSS control procedure. */
10508f55a568SDoug Rabson 	switch (gc.gc_proc) {
10518f55a568SDoug Rabson 
10528f55a568SDoug Rabson 	case RPCSEC_GSS_INIT:
10538f55a568SDoug Rabson 	case RPCSEC_GSS_CONTINUE_INIT:
10548f55a568SDoug Rabson 		if (rqst->rq_proc != NULLPROC) {
10558f55a568SDoug Rabson 			result = AUTH_REJECTEDCRED;
10568f55a568SDoug Rabson 			break;
10578f55a568SDoug Rabson 		}
10588f55a568SDoug Rabson 
10598f55a568SDoug Rabson 		memset(&gr, 0, sizeof(gr));
10608f55a568SDoug Rabson 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
10618f55a568SDoug Rabson 			result = AUTH_REJECTEDCRED;
10628f55a568SDoug Rabson 			break;
10638f55a568SDoug Rabson 		}
10648f55a568SDoug Rabson 
10658f55a568SDoug Rabson 		if (gr.gr_major == GSS_S_COMPLETE) {
10668f55a568SDoug Rabson 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
10678f55a568SDoug Rabson 				result = AUTH_REJECTEDCRED;
10688f55a568SDoug Rabson 				break;
10698f55a568SDoug Rabson 			}
10708f55a568SDoug Rabson 		} else {
10718f55a568SDoug Rabson 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
10728f55a568SDoug Rabson 			rqst->rq_xprt->xp_verf.oa_length = 0;
10738f55a568SDoug Rabson 		}
10748f55a568SDoug Rabson 
10758f55a568SDoug Rabson 		call_stat = svc_sendreply(rqst->rq_xprt,
10768f55a568SDoug Rabson 		    (xdrproc_t) xdr_rpc_gss_init_res,
10778f55a568SDoug Rabson 		    (caddr_t) &gr);
10788f55a568SDoug Rabson 
10798f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &gr.gr_token);
10808f55a568SDoug Rabson 
10818f55a568SDoug Rabson 		if (!call_stat) {
10828f55a568SDoug Rabson 			result = AUTH_FAILED;
10838f55a568SDoug Rabson 			break;
10848f55a568SDoug Rabson 		}
10858f55a568SDoug Rabson 
10868f55a568SDoug Rabson 		if (gr.gr_major == GSS_S_COMPLETE)
10878f55a568SDoug Rabson 			client->cl_state = CLIENT_ESTABLISHED;
10888f55a568SDoug Rabson 
10898f55a568SDoug Rabson 		result = RPCSEC_GSS_NODISPATCH;
10908f55a568SDoug Rabson 		break;
10918f55a568SDoug Rabson 
10928f55a568SDoug Rabson 	case RPCSEC_GSS_DATA:
10938f55a568SDoug Rabson 	case RPCSEC_GSS_DESTROY:
10948f55a568SDoug Rabson 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
10958f55a568SDoug Rabson 			result = RPCSEC_GSS_NODISPATCH;
10968f55a568SDoug Rabson 			break;
10978f55a568SDoug Rabson 		}
10988f55a568SDoug Rabson 
10998f55a568SDoug Rabson 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
11008f55a568SDoug Rabson 			result = RPCSEC_GSS_CREDPROBLEM;
11018f55a568SDoug Rabson 			break;
11028f55a568SDoug Rabson 		}
11038f55a568SDoug Rabson 
11048f55a568SDoug Rabson 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
11058f55a568SDoug Rabson 			result = RPCSEC_GSS_CTXPROBLEM;
11068f55a568SDoug Rabson 			break;
11078f55a568SDoug Rabson 		}
11088f55a568SDoug Rabson 
11098f55a568SDoug Rabson 		svc_rpc_gss_update_seq(client, gc.gc_seq);
11108f55a568SDoug Rabson 
11118f55a568SDoug Rabson 		/*
11128f55a568SDoug Rabson 		 * Change the SVCAUTH ops on the transport to point at
11138f55a568SDoug Rabson 		 * our own code so that we can unwrap the arguments
11148f55a568SDoug Rabson 		 * and wrap the result. The caller will re-set this on
11158f55a568SDoug Rabson 		 * every request to point to a set of null wrap/unwrap
11168f55a568SDoug Rabson 		 * methods.
11178f55a568SDoug Rabson 		 */
11188f55a568SDoug Rabson 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
11198f55a568SDoug Rabson 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
11208f55a568SDoug Rabson 
11218f55a568SDoug Rabson 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
11228f55a568SDoug Rabson 			/*
11238f55a568SDoug Rabson 			 * We might be ready to do a callback to the server to
11248f55a568SDoug Rabson 			 * see if it wants to accept/reject the connection.
11258f55a568SDoug Rabson 			 */
11268f55a568SDoug Rabson 			if (!client->cl_done_callback) {
11278f55a568SDoug Rabson 				client->cl_done_callback = TRUE;
11288f55a568SDoug Rabson 				client->cl_qop = qop;
11298f55a568SDoug Rabson 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
11308f55a568SDoug Rabson 					client->cl_rawcred.mechanism, qop);
11318f55a568SDoug Rabson 				if (!svc_rpc_gss_callback(client, rqst)) {
11328f55a568SDoug Rabson 					result = AUTH_REJECTEDCRED;
11338f55a568SDoug Rabson 					break;
11348f55a568SDoug Rabson 				}
11358f55a568SDoug Rabson 			}
11368f55a568SDoug Rabson 
11378f55a568SDoug Rabson 			/*
11388f55a568SDoug Rabson 			 * If the server has locked this client to a
11398f55a568SDoug Rabson 			 * particular service+qop pair, enforce that
11408f55a568SDoug Rabson 			 * restriction now.
11418f55a568SDoug Rabson 			 */
11428f55a568SDoug Rabson 			if (client->cl_locked) {
11438f55a568SDoug Rabson 				if (client->cl_rawcred.service != gc.gc_svc) {
11448f55a568SDoug Rabson 					result = AUTH_FAILED;
11458f55a568SDoug Rabson 					break;
11468f55a568SDoug Rabson 				} else if (client->cl_qop != qop) {
11478f55a568SDoug Rabson 					result = AUTH_BADVERF;
11488f55a568SDoug Rabson 					break;
11498f55a568SDoug Rabson 				}
11508f55a568SDoug Rabson 			}
11518f55a568SDoug Rabson 
11528f55a568SDoug Rabson 			/*
11538f55a568SDoug Rabson 			 * If the qop changed, look up the new qop
11548f55a568SDoug Rabson 			 * name for rawcred.
11558f55a568SDoug Rabson 			 */
11568f55a568SDoug Rabson 			if (client->cl_qop != qop) {
11578f55a568SDoug Rabson 				client->cl_qop = qop;
11588f55a568SDoug Rabson 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
11598f55a568SDoug Rabson 					client->cl_rawcred.mechanism, qop);
11608f55a568SDoug Rabson 			}
11618f55a568SDoug Rabson 
11628f55a568SDoug Rabson 			/*
11638f55a568SDoug Rabson 			 * Make sure we use the right service value
11648f55a568SDoug Rabson 			 * for unwrap/wrap.
11658f55a568SDoug Rabson 			 */
11668f55a568SDoug Rabson 			client->cl_rawcred.service = gc.gc_svc;
11678f55a568SDoug Rabson 
11688f55a568SDoug Rabson 			result = AUTH_OK;
11698f55a568SDoug Rabson 		} else {
11708f55a568SDoug Rabson 			if (rqst->rq_proc != NULLPROC) {
11718f55a568SDoug Rabson 				result = AUTH_REJECTEDCRED;
11728f55a568SDoug Rabson 				break;
11738f55a568SDoug Rabson 			}
11748f55a568SDoug Rabson 
11758f55a568SDoug Rabson 			call_stat = svc_sendreply(rqst->rq_xprt,
11768f55a568SDoug Rabson 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
11778f55a568SDoug Rabson 
11788f55a568SDoug Rabson 			if (!call_stat) {
11798f55a568SDoug Rabson 				result = AUTH_FAILED;
11808f55a568SDoug Rabson 				break;
11818f55a568SDoug Rabson 			}
11828f55a568SDoug Rabson 
11838f55a568SDoug Rabson 			svc_rpc_gss_destroy_client(client);
11848f55a568SDoug Rabson 
11858f55a568SDoug Rabson 			result = RPCSEC_GSS_NODISPATCH;
11868f55a568SDoug Rabson 			break;
11878f55a568SDoug Rabson 		}
11888f55a568SDoug Rabson 		break;
11898f55a568SDoug Rabson 
11908f55a568SDoug Rabson 	default:
11918f55a568SDoug Rabson 		result = AUTH_BADCRED;
11928f55a568SDoug Rabson 		break;
11938f55a568SDoug Rabson 	}
11948f55a568SDoug Rabson out:
11958f55a568SDoug Rabson 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
11968f55a568SDoug Rabson 	return (result);
11978f55a568SDoug Rabson }
11988f55a568SDoug Rabson 
11998f55a568SDoug Rabson bool_t
12008f55a568SDoug Rabson svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
12018f55a568SDoug Rabson {
12028f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
12038f55a568SDoug Rabson 
12048f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_wrap()");
12058f55a568SDoug Rabson 
12068f55a568SDoug Rabson 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
12078f55a568SDoug Rabson 	if (client->cl_state != CLIENT_ESTABLISHED
12088f55a568SDoug Rabson 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
12098f55a568SDoug Rabson 		return xdr_func(xdrs, xdr_ptr);
12108f55a568SDoug Rabson 	}
12118f55a568SDoug Rabson 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
12128f55a568SDoug Rabson 		client->cl_ctx, client->cl_qop,
12138f55a568SDoug Rabson 		client->cl_rawcred.service, client->cl_seq));
12148f55a568SDoug Rabson }
12158f55a568SDoug Rabson 
12168f55a568SDoug Rabson bool_t
12178f55a568SDoug Rabson svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
12188f55a568SDoug Rabson {
12198f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
12208f55a568SDoug Rabson 
12218f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_unwrap()");
12228f55a568SDoug Rabson 
12238f55a568SDoug Rabson 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
12248f55a568SDoug Rabson 	if (client->cl_state != CLIENT_ESTABLISHED
12258f55a568SDoug Rabson 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
12268f55a568SDoug Rabson 		return xdr_func(xdrs, xdr_ptr);
12278f55a568SDoug Rabson 	}
12288f55a568SDoug Rabson 	return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
12298f55a568SDoug Rabson 		client->cl_ctx, client->cl_qop,
12308f55a568SDoug Rabson 		client->cl_rawcred.service, client->cl_seq));
12318f55a568SDoug Rabson }
1232