xref: /freebsd/lib/librpcsec_gss/svc_rpcsec_gss.c (revision 9b118815692af9dc45c23384e8034a07d2c8b7ff)
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)
938f55a568SDoug Rabson 	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)
1058f55a568SDoug Rabson 	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
1438f55a568SDoug Rabson struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
1448f55a568SDoug Rabson 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 
1718f55a568SDoug Rabson 	scb = malloc(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 
2588f55a568SDoug Rabson 	buf.value = malloc(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);
2768f55a568SDoug Rabson 	free(buf.value);
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 
3038f55a568SDoug Rabson 	result = malloc(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)
4468f55a568SDoug Rabson 		free(client->cl_rawcred.client_principal);
4478f55a568SDoug Rabson 
4488f55a568SDoug Rabson 	if (client->cl_verf.value)
4498f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &client->cl_verf);
4508f55a568SDoug Rabson 
4518f55a568SDoug Rabson 	list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
4528f55a568SDoug Rabson 	TAILQ_REMOVE(list, client, cl_link);
4538f55a568SDoug Rabson 	TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
4548f55a568SDoug Rabson 	svc_rpc_gss_client_count--;
4558f55a568SDoug Rabson 	mem_free(client, sizeof(*client));
4568f55a568SDoug Rabson }
4578f55a568SDoug Rabson 
4588f55a568SDoug Rabson static void
4598f55a568SDoug Rabson svc_rpc_gss_timeout_clients(void)
4608f55a568SDoug Rabson {
4618f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
4628f55a568SDoug Rabson 	struct svc_rpc_gss_client *nclient;
4638f55a568SDoug Rabson 	time_t now = time(0);
4648f55a568SDoug Rabson 
4658f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_timeout_clients()");
4668f55a568SDoug Rabson 	/*
4678f55a568SDoug Rabson 	 * First enforce the max client limit. We keep
4688f55a568SDoug Rabson 	 * svc_rpc_gss_clients in LRU order.
4698f55a568SDoug Rabson 	 */
4708f55a568SDoug Rabson 	while (svc_rpc_gss_client_count > CLIENT_MAX)
4718f55a568SDoug Rabson 		svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
4728f55a568SDoug Rabson 			    svc_rpc_gss_client_list));
4738f55a568SDoug Rabson 	TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
4748f55a568SDoug Rabson 		if (client->cl_state == CLIENT_STALE
4758f55a568SDoug Rabson 		    || now > client->cl_expiration) {
4768f55a568SDoug Rabson 			log_debug("expiring client %p", client);
4778f55a568SDoug Rabson 			svc_rpc_gss_destroy_client(client);
4788f55a568SDoug Rabson 		}
4798f55a568SDoug Rabson 	}
4808f55a568SDoug Rabson }
4818f55a568SDoug Rabson 
4828f55a568SDoug Rabson #ifdef DEBUG
4838f55a568SDoug Rabson /*
4848f55a568SDoug Rabson  * OID<->string routines.  These are uuuuugly.
4858f55a568SDoug Rabson  */
4868f55a568SDoug Rabson static OM_uint32
4878f55a568SDoug Rabson gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
4888f55a568SDoug Rabson {
4898f55a568SDoug Rabson 	char		numstr[128];
4908f55a568SDoug Rabson 	unsigned long	number;
4918f55a568SDoug Rabson 	int		numshift;
4928f55a568SDoug Rabson 	size_t		string_length;
4938f55a568SDoug Rabson 	size_t		i;
4948f55a568SDoug Rabson 	unsigned char	*cp;
4958f55a568SDoug Rabson 	char		*bp;
4968f55a568SDoug Rabson 
4978f55a568SDoug Rabson 	/* Decoded according to krb5/gssapi_krb5.c */
4988f55a568SDoug Rabson 
4998f55a568SDoug Rabson 	/* First determine the size of the string */
5008f55a568SDoug Rabson 	string_length = 0;
5018f55a568SDoug Rabson 	number = 0;
5028f55a568SDoug Rabson 	numshift = 0;
5038f55a568SDoug Rabson 	cp = (unsigned char *) oid->elements;
5048f55a568SDoug Rabson 	number = (unsigned long) cp[0];
5058f55a568SDoug Rabson 	sprintf(numstr, "%ld ", number/40);
5068f55a568SDoug Rabson 	string_length += strlen(numstr);
5078f55a568SDoug Rabson 	sprintf(numstr, "%ld ", number%40);
5088f55a568SDoug Rabson 	string_length += strlen(numstr);
5098f55a568SDoug Rabson 	for (i=1; i<oid->length; i++) {
5108f55a568SDoug Rabson 		if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
5118f55a568SDoug Rabson 			number = (number << 7) | (cp[i] & 0x7f);
5128f55a568SDoug Rabson 			numshift += 7;
5138f55a568SDoug Rabson 		}
5148f55a568SDoug Rabson 		else {
5158f55a568SDoug Rabson 			*minor_status = 0;
5168f55a568SDoug Rabson 			return(GSS_S_FAILURE);
5178f55a568SDoug Rabson 		}
5188f55a568SDoug Rabson 		if ((cp[i] & 0x80) == 0) {
5198f55a568SDoug Rabson 			sprintf(numstr, "%ld ", number);
5208f55a568SDoug Rabson 			string_length += strlen(numstr);
5218f55a568SDoug Rabson 			number = 0;
5228f55a568SDoug Rabson 			numshift = 0;
5238f55a568SDoug Rabson 		}
5248f55a568SDoug Rabson 	}
5258f55a568SDoug Rabson 	/*
5268f55a568SDoug Rabson 	 * If we get here, we've calculated the length of "n n n ... n ".  Add 4
5278f55a568SDoug Rabson 	 * here for "{ " and "}\0".
5288f55a568SDoug Rabson 	 */
5298f55a568SDoug Rabson 	string_length += 4;
5308f55a568SDoug Rabson 	if ((bp = (char *) malloc(string_length))) {
5318f55a568SDoug Rabson 		strcpy(bp, "{ ");
5328f55a568SDoug Rabson 		number = (unsigned long) cp[0];
5338f55a568SDoug Rabson 		sprintf(numstr, "%ld ", number/40);
5348f55a568SDoug Rabson 		strcat(bp, numstr);
5358f55a568SDoug Rabson 		sprintf(numstr, "%ld ", number%40);
5368f55a568SDoug Rabson 		strcat(bp, numstr);
5378f55a568SDoug Rabson 		number = 0;
5388f55a568SDoug Rabson 		cp = (unsigned char *) oid->elements;
5398f55a568SDoug Rabson 		for (i=1; i<oid->length; i++) {
5408f55a568SDoug Rabson 			number = (number << 7) | (cp[i] & 0x7f);
5418f55a568SDoug Rabson 			if ((cp[i] & 0x80) == 0) {
5428f55a568SDoug Rabson 				sprintf(numstr, "%ld ", number);
5438f55a568SDoug Rabson 				strcat(bp, numstr);
5448f55a568SDoug Rabson 				number = 0;
5458f55a568SDoug Rabson 			}
5468f55a568SDoug Rabson 		}
5478f55a568SDoug Rabson 		strcat(bp, "}");
5488f55a568SDoug Rabson 		oid_str->length = strlen(bp)+1;
5498f55a568SDoug Rabson 		oid_str->value = (void *) bp;
5508f55a568SDoug Rabson 		*minor_status = 0;
5518f55a568SDoug Rabson 		return(GSS_S_COMPLETE);
5528f55a568SDoug Rabson 	}
5538f55a568SDoug Rabson 	*minor_status = 0;
5548f55a568SDoug Rabson 	return(GSS_S_FAILURE);
5558f55a568SDoug Rabson }
5568f55a568SDoug Rabson #endif
5578f55a568SDoug Rabson 
5588f55a568SDoug Rabson static void
5598f55a568SDoug Rabson svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
5608f55a568SDoug Rabson     const gss_name_t name)
5618f55a568SDoug Rabson {
5628f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
5638f55a568SDoug Rabson 	char			buf[128];
5648f55a568SDoug Rabson 	uid_t			uid;
5658f55a568SDoug Rabson 	struct passwd		pwd, *pw;
5668f55a568SDoug Rabson 	rpc_gss_ucred_t		*uc = &client->cl_ucred;
5678f55a568SDoug Rabson 
5688f55a568SDoug Rabson 	uc->uid = 65534;
5698f55a568SDoug Rabson 	uc->gid = 65534;
5708f55a568SDoug Rabson 	uc->gidlen = 0;
5718f55a568SDoug Rabson 	uc->gidlist = client->cl_gid_storage;
5728f55a568SDoug Rabson 
5738f55a568SDoug Rabson 	maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
5748f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE)
5758f55a568SDoug Rabson 		return;
5768f55a568SDoug Rabson 
5778f55a568SDoug Rabson 	getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
5788f55a568SDoug Rabson 	if (pw) {
5798f55a568SDoug Rabson 		int len = NGRPS;
5808f55a568SDoug Rabson 		uc->uid = pw->pw_uid;
5818f55a568SDoug Rabson 		uc->gid = pw->pw_gid;
5828f55a568SDoug Rabson 		uc->gidlist = client->cl_gid_storage;
5838f55a568SDoug Rabson 		getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
5848f55a568SDoug Rabson 		uc->gidlen = len;
5858f55a568SDoug Rabson 	}
5868f55a568SDoug Rabson }
5878f55a568SDoug Rabson 
5888f55a568SDoug Rabson static bool_t
5898f55a568SDoug Rabson svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
5908f55a568SDoug Rabson 			       struct svc_req *rqst,
5918f55a568SDoug Rabson 			       struct rpc_gss_init_res *gr,
5928f55a568SDoug Rabson 			       struct rpc_gss_cred *gc)
5938f55a568SDoug Rabson {
5948f55a568SDoug Rabson 	gss_buffer_desc		recv_tok;
5958f55a568SDoug Rabson 	gss_OID			mech;
5968f55a568SDoug Rabson 	OM_uint32		maj_stat = 0, min_stat = 0, ret_flags;
5978f55a568SDoug Rabson 	OM_uint32		cred_lifetime;
5988f55a568SDoug Rabson 	struct svc_rpc_gss_svc_name *sname;
5998f55a568SDoug Rabson 
6008f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_accept_context()");
6018f55a568SDoug Rabson 
6028f55a568SDoug Rabson 	/* Deserialize arguments. */
6038f55a568SDoug Rabson 	memset(&recv_tok, 0, sizeof(recv_tok));
6048f55a568SDoug Rabson 
6058f55a568SDoug Rabson 	if (!svc_getargs(rqst->rq_xprt,
6068f55a568SDoug Rabson 		(xdrproc_t) xdr_gss_buffer_desc,
6078f55a568SDoug Rabson 		(caddr_t) &recv_tok)) {
6088f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
6098f55a568SDoug Rabson 		return (FALSE);
6108f55a568SDoug Rabson 	}
6118f55a568SDoug Rabson 
6128f55a568SDoug Rabson 	/*
6138f55a568SDoug Rabson 	 * First time round, try all the server names we have until
6148f55a568SDoug Rabson 	 * one matches. Afterwards, stick with that one.
6158f55a568SDoug Rabson 	 */
6168f55a568SDoug Rabson 	if (!client->cl_sname) {
6178f55a568SDoug Rabson 		SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
6188f55a568SDoug Rabson 			if (sname->sn_program == rqst->rq_prog
6198f55a568SDoug Rabson 			    && sname->sn_version == rqst->rq_vers) {
6208f55a568SDoug Rabson 				gr->gr_major = gss_accept_sec_context(
6218f55a568SDoug Rabson 					&gr->gr_minor,
6228f55a568SDoug Rabson 					&client->cl_ctx,
6238f55a568SDoug Rabson 					sname->sn_cred,
6248f55a568SDoug Rabson 					&recv_tok,
6258f55a568SDoug Rabson 					GSS_C_NO_CHANNEL_BINDINGS,
6268f55a568SDoug Rabson 					&client->cl_cname,
6278f55a568SDoug Rabson 					&mech,
6288f55a568SDoug Rabson 					&gr->gr_token,
6298f55a568SDoug Rabson 					&ret_flags,
6308f55a568SDoug Rabson 					&cred_lifetime,
6318f55a568SDoug Rabson 					&client->cl_creds);
6328f55a568SDoug Rabson 				if (gr->gr_major == GSS_S_COMPLETE
6338f55a568SDoug Rabson 				    || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
6348f55a568SDoug Rabson 					client->cl_sname = sname;
6358f55a568SDoug Rabson 					break;
6368f55a568SDoug Rabson 				}
6378f55a568SDoug Rabson 			}
6388f55a568SDoug Rabson 		}
6398f55a568SDoug Rabson 	} else {
6408f55a568SDoug Rabson 		gr->gr_major = gss_accept_sec_context(
6418f55a568SDoug Rabson 			&gr->gr_minor,
6428f55a568SDoug Rabson 			&client->cl_ctx,
6438f55a568SDoug Rabson 			client->cl_sname->sn_cred,
6448f55a568SDoug Rabson 			&recv_tok,
6458f55a568SDoug Rabson 			GSS_C_NO_CHANNEL_BINDINGS,
6468f55a568SDoug Rabson 			&client->cl_cname,
6478f55a568SDoug Rabson 			&mech,
6488f55a568SDoug Rabson 			&gr->gr_token,
6498f55a568SDoug Rabson 			&ret_flags,
6508f55a568SDoug Rabson 			&cred_lifetime,
6518f55a568SDoug Rabson 			NULL);
6528f55a568SDoug Rabson 	}
6538f55a568SDoug Rabson 
6548f55a568SDoug Rabson 	xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
6558f55a568SDoug Rabson 
6568f55a568SDoug Rabson 	/*
6578f55a568SDoug Rabson 	 * If we get an error from gss_accept_sec_context, send the
6588f55a568SDoug Rabson 	 * reply anyway so that the client gets a chance to see what
6598f55a568SDoug Rabson 	 * is wrong.
6608f55a568SDoug Rabson 	 */
6618f55a568SDoug Rabson 	if (gr->gr_major != GSS_S_COMPLETE &&
6628f55a568SDoug Rabson 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
6638f55a568SDoug Rabson 		log_status("accept_sec_context", client->cl_mech,
6648f55a568SDoug Rabson 		    gr->gr_major, gr->gr_minor);
6658f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
6668f55a568SDoug Rabson 		return (FALSE);
6678f55a568SDoug Rabson 	}
6688f55a568SDoug Rabson 
6698f55a568SDoug Rabson 	gr->gr_handle.value = &client->cl_id;
6708f55a568SDoug Rabson 	gr->gr_handle.length = sizeof(uint32_t);
6718f55a568SDoug Rabson 	gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
6728f55a568SDoug Rabson 
6738f55a568SDoug Rabson 	/* Save client info. */
6748f55a568SDoug Rabson 	client->cl_mech = mech;
6758f55a568SDoug Rabson 	client->cl_qop = GSS_C_QOP_DEFAULT;
6768f55a568SDoug Rabson 	client->cl_seq = gc->gc_seq;
6778f55a568SDoug Rabson 	client->cl_win = gr->gr_win;
6788f55a568SDoug Rabson 	client->cl_done_callback = FALSE;
6798f55a568SDoug Rabson 
6808f55a568SDoug Rabson 	if (gr->gr_major == GSS_S_COMPLETE) {
6818f55a568SDoug Rabson 		gss_buffer_desc	export_name;
6828f55a568SDoug Rabson 
6838f55a568SDoug Rabson 		/*
6848f55a568SDoug Rabson 		 * Change client expiration time to be near when the
6858f55a568SDoug Rabson 		 * client creds expire (or 24 hours if we can't figure
6868f55a568SDoug Rabson 		 * that out).
6878f55a568SDoug Rabson 		 */
6888f55a568SDoug Rabson 		if (cred_lifetime == GSS_C_INDEFINITE)
6898f55a568SDoug Rabson 			cred_lifetime = time(0) + 24*60*60;
6908f55a568SDoug Rabson 
6918f55a568SDoug Rabson 		client->cl_expiration = time(0) + cred_lifetime;
6928f55a568SDoug Rabson 
6938f55a568SDoug Rabson 		/*
6948f55a568SDoug Rabson 		 * Fill in cred details in the rawcred structure.
6958f55a568SDoug Rabson 		 */
6968f55a568SDoug Rabson 		client->cl_rawcred.version = RPCSEC_GSS_VERSION;
6978f55a568SDoug Rabson 		rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
6988f55a568SDoug Rabson 		maj_stat = gss_export_name(&min_stat, client->cl_cname,
6998f55a568SDoug Rabson 		    &export_name);
7008f55a568SDoug Rabson 		if (maj_stat != GSS_S_COMPLETE) {
7018f55a568SDoug Rabson 			log_status("gss_export_name", client->cl_mech,
7028f55a568SDoug Rabson 			    maj_stat, min_stat);
7038f55a568SDoug Rabson 			return (FALSE);
7048f55a568SDoug Rabson 		}
7058f55a568SDoug Rabson 		client->cl_rawcred.client_principal =
7068f55a568SDoug Rabson 			malloc(sizeof(*client->cl_rawcred.client_principal)
7078f55a568SDoug Rabson 			    + export_name.length);
7088f55a568SDoug Rabson 		client->cl_rawcred.client_principal->len = export_name.length;
7098f55a568SDoug Rabson 		memcpy(client->cl_rawcred.client_principal->name,
7108f55a568SDoug Rabson 		    export_name.value, export_name.length);
7118f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &export_name);
7128f55a568SDoug Rabson 		client->cl_rawcred.svc_principal =
7138f55a568SDoug Rabson 			client->cl_sname->sn_principal;
7148f55a568SDoug Rabson 		client->cl_rawcred.service = gc->gc_svc;
7158f55a568SDoug Rabson 
7168f55a568SDoug Rabson 		/*
7178f55a568SDoug Rabson 		 * Use gss_pname_to_uid to map to unix creds. For
7188f55a568SDoug Rabson 		 * kerberos5, this uses krb5_aname_to_localname.
7198f55a568SDoug Rabson 		 */
7208f55a568SDoug Rabson 		svc_rpc_gss_build_ucred(client, client->cl_cname);
7218f55a568SDoug Rabson 
7228f55a568SDoug Rabson #ifdef DEBUG
7238f55a568SDoug Rabson 		{
7248f55a568SDoug Rabson 			gss_buffer_desc mechname;
7258f55a568SDoug Rabson 
7268f55a568SDoug Rabson 			gss_oid_to_str(&min_stat, mech, &mechname);
7278f55a568SDoug Rabson 
7288f55a568SDoug Rabson 			log_debug("accepted context for %s with "
7298f55a568SDoug Rabson 			    "<mech %.*s, qop %d, svc %d>",
7308f55a568SDoug Rabson 			    client->cl_rawcred.client_principal->name,
7318f55a568SDoug Rabson 			    mechname.length, (char *)mechname.value,
7328f55a568SDoug Rabson 			    client->cl_qop, client->rawcred.service);
7338f55a568SDoug Rabson 
7348f55a568SDoug Rabson 			gss_release_buffer(&min_stat, &mechname);
7358f55a568SDoug Rabson 		}
7368f55a568SDoug Rabson #endif /* DEBUG */
7378f55a568SDoug Rabson 	}
7388f55a568SDoug Rabson 	return (TRUE);
7398f55a568SDoug Rabson }
7408f55a568SDoug Rabson 
7418f55a568SDoug Rabson static bool_t
7428f55a568SDoug Rabson svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
7438f55a568SDoug Rabson 	gss_qop_t *qop)
7448f55a568SDoug Rabson {
7458f55a568SDoug Rabson 	struct opaque_auth	*oa;
7468f55a568SDoug Rabson 	gss_buffer_desc		 rpcbuf, checksum;
7478f55a568SDoug Rabson 	OM_uint32		 maj_stat, min_stat;
7488f55a568SDoug Rabson 	gss_qop_t		 qop_state;
7499b118815SDoug Rabson 	int32_t			 rpchdr[128 / sizeof(int32_t)];
7508f55a568SDoug Rabson 	int32_t			*buf;
7518f55a568SDoug Rabson 
7528f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_validate()");
7538f55a568SDoug Rabson 
7548f55a568SDoug Rabson 	memset(rpchdr, 0, sizeof(rpchdr));
7558f55a568SDoug Rabson 
7568f55a568SDoug Rabson 	/* Reconstruct RPC header for signing (from xdr_callmsg). */
7579b118815SDoug Rabson 	buf = rpchdr;
7588f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_xid);
7598f55a568SDoug Rabson 	IXDR_PUT_ENUM(buf, msg->rm_direction);
7608f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
7618f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
7628f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
7638f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
7648f55a568SDoug Rabson 	oa = &msg->rm_call.cb_cred;
7658f55a568SDoug Rabson 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
7668f55a568SDoug Rabson 	IXDR_PUT_LONG(buf, oa->oa_length);
7678f55a568SDoug Rabson 	if (oa->oa_length) {
7688f55a568SDoug Rabson 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
7698f55a568SDoug Rabson 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
7708f55a568SDoug Rabson 	}
7718f55a568SDoug Rabson 	rpcbuf.value = rpchdr;
7729b118815SDoug Rabson 	rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
7738f55a568SDoug Rabson 
7748f55a568SDoug Rabson 	checksum.value = msg->rm_call.cb_verf.oa_base;
7758f55a568SDoug Rabson 	checksum.length = msg->rm_call.cb_verf.oa_length;
7768f55a568SDoug Rabson 
7778f55a568SDoug Rabson 	maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
7788f55a568SDoug Rabson 				  &qop_state);
7798f55a568SDoug Rabson 
7808f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
7818f55a568SDoug Rabson 		log_status("gss_verify_mic", client->cl_mech,
7828f55a568SDoug Rabson 		    maj_stat, min_stat);
7838f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
7848f55a568SDoug Rabson 		return (FALSE);
7858f55a568SDoug Rabson 	}
7868f55a568SDoug Rabson 	*qop = qop_state;
7878f55a568SDoug Rabson 	return (TRUE);
7888f55a568SDoug Rabson }
7898f55a568SDoug Rabson 
7908f55a568SDoug Rabson static bool_t
7918f55a568SDoug Rabson svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
7928f55a568SDoug Rabson     struct svc_req *rqst, u_int seq)
7938f55a568SDoug Rabson {
7948f55a568SDoug Rabson 	gss_buffer_desc		signbuf;
7958f55a568SDoug Rabson 	OM_uint32		maj_stat, min_stat;
7968f55a568SDoug Rabson 	uint32_t		nseq;
7978f55a568SDoug Rabson 
7988f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_nextverf()");
7998f55a568SDoug Rabson 
8008f55a568SDoug Rabson 	nseq = htonl(seq);
8018f55a568SDoug Rabson 	signbuf.value = &nseq;
8028f55a568SDoug Rabson 	signbuf.length = sizeof(nseq);
8038f55a568SDoug Rabson 
8048f55a568SDoug Rabson 	if (client->cl_verf.value)
8058f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &client->cl_verf);
8068f55a568SDoug Rabson 
8078f55a568SDoug Rabson 	maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
8088f55a568SDoug Rabson 	    &signbuf, &client->cl_verf);
8098f55a568SDoug Rabson 
8108f55a568SDoug Rabson 	if (maj_stat != GSS_S_COMPLETE) {
8118f55a568SDoug Rabson 		log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
8128f55a568SDoug Rabson 		client->cl_state = CLIENT_STALE;
8138f55a568SDoug Rabson 		return (FALSE);
8148f55a568SDoug Rabson 	}
8158f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
8168f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
8178f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
8188f55a568SDoug Rabson 
8198f55a568SDoug Rabson 	return (TRUE);
8208f55a568SDoug Rabson }
8218f55a568SDoug Rabson 
8228f55a568SDoug Rabson static bool_t
8238f55a568SDoug Rabson svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
8248f55a568SDoug Rabson {
8258f55a568SDoug Rabson 	struct svc_rpc_gss_callback *scb;
8268f55a568SDoug Rabson 	rpc_gss_lock_t	lock;
8278f55a568SDoug Rabson 	void		*cookie;
8288f55a568SDoug Rabson 	bool_t		cb_res;
8298f55a568SDoug Rabson 	bool_t		result;
8308f55a568SDoug Rabson 
8318f55a568SDoug Rabson 	/*
8328f55a568SDoug Rabson 	 * See if we have a callback for this guy.
8338f55a568SDoug Rabson 	 */
8348f55a568SDoug Rabson 	result = TRUE;
8358f55a568SDoug Rabson 	SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
8368f55a568SDoug Rabson 		if (scb->cb_callback.program == rqst->rq_prog
8378f55a568SDoug Rabson 		    && scb->cb_callback.version == rqst->rq_vers) {
8388f55a568SDoug Rabson 			/*
8398f55a568SDoug Rabson 			 * This one matches. Call the callback and see
8408f55a568SDoug Rabson 			 * if it wants to veto or something.
8418f55a568SDoug Rabson 			 */
8428f55a568SDoug Rabson 			lock.locked = FALSE;
8438f55a568SDoug Rabson 			lock.raw_cred = &client->cl_rawcred;
8448f55a568SDoug Rabson 			cb_res = scb->cb_callback.callback(rqst,
8458f55a568SDoug Rabson 			    client->cl_creds,
8468f55a568SDoug Rabson 			    client->cl_ctx,
8478f55a568SDoug Rabson 			    &lock,
8488f55a568SDoug Rabson 			    &cookie);
8498f55a568SDoug Rabson 
8508f55a568SDoug Rabson 			if (!cb_res) {
8518f55a568SDoug Rabson 				client->cl_state = CLIENT_STALE;
8528f55a568SDoug Rabson 				result = FALSE;
8538f55a568SDoug Rabson 				break;
8548f55a568SDoug Rabson 			}
8558f55a568SDoug Rabson 
8568f55a568SDoug Rabson 			/*
8578f55a568SDoug Rabson 			 * The callback accepted the connection - it
8588f55a568SDoug Rabson 			 * is responsible for freeing client->cl_creds
8598f55a568SDoug Rabson 			 * now.
8608f55a568SDoug Rabson 			 */
8618f55a568SDoug Rabson 			client->cl_creds = GSS_C_NO_CREDENTIAL;
8628f55a568SDoug Rabson 			client->cl_locked = lock.locked;
8638f55a568SDoug Rabson 			client->cl_cookie = cookie;
8648f55a568SDoug Rabson 			return (TRUE);
8658f55a568SDoug Rabson 		}
8668f55a568SDoug Rabson 	}
8678f55a568SDoug Rabson 
8688f55a568SDoug Rabson 	/*
8698f55a568SDoug Rabson 	 * Either no callback exists for this program/version or one
8708f55a568SDoug Rabson 	 * of the callbacks rejected the connection. We just need to
8718f55a568SDoug Rabson 	 * clean up the delegated client creds, if any.
8728f55a568SDoug Rabson 	 */
8738f55a568SDoug Rabson 	if (client->cl_creds) {
8748f55a568SDoug Rabson 		OM_uint32 min_ver;
8758f55a568SDoug Rabson 		gss_release_cred(&min_ver, &client->cl_creds);
8768f55a568SDoug Rabson 	}
8778f55a568SDoug Rabson 	return (result);
8788f55a568SDoug Rabson }
8798f55a568SDoug Rabson 
8808f55a568SDoug Rabson static bool_t
8818f55a568SDoug Rabson svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
8828f55a568SDoug Rabson {
8838f55a568SDoug Rabson 	u_int32_t offset;
8848f55a568SDoug Rabson 	int word, bit;
8858f55a568SDoug Rabson 
8868f55a568SDoug Rabson 	if (seq < client->cl_seqlast) {
8878f55a568SDoug Rabson 		/*
8888f55a568SDoug Rabson 		 * The request sequence number is less than
8898f55a568SDoug Rabson 		 * the largest we have seen so far. If it is
8908f55a568SDoug Rabson 		 * outside the window or if we have seen a
8918f55a568SDoug Rabson 		 * request with this sequence before, silently
8928f55a568SDoug Rabson 		 * discard it.
8938f55a568SDoug Rabson 		 */
8948f55a568SDoug Rabson 		offset = client->cl_seqlast - seq;
8958f55a568SDoug Rabson 		if (offset >= client->cl_win)
8968f55a568SDoug Rabson 			return (FALSE);
8978f55a568SDoug Rabson 		word = offset / 32;
8988f55a568SDoug Rabson 		bit = offset % 32;
8998f55a568SDoug Rabson 		if (client->cl_seqmask[word] & (1 << bit))
9008f55a568SDoug Rabson 			return (FALSE);
9018f55a568SDoug Rabson 		client->cl_seqmask[word] |= (1 << bit);
9028f55a568SDoug Rabson 	}
9038f55a568SDoug Rabson 
9048f55a568SDoug Rabson 	return (TRUE);
9058f55a568SDoug Rabson }
9068f55a568SDoug Rabson 
9078f55a568SDoug Rabson static void
9088f55a568SDoug Rabson svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
9098f55a568SDoug Rabson {
9108f55a568SDoug Rabson 	int offset, i;
9118f55a568SDoug Rabson 	uint32_t carry, newcarry;
9128f55a568SDoug Rabson 
9138f55a568SDoug Rabson 	if (seq > client->cl_seqlast) {
9148f55a568SDoug Rabson 		/*
9158f55a568SDoug Rabson 		 * This request has a sequence number greater
9168f55a568SDoug Rabson 		 * than any we have seen so far. Advance the
9178f55a568SDoug Rabson 		 * seq window and set bit zero of the window
9188f55a568SDoug Rabson 		 * (which corresponds to the new sequence
9198f55a568SDoug Rabson 		 * number)
9208f55a568SDoug Rabson 		 */
9218f55a568SDoug Rabson 		offset = seq - client->cl_seqlast;
9228f55a568SDoug Rabson 		while (offset > 32) {
9238f55a568SDoug Rabson 			for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
9248f55a568SDoug Rabson 			     i > 0; i--) {
9258f55a568SDoug Rabson 				client->cl_seqmask[i] = client->cl_seqmask[i-1];
9268f55a568SDoug Rabson 			}
9278f55a568SDoug Rabson 			client->cl_seqmask[0] = 0;
9288f55a568SDoug Rabson 			offset -= 32;
9298f55a568SDoug Rabson 		}
9308f55a568SDoug Rabson 		carry = 0;
9318f55a568SDoug Rabson 		for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
9328f55a568SDoug Rabson 			newcarry = client->cl_seqmask[i] >> (32 - offset);
9338f55a568SDoug Rabson 			client->cl_seqmask[i] =
9348f55a568SDoug Rabson 				(client->cl_seqmask[i] << offset) | carry;
9358f55a568SDoug Rabson 			carry = newcarry;
9368f55a568SDoug Rabson 		}
9378f55a568SDoug Rabson 		client->cl_seqmask[0] |= 1;
9388f55a568SDoug Rabson 		client->cl_seqlast = seq;
9398f55a568SDoug Rabson 	}
9408f55a568SDoug Rabson }
9418f55a568SDoug Rabson 
9428f55a568SDoug Rabson enum auth_stat
9438f55a568SDoug Rabson svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
9448f55a568SDoug Rabson 
9458f55a568SDoug Rabson {
9468f55a568SDoug Rabson 	OM_uint32		 min_stat;
9478f55a568SDoug Rabson 	XDR	 		 xdrs;
9488f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
9498f55a568SDoug Rabson 	struct rpc_gss_cred	 gc;
9508f55a568SDoug Rabson 	struct rpc_gss_init_res	 gr;
9518f55a568SDoug Rabson 	gss_qop_t		 qop;
9528f55a568SDoug Rabson 	int			 call_stat;
9538f55a568SDoug Rabson 	enum auth_stat		 result;
9548f55a568SDoug Rabson 
9558f55a568SDoug Rabson 	log_debug("in svc_rpc_gss()");
9568f55a568SDoug Rabson 
9578f55a568SDoug Rabson 	/* Garbage collect old clients. */
9588f55a568SDoug Rabson 	svc_rpc_gss_timeout_clients();
9598f55a568SDoug Rabson 
9608f55a568SDoug Rabson 	/* Initialize reply. */
9618f55a568SDoug Rabson 	rqst->rq_xprt->xp_verf = _null_auth;
9628f55a568SDoug Rabson 
9638f55a568SDoug Rabson 	/* Deserialize client credentials. */
9648f55a568SDoug Rabson 	if (rqst->rq_cred.oa_length <= 0)
9658f55a568SDoug Rabson 		return (AUTH_BADCRED);
9668f55a568SDoug Rabson 
9678f55a568SDoug Rabson 	memset(&gc, 0, sizeof(gc));
9688f55a568SDoug Rabson 
9698f55a568SDoug Rabson 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
9708f55a568SDoug Rabson 	    rqst->rq_cred.oa_length, XDR_DECODE);
9718f55a568SDoug Rabson 
9728f55a568SDoug Rabson 	if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
9738f55a568SDoug Rabson 		XDR_DESTROY(&xdrs);
9748f55a568SDoug Rabson 		return (AUTH_BADCRED);
9758f55a568SDoug Rabson 	}
9768f55a568SDoug Rabson 	XDR_DESTROY(&xdrs);
9778f55a568SDoug Rabson 
9788f55a568SDoug Rabson 	/* Check version. */
9798f55a568SDoug Rabson 	if (gc.gc_version != RPCSEC_GSS_VERSION) {
9808f55a568SDoug Rabson 		result = AUTH_BADCRED;
9818f55a568SDoug Rabson 		goto out;
9828f55a568SDoug Rabson 	}
9838f55a568SDoug Rabson 
9848f55a568SDoug Rabson 	/* Check the proc and find the client (or create it) */
9858f55a568SDoug Rabson 	if (gc.gc_proc == RPCSEC_GSS_INIT) {
9868f55a568SDoug Rabson 		client = svc_rpc_gss_create_client();
9878f55a568SDoug Rabson 	} else {
9888f55a568SDoug Rabson 		if (gc.gc_handle.length != sizeof(uint32_t)) {
9898f55a568SDoug Rabson 			result = AUTH_BADCRED;
9908f55a568SDoug Rabson 			goto out;
9918f55a568SDoug Rabson 		}
9928f55a568SDoug Rabson 		uint32_t *p = gc.gc_handle.value;
9938f55a568SDoug Rabson 		client = svc_rpc_gss_find_client(*p);
9948f55a568SDoug Rabson 		if (!client) {
9958f55a568SDoug Rabson 			/*
9968f55a568SDoug Rabson 			 * Can't find the client - we may have
9978f55a568SDoug Rabson 			 * destroyed it - tell the other side to
9988f55a568SDoug Rabson 			 * re-authenticate.
9998f55a568SDoug Rabson 			 */
10008f55a568SDoug Rabson 			result = RPCSEC_GSS_CREDPROBLEM;
10018f55a568SDoug Rabson 			goto out;
10028f55a568SDoug Rabson 		}
10038f55a568SDoug Rabson 	}
10048f55a568SDoug Rabson 	rqst->rq_clntcred = client;
10058f55a568SDoug Rabson 
10068f55a568SDoug Rabson 	/*
10078f55a568SDoug Rabson 	 * The service and sequence number must be ignored for
10088f55a568SDoug Rabson 	 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
10098f55a568SDoug Rabson 	 */
10108f55a568SDoug Rabson 	if (gc.gc_proc != RPCSEC_GSS_INIT
10118f55a568SDoug Rabson 	    && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
10128f55a568SDoug Rabson 		/*
10138f55a568SDoug Rabson 		 * Check for sequence number overflow.
10148f55a568SDoug Rabson 		 */
10158f55a568SDoug Rabson 		if (gc.gc_seq >= MAXSEQ) {
10168f55a568SDoug Rabson 			result = RPCSEC_GSS_CTXPROBLEM;
10178f55a568SDoug Rabson 			goto out;
10188f55a568SDoug Rabson 		}
10198f55a568SDoug Rabson 		client->cl_seq = gc.gc_seq;
10208f55a568SDoug Rabson 
10218f55a568SDoug Rabson 		/*
10228f55a568SDoug Rabson 		 * Check for valid service.
10238f55a568SDoug Rabson 		 */
10248f55a568SDoug Rabson 		if (gc.gc_svc != rpc_gss_svc_none &&
10258f55a568SDoug Rabson 		    gc.gc_svc != rpc_gss_svc_integrity &&
10268f55a568SDoug Rabson 		    gc.gc_svc != rpc_gss_svc_privacy) {
10278f55a568SDoug Rabson 			result = AUTH_BADCRED;
10288f55a568SDoug Rabson 			goto out;
10298f55a568SDoug Rabson 		}
10308f55a568SDoug Rabson 	}
10318f55a568SDoug Rabson 
10328f55a568SDoug Rabson 	/* Handle RPCSEC_GSS control procedure. */
10338f55a568SDoug Rabson 	switch (gc.gc_proc) {
10348f55a568SDoug Rabson 
10358f55a568SDoug Rabson 	case RPCSEC_GSS_INIT:
10368f55a568SDoug Rabson 	case RPCSEC_GSS_CONTINUE_INIT:
10378f55a568SDoug Rabson 		if (rqst->rq_proc != NULLPROC) {
10388f55a568SDoug Rabson 			result = AUTH_REJECTEDCRED;
10398f55a568SDoug Rabson 			break;
10408f55a568SDoug Rabson 		}
10418f55a568SDoug Rabson 
10428f55a568SDoug Rabson 		memset(&gr, 0, sizeof(gr));
10438f55a568SDoug Rabson 		if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
10448f55a568SDoug Rabson 			result = AUTH_REJECTEDCRED;
10458f55a568SDoug Rabson 			break;
10468f55a568SDoug Rabson 		}
10478f55a568SDoug Rabson 
10488f55a568SDoug Rabson 		if (gr.gr_major == GSS_S_COMPLETE) {
10498f55a568SDoug Rabson 			if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
10508f55a568SDoug Rabson 				result = AUTH_REJECTEDCRED;
10518f55a568SDoug Rabson 				break;
10528f55a568SDoug Rabson 			}
10538f55a568SDoug Rabson 		} else {
10548f55a568SDoug Rabson 			rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
10558f55a568SDoug Rabson 			rqst->rq_xprt->xp_verf.oa_length = 0;
10568f55a568SDoug Rabson 		}
10578f55a568SDoug Rabson 
10588f55a568SDoug Rabson 		call_stat = svc_sendreply(rqst->rq_xprt,
10598f55a568SDoug Rabson 		    (xdrproc_t) xdr_rpc_gss_init_res,
10608f55a568SDoug Rabson 		    (caddr_t) &gr);
10618f55a568SDoug Rabson 
10628f55a568SDoug Rabson 		gss_release_buffer(&min_stat, &gr.gr_token);
10638f55a568SDoug Rabson 
10648f55a568SDoug Rabson 		if (!call_stat) {
10658f55a568SDoug Rabson 			result = AUTH_FAILED;
10668f55a568SDoug Rabson 			break;
10678f55a568SDoug Rabson 		}
10688f55a568SDoug Rabson 
10698f55a568SDoug Rabson 		if (gr.gr_major == GSS_S_COMPLETE)
10708f55a568SDoug Rabson 			client->cl_state = CLIENT_ESTABLISHED;
10718f55a568SDoug Rabson 
10728f55a568SDoug Rabson 		result = RPCSEC_GSS_NODISPATCH;
10738f55a568SDoug Rabson 		break;
10748f55a568SDoug Rabson 
10758f55a568SDoug Rabson 	case RPCSEC_GSS_DATA:
10768f55a568SDoug Rabson 	case RPCSEC_GSS_DESTROY:
10778f55a568SDoug Rabson 		if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
10788f55a568SDoug Rabson 			result = RPCSEC_GSS_NODISPATCH;
10798f55a568SDoug Rabson 			break;
10808f55a568SDoug Rabson 		}
10818f55a568SDoug Rabson 
10828f55a568SDoug Rabson 		if (!svc_rpc_gss_validate(client, msg, &qop)) {
10838f55a568SDoug Rabson 			result = RPCSEC_GSS_CREDPROBLEM;
10848f55a568SDoug Rabson 			break;
10858f55a568SDoug Rabson 		}
10868f55a568SDoug Rabson 
10878f55a568SDoug Rabson 		if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
10888f55a568SDoug Rabson 			result = RPCSEC_GSS_CTXPROBLEM;
10898f55a568SDoug Rabson 			break;
10908f55a568SDoug Rabson 		}
10918f55a568SDoug Rabson 
10928f55a568SDoug Rabson 		svc_rpc_gss_update_seq(client, gc.gc_seq);
10938f55a568SDoug Rabson 
10948f55a568SDoug Rabson 		/*
10958f55a568SDoug Rabson 		 * Change the SVCAUTH ops on the transport to point at
10968f55a568SDoug Rabson 		 * our own code so that we can unwrap the arguments
10978f55a568SDoug Rabson 		 * and wrap the result. The caller will re-set this on
10988f55a568SDoug Rabson 		 * every request to point to a set of null wrap/unwrap
10998f55a568SDoug Rabson 		 * methods.
11008f55a568SDoug Rabson 		 */
11018f55a568SDoug Rabson 		SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
11028f55a568SDoug Rabson 		SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
11038f55a568SDoug Rabson 
11048f55a568SDoug Rabson 		if (gc.gc_proc == RPCSEC_GSS_DATA) {
11058f55a568SDoug Rabson 			/*
11068f55a568SDoug Rabson 			 * We might be ready to do a callback to the server to
11078f55a568SDoug Rabson 			 * see if it wants to accept/reject the connection.
11088f55a568SDoug Rabson 			 */
11098f55a568SDoug Rabson 			if (!client->cl_done_callback) {
11108f55a568SDoug Rabson 				client->cl_done_callback = TRUE;
11118f55a568SDoug Rabson 				client->cl_qop = qop;
11128f55a568SDoug Rabson 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
11138f55a568SDoug Rabson 					client->cl_rawcred.mechanism, qop);
11148f55a568SDoug Rabson 				if (!svc_rpc_gss_callback(client, rqst)) {
11158f55a568SDoug Rabson 					result = AUTH_REJECTEDCRED;
11168f55a568SDoug Rabson 					break;
11178f55a568SDoug Rabson 				}
11188f55a568SDoug Rabson 			}
11198f55a568SDoug Rabson 
11208f55a568SDoug Rabson 			/*
11218f55a568SDoug Rabson 			 * If the server has locked this client to a
11228f55a568SDoug Rabson 			 * particular service+qop pair, enforce that
11238f55a568SDoug Rabson 			 * restriction now.
11248f55a568SDoug Rabson 			 */
11258f55a568SDoug Rabson 			if (client->cl_locked) {
11268f55a568SDoug Rabson 				if (client->cl_rawcred.service != gc.gc_svc) {
11278f55a568SDoug Rabson 					result = AUTH_FAILED;
11288f55a568SDoug Rabson 					break;
11298f55a568SDoug Rabson 				} else if (client->cl_qop != qop) {
11308f55a568SDoug Rabson 					result = AUTH_BADVERF;
11318f55a568SDoug Rabson 					break;
11328f55a568SDoug Rabson 				}
11338f55a568SDoug Rabson 			}
11348f55a568SDoug Rabson 
11358f55a568SDoug Rabson 			/*
11368f55a568SDoug Rabson 			 * If the qop changed, look up the new qop
11378f55a568SDoug Rabson 			 * name for rawcred.
11388f55a568SDoug Rabson 			 */
11398f55a568SDoug Rabson 			if (client->cl_qop != qop) {
11408f55a568SDoug Rabson 				client->cl_qop = qop;
11418f55a568SDoug Rabson 				client->cl_rawcred.qop = _rpc_gss_num_to_qop(
11428f55a568SDoug Rabson 					client->cl_rawcred.mechanism, qop);
11438f55a568SDoug Rabson 			}
11448f55a568SDoug Rabson 
11458f55a568SDoug Rabson 			/*
11468f55a568SDoug Rabson 			 * Make sure we use the right service value
11478f55a568SDoug Rabson 			 * for unwrap/wrap.
11488f55a568SDoug Rabson 			 */
11498f55a568SDoug Rabson 			client->cl_rawcred.service = gc.gc_svc;
11508f55a568SDoug Rabson 
11518f55a568SDoug Rabson 			result = AUTH_OK;
11528f55a568SDoug Rabson 		} else {
11538f55a568SDoug Rabson 			if (rqst->rq_proc != NULLPROC) {
11548f55a568SDoug Rabson 				result = AUTH_REJECTEDCRED;
11558f55a568SDoug Rabson 				break;
11568f55a568SDoug Rabson 			}
11578f55a568SDoug Rabson 
11588f55a568SDoug Rabson 			call_stat = svc_sendreply(rqst->rq_xprt,
11598f55a568SDoug Rabson 			    (xdrproc_t) xdr_void, (caddr_t) NULL);
11608f55a568SDoug Rabson 
11618f55a568SDoug Rabson 			if (!call_stat) {
11628f55a568SDoug Rabson 				result = AUTH_FAILED;
11638f55a568SDoug Rabson 				break;
11648f55a568SDoug Rabson 			}
11658f55a568SDoug Rabson 
11668f55a568SDoug Rabson 			svc_rpc_gss_destroy_client(client);
11678f55a568SDoug Rabson 
11688f55a568SDoug Rabson 			result = RPCSEC_GSS_NODISPATCH;
11698f55a568SDoug Rabson 			break;
11708f55a568SDoug Rabson 		}
11718f55a568SDoug Rabson 		break;
11728f55a568SDoug Rabson 
11738f55a568SDoug Rabson 	default:
11748f55a568SDoug Rabson 		result = AUTH_BADCRED;
11758f55a568SDoug Rabson 		break;
11768f55a568SDoug Rabson 	}
11778f55a568SDoug Rabson out:
11788f55a568SDoug Rabson 	xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
11798f55a568SDoug Rabson 	return (result);
11808f55a568SDoug Rabson }
11818f55a568SDoug Rabson 
11828f55a568SDoug Rabson bool_t
11838f55a568SDoug Rabson svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
11848f55a568SDoug Rabson {
11858f55a568SDoug Rabson 	struct svc_rpc_gss_client *client;
11868f55a568SDoug Rabson 
11878f55a568SDoug Rabson 	log_debug("in svc_rpc_gss_wrap()");
11888f55a568SDoug Rabson 
11898f55a568SDoug Rabson 	client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
11908f55a568SDoug Rabson 	if (client->cl_state != CLIENT_ESTABLISHED
11918f55a568SDoug Rabson 	    || client->cl_rawcred.service == rpc_gss_svc_none) {
11928f55a568SDoug Rabson 		return xdr_func(xdrs, xdr_ptr);
11938f55a568SDoug Rabson 	}
11948f55a568SDoug Rabson 	return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
11958f55a568SDoug Rabson 		client->cl_ctx, client->cl_qop,
11968f55a568SDoug Rabson 		client->cl_rawcred.service, client->cl_seq));
11978f55a568SDoug Rabson }
11988f55a568SDoug Rabson 
11998f55a568SDoug Rabson bool_t
12008f55a568SDoug Rabson svc_rpc_gss_unwrap(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_unwrap()");
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_unwrap_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 }
1215