17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5158643e0Sps  * Common Development and Distribution License (the "License").
6158643e0Sps  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21cf98b944SMarcel Telka 
227c478bd9Sstevel@tonic-gate /*
23*02ac56e0SMatt Barden  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
240a4b0810SKaren Rochford  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
25b97d6ca7SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
264a3b0527SAndy Fiddaman  * Copyright 2012 Marcel Telka <marcel@telka.sk>
274a3b0527SAndy Fiddaman  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * $Id: svc_auth_gssapi.c,v 1.19 1994/10/27 12:38:51 jik Exp $
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate /*
377c478bd9Sstevel@tonic-gate  * Server side handling of RPCSEC_GSS flavor.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #include <sys/systm.h>
417c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
427c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
437c478bd9Sstevel@tonic-gate #include <sys/debug.h>
447c478bd9Sstevel@tonic-gate #include <sys/types.h>
457c478bd9Sstevel@tonic-gate #include <sys/time.h>
467c478bd9Sstevel@tonic-gate #include <gssapi/gssapi.h>
477c478bd9Sstevel@tonic-gate #include <gssapi/gssapi_ext.h>
487c478bd9Sstevel@tonic-gate #include <rpc/rpc.h>
497c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_defs.h>
50bfd8310aSGlenn Barry #include <sys/sunddi.h>
51bfd8310aSGlenn Barry #include <sys/atomic.h>
52*02ac56e0SMatt Barden #include <sys/disp.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate extern bool_t __rpc_gss_make_principal(rpc_gss_principal_t *, gss_buffer_t);
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate #ifdef	DEBUG
57b97d6ca7SMilan Jurik extern void prom_printf(const char *, ...);
587c478bd9Sstevel@tonic-gate #endif
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #ifdef  _KERNEL
617c478bd9Sstevel@tonic-gate #define	memcmp(a, b, l) bcmp((a), (b), (l))
627c478bd9Sstevel@tonic-gate #endif
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate  * Sequence window definitions.
677c478bd9Sstevel@tonic-gate  */
687c478bd9Sstevel@tonic-gate #define	SEQ_ARR_SIZE	4
697c478bd9Sstevel@tonic-gate #define	SEQ_WIN		(SEQ_ARR_SIZE*32)
707c478bd9Sstevel@tonic-gate #define	SEQ_HI_BIT	0x80000000
717c478bd9Sstevel@tonic-gate #define	SEQ_LO_BIT	1
727c478bd9Sstevel@tonic-gate #define	DIV_BY_32	5
737c478bd9Sstevel@tonic-gate #define	SEQ_MASK	0x1f
747c478bd9Sstevel@tonic-gate #define	SEQ_MAX		((unsigned int)0x80000000)
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /* cache retransmit data */
787c478bd9Sstevel@tonic-gate typedef struct _retrans_entry {
797c478bd9Sstevel@tonic-gate 	uint32_t	xid;
807c478bd9Sstevel@tonic-gate 	rpc_gss_init_res result;
817c478bd9Sstevel@tonic-gate } retrans_entry;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * Server side RPCSEC_GSS context information.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate typedef struct _svc_rpc_gss_data {
877c478bd9Sstevel@tonic-gate 	struct _svc_rpc_gss_data	*next, *prev;
887c478bd9Sstevel@tonic-gate 	struct _svc_rpc_gss_data	*lru_next, *lru_prev;
897c478bd9Sstevel@tonic-gate 	bool_t				established;
907c478bd9Sstevel@tonic-gate 	gss_ctx_id_t			context;
917c478bd9Sstevel@tonic-gate 	gss_buffer_desc			client_name;
927c478bd9Sstevel@tonic-gate 	time_t				expiration;
937c478bd9Sstevel@tonic-gate 	uint_t				seq_num;
947c478bd9Sstevel@tonic-gate 	uint_t				seq_bits[SEQ_ARR_SIZE];
957c478bd9Sstevel@tonic-gate 	uint_t				key;
967c478bd9Sstevel@tonic-gate 	OM_uint32			qop;
977c478bd9Sstevel@tonic-gate 	bool_t				done_docallback;
987c478bd9Sstevel@tonic-gate 	bool_t				locked;
997c478bd9Sstevel@tonic-gate 	rpc_gss_rawcred_t		raw_cred;
1007c478bd9Sstevel@tonic-gate 	rpc_gss_ucred_t			u_cred;
1017c478bd9Sstevel@tonic-gate 	time_t				u_cred_set;
1027c478bd9Sstevel@tonic-gate 	void				*cookie;
1037c478bd9Sstevel@tonic-gate 	gss_cred_id_t			deleg;
1047c478bd9Sstevel@tonic-gate 	kmutex_t			clm;
1057c478bd9Sstevel@tonic-gate 	int				ref_cnt;
1067c478bd9Sstevel@tonic-gate 	time_t				last_ref_time;
1077c478bd9Sstevel@tonic-gate 	bool_t				stale;
1087c478bd9Sstevel@tonic-gate 	retrans_entry			*retrans_data;
1097c478bd9Sstevel@tonic-gate } svc_rpc_gss_data;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * Data structures used for LRU based context management.
1137c478bd9Sstevel@tonic-gate  */
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate #define	HASH(key) ((key) % svc_rpc_gss_hashmod)
1177c478bd9Sstevel@tonic-gate /* Size of hash table for svc_rpc_gss_data structures */
1187c478bd9Sstevel@tonic-gate #define	GSS_DATA_HASH_SIZE	1024
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * The following two defines specify a time delta that is used in
1227c478bd9Sstevel@tonic-gate  * sweep_clients. When the last_ref_time of a context is older than
1237c478bd9Sstevel@tonic-gate  * than the current time minus the delta, i.e, the context has not
1247c478bd9Sstevel@tonic-gate  * been referenced in the last delta seconds, we will return the
1257c478bd9Sstevel@tonic-gate  * context back to the cache if the ref_cnt is zero. The first delta
1267c478bd9Sstevel@tonic-gate  * value will be used when sweep_clients is called from
1277c478bd9Sstevel@tonic-gate  * svc_data_reclaim, the kmem_cache reclaim call back. We will reclaim
1287c478bd9Sstevel@tonic-gate  * all entries except those that are currently "active". By active we
1297c478bd9Sstevel@tonic-gate  * mean those that have been referenced in the last ACTIVE_DELTA
1307c478bd9Sstevel@tonic-gate  * seconds. If sweep_client is not being called from reclaim, then we
1317c478bd9Sstevel@tonic-gate  * will reclaim all entries that are "inactive". By inactive we mean
1327c478bd9Sstevel@tonic-gate  * those entries that have not been accessed in INACTIVE_DELTA
1337c478bd9Sstevel@tonic-gate  * seconds.  Note we always assume that ACTIVE_DELTA is less than
1347c478bd9Sstevel@tonic-gate  * INACTIVE_DELTA, so that reaping entries from a reclaim operation
1357c478bd9Sstevel@tonic-gate  * will necessarily imply reaping all "inactive" entries and then
1367c478bd9Sstevel@tonic-gate  * some.
1377c478bd9Sstevel@tonic-gate  */
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate /*
1407c478bd9Sstevel@tonic-gate  * If low on memory reap cache entries that have not been active for
1417c478bd9Sstevel@tonic-gate  * ACTIVE_DELTA seconds and have a ref_cnt equal to zero.
1427c478bd9Sstevel@tonic-gate  */
1437c478bd9Sstevel@tonic-gate #define	ACTIVE_DELTA		30*60		/* 30 minutes */
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * If in sweeping contexts we find contexts with a ref_cnt equal to zero
1477c478bd9Sstevel@tonic-gate  * and the context has not been referenced in INACTIVE_DELTA seconds, return
1487c478bd9Sstevel@tonic-gate  * the entry to the cache.
1497c478bd9Sstevel@tonic-gate  */
1507c478bd9Sstevel@tonic-gate #define	INACTIVE_DELTA		8*60*60		/* 8 hours */
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate int				svc_rpc_gss_hashmod = GSS_DATA_HASH_SIZE;
1537c478bd9Sstevel@tonic-gate static svc_rpc_gss_data		**clients;
1547c478bd9Sstevel@tonic-gate static svc_rpc_gss_data		*lru_first, *lru_last;
1557c478bd9Sstevel@tonic-gate static time_t			sweep_interval = 60*60;
1567c478bd9Sstevel@tonic-gate static time_t			last_swept = 0;
1577c478bd9Sstevel@tonic-gate static int			num_gss_contexts = 0;
1587c478bd9Sstevel@tonic-gate static time_t			svc_rpcgss_gid_timeout = 60*60*12;
1597c478bd9Sstevel@tonic-gate static kmem_cache_t		*svc_data_handle;
1607c478bd9Sstevel@tonic-gate static time_t			svc_rpc_gss_active_delta = ACTIVE_DELTA;
1617c478bd9Sstevel@tonic-gate static time_t			svc_rpc_gss_inactive_delta = INACTIVE_DELTA;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate  * lock used with context/lru variables
1657c478bd9Sstevel@tonic-gate  */
1667c478bd9Sstevel@tonic-gate static kmutex_t			ctx_mutex;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate  * Data structure to contain cache statistics
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate static struct {
1737c478bd9Sstevel@tonic-gate 	int64_t total_entries_allocated;
1747c478bd9Sstevel@tonic-gate 	int64_t no_reclaims;
1757c478bd9Sstevel@tonic-gate 	int64_t no_returned_by_reclaim;
1767c478bd9Sstevel@tonic-gate } svc_rpc_gss_cache_stats;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate /*
1807c478bd9Sstevel@tonic-gate  * lock used with server credential variables list
1817c478bd9Sstevel@tonic-gate  *
1827c478bd9Sstevel@tonic-gate  * server cred list locking guidelines:
1837c478bd9Sstevel@tonic-gate  * - Writer's lock holder has exclusive access to the list
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate static krwlock_t		cred_lock;
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * server callback list
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate typedef struct rpc_gss_cblist_s {
1917c478bd9Sstevel@tonic-gate 	struct rpc_gss_cblist_s		*next;
1927c478bd9Sstevel@tonic-gate 	rpc_gss_callback_t	cb;
1937c478bd9Sstevel@tonic-gate } rpc_gss_cblist_t;
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate static rpc_gss_cblist_t			*rpc_gss_cblist = NULL;
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate /*
1987c478bd9Sstevel@tonic-gate  * lock used with callback variables
1997c478bd9Sstevel@tonic-gate  */
2007c478bd9Sstevel@tonic-gate static kmutex_t			cb_mutex;
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate  * forward declarations
2047c478bd9Sstevel@tonic-gate  */
2057c478bd9Sstevel@tonic-gate static bool_t			svc_rpc_gss_wrap();
2067c478bd9Sstevel@tonic-gate static bool_t			svc_rpc_gss_unwrap();
2077c478bd9Sstevel@tonic-gate static svc_rpc_gss_data		*create_client();
2087c478bd9Sstevel@tonic-gate static svc_rpc_gss_data		*get_client();
2097c478bd9Sstevel@tonic-gate static svc_rpc_gss_data		*find_client();
2107c478bd9Sstevel@tonic-gate static void			destroy_client();
2117c478bd9Sstevel@tonic-gate static void			sweep_clients(bool_t);
2127c478bd9Sstevel@tonic-gate static void			insert_client();
2137c478bd9Sstevel@tonic-gate static bool_t			check_verf(struct rpc_msg *, gss_ctx_id_t,
2147c478bd9Sstevel@tonic-gate 					int *, uid_t);
2157c478bd9Sstevel@tonic-gate static bool_t			set_response_verf();
2167c478bd9Sstevel@tonic-gate static void			retrans_add(svc_rpc_gss_data *, uint32_t,
2177c478bd9Sstevel@tonic-gate 					rpc_gss_init_res *);
2187c478bd9Sstevel@tonic-gate static void			retrans_del(svc_rpc_gss_data *);
2197c478bd9Sstevel@tonic-gate static bool_t			transfer_sec_context(svc_rpc_gss_data *);
2207c478bd9Sstevel@tonic-gate static void			common_client_data_free(svc_rpc_gss_data *);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate /*
2237c478bd9Sstevel@tonic-gate  * server side wrap/unwrap routines
2247c478bd9Sstevel@tonic-gate  */
2257c478bd9Sstevel@tonic-gate struct svc_auth_ops svc_rpc_gss_ops = {
2267c478bd9Sstevel@tonic-gate 	svc_rpc_gss_wrap,
2277c478bd9Sstevel@tonic-gate 	svc_rpc_gss_unwrap,
2287c478bd9Sstevel@tonic-gate };
2297c478bd9Sstevel@tonic-gate 
230bfd8310aSGlenn Barry /* taskq(9F) */
231bfd8310aSGlenn Barry typedef struct svcrpcsec_gss_taskq_arg {
232bfd8310aSGlenn Barry 	SVCXPRT			*rq_xprt;
233bfd8310aSGlenn Barry 	rpc_gss_init_arg	*rpc_call_arg;
234bfd8310aSGlenn Barry 	struct rpc_msg		*msg;
235bfd8310aSGlenn Barry 	svc_rpc_gss_data	*client_data;
236bfd8310aSGlenn Barry 	uint_t			cr_version;
237bfd8310aSGlenn Barry 	rpc_gss_service_t	cr_service;
238bfd8310aSGlenn Barry } svcrpcsec_gss_taskq_arg_t;
239bfd8310aSGlenn Barry 
240bfd8310aSGlenn Barry /* gssd is single threaded, so 1 thread for the taskq is probably good/ok */
241bfd8310aSGlenn Barry int rpcsec_gss_init_taskq_nthreads = 1;
242bfd8310aSGlenn Barry 
243bfd8310aSGlenn Barry extern struct rpc_msg *rpc_msg_dup(struct rpc_msg *);
244bfd8310aSGlenn Barry extern void rpc_msg_free(struct rpc_msg **, int);
245bfd8310aSGlenn Barry 
246bfd8310aSGlenn Barry /*
247bfd8310aSGlenn Barry  * from svc_clts.c:
248bfd8310aSGlenn Barry  * Transport private data.
249bfd8310aSGlenn Barry  * Kept in xprt->xp_p2buf.
250bfd8310aSGlenn Barry  */
251bfd8310aSGlenn Barry struct udp_data {
252bfd8310aSGlenn Barry 	mblk_t	*ud_resp;			/* buffer for response */
253bfd8310aSGlenn Barry 	mblk_t	*ud_inmp;			/* mblk chain of request */
254bfd8310aSGlenn Barry };
2557c478bd9Sstevel@tonic-gate 
256*02ac56e0SMatt Barden static zone_key_t svc_gss_zone_key;
257*02ac56e0SMatt Barden static uint_t svc_gss_tsd_key;
258*02ac56e0SMatt Barden 
259*02ac56e0SMatt Barden typedef struct svc_gss_zsd {
260*02ac56e0SMatt Barden 	zoneid_t sgz_zoneid;
261*02ac56e0SMatt Barden 	kmutex_t sgz_lock;
262*02ac56e0SMatt Barden 	taskq_t *sgz_init_taskq;
263*02ac56e0SMatt Barden } svc_gss_zsd_t;
264*02ac56e0SMatt Barden 
265*02ac56e0SMatt Barden static taskq_t *
svc_gss_create_taskq(zone_t * zone)266*02ac56e0SMatt Barden svc_gss_create_taskq(zone_t *zone)
267*02ac56e0SMatt Barden {
268*02ac56e0SMatt Barden 	taskq_t *tq;
269*02ac56e0SMatt Barden 
270*02ac56e0SMatt Barden 	if (zone == NULL) {
271*02ac56e0SMatt Barden 		cmn_err(CE_NOTE, "%s: couldn't find zone", __func__);
272*02ac56e0SMatt Barden 		return (NULL);
273*02ac56e0SMatt Barden 	}
274*02ac56e0SMatt Barden 
275*02ac56e0SMatt Barden 	/* Like ddi_taskq_create(), but for zones, just for now */
276*02ac56e0SMatt Barden 	tq = taskq_create_proc("rpcsec_gss_init_taskq",
277*02ac56e0SMatt Barden 	    rpcsec_gss_init_taskq_nthreads, minclsyspri,
278*02ac56e0SMatt Barden 	    rpcsec_gss_init_taskq_nthreads, INT_MAX, zone->zone_zsched,
279*02ac56e0SMatt Barden 	    TASKQ_PREPOPULATE);
280*02ac56e0SMatt Barden 
281*02ac56e0SMatt Barden 	if (tq == NULL)
282*02ac56e0SMatt Barden 		cmn_err(CE_NOTE, "%s: taskq_create_proc failed", __func__);
283*02ac56e0SMatt Barden 
284*02ac56e0SMatt Barden 	return (tq);
285*02ac56e0SMatt Barden }
286*02ac56e0SMatt Barden 
287*02ac56e0SMatt Barden static void *
svc_gss_zone_init(zoneid_t zoneid)288*02ac56e0SMatt Barden svc_gss_zone_init(zoneid_t zoneid)
289*02ac56e0SMatt Barden {
290*02ac56e0SMatt Barden 	svc_gss_zsd_t *zsd;
291*02ac56e0SMatt Barden 	zone_t *zone = curzone;
292*02ac56e0SMatt Barden 
293*02ac56e0SMatt Barden 	zsd = kmem_alloc(sizeof (*zsd), KM_SLEEP);
294*02ac56e0SMatt Barden 	mutex_init(&zsd->sgz_lock, NULL, MUTEX_DEFAULT, NULL);
295*02ac56e0SMatt Barden 	zsd->sgz_zoneid = zoneid;
296*02ac56e0SMatt Barden 
297*02ac56e0SMatt Barden 	if (zone->zone_id != zoneid)
298*02ac56e0SMatt Barden 		zone = zone_find_by_id_nolock(zoneid);
299*02ac56e0SMatt Barden 
300*02ac56e0SMatt Barden 	zsd->sgz_init_taskq = svc_gss_create_taskq(zone);
301*02ac56e0SMatt Barden 	return (zsd);
302*02ac56e0SMatt Barden }
303*02ac56e0SMatt Barden 
304*02ac56e0SMatt Barden /*
305*02ac56e0SMatt Barden  * taskq_destroy() wakes all taskq threads and tells them to exit.
306*02ac56e0SMatt Barden  * It then cv_wait()'s for all of them to finish exiting.
307*02ac56e0SMatt Barden  * cv_wait() calls resume(), which accesses the target's process.
308*02ac56e0SMatt Barden  * That may be one of our taskq threads, which are attached to zone_zsched.
309*02ac56e0SMatt Barden  *
310*02ac56e0SMatt Barden  * If we do taskq_destroy() in the zsd_destroy callback, then zone_zsched
311*02ac56e0SMatt Barden  * will have exited and been destroyed before it runs, and we can panic
312*02ac56e0SMatt Barden  * in resume(). Our taskq threads are not accounted for in either
313*02ac56e0SMatt Barden  * zone_ntasks or zone_kthreads, which means zsched does not wait for
314*02ac56e0SMatt Barden  * taskq threads attached to it to complete before exiting.
315*02ac56e0SMatt Barden  *
316*02ac56e0SMatt Barden  * We therefore need to do this at shutdown time. At the point where
317*02ac56e0SMatt Barden  * the zsd_shutdown callback is invoked, all other zone tasks (processes)
318*02ac56e0SMatt Barden  * have exited, but zone_kthreads and other taskqs hanging off zsched have not.
319*02ac56e0SMatt Barden  *
320*02ac56e0SMatt Barden  * We need to be careful not to allow RPC services to be ran from
321*02ac56e0SMatt Barden  * zsched-attached taskqs or zone_kthreads.
322*02ac56e0SMatt Barden  */
323*02ac56e0SMatt Barden static void
svc_gss_zone_shutdown(zoneid_t zoneid,void * arg)324*02ac56e0SMatt Barden svc_gss_zone_shutdown(zoneid_t zoneid, void *arg)
325*02ac56e0SMatt Barden {
326*02ac56e0SMatt Barden 	svc_gss_zsd_t *zsd = arg;
327*02ac56e0SMatt Barden 
328*02ac56e0SMatt Barden 	/* All non-zsched-hung threads should be finished. */
329*02ac56e0SMatt Barden 	mutex_enter(&zsd->sgz_lock);
330*02ac56e0SMatt Barden 	if (zsd->sgz_init_taskq != NULL) {
331*02ac56e0SMatt Barden 		taskq_destroy(zsd->sgz_init_taskq);
332*02ac56e0SMatt Barden 		zsd->sgz_init_taskq = NULL;
333*02ac56e0SMatt Barden 	}
334*02ac56e0SMatt Barden 	mutex_exit(&zsd->sgz_lock);
335*02ac56e0SMatt Barden }
336*02ac56e0SMatt Barden 
337*02ac56e0SMatt Barden static void
svc_gss_zone_fini(zoneid_t zoneid,void * arg)338*02ac56e0SMatt Barden svc_gss_zone_fini(zoneid_t zoneid, void *arg)
339*02ac56e0SMatt Barden {
340*02ac56e0SMatt Barden 	svc_gss_zsd_t *zsd = arg;
341*02ac56e0SMatt Barden 
342*02ac56e0SMatt Barden 	mutex_destroy(&zsd->sgz_lock);
343*02ac56e0SMatt Barden 	kmem_free(zsd, sizeof (*zsd));
344*02ac56e0SMatt Barden }
345*02ac56e0SMatt Barden 
346*02ac56e0SMatt Barden static svc_gss_zsd_t *
svc_gss_get_zsd(void)347*02ac56e0SMatt Barden svc_gss_get_zsd(void)
348*02ac56e0SMatt Barden {
349*02ac56e0SMatt Barden 	svc_gss_zsd_t *zsd;
350*02ac56e0SMatt Barden 
351*02ac56e0SMatt Barden 	zsd = tsd_get(svc_gss_tsd_key);
352*02ac56e0SMatt Barden 	if (zsd == NULL) {
353*02ac56e0SMatt Barden 		zsd = zone_getspecific(svc_gss_zone_key, curzone);
354*02ac56e0SMatt Barden 		(void) tsd_set(svc_gss_tsd_key, zsd);
355*02ac56e0SMatt Barden 	}
356*02ac56e0SMatt Barden 
357*02ac56e0SMatt Barden 	return (zsd);
358*02ac56e0SMatt Barden }
359*02ac56e0SMatt Barden 
3607c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3617c478bd9Sstevel@tonic-gate static int
svc_gss_data_create(void * buf,void * pdata,int kmflag)3627c478bd9Sstevel@tonic-gate svc_gss_data_create(void *buf, void *pdata, int kmflag)
3637c478bd9Sstevel@tonic-gate {
3647c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	mutex_init(&client_data->clm, NULL, MUTEX_DEFAULT, NULL);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	return (0);
3697c478bd9Sstevel@tonic-gate }
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3727c478bd9Sstevel@tonic-gate static void
svc_gss_data_destroy(void * buf,void * pdata)3737c478bd9Sstevel@tonic-gate svc_gss_data_destroy(void *buf, void *pdata)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	mutex_destroy(&client_data->clm);
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3827c478bd9Sstevel@tonic-gate static void
svc_gss_data_reclaim(void * pdata)3837c478bd9Sstevel@tonic-gate svc_gss_data_reclaim(void *pdata)
3847c478bd9Sstevel@tonic-gate {
3857c478bd9Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	svc_rpc_gss_cache_stats.no_reclaims++;
3887c478bd9Sstevel@tonic-gate 	sweep_clients(TRUE);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate /*
3947c478bd9Sstevel@tonic-gate  *  Init stuff on the server side.
3957c478bd9Sstevel@tonic-gate  */
3967c478bd9Sstevel@tonic-gate void
svc_gss_init()3977c478bd9Sstevel@tonic-gate svc_gss_init()
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate 	mutex_init(&cb_mutex, NULL, MUTEX_DEFAULT, NULL);
4007c478bd9Sstevel@tonic-gate 	mutex_init(&ctx_mutex, NULL, MUTEX_DEFAULT, NULL);
4017c478bd9Sstevel@tonic-gate 	rw_init(&cred_lock, NULL, RW_DEFAULT, NULL);
4027c478bd9Sstevel@tonic-gate 	clients = (svc_rpc_gss_data **)
403bfd8310aSGlenn Barry 	    kmem_zalloc(svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *),
404bfd8310aSGlenn Barry 	    KM_SLEEP);
4057c478bd9Sstevel@tonic-gate 	svc_data_handle = kmem_cache_create("rpc_gss_data_cache",
406bfd8310aSGlenn Barry 	    sizeof (svc_rpc_gss_data), 0,
407bfd8310aSGlenn Barry 	    svc_gss_data_create,
408bfd8310aSGlenn Barry 	    svc_gss_data_destroy,
409bfd8310aSGlenn Barry 	    svc_gss_data_reclaim,
410bfd8310aSGlenn Barry 	    NULL, NULL, 0);
411bfd8310aSGlenn Barry 
412*02ac56e0SMatt Barden 	tsd_create(&svc_gss_tsd_key, NULL);
413*02ac56e0SMatt Barden 	zone_key_create(&svc_gss_zone_key, svc_gss_zone_init,
414*02ac56e0SMatt Barden 	    svc_gss_zone_shutdown, svc_gss_zone_fini);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * Destroy structures allocated in svc_gss_init().
4197c478bd9Sstevel@tonic-gate  * This routine is called by _init() if mod_install() failed.
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate void
svc_gss_fini()4227c478bd9Sstevel@tonic-gate svc_gss_fini()
4237c478bd9Sstevel@tonic-gate {
424*02ac56e0SMatt Barden 	if (zone_key_delete(svc_gss_zone_key) != 0)
425*02ac56e0SMatt Barden 		cmn_err(CE_WARN, "%s: failed to delete zone key", __func__);
426*02ac56e0SMatt Barden 	tsd_destroy(&svc_gss_tsd_key);
4277c478bd9Sstevel@tonic-gate 	mutex_destroy(&cb_mutex);
4287c478bd9Sstevel@tonic-gate 	mutex_destroy(&ctx_mutex);
4297c478bd9Sstevel@tonic-gate 	rw_destroy(&cred_lock);
4307c478bd9Sstevel@tonic-gate 	kmem_free(clients, svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *));
4317c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(svc_data_handle);
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * Cleanup routine for destroying context, called after service
4367c478bd9Sstevel@tonic-gate  * procedure is executed. Actually we just decrement the reference count
4377c478bd9Sstevel@tonic-gate  * associated with this context. If the reference count is zero and the
4387c478bd9Sstevel@tonic-gate  * context is marked as stale, we would then destroy the context. Additionally,
4397c478bd9Sstevel@tonic-gate  * we check if its been longer than sweep_interval since the last sweep_clients
4407c478bd9Sstevel@tonic-gate  * was run, and if so run sweep_clients to free all stale contexts with zero
4417c478bd9Sstevel@tonic-gate  * reference counts or contexts that are old. (Haven't been access in
4427c478bd9Sstevel@tonic-gate  * svc_rpc_inactive_delta seconds).
4437c478bd9Sstevel@tonic-gate  */
4447c478bd9Sstevel@tonic-gate void
rpc_gss_cleanup(SVCXPRT * clone_xprt)4457c478bd9Sstevel@tonic-gate rpc_gss_cleanup(SVCXPRT *clone_xprt)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
4487c478bd9Sstevel@tonic-gate 	SVCAUTH			*svcauth;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	/*
4517c478bd9Sstevel@tonic-gate 	 * First check if current context needs to be cleaned up.
4527c478bd9Sstevel@tonic-gate 	 * There might be other threads stale this client data
4537c478bd9Sstevel@tonic-gate 	 * in between.
4547c478bd9Sstevel@tonic-gate 	 */
4557c478bd9Sstevel@tonic-gate 	svcauth = &clone_xprt->xp_auth;
4567c478bd9Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
4577c478bd9Sstevel@tonic-gate 	if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
4587c478bd9Sstevel@tonic-gate 		mutex_enter(&cl->clm);
4597c478bd9Sstevel@tonic-gate 		ASSERT(cl->ref_cnt > 0);
4607c478bd9Sstevel@tonic-gate 		if (--cl->ref_cnt == 0 && cl->stale) {
4617c478bd9Sstevel@tonic-gate 			mutex_exit(&cl->clm);
4627c478bd9Sstevel@tonic-gate 			destroy_client(cl);
4637c478bd9Sstevel@tonic-gate 			svcauth->svc_ah_private = NULL;
4647c478bd9Sstevel@tonic-gate 		} else
4657c478bd9Sstevel@tonic-gate 			mutex_exit(&cl->clm);
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	/*
4697c478bd9Sstevel@tonic-gate 	 * Check for other expired contexts.
4707c478bd9Sstevel@tonic-gate 	 */
4717c478bd9Sstevel@tonic-gate 	if ((gethrestime_sec() - last_swept) > sweep_interval)
4727c478bd9Sstevel@tonic-gate 		sweep_clients(FALSE);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
4757c478bd9Sstevel@tonic-gate }
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate /*
4787c478bd9Sstevel@tonic-gate  * Shift the array arr of length arrlen right by nbits bits.
4797c478bd9Sstevel@tonic-gate  */
4807c478bd9Sstevel@tonic-gate static void
shift_bits(uint_t * arr,int arrlen,int nbits)4814a3b0527SAndy Fiddaman shift_bits(uint_t *arr, int arrlen, int nbits)
4827c478bd9Sstevel@tonic-gate {
4837c478bd9Sstevel@tonic-gate 	int	i, j;
4847c478bd9Sstevel@tonic-gate 	uint_t	lo, hi;
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 	/*
4877c478bd9Sstevel@tonic-gate 	 * If the number of bits to be shifted exceeds SEQ_WIN, just
4887c478bd9Sstevel@tonic-gate 	 * zero out the array.
4897c478bd9Sstevel@tonic-gate 	 */
4907c478bd9Sstevel@tonic-gate 	if (nbits < SEQ_WIN) {
4917c478bd9Sstevel@tonic-gate 		for (i = 0; i < nbits; i++) {
4927c478bd9Sstevel@tonic-gate 			hi = 0;
4937c478bd9Sstevel@tonic-gate 			for (j = 0; j < arrlen; j++) {
4947c478bd9Sstevel@tonic-gate 				lo = arr[j] & SEQ_LO_BIT;
4957c478bd9Sstevel@tonic-gate 				arr[j] >>= 1;
4967c478bd9Sstevel@tonic-gate 				if (hi)
4977c478bd9Sstevel@tonic-gate 					arr[j] |= SEQ_HI_BIT;
4987c478bd9Sstevel@tonic-gate 				hi = lo;
4997c478bd9Sstevel@tonic-gate 			}
5007c478bd9Sstevel@tonic-gate 		}
5017c478bd9Sstevel@tonic-gate 	} else {
5027c478bd9Sstevel@tonic-gate 		for (j = 0; j < arrlen; j++)
5037c478bd9Sstevel@tonic-gate 			arr[j] = 0;
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate /*
5087c478bd9Sstevel@tonic-gate  * Check that the received sequence number seq_num is valid.
5097c478bd9Sstevel@tonic-gate  */
5107c478bd9Sstevel@tonic-gate static bool_t
check_seq(svc_rpc_gss_data * cl,uint_t seq_num,bool_t * kill_context)5114a3b0527SAndy Fiddaman check_seq(svc_rpc_gss_data *cl, uint_t seq_num, bool_t *kill_context)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	int			i, j;
5147c478bd9Sstevel@tonic-gate 	uint_t			bit;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/*
5177c478bd9Sstevel@tonic-gate 	 * If it exceeds the maximum, kill context.
5187c478bd9Sstevel@tonic-gate 	 */
5197c478bd9Sstevel@tonic-gate 	if (seq_num >= SEQ_MAX) {
5207c478bd9Sstevel@tonic-gate 		*kill_context = TRUE;
5217c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: seq_num not valid\n");
5227c478bd9Sstevel@tonic-gate 		return (FALSE);
5237c478bd9Sstevel@tonic-gate 	}
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	/*
5267c478bd9Sstevel@tonic-gate 	 * If greater than the last seen sequence number, just shift
5277c478bd9Sstevel@tonic-gate 	 * the sequence window so that it starts at the new sequence
5287c478bd9Sstevel@tonic-gate 	 * number and extends downwards by SEQ_WIN.
5297c478bd9Sstevel@tonic-gate 	 */
5307c478bd9Sstevel@tonic-gate 	if (seq_num > cl->seq_num) {
5317c478bd9Sstevel@tonic-gate 		(void) shift_bits(cl->seq_bits, SEQ_ARR_SIZE,
5324a3b0527SAndy Fiddaman 		    (int)(seq_num - cl->seq_num));
5337c478bd9Sstevel@tonic-gate 		cl->seq_bits[0] |= SEQ_HI_BIT;
5347c478bd9Sstevel@tonic-gate 		cl->seq_num = seq_num;
5357c478bd9Sstevel@tonic-gate 		return (TRUE);
5367c478bd9Sstevel@tonic-gate 	}
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	/*
5397c478bd9Sstevel@tonic-gate 	 * If it is outside the sequence window, return failure.
5407c478bd9Sstevel@tonic-gate 	 */
5417c478bd9Sstevel@tonic-gate 	i = cl->seq_num - seq_num;
5427c478bd9Sstevel@tonic-gate 	if (i >= SEQ_WIN) {
5437c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: seq_num is outside the window\n");
5447c478bd9Sstevel@tonic-gate 		return (FALSE);
5457c478bd9Sstevel@tonic-gate 	}
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	/*
5487c478bd9Sstevel@tonic-gate 	 * If within sequence window, set the bit corresponding to it
5497c478bd9Sstevel@tonic-gate 	 * if not already seen;  if already seen, return failure.
5507c478bd9Sstevel@tonic-gate 	 */
5517c478bd9Sstevel@tonic-gate 	j = SEQ_MASK - (i & SEQ_MASK);
5527c478bd9Sstevel@tonic-gate 	bit = j > 0 ? (1 << j) : 1;
5537c478bd9Sstevel@tonic-gate 	i >>= DIV_BY_32;
5547c478bd9Sstevel@tonic-gate 	if (cl->seq_bits[i] & bit) {
5557c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: sequence number already seen\n");
5567c478bd9Sstevel@tonic-gate 		return (FALSE);
5577c478bd9Sstevel@tonic-gate 	}
5587c478bd9Sstevel@tonic-gate 	cl->seq_bits[i] |= bit;
5597c478bd9Sstevel@tonic-gate 	return (TRUE);
5607c478bd9Sstevel@tonic-gate }
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate /*
5637c478bd9Sstevel@tonic-gate  * Set server callback.
5647c478bd9Sstevel@tonic-gate  */
5657c478bd9Sstevel@tonic-gate bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)5664a3b0527SAndy Fiddaman rpc_gss_set_callback(rpc_gss_callback_t *cb)
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	rpc_gss_cblist_t		*cbl, *tmp;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	if (cb->callback == NULL) {
5717c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(1, "rpc_gss_set_callback: no callback to set\n");
5727c478bd9Sstevel@tonic-gate 		return (FALSE);
5737c478bd9Sstevel@tonic-gate 	}
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	/* check if there is already an entry in the rpc_gss_cblist. */
5767c478bd9Sstevel@tonic-gate 	mutex_enter(&cb_mutex);
5777c478bd9Sstevel@tonic-gate 	if (rpc_gss_cblist) {
5787c478bd9Sstevel@tonic-gate 		for (tmp = rpc_gss_cblist; tmp != NULL; tmp = tmp->next) {
5797c478bd9Sstevel@tonic-gate 			if ((tmp->cb.callback == cb->callback) &&
5807c478bd9Sstevel@tonic-gate 			    (tmp->cb.version == cb->version) &&
5817c478bd9Sstevel@tonic-gate 			    (tmp->cb.program == cb->program)) {
5827c478bd9Sstevel@tonic-gate 				mutex_exit(&cb_mutex);
5837c478bd9Sstevel@tonic-gate 				return (TRUE);
5847c478bd9Sstevel@tonic-gate 			}
5857c478bd9Sstevel@tonic-gate 		}
5867c478bd9Sstevel@tonic-gate 	}
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/* Not in rpc_gss_cblist.  Create a new entry. */
5897c478bd9Sstevel@tonic-gate 	if ((cbl = (rpc_gss_cblist_t *)kmem_alloc(sizeof (*cbl), KM_SLEEP))
5907c478bd9Sstevel@tonic-gate 	    == NULL) {
5917c478bd9Sstevel@tonic-gate 		mutex_exit(&cb_mutex);
5927c478bd9Sstevel@tonic-gate 		return (FALSE);
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 	cbl->cb = *cb;
5957c478bd9Sstevel@tonic-gate 	cbl->next = rpc_gss_cblist;
5967c478bd9Sstevel@tonic-gate 	rpc_gss_cblist = cbl;
5977c478bd9Sstevel@tonic-gate 	mutex_exit(&cb_mutex);
5987c478bd9Sstevel@tonic-gate 	return (TRUE);
5997c478bd9Sstevel@tonic-gate }
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate  * Locate callback (if specified) and call server.  Release any
6037c478bd9Sstevel@tonic-gate  * delegated credentials unless passed to server and the server
6047c478bd9Sstevel@tonic-gate  * accepts the context.  If a callback is not specified, accept
6057c478bd9Sstevel@tonic-gate  * the incoming context.
6067c478bd9Sstevel@tonic-gate  */
6077c478bd9Sstevel@tonic-gate static bool_t
do_callback(struct svc_req * req,svc_rpc_gss_data * client_data)6084a3b0527SAndy Fiddaman do_callback(struct svc_req *req, svc_rpc_gss_data *client_data)
6097c478bd9Sstevel@tonic-gate {
6107c478bd9Sstevel@tonic-gate 	rpc_gss_cblist_t		*cbl;
6117c478bd9Sstevel@tonic-gate 	bool_t			ret = TRUE, found = FALSE;
6127c478bd9Sstevel@tonic-gate 	rpc_gss_lock_t		lock;
6137c478bd9Sstevel@tonic-gate 	OM_uint32		minor;
6147c478bd9Sstevel@tonic-gate 	mutex_enter(&cb_mutex);
6157c478bd9Sstevel@tonic-gate 	for (cbl = rpc_gss_cblist; cbl != NULL; cbl = cbl->next) {
6167c478bd9Sstevel@tonic-gate 		if (req->rq_prog != cbl->cb.program ||
6174a3b0527SAndy Fiddaman 		    req->rq_vers != cbl->cb.version)
6187c478bd9Sstevel@tonic-gate 			continue;
6197c478bd9Sstevel@tonic-gate 		found = TRUE;
6207c478bd9Sstevel@tonic-gate 		lock.locked = FALSE;
6217c478bd9Sstevel@tonic-gate 		lock.raw_cred = &client_data->raw_cred;
6227c478bd9Sstevel@tonic-gate 		ret = (*cbl->cb.callback)(req, client_data->deleg,
6234a3b0527SAndy Fiddaman 		    client_data->context, &lock, &client_data->cookie);
6247c478bd9Sstevel@tonic-gate 		req->rq_xprt->xp_cookie = client_data->cookie;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 		if (ret) {
6277c478bd9Sstevel@tonic-gate 			client_data->locked = lock.locked;
6287c478bd9Sstevel@tonic-gate 			client_data->deleg = GSS_C_NO_CREDENTIAL;
6297c478bd9Sstevel@tonic-gate 		}
6307c478bd9Sstevel@tonic-gate 		break;
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 	if (!found) {
6337c478bd9Sstevel@tonic-gate 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
6347c478bd9Sstevel@tonic-gate 			(void) kgss_release_cred(&minor, &client_data->deleg,
6354a3b0527SAndy Fiddaman 			    crgetuid(CRED()));
6367c478bd9Sstevel@tonic-gate 			client_data->deleg = GSS_C_NO_CREDENTIAL;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 	mutex_exit(&cb_mutex);
6407c478bd9Sstevel@tonic-gate 	return (ret);
6417c478bd9Sstevel@tonic-gate }
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate /*
6447c478bd9Sstevel@tonic-gate  * Get caller credentials.
6457c478bd9Sstevel@tonic-gate  */
6467c478bd9Sstevel@tonic-gate bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)6474a3b0527SAndy Fiddaman rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
6484a3b0527SAndy Fiddaman     rpc_gss_ucred_t **ucred, void **cookie)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate 	SVCAUTH			*svcauth;
6517c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
6527c478bd9Sstevel@tonic-gate 	int			gssstat, gidlen;
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	svcauth = &req->rq_xprt->xp_auth;
6557c478bd9Sstevel@tonic-gate 	client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	mutex_enter(&client_data->clm);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	if (rcred != NULL) {
6607c478bd9Sstevel@tonic-gate 		svcauth->raw_cred = client_data->raw_cred;
6617c478bd9Sstevel@tonic-gate 		*rcred = &svcauth->raw_cred;
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 	if (ucred != NULL) {
6647c478bd9Sstevel@tonic-gate 		*ucred = &client_data->u_cred;
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 		if (client_data->u_cred_set == 0 ||
6677c478bd9Sstevel@tonic-gate 		    client_data->u_cred_set < gethrestime_sec()) {
6684a3b0527SAndy Fiddaman 			if (client_data->u_cred_set == 0) {
6694a3b0527SAndy Fiddaman 				if ((gssstat = kgsscred_expname_to_unix_cred(
6704a3b0527SAndy Fiddaman 				    &client_data->client_name,
6714a3b0527SAndy Fiddaman 				    &client_data->u_cred.uid,
6724a3b0527SAndy Fiddaman 				    &client_data->u_cred.gid,
6734a3b0527SAndy Fiddaman 				    &client_data->u_cred.gidlist,
6744a3b0527SAndy Fiddaman 				    &gidlen, crgetuid(CRED())))
6754a3b0527SAndy Fiddaman 				    != GSS_S_COMPLETE) {
6764a3b0527SAndy Fiddaman 					RPCGSS_LOG(1, "rpc_gss_getcred: "
6774a3b0527SAndy Fiddaman 					    "kgsscred_expname_to_unix_cred "
6784a3b0527SAndy Fiddaman 					    "failed %x\n", gssstat);
6794a3b0527SAndy Fiddaman 					*ucred = NULL;
6804a3b0527SAndy Fiddaman 				} else {
6814a3b0527SAndy Fiddaman 					client_data->u_cred.gidlen =
6824a3b0527SAndy Fiddaman 					    (short)gidlen;
6834a3b0527SAndy Fiddaman 					client_data->u_cred_set =
6844a3b0527SAndy Fiddaman 					    gethrestime_sec() +
6854a3b0527SAndy Fiddaman 					    svc_rpcgss_gid_timeout;
6864a3b0527SAndy Fiddaman 				}
6874a3b0527SAndy Fiddaman 			} else if (client_data->u_cred_set
6884a3b0527SAndy Fiddaman 			    < gethrestime_sec()) {
6894a3b0527SAndy Fiddaman 				if ((gssstat = kgss_get_group_info(
6904a3b0527SAndy Fiddaman 				    client_data->u_cred.uid,
6914a3b0527SAndy Fiddaman 				    &client_data->u_cred.gid,
6924a3b0527SAndy Fiddaman 				    &client_data->u_cred.gidlist,
6934a3b0527SAndy Fiddaman 				    &gidlen, crgetuid(CRED())))
6944a3b0527SAndy Fiddaman 				    != GSS_S_COMPLETE) {
6954a3b0527SAndy Fiddaman 					RPCGSS_LOG(1, "rpc_gss_getcred: "
6964a3b0527SAndy Fiddaman 					    "kgss_get_group_info failed %x\n",
6974a3b0527SAndy Fiddaman 					    gssstat);
6984a3b0527SAndy Fiddaman 					*ucred = NULL;
6994a3b0527SAndy Fiddaman 				} else {
7004a3b0527SAndy Fiddaman 					client_data->u_cred.gidlen =
7014a3b0527SAndy Fiddaman 					    (short)gidlen;
7024a3b0527SAndy Fiddaman 					client_data->u_cred_set =
7034a3b0527SAndy Fiddaman 					    gethrestime_sec() +
7044a3b0527SAndy Fiddaman 					    svc_rpcgss_gid_timeout;
7054a3b0527SAndy Fiddaman 				}
7067c478bd9Sstevel@tonic-gate 			}
7077c478bd9Sstevel@tonic-gate 		}
7087c478bd9Sstevel@tonic-gate 	}
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	if (cookie != NULL)
7117c478bd9Sstevel@tonic-gate 		*cookie = client_data->cookie;
7127c478bd9Sstevel@tonic-gate 	req->rq_xprt->xp_cookie = client_data->cookie;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	return (TRUE);
7177c478bd9Sstevel@tonic-gate }
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate /*
7207c478bd9Sstevel@tonic-gate  * Transfer the context data from the user land to the kernel.
7217c478bd9Sstevel@tonic-gate  */
transfer_sec_context(svc_rpc_gss_data * client_data)7227c478bd9Sstevel@tonic-gate bool_t transfer_sec_context(svc_rpc_gss_data *client_data) {
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	gss_buffer_desc process_token;
7257c478bd9Sstevel@tonic-gate 	OM_uint32 gssstat, minor;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/*
7287c478bd9Sstevel@tonic-gate 	 * Call kgss_export_sec_context
7297c478bd9Sstevel@tonic-gate 	 * if an error is returned log a message
7307c478bd9Sstevel@tonic-gate 	 * go to error handling
7317c478bd9Sstevel@tonic-gate 	 * Otherwise call kgss_import_sec_context to
7327c478bd9Sstevel@tonic-gate 	 * convert the token into a context
7337c478bd9Sstevel@tonic-gate 	 */
7347c478bd9Sstevel@tonic-gate 	gssstat  = kgss_export_sec_context(&minor, client_data->context,
7357c478bd9Sstevel@tonic-gate 				&process_token);
7367c478bd9Sstevel@tonic-gate 	/*
7377c478bd9Sstevel@tonic-gate 	 * if export_sec_context returns an error we delete the
7387c478bd9Sstevel@tonic-gate 	 * context just to be safe.
7397c478bd9Sstevel@tonic-gate 	 */
7407c478bd9Sstevel@tonic-gate 	if (gssstat == GSS_S_NAME_NOT_MN) {
7417c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(4, "svc_rpcsec_gss: export sec context "
7427c478bd9Sstevel@tonic-gate 				"Kernel mod unavailable\n");
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	} else if (gssstat != GSS_S_COMPLETE) {
7457c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(1, "svc_rpcsec_gss: export sec context failed  "
7467c478bd9Sstevel@tonic-gate 				" gssstat = 0x%x\n", gssstat);
7477c478bd9Sstevel@tonic-gate 		(void) gss_release_buffer(&minor, &process_token);
7487c478bd9Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
7497c478bd9Sstevel@tonic-gate 				NULL);
7507c478bd9Sstevel@tonic-gate 		return (FALSE);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	} else if (process_token.length == 0) {
7537c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(1, "svc_rpcsec_gss:zero length token in response "
7547c478bd9Sstevel@tonic-gate 				"for export_sec_context, but "
7557c478bd9Sstevel@tonic-gate 				"gsstat == GSS_S_COMPLETE\n");
7567c478bd9Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
7577c478bd9Sstevel@tonic-gate 				NULL);
7587c478bd9Sstevel@tonic-gate 		return (FALSE);
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	} else {
7617c478bd9Sstevel@tonic-gate 		gssstat = kgss_import_sec_context(&minor, &process_token,
7627c478bd9Sstevel@tonic-gate 					client_data->context);
7637c478bd9Sstevel@tonic-gate 		if (gssstat != GSS_S_COMPLETE) {
7647c478bd9Sstevel@tonic-gate 			RPCGSS_LOG(1, "svc_rpcsec_gss: import sec context "
7657c478bd9Sstevel@tonic-gate 				" failed gssstat = 0x%x\n", gssstat);
7667c478bd9Sstevel@tonic-gate 			(void) kgss_delete_sec_context(&minor,
7677c478bd9Sstevel@tonic-gate 				&client_data->context, NULL);
7687c478bd9Sstevel@tonic-gate 			(void) gss_release_buffer(&minor, &process_token);
7697c478bd9Sstevel@tonic-gate 			return (FALSE);
7707c478bd9Sstevel@tonic-gate 		}
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(4, "gss_import_sec_context successful\n");
7737c478bd9Sstevel@tonic-gate 		(void) gss_release_buffer(&minor, &process_token);
7747c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	return (TRUE);
7777c478bd9Sstevel@tonic-gate }
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate /*
780bfd8310aSGlenn Barry  * do_gss_accept is called from a taskq and does all the work for a
781bfd8310aSGlenn Barry  * RPCSEC_GSS_INIT call (mostly calling kgss_accept_sec_context()).
7827c478bd9Sstevel@tonic-gate  */
783bfd8310aSGlenn Barry static enum auth_stat
do_gss_accept(SVCXPRT * xprt,rpc_gss_init_arg * call_arg,struct rpc_msg * msg,svc_rpc_gss_data * client_data,uint_t cr_version,rpc_gss_service_t cr_service)784bfd8310aSGlenn Barry do_gss_accept(
785bfd8310aSGlenn Barry 	SVCXPRT *xprt,
786bfd8310aSGlenn Barry 	rpc_gss_init_arg *call_arg,
787bfd8310aSGlenn Barry 	struct rpc_msg *msg,
788bfd8310aSGlenn Barry 	svc_rpc_gss_data *client_data,
789bfd8310aSGlenn Barry 	uint_t cr_version,
790bfd8310aSGlenn Barry 	rpc_gss_service_t cr_service)
7917c478bd9Sstevel@tonic-gate {
792bfd8310aSGlenn Barry 	rpc_gss_init_res	call_res;
7937c478bd9Sstevel@tonic-gate 	gss_buffer_desc		output_token;
7947c478bd9Sstevel@tonic-gate 	OM_uint32		gssstat, minor, minor_stat, time_rec;
7957c478bd9Sstevel@tonic-gate 	int			ret_flags, ret;
7964a3b0527SAndy Fiddaman 	gss_OID			mech_type = GSS_C_NULL_OID;
7977c478bd9Sstevel@tonic-gate 	int			free_mech_type = 1;
798bfd8310aSGlenn Barry 	struct svc_req		r, *rqst;
7997c478bd9Sstevel@tonic-gate 
800bfd8310aSGlenn Barry 	rqst = &r;
801bfd8310aSGlenn Barry 	rqst->rq_xprt = xprt;
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	/*
804bfd8310aSGlenn Barry 	 * Initialize output_token.
8057c478bd9Sstevel@tonic-gate 	 */
806bfd8310aSGlenn Barry 	output_token.length = 0;
807bfd8310aSGlenn Barry 	output_token.value = NULL;
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 	bzero((char *)&call_res, sizeof (call_res));
8107c478bd9Sstevel@tonic-gate 
811bfd8310aSGlenn Barry 	mutex_enter(&client_data->clm);
812bfd8310aSGlenn Barry 	if (client_data->stale) {
813bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
814bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
815bfd8310aSGlenn Barry 		goto error2;
816bfd8310aSGlenn Barry 	}
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	/*
819bfd8310aSGlenn Barry 	 * Any response we send will use ctx_handle, so set it now;
820bfd8310aSGlenn Barry 	 * also set seq_window since this won't change.
8217c478bd9Sstevel@tonic-gate 	 */
822bfd8310aSGlenn Barry 	call_res.ctx_handle.length = sizeof (client_data->key);
823bfd8310aSGlenn Barry 	call_res.ctx_handle.value = (char *)&client_data->key;
824bfd8310aSGlenn Barry 	call_res.seq_window = SEQ_WIN;
8257c478bd9Sstevel@tonic-gate 
826bfd8310aSGlenn Barry 	gssstat = GSS_S_FAILURE;
827bfd8310aSGlenn Barry 	minor = 0;
828bfd8310aSGlenn Barry 	minor_stat = 0;
829bfd8310aSGlenn Barry 	rw_enter(&cred_lock, RW_READER);
830bfd8310aSGlenn Barry 
831bfd8310aSGlenn Barry 	if (client_data->client_name.length) {
832bfd8310aSGlenn Barry 		(void) gss_release_buffer(&minor,
833bfd8310aSGlenn Barry 		    &client_data->client_name);
8347c478bd9Sstevel@tonic-gate 	}
835bfd8310aSGlenn Barry 	gssstat = kgss_accept_sec_context(&minor_stat,
836bfd8310aSGlenn Barry 	    &client_data->context,
837bfd8310aSGlenn Barry 	    GSS_C_NO_CREDENTIAL,
838bfd8310aSGlenn Barry 	    call_arg,
839bfd8310aSGlenn Barry 	    GSS_C_NO_CHANNEL_BINDINGS,
840bfd8310aSGlenn Barry 	    &client_data->client_name,
841bfd8310aSGlenn Barry 	    &mech_type,
842bfd8310aSGlenn Barry 	    &output_token,
843bfd8310aSGlenn Barry 	    &ret_flags,
844bfd8310aSGlenn Barry 	    &time_rec,
845bfd8310aSGlenn Barry 	    NULL,		/* don't need a delegated cred back */
846bfd8310aSGlenn Barry 	    crgetuid(CRED()));
847bfd8310aSGlenn Barry 
848bfd8310aSGlenn Barry 	RPCGSS_LOG(4, "gssstat 0x%x \n", gssstat);
849bfd8310aSGlenn Barry 
850bfd8310aSGlenn Barry 	if (gssstat == GSS_S_COMPLETE) {
851bfd8310aSGlenn Barry 		/*
852bfd8310aSGlenn Barry 		 * Set the raw and unix credentials at this
853bfd8310aSGlenn Barry 		 * point.  This saves a lot of computation
854bfd8310aSGlenn Barry 		 * later when credentials are retrieved.
855bfd8310aSGlenn Barry 		 */
856bfd8310aSGlenn Barry 		client_data->raw_cred.version = cr_version;
857bfd8310aSGlenn Barry 		client_data->raw_cred.service = cr_service;
8587c478bd9Sstevel@tonic-gate 
859bfd8310aSGlenn Barry 		if (client_data->raw_cred.mechanism) {
860bfd8310aSGlenn Barry 			kgss_free_oid(client_data->raw_cred.mechanism);
861bfd8310aSGlenn Barry 			client_data->raw_cred.mechanism = NULL;
862bfd8310aSGlenn Barry 		}
863bfd8310aSGlenn Barry 		client_data->raw_cred.mechanism = (rpc_gss_OID) mech_type;
864bfd8310aSGlenn Barry 		/*
865bfd8310aSGlenn Barry 		 * client_data is now responsible for freeing
866bfd8310aSGlenn Barry 		 * the data of 'mech_type'.
867bfd8310aSGlenn Barry 		 */
868bfd8310aSGlenn Barry 		free_mech_type = 0;
869bfd8310aSGlenn Barry 
870bfd8310aSGlenn Barry 		if (client_data->raw_cred.client_principal) {
871bfd8310aSGlenn Barry 			kmem_free((caddr_t)client_data->\
872bfd8310aSGlenn Barry 			    raw_cred.client_principal,
873bfd8310aSGlenn Barry 			    client_data->raw_cred.\
874bfd8310aSGlenn Barry 			    client_principal->len + sizeof (int));
875bfd8310aSGlenn Barry 			client_data->raw_cred.client_principal = NULL;
876bfd8310aSGlenn Barry 		}
877bfd8310aSGlenn Barry 
878bfd8310aSGlenn Barry 		/*
879bfd8310aSGlenn Barry 		 *  The client_name returned from
880bfd8310aSGlenn Barry 		 *  kgss_accept_sec_context() is in an
881bfd8310aSGlenn Barry 		 *  exported flat format.
882bfd8310aSGlenn Barry 		 */
883bfd8310aSGlenn Barry 		if (! __rpc_gss_make_principal(
884bfd8310aSGlenn Barry 		    &client_data->raw_cred.client_principal,
885bfd8310aSGlenn Barry 		    &client_data->client_name)) {
886bfd8310aSGlenn Barry 			RPCGSS_LOG0(1, "_svcrpcsec_gss: "
887bfd8310aSGlenn Barry 			    "make principal failed\n");
888bfd8310aSGlenn Barry 			gssstat = GSS_S_FAILURE;
889bfd8310aSGlenn Barry 			(void) gss_release_buffer(&minor_stat, &output_token);
890bfd8310aSGlenn Barry 		}
891bfd8310aSGlenn Barry 	}
892bfd8310aSGlenn Barry 
893bfd8310aSGlenn Barry 	rw_exit(&cred_lock);
894bfd8310aSGlenn Barry 
895bfd8310aSGlenn Barry 	call_res.gss_major = gssstat;
896bfd8310aSGlenn Barry 	call_res.gss_minor = minor_stat;
897bfd8310aSGlenn Barry 
898bfd8310aSGlenn Barry 	if (gssstat != GSS_S_COMPLETE &&
899bfd8310aSGlenn Barry 	    gssstat != GSS_S_CONTINUE_NEEDED) {
900bfd8310aSGlenn Barry 		call_res.ctx_handle.length = 0;
901bfd8310aSGlenn Barry 		call_res.ctx_handle.value = NULL;
902bfd8310aSGlenn Barry 		call_res.seq_window = 0;
903bfd8310aSGlenn Barry 		rpc_gss_display_status(gssstat, minor_stat, mech_type,
904bfd8310aSGlenn Barry 		    crgetuid(CRED()),
905bfd8310aSGlenn Barry 		    "_svc_rpcsec_gss gss_accept_sec_context");
906bfd8310aSGlenn Barry 		(void) svc_sendreply(rqst->rq_xprt,
907bfd8310aSGlenn Barry 		    __xdr_rpc_gss_init_res, (caddr_t)&call_res);
908bfd8310aSGlenn Barry 		client_data->stale = TRUE;
909bfd8310aSGlenn Barry 		ret = AUTH_OK;
910bfd8310aSGlenn Barry 		goto error2;
9117c478bd9Sstevel@tonic-gate 	}
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	/*
914bfd8310aSGlenn Barry 	 * If appropriate, set established to TRUE *after* sending
915bfd8310aSGlenn Barry 	 * response (otherwise, the client will receive the final
916bfd8310aSGlenn Barry 	 * token encrypted)
9177c478bd9Sstevel@tonic-gate 	 */
918bfd8310aSGlenn Barry 	if (gssstat == GSS_S_COMPLETE) {
9197c478bd9Sstevel@tonic-gate 		/*
920bfd8310aSGlenn Barry 		 * Context is established.  Set expiration time
921bfd8310aSGlenn Barry 		 * for the context.
9227c478bd9Sstevel@tonic-gate 		 */
923bfd8310aSGlenn Barry 		client_data->seq_num = 1;
924bfd8310aSGlenn Barry 		if ((time_rec == GSS_C_INDEFINITE) || (time_rec == 0)) {
925bfd8310aSGlenn Barry 			client_data->expiration = GSS_C_INDEFINITE;
926bfd8310aSGlenn Barry 		} else {
927bfd8310aSGlenn Barry 			client_data->expiration =
928bfd8310aSGlenn Barry 			    time_rec + gethrestime_sec();
9297c478bd9Sstevel@tonic-gate 		}
930bfd8310aSGlenn Barry 
931bfd8310aSGlenn Barry 		if (!transfer_sec_context(client_data)) {
932bfd8310aSGlenn Barry 			ret = RPCSEC_GSS_FAILED;
933bfd8310aSGlenn Barry 			client_data->stale = TRUE;
934bfd8310aSGlenn Barry 			RPCGSS_LOG0(1,
935bfd8310aSGlenn Barry 			    "_svc_rpcsec_gss: transfer sec context failed\n");
936bfd8310aSGlenn Barry 			goto error2;
9377c478bd9Sstevel@tonic-gate 		}
938bfd8310aSGlenn Barry 
939bfd8310aSGlenn Barry 		client_data->established = TRUE;
940bfd8310aSGlenn Barry 	}
941bfd8310aSGlenn Barry 
942bfd8310aSGlenn Barry 	/*
943bfd8310aSGlenn Barry 	 * This step succeeded.  Send a response, along with
944bfd8310aSGlenn Barry 	 * a token if there's one.  Don't dispatch.
945bfd8310aSGlenn Barry 	 */
946bfd8310aSGlenn Barry 
947bfd8310aSGlenn Barry 	if (output_token.length != 0)
948bfd8310aSGlenn Barry 		GSS_COPY_BUFFER(call_res.token, output_token);
949bfd8310aSGlenn Barry 
950bfd8310aSGlenn Barry 	/*
951bfd8310aSGlenn Barry 	 * If GSS_S_COMPLETE: set response verifier to
952bfd8310aSGlenn Barry 	 * checksum of SEQ_WIN
953bfd8310aSGlenn Barry 	 */
954bfd8310aSGlenn Barry 	if (gssstat == GSS_S_COMPLETE) {
955bfd8310aSGlenn Barry 		if (!set_response_verf(rqst, msg, client_data,
956bfd8310aSGlenn Barry 		    (uint_t)SEQ_WIN)) {
957bfd8310aSGlenn Barry 			ret = RPCSEC_GSS_FAILED;
958bfd8310aSGlenn Barry 			client_data->stale = TRUE;
959bfd8310aSGlenn Barry 			RPCGSS_LOG0(1,
960bfd8310aSGlenn Barry 			    "_svc_rpcsec_gss:set response verifier failed\n");
961bfd8310aSGlenn Barry 			goto error2;
9627c478bd9Sstevel@tonic-gate 		}
9637c478bd9Sstevel@tonic-gate 	}
9647c478bd9Sstevel@tonic-gate 
965bfd8310aSGlenn Barry 	if (!svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
966bfd8310aSGlenn Barry 	    (caddr_t)&call_res)) {
967bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_FAILED;
968bfd8310aSGlenn Barry 		client_data->stale = TRUE;
969bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svc_rpcsec_gss:send reply failed\n");
970bfd8310aSGlenn Barry 		goto error2;
971bfd8310aSGlenn Barry 	}
972bfd8310aSGlenn Barry 
973bfd8310aSGlenn Barry 	/*
974bfd8310aSGlenn Barry 	 * Cache last response in case it is lost and the client
975bfd8310aSGlenn Barry 	 * retries on an established context.
976bfd8310aSGlenn Barry 	 */
977bfd8310aSGlenn Barry 	(void) retrans_add(client_data, msg->rm_xid, &call_res);
978bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
979bfd8310aSGlenn Barry 	client_data->ref_cnt--;
980bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
981bfd8310aSGlenn Barry 
982bfd8310aSGlenn Barry 	(void) gss_release_buffer(&minor_stat, &output_token);
983bfd8310aSGlenn Barry 
984bfd8310aSGlenn Barry 	return (AUTH_OK);
985bfd8310aSGlenn Barry 
986bfd8310aSGlenn Barry error2:
987bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
988bfd8310aSGlenn Barry 	client_data->ref_cnt--;
989bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
990bfd8310aSGlenn Barry 	(void) gss_release_buffer(&minor_stat, &output_token);
991bfd8310aSGlenn Barry 	if (free_mech_type && mech_type)
992bfd8310aSGlenn Barry 		kgss_free_oid(mech_type);
993bfd8310aSGlenn Barry 
994bfd8310aSGlenn Barry 	return (ret);
995bfd8310aSGlenn Barry }
996bfd8310aSGlenn Barry 
997bfd8310aSGlenn Barry static void
svcrpcsec_gss_taskq_func(void * svcrpcsecgss_taskq_arg)998bfd8310aSGlenn Barry svcrpcsec_gss_taskq_func(void *svcrpcsecgss_taskq_arg)
999bfd8310aSGlenn Barry {
1000bfd8310aSGlenn Barry 	enum auth_stat retval;
1001bfd8310aSGlenn Barry 	svcrpcsec_gss_taskq_arg_t *arg = svcrpcsecgss_taskq_arg;
1002bfd8310aSGlenn Barry 
1003bfd8310aSGlenn Barry 	retval = do_gss_accept(arg->rq_xprt, arg->rpc_call_arg, arg->msg,
1004bfd8310aSGlenn Barry 	    arg->client_data, arg->cr_version, arg->cr_service);
1005bfd8310aSGlenn Barry 	if (retval != AUTH_OK) {
1006bfd8310aSGlenn Barry 		cmn_err(CE_NOTE,
1007bfd8310aSGlenn Barry 		    "svcrpcsec_gss_taskq_func:  do_gss_accept fail 0x%x",
1008bfd8310aSGlenn Barry 		    retval);
1009bfd8310aSGlenn Barry 	}
1010bfd8310aSGlenn Barry 	rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
10114a3b0527SAndy Fiddaman 	SVC_RELE(arg->rq_xprt, NULL, FALSE);
1012bfd8310aSGlenn Barry 	svc_clone_unlink(arg->rq_xprt);
1013bfd8310aSGlenn Barry 	svc_clone_free(arg->rq_xprt);
1014bfd8310aSGlenn Barry 	xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)arg->rpc_call_arg);
1015bfd8310aSGlenn Barry 	kmem_free(arg->rpc_call_arg, sizeof (*arg->rpc_call_arg));
1016bfd8310aSGlenn Barry 
1017bfd8310aSGlenn Barry 	kmem_free(arg, sizeof (*arg));
1018bfd8310aSGlenn Barry }
1019bfd8310aSGlenn Barry 
1020bfd8310aSGlenn Barry static enum auth_stat
rpcsec_gss_init(struct svc_req * rqst,struct rpc_msg * msg,rpc_gss_creds creds,bool_t * no_dispatch,svc_rpc_gss_data * c_d)1021bfd8310aSGlenn Barry rpcsec_gss_init(
1022bfd8310aSGlenn Barry 	struct svc_req		*rqst,
1023bfd8310aSGlenn Barry 	struct rpc_msg		*msg,
1024bfd8310aSGlenn Barry 	rpc_gss_creds		creds,
1025bfd8310aSGlenn Barry 	bool_t			*no_dispatch,
1026bfd8310aSGlenn Barry 	svc_rpc_gss_data	*c_d) /* client data, can be NULL */
1027bfd8310aSGlenn Barry {
1028bfd8310aSGlenn Barry 	svc_rpc_gss_data	*client_data;
1029bfd8310aSGlenn Barry 	int ret;
1030bfd8310aSGlenn Barry 	svcrpcsec_gss_taskq_arg_t *arg;
1031*02ac56e0SMatt Barden 	svc_gss_zsd_t *zsd = svc_gss_get_zsd();
1032*02ac56e0SMatt Barden 	taskq_t *tq = zsd->sgz_init_taskq;
1033*02ac56e0SMatt Barden 
1034*02ac56e0SMatt Barden 	if (tq == NULL) {
1035*02ac56e0SMatt Barden 		mutex_enter(&zsd->sgz_lock);
1036*02ac56e0SMatt Barden 		if (zsd->sgz_init_taskq == NULL)
1037*02ac56e0SMatt Barden 			zsd->sgz_init_taskq = svc_gss_create_taskq(curzone);
1038*02ac56e0SMatt Barden 		tq = zsd->sgz_init_taskq;
1039*02ac56e0SMatt Barden 		mutex_exit(&zsd->sgz_lock);
1040*02ac56e0SMatt Barden 		if (tq == NULL) {
1041*02ac56e0SMatt Barden 			cmn_err(CE_NOTE, "%s: no taskq available", __func__);
1042*02ac56e0SMatt Barden 			return (RPCSEC_GSS_FAILED);
1043*02ac56e0SMatt Barden 		}
1044*02ac56e0SMatt Barden 	}
1045bfd8310aSGlenn Barry 
1046bfd8310aSGlenn Barry 	if (creds.ctx_handle.length != 0) {
1047bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: ctx_handle not null\n");
1048bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1049bfd8310aSGlenn Barry 		return (ret);
1050bfd8310aSGlenn Barry 	}
1051bfd8310aSGlenn Barry 
1052bfd8310aSGlenn Barry 	client_data = c_d ? c_d : create_client();
1053bfd8310aSGlenn Barry 	if (client_data == NULL) {
1054bfd8310aSGlenn Barry 		RPCGSS_LOG0(1,
1055bfd8310aSGlenn Barry 		    "_svcrpcsec_gss: can't create a new cache entry\n");
1056bfd8310aSGlenn Barry 		ret = AUTH_FAILED;
1057bfd8310aSGlenn Barry 		return (ret);
1058bfd8310aSGlenn Barry 	}
1059bfd8310aSGlenn Barry 
1060bfd8310aSGlenn Barry 	mutex_enter(&client_data->clm);
1061bfd8310aSGlenn Barry 	if (client_data->stale) {
1062bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1063bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
1064bfd8310aSGlenn Barry 		goto error2;
1065bfd8310aSGlenn Barry 	}
1066bfd8310aSGlenn Barry 
10677c478bd9Sstevel@tonic-gate 	/*
1068bbf21555SRichard Lowe 	 * kgss_accept_sec_context()/gssd(8) can be overly time
1069bfd8310aSGlenn Barry 	 * consuming so let's queue it and return asap.
1070bfd8310aSGlenn Barry 	 *
1071bfd8310aSGlenn Barry 	 * taskq func must free arg.
10727c478bd9Sstevel@tonic-gate 	 */
1073bfd8310aSGlenn Barry 	arg = kmem_alloc(sizeof (*arg), KM_SLEEP);
1074bfd8310aSGlenn Barry 
1075bfd8310aSGlenn Barry 	/* taskq func must free rpc_call_arg & deserialized arguments */
1076a0f9c00cSJosef 'Jeff' Sipek 	arg->rpc_call_arg = kmem_zalloc(sizeof (*arg->rpc_call_arg), KM_SLEEP);
1077bfd8310aSGlenn Barry 
1078bfd8310aSGlenn Barry 	/* deserialize arguments */
1079bfd8310aSGlenn Barry 	if (!SVC_GETARGS(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
1080bfd8310aSGlenn Barry 	    (caddr_t)arg->rpc_call_arg)) {
1081bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_FAILED;
1082bfd8310aSGlenn Barry 		client_data->stale = TRUE;
1083bfd8310aSGlenn Barry 		goto error2;
1084bfd8310aSGlenn Barry 	}
1085bfd8310aSGlenn Barry 
1086bfd8310aSGlenn Barry 	/* get a xprt clone for taskq thread, taskq func must free it */
1087bfd8310aSGlenn Barry 	arg->rq_xprt = svc_clone_init();
108860536ef9SKaren Rochford 	svc_clone_link(rqst->rq_xprt->xp_master, arg->rq_xprt, rqst->rq_xprt);
1089bfd8310aSGlenn Barry 	arg->rq_xprt->xp_xid = rqst->rq_xprt->xp_xid;
1090bfd8310aSGlenn Barry 
10914a3b0527SAndy Fiddaman 	/*
10924a3b0527SAndy Fiddaman 	 * Increment the reference count on the rpcmod slot so that is not
10934a3b0527SAndy Fiddaman 	 * freed before the task has finished.
10944a3b0527SAndy Fiddaman 	 */
10954a3b0527SAndy Fiddaman 	SVC_HOLD(arg->rq_xprt);
1096bfd8310aSGlenn Barry 
1097bfd8310aSGlenn Barry 	/* set the appropriate wrap/unwrap routine for RPCSEC_GSS */
1098bfd8310aSGlenn Barry 	arg->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
1099bfd8310aSGlenn Barry 	arg->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
1100bfd8310aSGlenn Barry 
1101bfd8310aSGlenn Barry 	/* get a dup of rpc msg for taskq thread */
1102bfd8310aSGlenn Barry 	arg->msg = rpc_msg_dup(msg);  /* taskq func must free msg dup */
1103bfd8310aSGlenn Barry 
1104bfd8310aSGlenn Barry 	arg->client_data = client_data;
1105bfd8310aSGlenn Barry 	arg->cr_version = creds.version;
1106bfd8310aSGlenn Barry 	arg->cr_service = creds.service;
1107bfd8310aSGlenn Barry 
1108bfd8310aSGlenn Barry 	/* should be ok to hold clm lock as taskq will have new thread(s) */
1109*02ac56e0SMatt Barden 	if (taskq_dispatch(tq, svcrpcsec_gss_taskq_func, arg, TQ_SLEEP)
1110*02ac56e0SMatt Barden 	    == DDI_FAILURE) {
1111*02ac56e0SMatt Barden 		cmn_err(CE_NOTE, "%s: taskq dispatch fail", __func__);
1112bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_FAILED;
1113bfd8310aSGlenn Barry 		rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
11144a3b0527SAndy Fiddaman 		SVC_RELE(arg->rq_xprt, NULL, FALSE);
1115bfd8310aSGlenn Barry 		svc_clone_unlink(arg->rq_xprt);
1116bfd8310aSGlenn Barry 		svc_clone_free(arg->rq_xprt);
1117bfd8310aSGlenn Barry 		kmem_free(arg, sizeof (*arg));
1118bfd8310aSGlenn Barry 		goto error2;
1119bfd8310aSGlenn Barry 	}
1120bfd8310aSGlenn Barry 
1121bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
1122bfd8310aSGlenn Barry 	*no_dispatch = TRUE;
1123bfd8310aSGlenn Barry 	return (AUTH_OK);
1124bfd8310aSGlenn Barry 
1125bfd8310aSGlenn Barry error2:
1126bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
1127bfd8310aSGlenn Barry 	client_data->ref_cnt--;
1128bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
1129bfd8310aSGlenn Barry 	cmn_err(CE_NOTE, "rpcsec_gss_init: error 0x%x", ret);
1130bfd8310aSGlenn Barry 	return (ret);
1131bfd8310aSGlenn Barry }
1132bfd8310aSGlenn Barry 
1133bfd8310aSGlenn Barry static enum auth_stat
rpcsec_gss_continue_init(struct svc_req * rqst,struct rpc_msg * msg,rpc_gss_creds creds,bool_t * no_dispatch)1134bfd8310aSGlenn Barry rpcsec_gss_continue_init(
1135bfd8310aSGlenn Barry 	struct svc_req		*rqst,
1136bfd8310aSGlenn Barry 	struct rpc_msg		*msg,
1137bfd8310aSGlenn Barry 	rpc_gss_creds		creds,
1138bfd8310aSGlenn Barry 	bool_t			*no_dispatch)
1139bfd8310aSGlenn Barry {
1140bfd8310aSGlenn Barry 	int ret;
1141bfd8310aSGlenn Barry 	svc_rpc_gss_data	*client_data;
1142bfd8310aSGlenn Barry 	svc_rpc_gss_parms_t	*gss_parms;
1143bfd8310aSGlenn Barry 	rpc_gss_init_res	*retrans_result;
1144bfd8310aSGlenn Barry 
1145bfd8310aSGlenn Barry 	if (creds.ctx_handle.length == 0) {
1146bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
1147bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1148bfd8310aSGlenn Barry 		return (ret);
1149bfd8310aSGlenn Barry 	}
1150bfd8310aSGlenn Barry 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
1151bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1152bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
1153bfd8310aSGlenn Barry 		return (ret);
1154bfd8310aSGlenn Barry 	}
1155bfd8310aSGlenn Barry 
11567c478bd9Sstevel@tonic-gate 	mutex_enter(&client_data->clm);
11577c478bd9Sstevel@tonic-gate 	if (client_data->stale) {
11587c478bd9Sstevel@tonic-gate 		ret = RPCSEC_GSS_NOCRED;
11597c478bd9Sstevel@tonic-gate 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
11607c478bd9Sstevel@tonic-gate 		goto error2;
11617c478bd9Sstevel@tonic-gate 	}
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 	/*
1164bfd8310aSGlenn Barry 	 * If context not established, go thru INIT code but with
1165bfd8310aSGlenn Barry 	 * this client handle.
11667c478bd9Sstevel@tonic-gate 	 */
1167bfd8310aSGlenn Barry 	if (!client_data->established) {
1168bfd8310aSGlenn Barry 		mutex_exit(&client_data->clm);
1169bfd8310aSGlenn Barry 		return (rpcsec_gss_init(rqst, msg, creds, no_dispatch,
1170bfd8310aSGlenn Barry 		    client_data));
1171bfd8310aSGlenn Barry 	}
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	/*
11747c478bd9Sstevel@tonic-gate 	 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
11757c478bd9Sstevel@tonic-gate 	 */
11767c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
11777c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	/*
11807c478bd9Sstevel@tonic-gate 	 * Keep copy of parameters we'll need for response, for the
11817c478bd9Sstevel@tonic-gate 	 * sake of reentrancy (we don't want to look in the context
11827c478bd9Sstevel@tonic-gate 	 * data because when we are sending a response, another
11837c478bd9Sstevel@tonic-gate 	 * request may have come in).
11847c478bd9Sstevel@tonic-gate 	 */
11857c478bd9Sstevel@tonic-gate 	gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
11867c478bd9Sstevel@tonic-gate 	gss_parms->established = client_data->established;
11877c478bd9Sstevel@tonic-gate 	gss_parms->service = creds.service;
11887c478bd9Sstevel@tonic-gate 	gss_parms->qop_rcvd = (uint_t)client_data->qop;
11897c478bd9Sstevel@tonic-gate 	gss_parms->context = (void *)client_data->context;
11907c478bd9Sstevel@tonic-gate 	gss_parms->seq_num = creds.seq_num;
11917c478bd9Sstevel@tonic-gate 
1192bfd8310aSGlenn Barry 	/*
1193bfd8310aSGlenn Barry 	 * This is an established context. Continue to
1194bfd8310aSGlenn Barry 	 * satisfy retried continue init requests out of
1195bfd8310aSGlenn Barry 	 * the retransmit cache.  Throw away any that don't
1196bfd8310aSGlenn Barry 	 * have a matching xid or the cach is empty.
1197bfd8310aSGlenn Barry 	 * Delete the retransmit cache once the client sends
1198bfd8310aSGlenn Barry 	 * a data request.
1199bfd8310aSGlenn Barry 	 */
1200bfd8310aSGlenn Barry 	if (client_data->retrans_data &&
1201bfd8310aSGlenn Barry 	    (client_data->retrans_data->xid == msg->rm_xid)) {
1202bfd8310aSGlenn Barry 		retrans_result = &client_data->retrans_data->result;
1203bfd8310aSGlenn Barry 		if (set_response_verf(rqst, msg, client_data,
1204bfd8310aSGlenn Barry 		    (uint_t)retrans_result->seq_window)) {
1205bfd8310aSGlenn Barry 			gss_parms->established = FALSE;
12067c478bd9Sstevel@tonic-gate 			(void) svc_sendreply(rqst->rq_xprt,
1207bfd8310aSGlenn Barry 			    __xdr_rpc_gss_init_res, (caddr_t)retrans_result);
12087c478bd9Sstevel@tonic-gate 			*no_dispatch = TRUE;
1209bfd8310aSGlenn Barry 			ASSERT(client_data->ref_cnt > 0);
1210bfd8310aSGlenn Barry 			client_data->ref_cnt--;
12117c478bd9Sstevel@tonic-gate 		}
1212bfd8310aSGlenn Barry 	}
1213bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
12147c478bd9Sstevel@tonic-gate 
1215bfd8310aSGlenn Barry 	return (AUTH_OK);
12167c478bd9Sstevel@tonic-gate 
1217bfd8310aSGlenn Barry error2:
1218bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
1219bfd8310aSGlenn Barry 	client_data->ref_cnt--;
1220bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
1221bfd8310aSGlenn Barry 	return (ret);
1222bfd8310aSGlenn Barry }
12237c478bd9Sstevel@tonic-gate 
1224bfd8310aSGlenn Barry static enum auth_stat
rpcsec_gss_data(struct svc_req * rqst,struct rpc_msg * msg,rpc_gss_creds creds,bool_t * no_dispatch)1225bfd8310aSGlenn Barry rpcsec_gss_data(
1226bfd8310aSGlenn Barry 	struct svc_req		*rqst,
1227bfd8310aSGlenn Barry 	struct rpc_msg		*msg,
1228bfd8310aSGlenn Barry 	rpc_gss_creds		creds,
1229bfd8310aSGlenn Barry 	bool_t			*no_dispatch)
1230bfd8310aSGlenn Barry {
1231bfd8310aSGlenn Barry 	int ret;
1232bfd8310aSGlenn Barry 	svc_rpc_gss_parms_t	*gss_parms;
1233bfd8310aSGlenn Barry 	svc_rpc_gss_data	*client_data;
12347c478bd9Sstevel@tonic-gate 
1235bfd8310aSGlenn Barry 	switch (creds.service) {
1236bfd8310aSGlenn Barry 	case rpc_gss_svc_none:
1237bfd8310aSGlenn Barry 	case rpc_gss_svc_integrity:
1238bfd8310aSGlenn Barry 	case rpc_gss_svc_privacy:
1239bfd8310aSGlenn Barry 		break;
1240bfd8310aSGlenn Barry 	default:
1241bfd8310aSGlenn Barry 		cmn_err(CE_NOTE, "__svcrpcsec_gss: unknown service type=0x%x",
1242bfd8310aSGlenn Barry 		    creds.service);
1243bfd8310aSGlenn Barry 		RPCGSS_LOG(1, "_svcrpcsec_gss: unknown service type: 0x%x\n",
1244bfd8310aSGlenn Barry 		    creds.service);
1245bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1246bfd8310aSGlenn Barry 		return (ret);
1247bfd8310aSGlenn Barry 	}
12487c478bd9Sstevel@tonic-gate 
1249bfd8310aSGlenn Barry 	if (creds.ctx_handle.length == 0) {
1250bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
1251bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1252bfd8310aSGlenn Barry 		return (ret);
1253bfd8310aSGlenn Barry 	}
1254bfd8310aSGlenn Barry 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
1255bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1256bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
1257bfd8310aSGlenn Barry 		return (ret);
1258bfd8310aSGlenn Barry 	}
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 
1261bfd8310aSGlenn Barry 	mutex_enter(&client_data->clm);
1262bfd8310aSGlenn Barry 	if (!client_data->established) {
1263bfd8310aSGlenn Barry 		ret = AUTH_FAILED;
1264bfd8310aSGlenn Barry 		goto error2;
1265bfd8310aSGlenn Barry 	}
1266bfd8310aSGlenn Barry 	if (client_data->stale) {
1267bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1268bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
1269bfd8310aSGlenn Barry 		goto error2;
1270bfd8310aSGlenn Barry 	}
12717c478bd9Sstevel@tonic-gate 
1272bfd8310aSGlenn Barry 	/*
1273bfd8310aSGlenn Barry 	 * Once the context is established and there is no more
1274bfd8310aSGlenn Barry 	 * retransmission of last continue init request, it is safe
1275bfd8310aSGlenn Barry 	 * to delete the retransmit cache entry.
1276bfd8310aSGlenn Barry 	 */
1277bfd8310aSGlenn Barry 	if (client_data->retrans_data)
1278bfd8310aSGlenn Barry 		retrans_del(client_data);
12797c478bd9Sstevel@tonic-gate 
1280bfd8310aSGlenn Barry 	/*
1281bfd8310aSGlenn Barry 	 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
1282bfd8310aSGlenn Barry 	 */
1283bfd8310aSGlenn Barry 	rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
1284bfd8310aSGlenn Barry 	rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
12857c478bd9Sstevel@tonic-gate 
1286bfd8310aSGlenn Barry 	/*
1287bfd8310aSGlenn Barry 	 * Keep copy of parameters we'll need for response, for the
1288bfd8310aSGlenn Barry 	 * sake of reentrancy (we don't want to look in the context
1289bfd8310aSGlenn Barry 	 * data because when we are sending a response, another
1290bfd8310aSGlenn Barry 	 * request may have come in).
1291bfd8310aSGlenn Barry 	 */
1292bfd8310aSGlenn Barry 	gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
1293bfd8310aSGlenn Barry 	gss_parms->established = client_data->established;
1294bfd8310aSGlenn Barry 	gss_parms->service = creds.service;
1295bfd8310aSGlenn Barry 	gss_parms->qop_rcvd = (uint_t)client_data->qop;
1296bfd8310aSGlenn Barry 	gss_parms->context = (void *)client_data->context;
1297bfd8310aSGlenn Barry 	gss_parms->seq_num = creds.seq_num;
12987c478bd9Sstevel@tonic-gate 
1299bfd8310aSGlenn Barry 	/*
1300bfd8310aSGlenn Barry 	 * Context is already established.  Check verifier, and
1301bfd8310aSGlenn Barry 	 * note parameters we will need for response in gss_parms.
1302bfd8310aSGlenn Barry 	 */
1303bfd8310aSGlenn Barry 	if (!check_verf(msg, client_data->context,
1304bfd8310aSGlenn Barry 	    (int *)&gss_parms->qop_rcvd, client_data->u_cred.uid)) {
1305bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1306bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: check verf failed\n");
1307bfd8310aSGlenn Barry 		goto error2;
1308bfd8310aSGlenn Barry 	}
13097c478bd9Sstevel@tonic-gate 
1310bfd8310aSGlenn Barry 	/*
1311bfd8310aSGlenn Barry 	 *  Check and invoke callback if necessary.
1312bfd8310aSGlenn Barry 	 */
1313bfd8310aSGlenn Barry 	if (!client_data->done_docallback) {
1314bfd8310aSGlenn Barry 		client_data->done_docallback = TRUE;
1315bfd8310aSGlenn Barry 		client_data->qop = gss_parms->qop_rcvd;
1316bfd8310aSGlenn Barry 		client_data->raw_cred.qop = gss_parms->qop_rcvd;
1317bfd8310aSGlenn Barry 		client_data->raw_cred.service = creds.service;
1318bfd8310aSGlenn Barry 		if (!do_callback(rqst, client_data)) {
13197c478bd9Sstevel@tonic-gate 			ret = AUTH_FAILED;
1320bfd8310aSGlenn Barry 			RPCGSS_LOG0(1, "_svc_rpcsec_gss:callback failed\n");
13217c478bd9Sstevel@tonic-gate 			goto error2;
13227c478bd9Sstevel@tonic-gate 		}
1323bfd8310aSGlenn Barry 	}
13247c478bd9Sstevel@tonic-gate 
1325bfd8310aSGlenn Barry 	/*
1326bfd8310aSGlenn Barry 	 * If the context was locked, make sure that the client
1327bfd8310aSGlenn Barry 	 * has not changed QOP.
1328bfd8310aSGlenn Barry 	 */
1329bfd8310aSGlenn Barry 	if (client_data->locked && gss_parms->qop_rcvd != client_data->qop) {
1330bfd8310aSGlenn Barry 		ret = AUTH_BADVERF;
1331bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: can not change qop\n");
1332bfd8310aSGlenn Barry 		goto error2;
1333bfd8310aSGlenn Barry 	}
13347c478bd9Sstevel@tonic-gate 
1335bfd8310aSGlenn Barry 	/*
1336bfd8310aSGlenn Barry 	 * Validate sequence number.
1337bfd8310aSGlenn Barry 	 */
1338bfd8310aSGlenn Barry 	if (!check_seq(client_data, creds.seq_num, &client_data->stale)) {
1339bfd8310aSGlenn Barry 		if (client_data->stale) {
13407c478bd9Sstevel@tonic-gate 			ret = RPCSEC_GSS_FAILED;
13417c478bd9Sstevel@tonic-gate 			RPCGSS_LOG0(1,
1342bfd8310aSGlenn Barry 			    "_svc_rpcsec_gss:check seq failed\n");
1343bfd8310aSGlenn Barry 		} else {
1344bfd8310aSGlenn Barry 			RPCGSS_LOG0(4, "_svc_rpcsec_gss:check seq "
1345bfd8310aSGlenn Barry 			    "failed on good context. Ignoring "
1346bfd8310aSGlenn Barry 			    "request\n");
13477c478bd9Sstevel@tonic-gate 			/*
1348bfd8310aSGlenn Barry 			 * Operational error, drop packet silently.
1349bfd8310aSGlenn Barry 			 * The client will recover after timing out,
1350bfd8310aSGlenn Barry 			 * assuming this is a client error and not
1351bfd8310aSGlenn Barry 			 * a relpay attack.  Don't dispatch.
13527c478bd9Sstevel@tonic-gate 			 */
1353bfd8310aSGlenn Barry 			ret = AUTH_OK;
13547c478bd9Sstevel@tonic-gate 			*no_dispatch = TRUE;
1355bfd8310aSGlenn Barry 		}
1356bfd8310aSGlenn Barry 		goto error2;
1357bfd8310aSGlenn Barry 	}
13587c478bd9Sstevel@tonic-gate 
1359bfd8310aSGlenn Barry 	/*
1360bfd8310aSGlenn Barry 	 * set response verifier
1361bfd8310aSGlenn Barry 	 */
1362bfd8310aSGlenn Barry 	if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
1363bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_FAILED;
1364bfd8310aSGlenn Barry 		client_data->stale = TRUE;
1365bfd8310aSGlenn Barry 		RPCGSS_LOG0(1,
1366bfd8310aSGlenn Barry 		    "_svc_rpcsec_gss:set response verifier failed\n");
1367bfd8310aSGlenn Barry 		goto error2;
1368bfd8310aSGlenn Barry 	}
13697c478bd9Sstevel@tonic-gate 
1370bfd8310aSGlenn Barry 	/*
1371bfd8310aSGlenn Barry 	 * If context is locked, make sure that the client
1372bfd8310aSGlenn Barry 	 * has not changed the security service.
1373bfd8310aSGlenn Barry 	 */
1374bfd8310aSGlenn Barry 	if (client_data->locked &&
1375bfd8310aSGlenn Barry 	    client_data->raw_cred.service != creds.service) {
1376bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svc_rpcsec_gss: "
1377bfd8310aSGlenn Barry 		    "security service changed.\n");
1378bfd8310aSGlenn Barry 		ret = AUTH_FAILED;
1379bfd8310aSGlenn Barry 		goto error2;
13807c478bd9Sstevel@tonic-gate 	}
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate 	/*
1383bfd8310aSGlenn Barry 	 * Set client credentials to raw credential
1384bfd8310aSGlenn Barry 	 * structure in context.  This is okay, since
1385bfd8310aSGlenn Barry 	 * this will not change during the lifetime of
1386bfd8310aSGlenn Barry 	 * the context (so it's MT safe).
13877c478bd9Sstevel@tonic-gate 	 */
1388bfd8310aSGlenn Barry 	rqst->rq_clntcred = (char *)&client_data->raw_cred;
1389bfd8310aSGlenn Barry 
1390bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
1391bfd8310aSGlenn Barry 	return (AUTH_OK);
1392bfd8310aSGlenn Barry 
1393bfd8310aSGlenn Barry error2:
1394bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
1395bfd8310aSGlenn Barry 	client_data->ref_cnt--;
13967c478bd9Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
1397bfd8310aSGlenn Barry 	return (ret);
1398bfd8310aSGlenn Barry }
1399bfd8310aSGlenn Barry 
1400bfd8310aSGlenn Barry /*
1401bfd8310aSGlenn Barry  * Note we don't have a client yet to use this routine and test it.
1402bfd8310aSGlenn Barry  */
1403bfd8310aSGlenn Barry static enum auth_stat
rpcsec_gss_destroy(struct svc_req * rqst,rpc_gss_creds creds,bool_t * no_dispatch)1404bfd8310aSGlenn Barry rpcsec_gss_destroy(
1405bfd8310aSGlenn Barry 	struct svc_req		*rqst,
1406bfd8310aSGlenn Barry 	rpc_gss_creds		creds,
1407bfd8310aSGlenn Barry 	bool_t			*no_dispatch)
1408bfd8310aSGlenn Barry {
1409bfd8310aSGlenn Barry 	svc_rpc_gss_data	*client_data;
1410bfd8310aSGlenn Barry 	int ret;
1411bfd8310aSGlenn Barry 
1412bfd8310aSGlenn Barry 	if (creds.ctx_handle.length == 0) {
1413bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
1414bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1415bfd8310aSGlenn Barry 		return (ret);
1416bfd8310aSGlenn Barry 	}
1417bfd8310aSGlenn Barry 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
1418bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1419bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
1420bfd8310aSGlenn Barry 		return (ret);
1421bfd8310aSGlenn Barry 	}
1422bfd8310aSGlenn Barry 
1423bfd8310aSGlenn Barry 	mutex_enter(&client_data->clm);
1424bfd8310aSGlenn Barry 	if (!client_data->established) {
1425bfd8310aSGlenn Barry 		ret = AUTH_FAILED;
1426bfd8310aSGlenn Barry 		goto error2;
1427bfd8310aSGlenn Barry 	}
1428bfd8310aSGlenn Barry 	if (client_data->stale) {
1429bfd8310aSGlenn Barry 		ret = RPCSEC_GSS_NOCRED;
1430bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
1431bfd8310aSGlenn Barry 		goto error2;
1432bfd8310aSGlenn Barry 	}
14337c478bd9Sstevel@tonic-gate 
1434bfd8310aSGlenn Barry 	(void) svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
1435bfd8310aSGlenn Barry 	*no_dispatch = TRUE;
1436bfd8310aSGlenn Barry 	ASSERT(client_data->ref_cnt > 0);
1437bfd8310aSGlenn Barry 	client_data->ref_cnt--;
1438bfd8310aSGlenn Barry 	client_data->stale = TRUE;
1439bfd8310aSGlenn Barry 	mutex_exit(&client_data->clm);
14407c478bd9Sstevel@tonic-gate 	return (AUTH_OK);
1441bfd8310aSGlenn Barry 
14427c478bd9Sstevel@tonic-gate error2:
14437c478bd9Sstevel@tonic-gate 	ASSERT(client_data->ref_cnt > 0);
14447c478bd9Sstevel@tonic-gate 	client_data->ref_cnt--;
1445bfd8310aSGlenn Barry 	client_data->stale = TRUE;
14467c478bd9Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
1447bfd8310aSGlenn Barry 	return (ret);
1448bfd8310aSGlenn Barry }
1449bfd8310aSGlenn Barry 
1450bfd8310aSGlenn Barry /*
1451bfd8310aSGlenn Barry  * Server side authentication for RPCSEC_GSS.
1452bfd8310aSGlenn Barry  */
1453bfd8310aSGlenn Barry enum auth_stat
__svcrpcsec_gss(struct svc_req * rqst,struct rpc_msg * msg,bool_t * no_dispatch)1454bfd8310aSGlenn Barry __svcrpcsec_gss(
1455bfd8310aSGlenn Barry 	struct svc_req		*rqst,
1456bfd8310aSGlenn Barry 	struct rpc_msg		*msg,
1457bfd8310aSGlenn Barry 	bool_t			*no_dispatch)
1458bfd8310aSGlenn Barry {
1459bfd8310aSGlenn Barry 	XDR			xdrs;
1460bfd8310aSGlenn Barry 	rpc_gss_creds		creds;
1461bfd8310aSGlenn Barry 	struct opaque_auth	*cred;
1462bfd8310aSGlenn Barry 	int			ret;
1463bfd8310aSGlenn Barry 
1464bfd8310aSGlenn Barry 	*no_dispatch = FALSE;
1465bfd8310aSGlenn Barry 
1466bfd8310aSGlenn Barry 	/*
1467bfd8310aSGlenn Barry 	 * Initialize response verifier to NULL verifier.  If
1468bfd8310aSGlenn Barry 	 * necessary, this will be changed later.
1469bfd8310aSGlenn Barry 	 */
1470bfd8310aSGlenn Barry 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
1471bfd8310aSGlenn Barry 	rqst->rq_xprt->xp_verf.oa_base = NULL;
1472bfd8310aSGlenn Barry 	rqst->rq_xprt->xp_verf.oa_length = 0;
1473bfd8310aSGlenn Barry 
14747c478bd9Sstevel@tonic-gate 	/*
1475bfd8310aSGlenn Barry 	 * Pull out and check credential and verifier.
14767c478bd9Sstevel@tonic-gate 	 */
1477bfd8310aSGlenn Barry 	cred = &msg->rm_call.cb_cred;
1478bfd8310aSGlenn Barry 
1479bfd8310aSGlenn Barry 	if (cred->oa_length == 0) {
1480bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: zero length cred\n");
1481bfd8310aSGlenn Barry 		return (AUTH_BADCRED);
1482bfd8310aSGlenn Barry 	}
1483bfd8310aSGlenn Barry 
1484bfd8310aSGlenn Barry 	xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
1485bfd8310aSGlenn Barry 	bzero((char *)&creds, sizeof (creds));
1486bfd8310aSGlenn Barry 	if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
1487bfd8310aSGlenn Barry 		XDR_DESTROY(&xdrs);
1488bfd8310aSGlenn Barry 		RPCGSS_LOG0(1, "_svcrpcsec_gss: can't decode creds\n");
1489bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1490bfd8310aSGlenn Barry 		return (AUTH_BADCRED);
1491bfd8310aSGlenn Barry 	}
1492bfd8310aSGlenn Barry 	XDR_DESTROY(&xdrs);
1493bfd8310aSGlenn Barry 
1494bfd8310aSGlenn Barry 	switch (creds.gss_proc) {
1495bfd8310aSGlenn Barry 	case RPCSEC_GSS_INIT:
1496bfd8310aSGlenn Barry 		ret = rpcsec_gss_init(rqst, msg, creds, no_dispatch, NULL);
1497bfd8310aSGlenn Barry 		break;
1498bfd8310aSGlenn Barry 	case RPCSEC_GSS_CONTINUE_INIT:
1499bfd8310aSGlenn Barry 		ret = rpcsec_gss_continue_init(rqst, msg, creds, no_dispatch);
1500bfd8310aSGlenn Barry 		break;
1501bfd8310aSGlenn Barry 	case RPCSEC_GSS_DATA:
1502bfd8310aSGlenn Barry 		ret = rpcsec_gss_data(rqst, msg, creds, no_dispatch);
1503bfd8310aSGlenn Barry 		break;
1504bfd8310aSGlenn Barry 	case RPCSEC_GSS_DESTROY:
1505bfd8310aSGlenn Barry 		ret = rpcsec_gss_destroy(rqst, creds, no_dispatch);
1506bfd8310aSGlenn Barry 		break;
1507bfd8310aSGlenn Barry 	default:
1508bfd8310aSGlenn Barry 		cmn_err(CE_NOTE, "__svcrpcsec_gss: bad proc=%d",
1509bfd8310aSGlenn Barry 		    creds.gss_proc);
1510bfd8310aSGlenn Barry 		ret = AUTH_BADCRED;
1511bfd8310aSGlenn Barry 	}
1512bfd8310aSGlenn Barry 
15137c478bd9Sstevel@tonic-gate 	if (creds.ctx_handle.length != 0)
15147c478bd9Sstevel@tonic-gate 		xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
15157c478bd9Sstevel@tonic-gate 	return (ret);
15167c478bd9Sstevel@tonic-gate }
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate /*
15197c478bd9Sstevel@tonic-gate  * Check verifier.  The verifier is the checksum of the RPC header
15207c478bd9Sstevel@tonic-gate  * upto and including the credentials field.
15217c478bd9Sstevel@tonic-gate  */
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate /* ARGSUSED */
15247c478bd9Sstevel@tonic-gate static bool_t
check_verf(struct rpc_msg * msg,gss_ctx_id_t context,int * qop_state,uid_t uid)15257c478bd9Sstevel@tonic-gate check_verf(struct rpc_msg *msg, gss_ctx_id_t context, int *qop_state, uid_t uid)
15267c478bd9Sstevel@tonic-gate {
15277c478bd9Sstevel@tonic-gate 	int			*buf, *tmp;
15287c478bd9Sstevel@tonic-gate 	char			hdr[128];
15297c478bd9Sstevel@tonic-gate 	struct opaque_auth	*oa;
15307c478bd9Sstevel@tonic-gate 	int			len;
15317c478bd9Sstevel@tonic-gate 	gss_buffer_desc		msg_buf;
15327c478bd9Sstevel@tonic-gate 	gss_buffer_desc		tok_buf;
15337c478bd9Sstevel@tonic-gate 	OM_uint32		gssstat, minor_stat;
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 	/*
15367c478bd9Sstevel@tonic-gate 	 * We have to reconstruct the RPC header from the previously
15377c478bd9Sstevel@tonic-gate 	 * parsed information, since we haven't kept the header intact.
15387c478bd9Sstevel@tonic-gate 	 */
1539a38dd497Spk 
1540a38dd497Spk 	oa = &msg->rm_call.cb_cred;
1541a38dd497Spk 	if (oa->oa_length > MAX_AUTH_BYTES)
1542a38dd497Spk 		return (FALSE);
1543a38dd497Spk 
1544a38dd497Spk 	/* 8 XDR units from the IXDR macro calls. */
1545a38dd497Spk 	if (sizeof (hdr) < (8 * BYTES_PER_XDR_UNIT +
1546a38dd497Spk 	    RNDUP(oa->oa_length)))
1547a38dd497Spk 		return (FALSE);
15487c478bd9Sstevel@tonic-gate 	buf = (int *)hdr;
15497c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_xid);
15507c478bd9Sstevel@tonic-gate 	IXDR_PUT_ENUM(buf, msg->rm_direction);
15517c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
15527c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
15537c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
15547c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
15557c478bd9Sstevel@tonic-gate 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
15567c478bd9Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, oa->oa_length);
15577c478bd9Sstevel@tonic-gate 	if (oa->oa_length) {
15587c478bd9Sstevel@tonic-gate 		len = RNDUP(oa->oa_length);
15597c478bd9Sstevel@tonic-gate 		tmp = buf;
15607c478bd9Sstevel@tonic-gate 		buf += len / sizeof (int);
15617c478bd9Sstevel@tonic-gate 		*(buf - 1) = 0;
15627c478bd9Sstevel@tonic-gate 		(void) bcopy(oa->oa_base, (caddr_t)tmp, oa->oa_length);
15637c478bd9Sstevel@tonic-gate 	}
15647c478bd9Sstevel@tonic-gate 	len = ((char *)buf) - hdr;
15657c478bd9Sstevel@tonic-gate 	msg_buf.length = len;
15667c478bd9Sstevel@tonic-gate 	msg_buf.value = hdr;
15677c478bd9Sstevel@tonic-gate 	oa = &msg->rm_call.cb_verf;
15687c478bd9Sstevel@tonic-gate 	tok_buf.length = oa->oa_length;
15697c478bd9Sstevel@tonic-gate 	tok_buf.value = oa->oa_base;
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	gssstat = kgss_verify(&minor_stat, context, &msg_buf, &tok_buf,
1572bfd8310aSGlenn Barry 	    qop_state);
15737c478bd9Sstevel@tonic-gate 	if (gssstat != GSS_S_COMPLETE) {
15747c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(1, "check_verf: kgss_verify status 0x%x\n", gssstat);
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: msg_buf length %d\n", len);
15777c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: msg_buf value 0x%x\n", *(int *)hdr);
15787c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: tok_buf length %ld\n",
1579bfd8310aSGlenn Barry 		    tok_buf.length);
15807c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: tok_buf value 0x%p\n",
1581bfd8310aSGlenn Barry 		    (void *)oa->oa_base);
15827c478bd9Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: context 0x%p\n", (void *)context);
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate 		return (FALSE);
15857c478bd9Sstevel@tonic-gate 	}
15867c478bd9Sstevel@tonic-gate 	return (TRUE);
15877c478bd9Sstevel@tonic-gate }
15887c478bd9Sstevel@tonic-gate 
1589bfd8310aSGlenn Barry 
15907c478bd9Sstevel@tonic-gate /*
15917c478bd9Sstevel@tonic-gate  * Set response verifier.  This is the checksum of the given number.
15927c478bd9Sstevel@tonic-gate  * (e.g. sequence number or sequence window)
15937c478bd9Sstevel@tonic-gate  */
15947c478bd9Sstevel@tonic-gate static bool_t
set_response_verf(struct svc_req * rqst,struct rpc_msg * msg,svc_rpc_gss_data * cl,uint_t num)15954a3b0527SAndy Fiddaman set_response_verf(struct svc_req *rqst, struct rpc_msg *msg,
15964a3b0527SAndy Fiddaman     svc_rpc_gss_data *cl, uint_t num)
15977c478bd9Sstevel@tonic-gate {
15987c478bd9Sstevel@tonic-gate 	OM_uint32		minor;
15997c478bd9Sstevel@tonic-gate 	gss_buffer_desc		in_buf, out_buf;
16007c478bd9Sstevel@tonic-gate 	uint_t			num_net;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	num_net = (uint_t)htonl(num);
16037c478bd9Sstevel@tonic-gate 	in_buf.length = sizeof (num);
16047c478bd9Sstevel@tonic-gate 	in_buf.value = (char *)&num_net;
16057c478bd9Sstevel@tonic-gate /* XXX uid ? */
1606bfd8310aSGlenn Barry 
16074a3b0527SAndy Fiddaman 	if ((kgss_sign(&minor, cl->context, cl->qop, &in_buf, &out_buf))
16084a3b0527SAndy Fiddaman 	    != GSS_S_COMPLETE)
16097c478bd9Sstevel@tonic-gate 		return (FALSE);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
16127c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
16137c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
16147c478bd9Sstevel@tonic-gate 	bcopy(out_buf.value, rqst->rq_xprt->xp_verf.oa_base, out_buf.length);
16157c478bd9Sstevel@tonic-gate 	(void) gss_release_buffer(&minor, &out_buf);
16167c478bd9Sstevel@tonic-gate 	return (TRUE);
16177c478bd9Sstevel@tonic-gate }
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate /*
16207c478bd9Sstevel@tonic-gate  * Create client context.
16217c478bd9Sstevel@tonic-gate  */
16227c478bd9Sstevel@tonic-gate static svc_rpc_gss_data *
create_client()16237c478bd9Sstevel@tonic-gate create_client()
16247c478bd9Sstevel@tonic-gate {
16257c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
16267c478bd9Sstevel@tonic-gate 	static uint_t		key = 1;
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 	client_data = (svc_rpc_gss_data *) kmem_cache_alloc(svc_data_handle,
1629bfd8310aSGlenn Barry 	    KM_SLEEP);
16307c478bd9Sstevel@tonic-gate 	if (client_data == NULL)
16317c478bd9Sstevel@tonic-gate 		return (NULL);
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	/*
16347c478bd9Sstevel@tonic-gate 	 * set up client data structure
16357c478bd9Sstevel@tonic-gate 	 */
16367c478bd9Sstevel@tonic-gate 	client_data->next = NULL;
16377c478bd9Sstevel@tonic-gate 	client_data->prev = NULL;
16387c478bd9Sstevel@tonic-gate 	client_data->lru_next = NULL;
16397c478bd9Sstevel@tonic-gate 	client_data->lru_prev = NULL;
16407c478bd9Sstevel@tonic-gate 	client_data->client_name.length = 0;
16417c478bd9Sstevel@tonic-gate 	client_data->client_name.value = NULL;
16427c478bd9Sstevel@tonic-gate 	client_data->seq_num = 0;
16437c478bd9Sstevel@tonic-gate 	bzero(client_data->seq_bits, sizeof (client_data->seq_bits));
16447c478bd9Sstevel@tonic-gate 	client_data->key = 0;
16457c478bd9Sstevel@tonic-gate 	client_data->cookie = NULL;
16467c478bd9Sstevel@tonic-gate 	bzero(&client_data->u_cred, sizeof (client_data->u_cred));
16477c478bd9Sstevel@tonic-gate 	client_data->established = FALSE;
16487c478bd9Sstevel@tonic-gate 	client_data->locked = FALSE;
16497c478bd9Sstevel@tonic-gate 	client_data->u_cred_set = 0;
16507c478bd9Sstevel@tonic-gate 	client_data->context = GSS_C_NO_CONTEXT;
16517c478bd9Sstevel@tonic-gate 	client_data->expiration = GSS_C_INDEFINITE;
16527c478bd9Sstevel@tonic-gate 	client_data->deleg = GSS_C_NO_CREDENTIAL;
16537c478bd9Sstevel@tonic-gate 	client_data->ref_cnt = 1;
16547c478bd9Sstevel@tonic-gate 	client_data->last_ref_time = gethrestime_sec();
16557c478bd9Sstevel@tonic-gate 	client_data->qop = GSS_C_QOP_DEFAULT;
16567c478bd9Sstevel@tonic-gate 	client_data->done_docallback = FALSE;
16577c478bd9Sstevel@tonic-gate 	client_data->stale = FALSE;
16587c478bd9Sstevel@tonic-gate 	client_data->retrans_data = NULL;
16597c478bd9Sstevel@tonic-gate 	bzero(&client_data->raw_cred, sizeof (client_data->raw_cred));
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	/*
16627c478bd9Sstevel@tonic-gate 	 * The client context handle is a 32-bit key (unsigned int).
16637c478bd9Sstevel@tonic-gate 	 * The key is incremented until there is no duplicate for it.
16647c478bd9Sstevel@tonic-gate 	 */
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate 	svc_rpc_gss_cache_stats.total_entries_allocated++;
16677c478bd9Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
16687c478bd9Sstevel@tonic-gate 	for (;;) {
16697c478bd9Sstevel@tonic-gate 		client_data->key = key++;
16707c478bd9Sstevel@tonic-gate 		if (find_client(client_data->key) == NULL) {
16717c478bd9Sstevel@tonic-gate 			insert_client(client_data);
16727c478bd9Sstevel@tonic-gate 			mutex_exit(&ctx_mutex);
16737c478bd9Sstevel@tonic-gate 			return (client_data);
16747c478bd9Sstevel@tonic-gate 		}
16757c478bd9Sstevel@tonic-gate 	}
16767c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
16777c478bd9Sstevel@tonic-gate }
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate /*
16807c478bd9Sstevel@tonic-gate  * Insert client context into hash list and LRU list.
16817c478bd9Sstevel@tonic-gate  */
16827c478bd9Sstevel@tonic-gate static void
insert_client(svc_rpc_gss_data * client_data)16834a3b0527SAndy Fiddaman insert_client(svc_rpc_gss_data *client_data)
16847c478bd9Sstevel@tonic-gate {
16857c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
16867c478bd9Sstevel@tonic-gate 	int			index = HASH(client_data->key);
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate 	client_data->prev = NULL;
16917c478bd9Sstevel@tonic-gate 	cl = clients[index];
16927c478bd9Sstevel@tonic-gate 	if ((client_data->next = cl) != NULL)
16937c478bd9Sstevel@tonic-gate 		cl->prev = client_data;
16947c478bd9Sstevel@tonic-gate 	clients[index] = client_data;
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	client_data->lru_prev = NULL;
16977c478bd9Sstevel@tonic-gate 	if ((client_data->lru_next = lru_first) != NULL)
16987c478bd9Sstevel@tonic-gate 		lru_first->lru_prev = client_data;
16997c478bd9Sstevel@tonic-gate 	else
17007c478bd9Sstevel@tonic-gate 		lru_last = client_data;
17017c478bd9Sstevel@tonic-gate 	lru_first = client_data;
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 	num_gss_contexts++;
17047c478bd9Sstevel@tonic-gate }
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate /*
17077c478bd9Sstevel@tonic-gate  * Fetch a client, given the client context handle.  Move it to the
17087c478bd9Sstevel@tonic-gate  * top of the LRU list since this is the most recently used context.
17097c478bd9Sstevel@tonic-gate  */
17107c478bd9Sstevel@tonic-gate static svc_rpc_gss_data *
get_client(gss_buffer_t ctx_handle)17114a3b0527SAndy Fiddaman get_client(gss_buffer_t ctx_handle)
17127c478bd9Sstevel@tonic-gate {
17137c478bd9Sstevel@tonic-gate 	uint_t			key = *(uint_t *)ctx_handle->value;
17147c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
17177c478bd9Sstevel@tonic-gate 	if ((cl = find_client(key)) != NULL) {
17187c478bd9Sstevel@tonic-gate 		mutex_enter(&cl->clm);
17197c478bd9Sstevel@tonic-gate 		if (cl->stale) {
17207c478bd9Sstevel@tonic-gate 			if (cl->ref_cnt == 0) {
17217c478bd9Sstevel@tonic-gate 				mutex_exit(&cl->clm);
17227c478bd9Sstevel@tonic-gate 				destroy_client(cl);
17237c478bd9Sstevel@tonic-gate 			} else {
17247c478bd9Sstevel@tonic-gate 				mutex_exit(&cl->clm);
17257c478bd9Sstevel@tonic-gate 			}
17267c478bd9Sstevel@tonic-gate 			mutex_exit(&ctx_mutex);
17277c478bd9Sstevel@tonic-gate 			return (NULL);
17287c478bd9Sstevel@tonic-gate 		}
17297c478bd9Sstevel@tonic-gate 		cl->ref_cnt++;
17307c478bd9Sstevel@tonic-gate 		cl->last_ref_time = gethrestime_sec();
17317c478bd9Sstevel@tonic-gate 		mutex_exit(&cl->clm);
17327c478bd9Sstevel@tonic-gate 		if (cl != lru_first) {
17337c478bd9Sstevel@tonic-gate 			cl->lru_prev->lru_next = cl->lru_next;
17347c478bd9Sstevel@tonic-gate 			if (cl->lru_next != NULL)
17357c478bd9Sstevel@tonic-gate 				cl->lru_next->lru_prev = cl->lru_prev;
17367c478bd9Sstevel@tonic-gate 			else
17377c478bd9Sstevel@tonic-gate 				lru_last = cl->lru_prev;
17387c478bd9Sstevel@tonic-gate 			cl->lru_prev = NULL;
17397c478bd9Sstevel@tonic-gate 			cl->lru_next = lru_first;
17407c478bd9Sstevel@tonic-gate 			lru_first->lru_prev = cl;
17417c478bd9Sstevel@tonic-gate 			lru_first = cl;
17427c478bd9Sstevel@tonic-gate 		}
17437c478bd9Sstevel@tonic-gate 	}
17447c478bd9Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
17457c478bd9Sstevel@tonic-gate 	return (cl);
17467c478bd9Sstevel@tonic-gate }
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate /*
17497c478bd9Sstevel@tonic-gate  * Given the client context handle, find the context corresponding to it.
17507c478bd9Sstevel@tonic-gate  * Don't change its LRU state since it may not be used.
17517c478bd9Sstevel@tonic-gate  */
17527c478bd9Sstevel@tonic-gate static svc_rpc_gss_data *
find_client(uint_t key)17534a3b0527SAndy Fiddaman find_client(uint_t key)
17547c478bd9Sstevel@tonic-gate {
17557c478bd9Sstevel@tonic-gate 	int			index = HASH(key);
17567c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*cl = NULL;
17577c478bd9Sstevel@tonic-gate 
17587c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 	for (cl = clients[index]; cl != NULL; cl = cl->next) {
17617c478bd9Sstevel@tonic-gate 		if (cl->key == key)
17627c478bd9Sstevel@tonic-gate 			break;
17637c478bd9Sstevel@tonic-gate 	}
17647c478bd9Sstevel@tonic-gate 	return (cl);
17657c478bd9Sstevel@tonic-gate }
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate /*
17687c478bd9Sstevel@tonic-gate  * Destroy a client context.
17697c478bd9Sstevel@tonic-gate  */
17707c478bd9Sstevel@tonic-gate static void
destroy_client(svc_rpc_gss_data * client_data)17714a3b0527SAndy Fiddaman destroy_client(svc_rpc_gss_data *client_data)
17727c478bd9Sstevel@tonic-gate {
17737c478bd9Sstevel@tonic-gate 	OM_uint32		minor;
17747c478bd9Sstevel@tonic-gate 	int			index = HASH(client_data->key);
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	/*
17797c478bd9Sstevel@tonic-gate 	 * remove from hash list
17807c478bd9Sstevel@tonic-gate 	 */
17817c478bd9Sstevel@tonic-gate 	if (client_data->prev == NULL)
17827c478bd9Sstevel@tonic-gate 		clients[index] = client_data->next;
17837c478bd9Sstevel@tonic-gate 	else
17847c478bd9Sstevel@tonic-gate 		client_data->prev->next = client_data->next;
17857c478bd9Sstevel@tonic-gate 	if (client_data->next != NULL)
17867c478bd9Sstevel@tonic-gate 		client_data->next->prev = client_data->prev;
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	/*
17897c478bd9Sstevel@tonic-gate 	 * remove from LRU list
17907c478bd9Sstevel@tonic-gate 	 */
17917c478bd9Sstevel@tonic-gate 	if (client_data->lru_prev == NULL)
17927c478bd9Sstevel@tonic-gate 		lru_first = client_data->lru_next;
17937c478bd9Sstevel@tonic-gate 	else
17947c478bd9Sstevel@tonic-gate 		client_data->lru_prev->lru_next = client_data->lru_next;
17957c478bd9Sstevel@tonic-gate 	if (client_data->lru_next != NULL)
17967c478bd9Sstevel@tonic-gate 		client_data->lru_next->lru_prev = client_data->lru_prev;
17977c478bd9Sstevel@tonic-gate 	else
17987c478bd9Sstevel@tonic-gate 		lru_last = client_data->lru_prev;
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate 	/*
18017c478bd9Sstevel@tonic-gate 	 * If there is a GSS context, clean up GSS state.
18027c478bd9Sstevel@tonic-gate 	 */
18037c478bd9Sstevel@tonic-gate 	if (client_data->context != GSS_C_NO_CONTEXT) {
18047c478bd9Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
18054a3b0527SAndy Fiddaman 		    NULL);
18067c478bd9Sstevel@tonic-gate 
18077c478bd9Sstevel@tonic-gate 		common_client_data_free(client_data);
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
18104a3b0527SAndy Fiddaman 			(void) kgss_release_cred(&minor, &client_data->deleg,
18114a3b0527SAndy Fiddaman 			    crgetuid(CRED()));
18127c478bd9Sstevel@tonic-gate 		}
18137c478bd9Sstevel@tonic-gate 	}
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 	if (client_data->u_cred.gidlist != NULL) {
18164a3b0527SAndy Fiddaman 		kmem_free((char *)client_data->u_cred.gidlist,
18174a3b0527SAndy Fiddaman 		    client_data->u_cred.gidlen * sizeof (gid_t));
18184a3b0527SAndy Fiddaman 		client_data->u_cred.gidlist = NULL;
18197c478bd9Sstevel@tonic-gate 	}
18207c478bd9Sstevel@tonic-gate 	if (client_data->retrans_data != NULL)
18217c478bd9Sstevel@tonic-gate 		retrans_del(client_data);
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 	kmem_cache_free(svc_data_handle, client_data);
18247c478bd9Sstevel@tonic-gate 	num_gss_contexts--;
18257c478bd9Sstevel@tonic-gate }
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate /*
18287c478bd9Sstevel@tonic-gate  * Check for expired and stale client contexts.
18297c478bd9Sstevel@tonic-gate  */
18307c478bd9Sstevel@tonic-gate static void
sweep_clients(bool_t from_reclaim)18317c478bd9Sstevel@tonic-gate sweep_clients(bool_t from_reclaim)
18327c478bd9Sstevel@tonic-gate {
18337c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data	*cl, *next;
18347c478bd9Sstevel@tonic-gate 	time_t			last_reference_needed;
18357c478bd9Sstevel@tonic-gate 	time_t			now = gethrestime_sec();
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
18387c478bd9Sstevel@tonic-gate 
18397c478bd9Sstevel@tonic-gate 	last_reference_needed = now - (from_reclaim ?
1840bfd8310aSGlenn Barry 	    svc_rpc_gss_active_delta : svc_rpc_gss_inactive_delta);
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 	cl = lru_last;
18437c478bd9Sstevel@tonic-gate 	while (cl) {
18447c478bd9Sstevel@tonic-gate 		/*
18457c478bd9Sstevel@tonic-gate 		 * We assume here that any manipulation of the LRU pointers
18467c478bd9Sstevel@tonic-gate 		 * and hash bucket pointers are only done when holding the
18477c478bd9Sstevel@tonic-gate 		 * ctx_mutex.
18487c478bd9Sstevel@tonic-gate 		 */
18497c478bd9Sstevel@tonic-gate 		next = cl->lru_prev;
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 		mutex_enter(&cl->clm);
18527c478bd9Sstevel@tonic-gate 
18537c478bd9Sstevel@tonic-gate 		if ((cl->expiration != GSS_C_INDEFINITE &&
18547c478bd9Sstevel@tonic-gate 		    cl->expiration <= now) || cl->stale ||
18557c478bd9Sstevel@tonic-gate 		    cl->last_ref_time <= last_reference_needed) {
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 			if ((cl->expiration != GSS_C_INDEFINITE &&
18587c478bd9Sstevel@tonic-gate 			    cl->expiration <= now) || cl->stale ||
18597c478bd9Sstevel@tonic-gate 			    (cl->last_ref_time <= last_reference_needed &&
18607c478bd9Sstevel@tonic-gate 			    cl->ref_cnt == 0)) {
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate 				cl->stale = TRUE;
18637c478bd9Sstevel@tonic-gate 
18647c478bd9Sstevel@tonic-gate 				if (cl->ref_cnt == 0) {
18657c478bd9Sstevel@tonic-gate 					mutex_exit(&cl->clm);
18667c478bd9Sstevel@tonic-gate 					if (from_reclaim)
18677c478bd9Sstevel@tonic-gate 						svc_rpc_gss_cache_stats.
18687c478bd9Sstevel@tonic-gate 						    no_returned_by_reclaim++;
18697c478bd9Sstevel@tonic-gate 					destroy_client(cl);
18707c478bd9Sstevel@tonic-gate 				} else
18717c478bd9Sstevel@tonic-gate 					mutex_exit(&cl->clm);
18727c478bd9Sstevel@tonic-gate 			} else
18737c478bd9Sstevel@tonic-gate 				mutex_exit(&cl->clm);
18747c478bd9Sstevel@tonic-gate 		} else
18757c478bd9Sstevel@tonic-gate 			mutex_exit(&cl->clm);
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 		cl = next;
18787c478bd9Sstevel@tonic-gate 	}
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 	last_swept = gethrestime_sec();
18817c478bd9Sstevel@tonic-gate }
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate /*
18847c478bd9Sstevel@tonic-gate  * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
18857c478bd9Sstevel@tonic-gate  * and write the result to xdrs.
18867c478bd9Sstevel@tonic-gate  */
18877c478bd9Sstevel@tonic-gate static bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,XDR * out_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)18884a3b0527SAndy Fiddaman svc_rpc_gss_wrap(SVCAUTH *auth, XDR *out_xdrs, bool_t (*xdr_func)(),
18894a3b0527SAndy Fiddaman     caddr_t xdr_ptr)
18907c478bd9Sstevel@tonic-gate {
18917c478bd9Sstevel@tonic-gate 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
1892bfd8310aSGlenn Barry 	bool_t ret;
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 	/*
18957c478bd9Sstevel@tonic-gate 	 * If context is not established, or if neither integrity nor
18967c478bd9Sstevel@tonic-gate 	 * privacy service is used, don't wrap - just XDR encode.
18977c478bd9Sstevel@tonic-gate 	 * Otherwise, wrap data using service and QOP parameters.
18987c478bd9Sstevel@tonic-gate 	 */
18994a3b0527SAndy Fiddaman 	if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
19007c478bd9Sstevel@tonic-gate 		return ((*xdr_func)(out_xdrs, xdr_ptr));
19017c478bd9Sstevel@tonic-gate 
1902bfd8310aSGlenn Barry 	ret = __rpc_gss_wrap_data(gss_parms->service,
19034a3b0527SAndy Fiddaman 	    (OM_uint32)gss_parms->qop_rcvd,
19044a3b0527SAndy Fiddaman 	    (gss_ctx_id_t)gss_parms->context,
19054a3b0527SAndy Fiddaman 	    gss_parms->seq_num,
19064a3b0527SAndy Fiddaman 	    out_xdrs, xdr_func, xdr_ptr);
1907bfd8310aSGlenn Barry 	return (ret);
19087c478bd9Sstevel@tonic-gate }
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate /*
19117c478bd9Sstevel@tonic-gate  * Decrypt the serialized arguments and XDR decode them.
19127c478bd9Sstevel@tonic-gate  */
19137c478bd9Sstevel@tonic-gate static bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,XDR * in_xdrs,bool_t (* xdr_func)(),caddr_t xdr_ptr)19144a3b0527SAndy Fiddaman svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(),
19154a3b0527SAndy Fiddaman     caddr_t xdr_ptr)
19167c478bd9Sstevel@tonic-gate {
19177c478bd9Sstevel@tonic-gate 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	/*
19207c478bd9Sstevel@tonic-gate 	 * If context is not established, or if neither integrity nor
19217c478bd9Sstevel@tonic-gate 	 * privacy service is used, don't unwrap - just XDR decode.
19227c478bd9Sstevel@tonic-gate 	 * Otherwise, unwrap data.
19237c478bd9Sstevel@tonic-gate 	 */
19244a3b0527SAndy Fiddaman 	if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
19257c478bd9Sstevel@tonic-gate 		return ((*xdr_func)(in_xdrs, xdr_ptr));
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	return (__rpc_gss_unwrap_data(gss_parms->service,
19284a3b0527SAndy Fiddaman 	    (gss_ctx_id_t)gss_parms->context,
19294a3b0527SAndy Fiddaman 	    gss_parms->seq_num,
19304a3b0527SAndy Fiddaman 	    gss_parms->qop_rcvd,
19314a3b0527SAndy Fiddaman 	    in_xdrs, xdr_func, xdr_ptr));
19327c478bd9Sstevel@tonic-gate }
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate /* ARGSUSED */
19367c478bd9Sstevel@tonic-gate int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)19377c478bd9Sstevel@tonic-gate rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
19387c478bd9Sstevel@tonic-gate {
19397c478bd9Sstevel@tonic-gate 	return (0);
19407c478bd9Sstevel@tonic-gate }
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate /*
19437c478bd9Sstevel@tonic-gate  * Add retransmit entry to the context cache entry for a new xid.
19447c478bd9Sstevel@tonic-gate  * If there is already an entry, delete it before adding the new one.
19457c478bd9Sstevel@tonic-gate  */
retrans_add(client,xid,result)19467c478bd9Sstevel@tonic-gate static void retrans_add(client, xid, result)
19477c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data *client;
19487c478bd9Sstevel@tonic-gate 	uint32_t	xid;
19497c478bd9Sstevel@tonic-gate 	rpc_gss_init_res *result;
19507c478bd9Sstevel@tonic-gate {
19517c478bd9Sstevel@tonic-gate 	retrans_entry	*rdata;
19527c478bd9Sstevel@tonic-gate 
19537c478bd9Sstevel@tonic-gate 	if (client->retrans_data && client->retrans_data->xid == xid)
19547c478bd9Sstevel@tonic-gate 		return;
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 	rdata = kmem_zalloc(sizeof (*rdata), KM_SLEEP);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	if (rdata == NULL)
19597c478bd9Sstevel@tonic-gate 		return;
19607c478bd9Sstevel@tonic-gate 
19617c478bd9Sstevel@tonic-gate 	rdata->xid = xid;
19627c478bd9Sstevel@tonic-gate 	rdata->result = *result;
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 	if (result->token.length != 0) {
19657c478bd9Sstevel@tonic-gate 		GSS_DUP_BUFFER(rdata->result.token, result->token);
19667c478bd9Sstevel@tonic-gate 	}
19677c478bd9Sstevel@tonic-gate 
19687c478bd9Sstevel@tonic-gate 	if (client->retrans_data)
19697c478bd9Sstevel@tonic-gate 		retrans_del(client);
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 	client->retrans_data = rdata;
19727c478bd9Sstevel@tonic-gate }
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate /*
19757c478bd9Sstevel@tonic-gate  * Delete the retransmit data from the context cache entry.
19767c478bd9Sstevel@tonic-gate  */
retrans_del(client)19777c478bd9Sstevel@tonic-gate static void retrans_del(client)
19787c478bd9Sstevel@tonic-gate 	svc_rpc_gss_data *client;
19797c478bd9Sstevel@tonic-gate {
19807c478bd9Sstevel@tonic-gate 	retrans_entry *rdata;
19817c478bd9Sstevel@tonic-gate 	OM_uint32 minor_stat;
19827c478bd9Sstevel@tonic-gate 
19837c478bd9Sstevel@tonic-gate 	if (client->retrans_data == NULL)
19847c478bd9Sstevel@tonic-gate 		return;
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 	rdata = client->retrans_data;
19877c478bd9Sstevel@tonic-gate 	if (rdata->result.token.length != 0) {
19887c478bd9Sstevel@tonic-gate 	    (void) gss_release_buffer(&minor_stat, &rdata->result.token);
19897c478bd9Sstevel@tonic-gate 	}
19907c478bd9Sstevel@tonic-gate 
19917c478bd9Sstevel@tonic-gate 	kmem_free((caddr_t)rdata, sizeof (*rdata));
19927c478bd9Sstevel@tonic-gate 	client->retrans_data = NULL;
19937c478bd9Sstevel@tonic-gate }
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate /*
19967c478bd9Sstevel@tonic-gate  * This function frees the following fields of svc_rpc_gss_data:
19977c478bd9Sstevel@tonic-gate  *	client_name, raw_cred.client_principal, raw_cred.mechanism.
19987c478bd9Sstevel@tonic-gate  */
19997c478bd9Sstevel@tonic-gate static void
common_client_data_free(svc_rpc_gss_data * client_data)20007c478bd9Sstevel@tonic-gate common_client_data_free(svc_rpc_gss_data *client_data)
20017c478bd9Sstevel@tonic-gate {
20027c478bd9Sstevel@tonic-gate 	if (client_data->client_name.length > 0) {
20037c478bd9Sstevel@tonic-gate 		(void) gss_release_buffer(NULL, &client_data->client_name);
20047c478bd9Sstevel@tonic-gate 	}
20057c478bd9Sstevel@tonic-gate 
20067c478bd9Sstevel@tonic-gate 	if (client_data->raw_cred.client_principal) {
20077c478bd9Sstevel@tonic-gate 		kmem_free((caddr_t)client_data->raw_cred.client_principal,
2008bfd8310aSGlenn Barry 		    client_data->raw_cred.client_principal->len +
2009bfd8310aSGlenn Barry 		    sizeof (int));
20107c478bd9Sstevel@tonic-gate 		client_data->raw_cred.client_principal = NULL;
20117c478bd9Sstevel@tonic-gate 	}
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 	/*
20147c478bd9Sstevel@tonic-gate 	 * In the user GSS-API library, mechanism (mech_type returned
20157c478bd9Sstevel@tonic-gate 	 * by gss_accept_sec_context) is static storage, however
20167c478bd9Sstevel@tonic-gate 	 * since all the work is done for gss_accept_sec_context under
20177c478bd9Sstevel@tonic-gate 	 * gssd, what is returned in the kernel, is a copy from the oid
20187c478bd9Sstevel@tonic-gate 	 * obtained under from gssd, so need to free it when destroying
20197c478bd9Sstevel@tonic-gate 	 * the client data.
20207c478bd9Sstevel@tonic-gate 	 */
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	if (client_data->raw_cred.mechanism) {
20237c478bd9Sstevel@tonic-gate 		kgss_free_oid(client_data->raw_cred.mechanism);
20247c478bd9Sstevel@tonic-gate 		client_data->raw_cred.mechanism = NULL;
20257c478bd9Sstevel@tonic-gate 	}
20267c478bd9Sstevel@tonic-gate }
2027