xref: /onnv-gate/usr/src/uts/common/rpc/sec_gss/svc_rpcsec_gss.c (revision 12553:e64e5d843075)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54554Sps57422  * Common Development and Distribution License (the "License").
64554Sps57422  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12553SKaren.Rochford@Sun.COM  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
270Sstevel@tonic-gate  *
280Sstevel@tonic-gate  * $Id: svc_auth_gssapi.c,v 1.19 1994/10/27 12:38:51 jik Exp $
290Sstevel@tonic-gate  */
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate  * Server side handling of RPCSEC_GSS flavor.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include <sys/systm.h>
360Sstevel@tonic-gate #include <sys/kstat.h>
370Sstevel@tonic-gate #include <sys/cmn_err.h>
380Sstevel@tonic-gate #include <sys/debug.h>
390Sstevel@tonic-gate #include <sys/types.h>
400Sstevel@tonic-gate #include <sys/time.h>
410Sstevel@tonic-gate #include <gssapi/gssapi.h>
420Sstevel@tonic-gate #include <gssapi/gssapi_ext.h>
430Sstevel@tonic-gate #include <rpc/rpc.h>
440Sstevel@tonic-gate #include <rpc/rpcsec_defs.h>
4510721SGlenn.Barry@Sun.COM #include <sys/sunddi.h>
4610721SGlenn.Barry@Sun.COM #include <sys/atomic.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate extern bool_t __rpc_gss_make_principal(rpc_gss_principal_t *, gss_buffer_t);
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #ifdef	DEBUG
510Sstevel@tonic-gate extern void prom_printf();
520Sstevel@tonic-gate #endif
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #ifdef  _KERNEL
550Sstevel@tonic-gate #define	memcmp(a, b, l) bcmp((a), (b), (l))
560Sstevel@tonic-gate #endif
570Sstevel@tonic-gate 
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*
600Sstevel@tonic-gate  * Sequence window definitions.
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate #define	SEQ_ARR_SIZE	4
630Sstevel@tonic-gate #define	SEQ_WIN		(SEQ_ARR_SIZE*32)
640Sstevel@tonic-gate #define	SEQ_HI_BIT	0x80000000
650Sstevel@tonic-gate #define	SEQ_LO_BIT	1
660Sstevel@tonic-gate #define	DIV_BY_32	5
670Sstevel@tonic-gate #define	SEQ_MASK	0x1f
680Sstevel@tonic-gate #define	SEQ_MAX		((unsigned int)0x80000000)
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /* cache retransmit data */
720Sstevel@tonic-gate typedef struct _retrans_entry {
730Sstevel@tonic-gate 	uint32_t	xid;
740Sstevel@tonic-gate 	rpc_gss_init_res result;
750Sstevel@tonic-gate } retrans_entry;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate  * Server side RPCSEC_GSS context information.
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate typedef struct _svc_rpc_gss_data {
810Sstevel@tonic-gate 	struct _svc_rpc_gss_data	*next, *prev;
820Sstevel@tonic-gate 	struct _svc_rpc_gss_data	*lru_next, *lru_prev;
830Sstevel@tonic-gate 	bool_t				established;
840Sstevel@tonic-gate 	gss_ctx_id_t			context;
850Sstevel@tonic-gate 	gss_buffer_desc			client_name;
860Sstevel@tonic-gate 	time_t				expiration;
870Sstevel@tonic-gate 	uint_t				seq_num;
880Sstevel@tonic-gate 	uint_t				seq_bits[SEQ_ARR_SIZE];
890Sstevel@tonic-gate 	uint_t				key;
900Sstevel@tonic-gate 	OM_uint32			qop;
910Sstevel@tonic-gate 	bool_t				done_docallback;
920Sstevel@tonic-gate 	bool_t				locked;
930Sstevel@tonic-gate 	rpc_gss_rawcred_t		raw_cred;
940Sstevel@tonic-gate 	rpc_gss_ucred_t			u_cred;
950Sstevel@tonic-gate 	time_t				u_cred_set;
960Sstevel@tonic-gate 	void				*cookie;
970Sstevel@tonic-gate 	gss_cred_id_t			deleg;
980Sstevel@tonic-gate 	kmutex_t			clm;
990Sstevel@tonic-gate 	int				ref_cnt;
1000Sstevel@tonic-gate 	time_t				last_ref_time;
1010Sstevel@tonic-gate 	bool_t				stale;
1020Sstevel@tonic-gate 	retrans_entry			*retrans_data;
1030Sstevel@tonic-gate } svc_rpc_gss_data;
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate  * Data structures used for LRU based context management.
1070Sstevel@tonic-gate  */
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate #define	HASH(key) ((key) % svc_rpc_gss_hashmod)
1110Sstevel@tonic-gate /* Size of hash table for svc_rpc_gss_data structures */
1120Sstevel@tonic-gate #define	GSS_DATA_HASH_SIZE	1024
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /*
1150Sstevel@tonic-gate  * The following two defines specify a time delta that is used in
1160Sstevel@tonic-gate  * sweep_clients. When the last_ref_time of a context is older than
1170Sstevel@tonic-gate  * than the current time minus the delta, i.e, the context has not
1180Sstevel@tonic-gate  * been referenced in the last delta seconds, we will return the
1190Sstevel@tonic-gate  * context back to the cache if the ref_cnt is zero. The first delta
1200Sstevel@tonic-gate  * value will be used when sweep_clients is called from
1210Sstevel@tonic-gate  * svc_data_reclaim, the kmem_cache reclaim call back. We will reclaim
1220Sstevel@tonic-gate  * all entries except those that are currently "active". By active we
1230Sstevel@tonic-gate  * mean those that have been referenced in the last ACTIVE_DELTA
1240Sstevel@tonic-gate  * seconds. If sweep_client is not being called from reclaim, then we
1250Sstevel@tonic-gate  * will reclaim all entries that are "inactive". By inactive we mean
1260Sstevel@tonic-gate  * those entries that have not been accessed in INACTIVE_DELTA
1270Sstevel@tonic-gate  * seconds.  Note we always assume that ACTIVE_DELTA is less than
1280Sstevel@tonic-gate  * INACTIVE_DELTA, so that reaping entries from a reclaim operation
1290Sstevel@tonic-gate  * will necessarily imply reaping all "inactive" entries and then
1300Sstevel@tonic-gate  * some.
1310Sstevel@tonic-gate  */
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate  * If low on memory reap cache entries that have not been active for
1350Sstevel@tonic-gate  * ACTIVE_DELTA seconds and have a ref_cnt equal to zero.
1360Sstevel@tonic-gate  */
1370Sstevel@tonic-gate #define	ACTIVE_DELTA		30*60		/* 30 minutes */
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate /*
1400Sstevel@tonic-gate  * If in sweeping contexts we find contexts with a ref_cnt equal to zero
1410Sstevel@tonic-gate  * and the context has not been referenced in INACTIVE_DELTA seconds, return
1420Sstevel@tonic-gate  * the entry to the cache.
1430Sstevel@tonic-gate  */
1440Sstevel@tonic-gate #define	INACTIVE_DELTA		8*60*60		/* 8 hours */
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate int				svc_rpc_gss_hashmod = GSS_DATA_HASH_SIZE;
1470Sstevel@tonic-gate static svc_rpc_gss_data		**clients;
1480Sstevel@tonic-gate static svc_rpc_gss_data		*lru_first, *lru_last;
1490Sstevel@tonic-gate static time_t			sweep_interval = 60*60;
1500Sstevel@tonic-gate static time_t			last_swept = 0;
1510Sstevel@tonic-gate static int			num_gss_contexts = 0;
1520Sstevel@tonic-gate static time_t			svc_rpcgss_gid_timeout = 60*60*12;
1530Sstevel@tonic-gate static kmem_cache_t		*svc_data_handle;
1540Sstevel@tonic-gate static time_t			svc_rpc_gss_active_delta = ACTIVE_DELTA;
1550Sstevel@tonic-gate static time_t			svc_rpc_gss_inactive_delta = INACTIVE_DELTA;
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate /*
1580Sstevel@tonic-gate  * lock used with context/lru variables
1590Sstevel@tonic-gate  */
1600Sstevel@tonic-gate static kmutex_t			ctx_mutex;
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate  * Data structure to contain cache statistics
1640Sstevel@tonic-gate  */
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate static struct {
1670Sstevel@tonic-gate 	int64_t total_entries_allocated;
1680Sstevel@tonic-gate 	int64_t no_reclaims;
1690Sstevel@tonic-gate 	int64_t no_returned_by_reclaim;
1700Sstevel@tonic-gate } svc_rpc_gss_cache_stats;
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate /*
1740Sstevel@tonic-gate  * lock used with server credential variables list
1750Sstevel@tonic-gate  *
1760Sstevel@tonic-gate  * server cred list locking guidelines:
1770Sstevel@tonic-gate  * - Writer's lock holder has exclusive access to the list
1780Sstevel@tonic-gate  */
1790Sstevel@tonic-gate static krwlock_t		cred_lock;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate /*
1820Sstevel@tonic-gate  * server callback list
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate typedef struct rpc_gss_cblist_s {
1850Sstevel@tonic-gate 	struct rpc_gss_cblist_s		*next;
1860Sstevel@tonic-gate 	rpc_gss_callback_t	cb;
1870Sstevel@tonic-gate } rpc_gss_cblist_t;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate static rpc_gss_cblist_t			*rpc_gss_cblist = NULL;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate /*
1920Sstevel@tonic-gate  * lock used with callback variables
1930Sstevel@tonic-gate  */
1940Sstevel@tonic-gate static kmutex_t			cb_mutex;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate  * forward declarations
1980Sstevel@tonic-gate  */
1990Sstevel@tonic-gate static bool_t			svc_rpc_gss_wrap();
2000Sstevel@tonic-gate static bool_t			svc_rpc_gss_unwrap();
2010Sstevel@tonic-gate static svc_rpc_gss_data		*create_client();
2020Sstevel@tonic-gate static svc_rpc_gss_data		*get_client();
2030Sstevel@tonic-gate static svc_rpc_gss_data		*find_client();
2040Sstevel@tonic-gate static void			destroy_client();
2050Sstevel@tonic-gate static void			sweep_clients(bool_t);
2060Sstevel@tonic-gate static void			insert_client();
2070Sstevel@tonic-gate static bool_t			check_verf(struct rpc_msg *, gss_ctx_id_t,
2080Sstevel@tonic-gate 					int *, uid_t);
2090Sstevel@tonic-gate static bool_t			set_response_verf();
2100Sstevel@tonic-gate static void			retrans_add(svc_rpc_gss_data *, uint32_t,
2110Sstevel@tonic-gate 					rpc_gss_init_res *);
2120Sstevel@tonic-gate static void			retrans_del(svc_rpc_gss_data *);
2130Sstevel@tonic-gate static bool_t			transfer_sec_context(svc_rpc_gss_data *);
2140Sstevel@tonic-gate static void			common_client_data_free(svc_rpc_gss_data *);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate  * server side wrap/unwrap routines
2180Sstevel@tonic-gate  */
2190Sstevel@tonic-gate struct svc_auth_ops svc_rpc_gss_ops = {
2200Sstevel@tonic-gate 	svc_rpc_gss_wrap,
2210Sstevel@tonic-gate 	svc_rpc_gss_unwrap,
2220Sstevel@tonic-gate };
2230Sstevel@tonic-gate 
22410721SGlenn.Barry@Sun.COM /* taskq(9F) */
22510721SGlenn.Barry@Sun.COM typedef struct svcrpcsec_gss_taskq_arg {
22610721SGlenn.Barry@Sun.COM 	SVCXPRT			*rq_xprt;
22710721SGlenn.Barry@Sun.COM 	rpc_gss_init_arg	*rpc_call_arg;
22810721SGlenn.Barry@Sun.COM 	struct rpc_msg		*msg;
22910721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*client_data;
23010721SGlenn.Barry@Sun.COM 	uint_t			cr_version;
23110721SGlenn.Barry@Sun.COM 	rpc_gss_service_t	cr_service;
23210721SGlenn.Barry@Sun.COM } svcrpcsec_gss_taskq_arg_t;
23310721SGlenn.Barry@Sun.COM 
23410721SGlenn.Barry@Sun.COM /* gssd is single threaded, so 1 thread for the taskq is probably good/ok */
23510721SGlenn.Barry@Sun.COM int rpcsec_gss_init_taskq_nthreads = 1;
23610721SGlenn.Barry@Sun.COM static ddi_taskq_t *svcrpcsec_gss_init_taskq = NULL;
23710721SGlenn.Barry@Sun.COM 
23810721SGlenn.Barry@Sun.COM extern struct rpc_msg *rpc_msg_dup(struct rpc_msg *);
23910721SGlenn.Barry@Sun.COM extern void rpc_msg_free(struct rpc_msg **, int);
24010721SGlenn.Barry@Sun.COM 
24110721SGlenn.Barry@Sun.COM /*
24210721SGlenn.Barry@Sun.COM  * from svc_clts.c:
24310721SGlenn.Barry@Sun.COM  * Transport private data.
24410721SGlenn.Barry@Sun.COM  * Kept in xprt->xp_p2buf.
24510721SGlenn.Barry@Sun.COM  */
24610721SGlenn.Barry@Sun.COM struct udp_data {
24710721SGlenn.Barry@Sun.COM 	mblk_t	*ud_resp;			/* buffer for response */
24810721SGlenn.Barry@Sun.COM 	mblk_t	*ud_inmp;			/* mblk chain of request */
24910721SGlenn.Barry@Sun.COM };
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate /*ARGSUSED*/
2520Sstevel@tonic-gate static int
svc_gss_data_create(void * buf,void * pdata,int kmflag)2530Sstevel@tonic-gate svc_gss_data_create(void *buf, void *pdata, int kmflag)
2540Sstevel@tonic-gate {
2550Sstevel@tonic-gate 	svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	mutex_init(&client_data->clm, NULL, MUTEX_DEFAULT, NULL);
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	return (0);
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate /*ARGSUSED*/
2630Sstevel@tonic-gate static void
svc_gss_data_destroy(void * buf,void * pdata)2640Sstevel@tonic-gate svc_gss_data_destroy(void *buf, void *pdata)
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	svc_rpc_gss_data *client_data = (svc_rpc_gss_data *)buf;
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	mutex_destroy(&client_data->clm);
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate /*ARGSUSED*/
2730Sstevel@tonic-gate static void
svc_gss_data_reclaim(void * pdata)2740Sstevel@tonic-gate svc_gss_data_reclaim(void *pdata)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	svc_rpc_gss_cache_stats.no_reclaims++;
2790Sstevel@tonic-gate 	sweep_clients(TRUE);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate /*
2850Sstevel@tonic-gate  *  Init stuff on the server side.
2860Sstevel@tonic-gate  */
2870Sstevel@tonic-gate void
svc_gss_init()2880Sstevel@tonic-gate svc_gss_init()
2890Sstevel@tonic-gate {
2900Sstevel@tonic-gate 	mutex_init(&cb_mutex, NULL, MUTEX_DEFAULT, NULL);
2910Sstevel@tonic-gate 	mutex_init(&ctx_mutex, NULL, MUTEX_DEFAULT, NULL);
2920Sstevel@tonic-gate 	rw_init(&cred_lock, NULL, RW_DEFAULT, NULL);
2930Sstevel@tonic-gate 	clients = (svc_rpc_gss_data **)
29410721SGlenn.Barry@Sun.COM 	    kmem_zalloc(svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *),
29510721SGlenn.Barry@Sun.COM 	    KM_SLEEP);
2960Sstevel@tonic-gate 	svc_data_handle = kmem_cache_create("rpc_gss_data_cache",
29710721SGlenn.Barry@Sun.COM 	    sizeof (svc_rpc_gss_data), 0,
29810721SGlenn.Barry@Sun.COM 	    svc_gss_data_create,
29910721SGlenn.Barry@Sun.COM 	    svc_gss_data_destroy,
30010721SGlenn.Barry@Sun.COM 	    svc_gss_data_reclaim,
30110721SGlenn.Barry@Sun.COM 	    NULL, NULL, 0);
3020Sstevel@tonic-gate 
30310721SGlenn.Barry@Sun.COM 	if (svcrpcsec_gss_init_taskq == NULL) {
30410721SGlenn.Barry@Sun.COM 		svcrpcsec_gss_init_taskq = ddi_taskq_create(NULL,
30510721SGlenn.Barry@Sun.COM 		    "rpcsec_gss_init_taskq", rpcsec_gss_init_taskq_nthreads,
30610721SGlenn.Barry@Sun.COM 		    TASKQ_DEFAULTPRI, 0);
30710721SGlenn.Barry@Sun.COM 		if (svcrpcsec_gss_init_taskq == NULL)
30810721SGlenn.Barry@Sun.COM 			cmn_err(CE_NOTE,
30910721SGlenn.Barry@Sun.COM 			    "svc_gss_init: ddi_taskq_create failed");
31010721SGlenn.Barry@Sun.COM 	}
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate  * Destroy structures allocated in svc_gss_init().
3150Sstevel@tonic-gate  * This routine is called by _init() if mod_install() failed.
3160Sstevel@tonic-gate  */
3170Sstevel@tonic-gate void
svc_gss_fini()3180Sstevel@tonic-gate svc_gss_fini()
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	mutex_destroy(&cb_mutex);
3210Sstevel@tonic-gate 	mutex_destroy(&ctx_mutex);
3220Sstevel@tonic-gate 	rw_destroy(&cred_lock);
3230Sstevel@tonic-gate 	kmem_free(clients, svc_rpc_gss_hashmod * sizeof (svc_rpc_gss_data *));
3240Sstevel@tonic-gate 	kmem_cache_destroy(svc_data_handle);
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate /*
3280Sstevel@tonic-gate  * Cleanup routine for destroying context, called after service
3290Sstevel@tonic-gate  * procedure is executed. Actually we just decrement the reference count
3300Sstevel@tonic-gate  * associated with this context. If the reference count is zero and the
3310Sstevel@tonic-gate  * context is marked as stale, we would then destroy the context. Additionally,
3320Sstevel@tonic-gate  * we check if its been longer than sweep_interval since the last sweep_clients
3330Sstevel@tonic-gate  * was run, and if so run sweep_clients to free all stale contexts with zero
3340Sstevel@tonic-gate  * reference counts or contexts that are old. (Haven't been access in
3350Sstevel@tonic-gate  * svc_rpc_inactive_delta seconds).
3360Sstevel@tonic-gate  */
3370Sstevel@tonic-gate void
rpc_gss_cleanup(SVCXPRT * clone_xprt)3380Sstevel@tonic-gate rpc_gss_cleanup(SVCXPRT *clone_xprt)
3390Sstevel@tonic-gate {
3400Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
3410Sstevel@tonic-gate 	SVCAUTH			*svcauth;
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	/*
3440Sstevel@tonic-gate 	 * First check if current context needs to be cleaned up.
3450Sstevel@tonic-gate 	 * There might be other threads stale this client data
3460Sstevel@tonic-gate 	 * in between.
3470Sstevel@tonic-gate 	 */
3480Sstevel@tonic-gate 	svcauth = &clone_xprt->xp_auth;
3490Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
3500Sstevel@tonic-gate 	if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
3510Sstevel@tonic-gate 		mutex_enter(&cl->clm);
3520Sstevel@tonic-gate 		ASSERT(cl->ref_cnt > 0);
3530Sstevel@tonic-gate 		if (--cl->ref_cnt == 0 && cl->stale) {
3540Sstevel@tonic-gate 			mutex_exit(&cl->clm);
3550Sstevel@tonic-gate 			destroy_client(cl);
3560Sstevel@tonic-gate 			svcauth->svc_ah_private = NULL;
3570Sstevel@tonic-gate 		} else
3580Sstevel@tonic-gate 			mutex_exit(&cl->clm);
3590Sstevel@tonic-gate 	}
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	/*
3620Sstevel@tonic-gate 	 * Check for other expired contexts.
3630Sstevel@tonic-gate 	 */
3640Sstevel@tonic-gate 	if ((gethrestime_sec() - last_swept) > sweep_interval)
3650Sstevel@tonic-gate 		sweep_clients(FALSE);
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate /*
3710Sstevel@tonic-gate  * Shift the array arr of length arrlen right by nbits bits.
3720Sstevel@tonic-gate  */
3730Sstevel@tonic-gate static void
shift_bits(arr,arrlen,nbits)3740Sstevel@tonic-gate shift_bits(arr, arrlen, nbits)
3750Sstevel@tonic-gate 	uint_t	*arr;
3760Sstevel@tonic-gate 	int	arrlen;
3770Sstevel@tonic-gate 	int	nbits;
3780Sstevel@tonic-gate {
3790Sstevel@tonic-gate 	int	i, j;
3800Sstevel@tonic-gate 	uint_t	lo, hi;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	/*
3830Sstevel@tonic-gate 	 * If the number of bits to be shifted exceeds SEQ_WIN, just
3840Sstevel@tonic-gate 	 * zero out the array.
3850Sstevel@tonic-gate 	 */
3860Sstevel@tonic-gate 	if (nbits < SEQ_WIN) {
3870Sstevel@tonic-gate 		for (i = 0; i < nbits; i++) {
3880Sstevel@tonic-gate 			hi = 0;
3890Sstevel@tonic-gate 			for (j = 0; j < arrlen; j++) {
3900Sstevel@tonic-gate 				lo = arr[j] & SEQ_LO_BIT;
3910Sstevel@tonic-gate 				arr[j] >>= 1;
3920Sstevel@tonic-gate 				if (hi)
3930Sstevel@tonic-gate 					arr[j] |= SEQ_HI_BIT;
3940Sstevel@tonic-gate 				hi = lo;
3950Sstevel@tonic-gate 			}
3960Sstevel@tonic-gate 		}
3970Sstevel@tonic-gate 	} else {
3980Sstevel@tonic-gate 		for (j = 0; j < arrlen; j++)
3990Sstevel@tonic-gate 			arr[j] = 0;
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate /*
4040Sstevel@tonic-gate  * Check that the received sequence number seq_num is valid.
4050Sstevel@tonic-gate  */
4060Sstevel@tonic-gate static bool_t
check_seq(cl,seq_num,kill_context)4070Sstevel@tonic-gate check_seq(cl, seq_num, kill_context)
4080Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
4090Sstevel@tonic-gate 	uint_t			seq_num;
4100Sstevel@tonic-gate 	bool_t			*kill_context;
4110Sstevel@tonic-gate {
4120Sstevel@tonic-gate 	int			i, j;
4130Sstevel@tonic-gate 	uint_t			bit;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	/*
4160Sstevel@tonic-gate 	 * If it exceeds the maximum, kill context.
4170Sstevel@tonic-gate 	 */
4180Sstevel@tonic-gate 	if (seq_num >= SEQ_MAX) {
4190Sstevel@tonic-gate 		*kill_context = TRUE;
4200Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: seq_num not valid\n");
4210Sstevel@tonic-gate 		return (FALSE);
4220Sstevel@tonic-gate 	}
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/*
4250Sstevel@tonic-gate 	 * If greater than the last seen sequence number, just shift
4260Sstevel@tonic-gate 	 * the sequence window so that it starts at the new sequence
4270Sstevel@tonic-gate 	 * number and extends downwards by SEQ_WIN.
4280Sstevel@tonic-gate 	 */
4290Sstevel@tonic-gate 	if (seq_num > cl->seq_num) {
4300Sstevel@tonic-gate 		(void) shift_bits(cl->seq_bits, SEQ_ARR_SIZE,
4310Sstevel@tonic-gate 				(int)(seq_num - cl->seq_num));
4320Sstevel@tonic-gate 		cl->seq_bits[0] |= SEQ_HI_BIT;
4330Sstevel@tonic-gate 		cl->seq_num = seq_num;
4340Sstevel@tonic-gate 		return (TRUE);
4350Sstevel@tonic-gate 	}
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	/*
4380Sstevel@tonic-gate 	 * If it is outside the sequence window, return failure.
4390Sstevel@tonic-gate 	 */
4400Sstevel@tonic-gate 	i = cl->seq_num - seq_num;
4410Sstevel@tonic-gate 	if (i >= SEQ_WIN) {
4420Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: seq_num is outside the window\n");
4430Sstevel@tonic-gate 		return (FALSE);
4440Sstevel@tonic-gate 	}
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	/*
4470Sstevel@tonic-gate 	 * If within sequence window, set the bit corresponding to it
4480Sstevel@tonic-gate 	 * if not already seen;  if already seen, return failure.
4490Sstevel@tonic-gate 	 */
4500Sstevel@tonic-gate 	j = SEQ_MASK - (i & SEQ_MASK);
4510Sstevel@tonic-gate 	bit = j > 0 ? (1 << j) : 1;
4520Sstevel@tonic-gate 	i >>= DIV_BY_32;
4530Sstevel@tonic-gate 	if (cl->seq_bits[i] & bit) {
4540Sstevel@tonic-gate 		RPCGSS_LOG0(4, "check_seq: sequence number already seen\n");
4550Sstevel@tonic-gate 		return (FALSE);
4560Sstevel@tonic-gate 	}
4570Sstevel@tonic-gate 	cl->seq_bits[i] |= bit;
4580Sstevel@tonic-gate 	return (TRUE);
4590Sstevel@tonic-gate }
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate /*
4620Sstevel@tonic-gate  * Set server callback.
4630Sstevel@tonic-gate  */
4640Sstevel@tonic-gate bool_t
rpc_gss_set_callback(cb)4650Sstevel@tonic-gate rpc_gss_set_callback(cb)
4660Sstevel@tonic-gate 	rpc_gss_callback_t	*cb;
4670Sstevel@tonic-gate {
4680Sstevel@tonic-gate 	rpc_gss_cblist_t		*cbl, *tmp;
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	if (cb->callback == NULL) {
4710Sstevel@tonic-gate 		RPCGSS_LOG0(1, "rpc_gss_set_callback: no callback to set\n");
4720Sstevel@tonic-gate 		return (FALSE);
4730Sstevel@tonic-gate 	}
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	/* check if there is already an entry in the rpc_gss_cblist. */
4760Sstevel@tonic-gate 	mutex_enter(&cb_mutex);
4770Sstevel@tonic-gate 	if (rpc_gss_cblist) {
4780Sstevel@tonic-gate 		for (tmp = rpc_gss_cblist; tmp != NULL; tmp = tmp->next) {
4790Sstevel@tonic-gate 			if ((tmp->cb.callback == cb->callback) &&
4800Sstevel@tonic-gate 			    (tmp->cb.version == cb->version) &&
4810Sstevel@tonic-gate 			    (tmp->cb.program == cb->program)) {
4820Sstevel@tonic-gate 				mutex_exit(&cb_mutex);
4830Sstevel@tonic-gate 				return (TRUE);
4840Sstevel@tonic-gate 			}
4850Sstevel@tonic-gate 		}
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	/* Not in rpc_gss_cblist.  Create a new entry. */
4890Sstevel@tonic-gate 	if ((cbl = (rpc_gss_cblist_t *)kmem_alloc(sizeof (*cbl), KM_SLEEP))
4900Sstevel@tonic-gate 	    == NULL) {
4910Sstevel@tonic-gate 		mutex_exit(&cb_mutex);
4920Sstevel@tonic-gate 		return (FALSE);
4930Sstevel@tonic-gate 	}
4940Sstevel@tonic-gate 	cbl->cb = *cb;
4950Sstevel@tonic-gate 	cbl->next = rpc_gss_cblist;
4960Sstevel@tonic-gate 	rpc_gss_cblist = cbl;
4970Sstevel@tonic-gate 	mutex_exit(&cb_mutex);
4980Sstevel@tonic-gate 	return (TRUE);
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate /*
5020Sstevel@tonic-gate  * Locate callback (if specified) and call server.  Release any
5030Sstevel@tonic-gate  * delegated credentials unless passed to server and the server
5040Sstevel@tonic-gate  * accepts the context.  If a callback is not specified, accept
5050Sstevel@tonic-gate  * the incoming context.
5060Sstevel@tonic-gate  */
5070Sstevel@tonic-gate static bool_t
do_callback(req,client_data)5080Sstevel@tonic-gate do_callback(req, client_data)
5090Sstevel@tonic-gate 	struct svc_req		*req;
5100Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
5110Sstevel@tonic-gate {
5120Sstevel@tonic-gate 	rpc_gss_cblist_t		*cbl;
5130Sstevel@tonic-gate 	bool_t			ret = TRUE, found = FALSE;
5140Sstevel@tonic-gate 	rpc_gss_lock_t		lock;
5150Sstevel@tonic-gate 	OM_uint32		minor;
5160Sstevel@tonic-gate 	mutex_enter(&cb_mutex);
5170Sstevel@tonic-gate 	for (cbl = rpc_gss_cblist; cbl != NULL; cbl = cbl->next) {
5180Sstevel@tonic-gate 		if (req->rq_prog != cbl->cb.program ||
5190Sstevel@tonic-gate 					req->rq_vers != cbl->cb.version)
5200Sstevel@tonic-gate 			continue;
5210Sstevel@tonic-gate 		found = TRUE;
5220Sstevel@tonic-gate 		lock.locked = FALSE;
5230Sstevel@tonic-gate 		lock.raw_cred = &client_data->raw_cred;
5240Sstevel@tonic-gate 		ret = (*cbl->cb.callback)(req, client_data->deleg,
5250Sstevel@tonic-gate 			client_data->context, &lock, &client_data->cookie);
5260Sstevel@tonic-gate 		req->rq_xprt->xp_cookie = client_data->cookie;
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 		if (ret) {
5290Sstevel@tonic-gate 			client_data->locked = lock.locked;
5300Sstevel@tonic-gate 			client_data->deleg = GSS_C_NO_CREDENTIAL;
5310Sstevel@tonic-gate 		}
5320Sstevel@tonic-gate 		break;
5330Sstevel@tonic-gate 	}
5340Sstevel@tonic-gate 	if (!found) {
5350Sstevel@tonic-gate 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
5360Sstevel@tonic-gate 			(void) kgss_release_cred(&minor, &client_data->deleg,
5370Sstevel@tonic-gate 					crgetuid(CRED()));
5380Sstevel@tonic-gate 			client_data->deleg = GSS_C_NO_CREDENTIAL;
5390Sstevel@tonic-gate 		}
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate 	mutex_exit(&cb_mutex);
5420Sstevel@tonic-gate 	return (ret);
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate /*
5460Sstevel@tonic-gate  * Get caller credentials.
5470Sstevel@tonic-gate  */
5480Sstevel@tonic-gate bool_t
rpc_gss_getcred(req,rcred,ucred,cookie)5490Sstevel@tonic-gate rpc_gss_getcred(req, rcred, ucred, cookie)
5500Sstevel@tonic-gate 	struct svc_req		*req;
5510Sstevel@tonic-gate 	rpc_gss_rawcred_t	**rcred;
5520Sstevel@tonic-gate 	rpc_gss_ucred_t		**ucred;
5530Sstevel@tonic-gate 	void			**cookie;
5540Sstevel@tonic-gate {
5550Sstevel@tonic-gate 	SVCAUTH			*svcauth;
5560Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
5570Sstevel@tonic-gate 	int			gssstat, gidlen;
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	svcauth = &req->rq_xprt->xp_auth;
5600Sstevel@tonic-gate 	client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	mutex_enter(&client_data->clm);
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	if (rcred != NULL) {
5650Sstevel@tonic-gate 		svcauth->raw_cred = client_data->raw_cred;
5660Sstevel@tonic-gate 		*rcred = &svcauth->raw_cred;
5670Sstevel@tonic-gate 	}
5680Sstevel@tonic-gate 	if (ucred != NULL) {
5690Sstevel@tonic-gate 		*ucred = &client_data->u_cred;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 		if (client_data->u_cred_set == 0 ||
5720Sstevel@tonic-gate 		    client_data->u_cred_set < gethrestime_sec()) {
5730Sstevel@tonic-gate 		    if (client_data->u_cred_set == 0) {
5740Sstevel@tonic-gate 			if ((gssstat = kgsscred_expname_to_unix_cred(
5750Sstevel@tonic-gate 			    &client_data->client_name,
5760Sstevel@tonic-gate 			    &client_data->u_cred.uid,
5770Sstevel@tonic-gate 			    &client_data->u_cred.gid,
5780Sstevel@tonic-gate 			    &client_data->u_cred.gidlist,
5790Sstevel@tonic-gate 			    &gidlen, crgetuid(CRED()))) != GSS_S_COMPLETE) {
5800Sstevel@tonic-gate 				RPCGSS_LOG(1, "rpc_gss_getcred: "
5810Sstevel@tonic-gate 				    "kgsscred_expname_to_unix_cred failed %x\n",
5820Sstevel@tonic-gate 				    gssstat);
5830Sstevel@tonic-gate 				*ucred = NULL;
5840Sstevel@tonic-gate 			} else {
5850Sstevel@tonic-gate 				client_data->u_cred.gidlen = (short)gidlen;
5860Sstevel@tonic-gate 				client_data->u_cred_set =
5870Sstevel@tonic-gate 				    gethrestime_sec() + svc_rpcgss_gid_timeout;
5880Sstevel@tonic-gate 			}
5890Sstevel@tonic-gate 		    } else if (client_data->u_cred_set < gethrestime_sec()) {
5900Sstevel@tonic-gate 			if ((gssstat = kgss_get_group_info(
5910Sstevel@tonic-gate 			    client_data->u_cred.uid,
5920Sstevel@tonic-gate 			    &client_data->u_cred.gid,
5930Sstevel@tonic-gate 			    &client_data->u_cred.gidlist,
5940Sstevel@tonic-gate 			    &gidlen, crgetuid(CRED()))) != GSS_S_COMPLETE) {
5950Sstevel@tonic-gate 				RPCGSS_LOG(1, "rpc_gss_getcred: "
5960Sstevel@tonic-gate 				    "kgss_get_group_info failed %x\n",
5970Sstevel@tonic-gate 				    gssstat);
5980Sstevel@tonic-gate 				*ucred = NULL;
5990Sstevel@tonic-gate 			} else {
6000Sstevel@tonic-gate 				client_data->u_cred.gidlen = (short)gidlen;
6010Sstevel@tonic-gate 				client_data->u_cred_set =
6020Sstevel@tonic-gate 				    gethrestime_sec() + svc_rpcgss_gid_timeout;
6030Sstevel@tonic-gate 			}
6040Sstevel@tonic-gate 		    }
6050Sstevel@tonic-gate 		}
6060Sstevel@tonic-gate 	}
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	if (cookie != NULL)
6090Sstevel@tonic-gate 		*cookie = client_data->cookie;
6100Sstevel@tonic-gate 	req->rq_xprt->xp_cookie = client_data->cookie;
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	return (TRUE);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate /*
6180Sstevel@tonic-gate  * Transfer the context data from the user land to the kernel.
6190Sstevel@tonic-gate  */
transfer_sec_context(svc_rpc_gss_data * client_data)6200Sstevel@tonic-gate bool_t transfer_sec_context(svc_rpc_gss_data *client_data) {
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	gss_buffer_desc process_token;
6230Sstevel@tonic-gate 	OM_uint32 gssstat, minor;
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	/*
6260Sstevel@tonic-gate 	 * Call kgss_export_sec_context
6270Sstevel@tonic-gate 	 * if an error is returned log a message
6280Sstevel@tonic-gate 	 * go to error handling
6290Sstevel@tonic-gate 	 * Otherwise call kgss_import_sec_context to
6300Sstevel@tonic-gate 	 * convert the token into a context
6310Sstevel@tonic-gate 	 */
6320Sstevel@tonic-gate 	gssstat  = kgss_export_sec_context(&minor, client_data->context,
6330Sstevel@tonic-gate 				&process_token);
6340Sstevel@tonic-gate 	/*
6350Sstevel@tonic-gate 	 * if export_sec_context returns an error we delete the
6360Sstevel@tonic-gate 	 * context just to be safe.
6370Sstevel@tonic-gate 	 */
6380Sstevel@tonic-gate 	if (gssstat == GSS_S_NAME_NOT_MN) {
6390Sstevel@tonic-gate 		RPCGSS_LOG0(4, "svc_rpcsec_gss: export sec context "
6400Sstevel@tonic-gate 				"Kernel mod unavailable\n");
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	} else if (gssstat != GSS_S_COMPLETE) {
6430Sstevel@tonic-gate 		RPCGSS_LOG(1, "svc_rpcsec_gss: export sec context failed  "
6440Sstevel@tonic-gate 				" gssstat = 0x%x\n", gssstat);
6450Sstevel@tonic-gate 		(void) gss_release_buffer(&minor, &process_token);
6460Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
6470Sstevel@tonic-gate 				NULL);
6480Sstevel@tonic-gate 		return (FALSE);
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 	} else if (process_token.length == 0) {
6510Sstevel@tonic-gate 		RPCGSS_LOG0(1, "svc_rpcsec_gss:zero length token in response "
6520Sstevel@tonic-gate 				"for export_sec_context, but "
6530Sstevel@tonic-gate 				"gsstat == GSS_S_COMPLETE\n");
6540Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
6550Sstevel@tonic-gate 				NULL);
6560Sstevel@tonic-gate 		return (FALSE);
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	} else {
6590Sstevel@tonic-gate 		gssstat = kgss_import_sec_context(&minor, &process_token,
6600Sstevel@tonic-gate 					client_data->context);
6610Sstevel@tonic-gate 		if (gssstat != GSS_S_COMPLETE) {
6620Sstevel@tonic-gate 			RPCGSS_LOG(1, "svc_rpcsec_gss: import sec context "
6630Sstevel@tonic-gate 				" failed gssstat = 0x%x\n", gssstat);
6640Sstevel@tonic-gate 			(void) kgss_delete_sec_context(&minor,
6650Sstevel@tonic-gate 				&client_data->context, NULL);
6660Sstevel@tonic-gate 			(void) gss_release_buffer(&minor, &process_token);
6670Sstevel@tonic-gate 			return (FALSE);
6680Sstevel@tonic-gate 		}
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 		RPCGSS_LOG0(4, "gss_import_sec_context successful\n");
6710Sstevel@tonic-gate 		(void) gss_release_buffer(&minor, &process_token);
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 	return (TRUE);
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate /*
67810721SGlenn.Barry@Sun.COM  * do_gss_accept is called from a taskq and does all the work for a
67910721SGlenn.Barry@Sun.COM  * RPCSEC_GSS_INIT call (mostly calling kgss_accept_sec_context()).
6800Sstevel@tonic-gate  */
68110721SGlenn.Barry@Sun.COM 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)68210721SGlenn.Barry@Sun.COM do_gss_accept(
68310721SGlenn.Barry@Sun.COM 	SVCXPRT *xprt,
68410721SGlenn.Barry@Sun.COM 	rpc_gss_init_arg *call_arg,
68510721SGlenn.Barry@Sun.COM 	struct rpc_msg *msg,
68610721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data *client_data,
68710721SGlenn.Barry@Sun.COM 	uint_t cr_version,
68810721SGlenn.Barry@Sun.COM 	rpc_gss_service_t cr_service)
6890Sstevel@tonic-gate {
69010721SGlenn.Barry@Sun.COM 	rpc_gss_init_res	call_res;
6910Sstevel@tonic-gate 	gss_buffer_desc		output_token;
6920Sstevel@tonic-gate 	OM_uint32		gssstat, minor, minor_stat, time_rec;
6930Sstevel@tonic-gate 	int			ret_flags, ret;
6940Sstevel@tonic-gate 	gss_OID 		mech_type = GSS_C_NULL_OID;
6950Sstevel@tonic-gate 	int			free_mech_type = 1;
69610721SGlenn.Barry@Sun.COM 	struct svc_req		r, *rqst;
6970Sstevel@tonic-gate 
69810721SGlenn.Barry@Sun.COM 	rqst = &r;
69910721SGlenn.Barry@Sun.COM 	rqst->rq_xprt = xprt;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	/*
7020Sstevel@tonic-gate 	 * Initialize output_token.
7030Sstevel@tonic-gate 	 */
7040Sstevel@tonic-gate 	output_token.length = 0;
7050Sstevel@tonic-gate 	output_token.value = NULL;
7060Sstevel@tonic-gate 
70710721SGlenn.Barry@Sun.COM 	bzero((char *)&call_res, sizeof (call_res));
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	mutex_enter(&client_data->clm);
7100Sstevel@tonic-gate 	if (client_data->stale) {
7110Sstevel@tonic-gate 		ret = RPCSEC_GSS_NOCRED;
7120Sstevel@tonic-gate 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
7130Sstevel@tonic-gate 		goto error2;
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 	/*
7170Sstevel@tonic-gate 	 * Any response we send will use ctx_handle, so set it now;
7180Sstevel@tonic-gate 	 * also set seq_window since this won't change.
7190Sstevel@tonic-gate 	 */
7200Sstevel@tonic-gate 	call_res.ctx_handle.length = sizeof (client_data->key);
7210Sstevel@tonic-gate 	call_res.ctx_handle.value = (char *)&client_data->key;
7220Sstevel@tonic-gate 	call_res.seq_window = SEQ_WIN;
7230Sstevel@tonic-gate 
72410721SGlenn.Barry@Sun.COM 	gssstat = GSS_S_FAILURE;
72510721SGlenn.Barry@Sun.COM 	minor = 0;
72610721SGlenn.Barry@Sun.COM 	minor_stat = 0;
72710721SGlenn.Barry@Sun.COM 	rw_enter(&cred_lock, RW_READER);
72810721SGlenn.Barry@Sun.COM 
72910721SGlenn.Barry@Sun.COM 	if (client_data->client_name.length) {
73010721SGlenn.Barry@Sun.COM 		(void) gss_release_buffer(&minor,
73110721SGlenn.Barry@Sun.COM 		    &client_data->client_name);
73210721SGlenn.Barry@Sun.COM 	}
73310721SGlenn.Barry@Sun.COM 	gssstat = kgss_accept_sec_context(&minor_stat,
73410721SGlenn.Barry@Sun.COM 	    &client_data->context,
73510721SGlenn.Barry@Sun.COM 	    GSS_C_NO_CREDENTIAL,
73610721SGlenn.Barry@Sun.COM 	    call_arg,
73710721SGlenn.Barry@Sun.COM 	    GSS_C_NO_CHANNEL_BINDINGS,
73810721SGlenn.Barry@Sun.COM 	    &client_data->client_name,
73910721SGlenn.Barry@Sun.COM 	    &mech_type,
74010721SGlenn.Barry@Sun.COM 	    &output_token,
74110721SGlenn.Barry@Sun.COM 	    &ret_flags,
74210721SGlenn.Barry@Sun.COM 	    &time_rec,
74310721SGlenn.Barry@Sun.COM 	    NULL,		/* don't need a delegated cred back */
74410721SGlenn.Barry@Sun.COM 	    crgetuid(CRED()));
74510721SGlenn.Barry@Sun.COM 
74610721SGlenn.Barry@Sun.COM 	RPCGSS_LOG(4, "gssstat 0x%x \n", gssstat);
74710721SGlenn.Barry@Sun.COM 
74810721SGlenn.Barry@Sun.COM 	if (gssstat == GSS_S_COMPLETE) {
74910721SGlenn.Barry@Sun.COM 		/*
75010721SGlenn.Barry@Sun.COM 		 * Set the raw and unix credentials at this
75110721SGlenn.Barry@Sun.COM 		 * point.  This saves a lot of computation
75210721SGlenn.Barry@Sun.COM 		 * later when credentials are retrieved.
75310721SGlenn.Barry@Sun.COM 		 */
75410721SGlenn.Barry@Sun.COM 		client_data->raw_cred.version = cr_version;
75510721SGlenn.Barry@Sun.COM 		client_data->raw_cred.service = cr_service;
75610721SGlenn.Barry@Sun.COM 
75710721SGlenn.Barry@Sun.COM 		if (client_data->raw_cred.mechanism) {
75810721SGlenn.Barry@Sun.COM 			kgss_free_oid(client_data->raw_cred.mechanism);
75910721SGlenn.Barry@Sun.COM 			client_data->raw_cred.mechanism = NULL;
76010721SGlenn.Barry@Sun.COM 		}
76110721SGlenn.Barry@Sun.COM 		client_data->raw_cred.mechanism = (rpc_gss_OID) mech_type;
76210721SGlenn.Barry@Sun.COM 		/*
76310721SGlenn.Barry@Sun.COM 		 * client_data is now responsible for freeing
76410721SGlenn.Barry@Sun.COM 		 * the data of 'mech_type'.
76510721SGlenn.Barry@Sun.COM 		 */
76610721SGlenn.Barry@Sun.COM 		free_mech_type = 0;
76710721SGlenn.Barry@Sun.COM 
76810721SGlenn.Barry@Sun.COM 		if (client_data->raw_cred.client_principal) {
76910721SGlenn.Barry@Sun.COM 			kmem_free((caddr_t)client_data->\
77010721SGlenn.Barry@Sun.COM 			    raw_cred.client_principal,
77110721SGlenn.Barry@Sun.COM 			    client_data->raw_cred.\
77210721SGlenn.Barry@Sun.COM 			    client_principal->len + sizeof (int));
77310721SGlenn.Barry@Sun.COM 			client_data->raw_cred.client_principal = NULL;
77410721SGlenn.Barry@Sun.COM 		}
77510721SGlenn.Barry@Sun.COM 
77610721SGlenn.Barry@Sun.COM 		/*
77710721SGlenn.Barry@Sun.COM 		 *  The client_name returned from
77810721SGlenn.Barry@Sun.COM 		 *  kgss_accept_sec_context() is in an
77910721SGlenn.Barry@Sun.COM 		 *  exported flat format.
78010721SGlenn.Barry@Sun.COM 		 */
78110721SGlenn.Barry@Sun.COM 		if (! __rpc_gss_make_principal(
78210721SGlenn.Barry@Sun.COM 		    &client_data->raw_cred.client_principal,
78310721SGlenn.Barry@Sun.COM 		    &client_data->client_name)) {
78410721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(1, "_svcrpcsec_gss: "
78510721SGlenn.Barry@Sun.COM 			    "make principal failed\n");
78610721SGlenn.Barry@Sun.COM 			gssstat = GSS_S_FAILURE;
78710721SGlenn.Barry@Sun.COM 			(void) gss_release_buffer(&minor_stat, &output_token);
78810721SGlenn.Barry@Sun.COM 		}
78910721SGlenn.Barry@Sun.COM 	}
79010721SGlenn.Barry@Sun.COM 
79110721SGlenn.Barry@Sun.COM 	rw_exit(&cred_lock);
79210721SGlenn.Barry@Sun.COM 
79310721SGlenn.Barry@Sun.COM 	call_res.gss_major = gssstat;
79410721SGlenn.Barry@Sun.COM 	call_res.gss_minor = minor_stat;
79510721SGlenn.Barry@Sun.COM 
79610721SGlenn.Barry@Sun.COM 	if (gssstat != GSS_S_COMPLETE &&
79710721SGlenn.Barry@Sun.COM 	    gssstat != GSS_S_CONTINUE_NEEDED) {
79810721SGlenn.Barry@Sun.COM 		call_res.ctx_handle.length = 0;
79910721SGlenn.Barry@Sun.COM 		call_res.ctx_handle.value = NULL;
80010721SGlenn.Barry@Sun.COM 		call_res.seq_window = 0;
80110721SGlenn.Barry@Sun.COM 		rpc_gss_display_status(gssstat, minor_stat, mech_type,
80210721SGlenn.Barry@Sun.COM 		    crgetuid(CRED()),
80310721SGlenn.Barry@Sun.COM 		    "_svc_rpcsec_gss gss_accept_sec_context");
80410721SGlenn.Barry@Sun.COM 		(void) svc_sendreply(rqst->rq_xprt,
80510721SGlenn.Barry@Sun.COM 		    __xdr_rpc_gss_init_res, (caddr_t)&call_res);
80610721SGlenn.Barry@Sun.COM 		client_data->stale = TRUE;
80710721SGlenn.Barry@Sun.COM 		ret = AUTH_OK;
80810721SGlenn.Barry@Sun.COM 		goto error2;
80910721SGlenn.Barry@Sun.COM 	}
81010721SGlenn.Barry@Sun.COM 
81110721SGlenn.Barry@Sun.COM 	/*
81210721SGlenn.Barry@Sun.COM 	 * If appropriate, set established to TRUE *after* sending
81310721SGlenn.Barry@Sun.COM 	 * response (otherwise, the client will receive the final
81410721SGlenn.Barry@Sun.COM 	 * token encrypted)
81510721SGlenn.Barry@Sun.COM 	 */
81610721SGlenn.Barry@Sun.COM 	if (gssstat == GSS_S_COMPLETE) {
81710721SGlenn.Barry@Sun.COM 		/*
81810721SGlenn.Barry@Sun.COM 		 * Context is established.  Set expiration time
81910721SGlenn.Barry@Sun.COM 		 * for the context.
82010721SGlenn.Barry@Sun.COM 		 */
82110721SGlenn.Barry@Sun.COM 		client_data->seq_num = 1;
82210721SGlenn.Barry@Sun.COM 		if ((time_rec == GSS_C_INDEFINITE) || (time_rec == 0)) {
82310721SGlenn.Barry@Sun.COM 			client_data->expiration = GSS_C_INDEFINITE;
82410721SGlenn.Barry@Sun.COM 		} else {
82510721SGlenn.Barry@Sun.COM 			client_data->expiration =
82610721SGlenn.Barry@Sun.COM 			    time_rec + gethrestime_sec();
82710721SGlenn.Barry@Sun.COM 		}
82810721SGlenn.Barry@Sun.COM 
82910721SGlenn.Barry@Sun.COM 		if (!transfer_sec_context(client_data)) {
83010721SGlenn.Barry@Sun.COM 			ret = RPCSEC_GSS_FAILED;
83110721SGlenn.Barry@Sun.COM 			client_data->stale = TRUE;
83210721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(1,
83310721SGlenn.Barry@Sun.COM 			    "_svc_rpcsec_gss: transfer sec context failed\n");
83410721SGlenn.Barry@Sun.COM 			goto error2;
83510721SGlenn.Barry@Sun.COM 		}
83610721SGlenn.Barry@Sun.COM 
83710721SGlenn.Barry@Sun.COM 		client_data->established = TRUE;
83810721SGlenn.Barry@Sun.COM 	}
83910721SGlenn.Barry@Sun.COM 
84010721SGlenn.Barry@Sun.COM 	/*
84110721SGlenn.Barry@Sun.COM 	 * This step succeeded.  Send a response, along with
84210721SGlenn.Barry@Sun.COM 	 * a token if there's one.  Don't dispatch.
84310721SGlenn.Barry@Sun.COM 	 */
84410721SGlenn.Barry@Sun.COM 
84510721SGlenn.Barry@Sun.COM 	if (output_token.length != 0)
84610721SGlenn.Barry@Sun.COM 		GSS_COPY_BUFFER(call_res.token, output_token);
84710721SGlenn.Barry@Sun.COM 
84810721SGlenn.Barry@Sun.COM 	/*
84910721SGlenn.Barry@Sun.COM 	 * If GSS_S_COMPLETE: set response verifier to
85010721SGlenn.Barry@Sun.COM 	 * checksum of SEQ_WIN
85110721SGlenn.Barry@Sun.COM 	 */
85210721SGlenn.Barry@Sun.COM 	if (gssstat == GSS_S_COMPLETE) {
85310721SGlenn.Barry@Sun.COM 		if (!set_response_verf(rqst, msg, client_data,
85410721SGlenn.Barry@Sun.COM 		    (uint_t)SEQ_WIN)) {
85510721SGlenn.Barry@Sun.COM 			ret = RPCSEC_GSS_FAILED;
85610721SGlenn.Barry@Sun.COM 			client_data->stale = TRUE;
85710721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(1,
85810721SGlenn.Barry@Sun.COM 			    "_svc_rpcsec_gss:set response verifier failed\n");
85910721SGlenn.Barry@Sun.COM 			goto error2;
86010721SGlenn.Barry@Sun.COM 		}
86110721SGlenn.Barry@Sun.COM 	}
86210721SGlenn.Barry@Sun.COM 
86310721SGlenn.Barry@Sun.COM 	if (!svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
86410721SGlenn.Barry@Sun.COM 	    (caddr_t)&call_res)) {
86510721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_FAILED;
86610721SGlenn.Barry@Sun.COM 		client_data->stale = TRUE;
86710721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svc_rpcsec_gss:send reply failed\n");
86810721SGlenn.Barry@Sun.COM 		goto error2;
86910721SGlenn.Barry@Sun.COM 	}
87010721SGlenn.Barry@Sun.COM 
87110721SGlenn.Barry@Sun.COM 	/*
87210721SGlenn.Barry@Sun.COM 	 * Cache last response in case it is lost and the client
87310721SGlenn.Barry@Sun.COM 	 * retries on an established context.
87410721SGlenn.Barry@Sun.COM 	 */
87510721SGlenn.Barry@Sun.COM 	(void) retrans_add(client_data, msg->rm_xid, &call_res);
87610721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
87710721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
87810721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
87910721SGlenn.Barry@Sun.COM 
88010721SGlenn.Barry@Sun.COM 	(void) gss_release_buffer(&minor_stat, &output_token);
88110721SGlenn.Barry@Sun.COM 
88210721SGlenn.Barry@Sun.COM 	return (AUTH_OK);
88310721SGlenn.Barry@Sun.COM 
88410721SGlenn.Barry@Sun.COM error2:
88510721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
88610721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
88710721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
88810721SGlenn.Barry@Sun.COM 	(void) gss_release_buffer(&minor_stat, &output_token);
88910721SGlenn.Barry@Sun.COM 	if (free_mech_type && mech_type)
89010721SGlenn.Barry@Sun.COM 		kgss_free_oid(mech_type);
89110721SGlenn.Barry@Sun.COM 
89210721SGlenn.Barry@Sun.COM 	return (ret);
89310721SGlenn.Barry@Sun.COM }
89410721SGlenn.Barry@Sun.COM 
89510721SGlenn.Barry@Sun.COM static void
svcrpcsec_gss_taskq_func(void * svcrpcsecgss_taskq_arg)89610721SGlenn.Barry@Sun.COM svcrpcsec_gss_taskq_func(void *svcrpcsecgss_taskq_arg)
89710721SGlenn.Barry@Sun.COM {
89810721SGlenn.Barry@Sun.COM 	enum auth_stat retval;
89910721SGlenn.Barry@Sun.COM 	svcrpcsec_gss_taskq_arg_t *arg = svcrpcsecgss_taskq_arg;
90010721SGlenn.Barry@Sun.COM 
90110721SGlenn.Barry@Sun.COM 	retval = do_gss_accept(arg->rq_xprt, arg->rpc_call_arg, arg->msg,
90210721SGlenn.Barry@Sun.COM 	    arg->client_data, arg->cr_version, arg->cr_service);
90310721SGlenn.Barry@Sun.COM 	if (retval != AUTH_OK) {
90410721SGlenn.Barry@Sun.COM 		cmn_err(CE_NOTE,
90510721SGlenn.Barry@Sun.COM 		    "svcrpcsec_gss_taskq_func:  do_gss_accept fail 0x%x",
90610721SGlenn.Barry@Sun.COM 		    retval);
90710721SGlenn.Barry@Sun.COM 	}
90810721SGlenn.Barry@Sun.COM 	rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
90910721SGlenn.Barry@Sun.COM 	svc_clone_unlink(arg->rq_xprt);
91010721SGlenn.Barry@Sun.COM 	svc_clone_free(arg->rq_xprt);
91110721SGlenn.Barry@Sun.COM 	xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)arg->rpc_call_arg);
91210721SGlenn.Barry@Sun.COM 	kmem_free(arg->rpc_call_arg, sizeof (*arg->rpc_call_arg));
91310721SGlenn.Barry@Sun.COM 
91410721SGlenn.Barry@Sun.COM 	kmem_free(arg, sizeof (*arg));
91510721SGlenn.Barry@Sun.COM }
91610721SGlenn.Barry@Sun.COM 
91710721SGlenn.Barry@Sun.COM 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)91810721SGlenn.Barry@Sun.COM rpcsec_gss_init(
91910721SGlenn.Barry@Sun.COM 	struct svc_req		*rqst,
92010721SGlenn.Barry@Sun.COM 	struct rpc_msg		*msg,
92110721SGlenn.Barry@Sun.COM 	rpc_gss_creds		creds,
92210721SGlenn.Barry@Sun.COM 	bool_t			*no_dispatch,
92310721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*c_d) /* client data, can be NULL */
92410721SGlenn.Barry@Sun.COM {
92510721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*client_data;
92610721SGlenn.Barry@Sun.COM 	int ret;
92710721SGlenn.Barry@Sun.COM 	svcrpcsec_gss_taskq_arg_t *arg;
92810721SGlenn.Barry@Sun.COM 
92910721SGlenn.Barry@Sun.COM 	if (creds.ctx_handle.length != 0) {
93010721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: ctx_handle not null\n");
93110721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
93210721SGlenn.Barry@Sun.COM 		return (ret);
93310721SGlenn.Barry@Sun.COM 	}
93410721SGlenn.Barry@Sun.COM 
93510721SGlenn.Barry@Sun.COM 	client_data = c_d ? c_d : create_client();
93610721SGlenn.Barry@Sun.COM 	if (client_data == NULL) {
93710721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1,
93810721SGlenn.Barry@Sun.COM 		    "_svcrpcsec_gss: can't create a new cache entry\n");
93910721SGlenn.Barry@Sun.COM 		ret = AUTH_FAILED;
94010721SGlenn.Barry@Sun.COM 		return (ret);
94110721SGlenn.Barry@Sun.COM 	}
94210721SGlenn.Barry@Sun.COM 
94310721SGlenn.Barry@Sun.COM 	mutex_enter(&client_data->clm);
94410721SGlenn.Barry@Sun.COM 	if (client_data->stale) {
94510721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
94610721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
94710721SGlenn.Barry@Sun.COM 		goto error2;
94810721SGlenn.Barry@Sun.COM 	}
94910721SGlenn.Barry@Sun.COM 
95010721SGlenn.Barry@Sun.COM 	/*
95110721SGlenn.Barry@Sun.COM 	 * kgss_accept_sec_context()/gssd(1M) can be overly time
95210721SGlenn.Barry@Sun.COM 	 * consuming so let's queue it and return asap.
95310721SGlenn.Barry@Sun.COM 	 *
95410721SGlenn.Barry@Sun.COM 	 * taskq func must free arg.
95510721SGlenn.Barry@Sun.COM 	 */
95610721SGlenn.Barry@Sun.COM 	arg = kmem_alloc(sizeof (*arg), KM_SLEEP);
95710721SGlenn.Barry@Sun.COM 
95810721SGlenn.Barry@Sun.COM 	/* taskq func must free rpc_call_arg & deserialized arguments */
95910721SGlenn.Barry@Sun.COM 	arg->rpc_call_arg = kmem_alloc(sizeof (*arg->rpc_call_arg), KM_SLEEP);
96010721SGlenn.Barry@Sun.COM 
96110721SGlenn.Barry@Sun.COM 	/* deserialize arguments */
96210721SGlenn.Barry@Sun.COM 	bzero(arg->rpc_call_arg, sizeof (*arg->rpc_call_arg));
96310721SGlenn.Barry@Sun.COM 	if (!SVC_GETARGS(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
96410721SGlenn.Barry@Sun.COM 	    (caddr_t)arg->rpc_call_arg)) {
96510721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_FAILED;
96610721SGlenn.Barry@Sun.COM 		client_data->stale = TRUE;
96710721SGlenn.Barry@Sun.COM 		goto error2;
96810721SGlenn.Barry@Sun.COM 	}
96910721SGlenn.Barry@Sun.COM 
97010721SGlenn.Barry@Sun.COM 	/* get a xprt clone for taskq thread, taskq func must free it */
97110721SGlenn.Barry@Sun.COM 	arg->rq_xprt = svc_clone_init();
97211967SKaren.Rochford@Sun.COM 	svc_clone_link(rqst->rq_xprt->xp_master, arg->rq_xprt, rqst->rq_xprt);
97310721SGlenn.Barry@Sun.COM 	arg->rq_xprt->xp_xid = rqst->rq_xprt->xp_xid;
97410721SGlenn.Barry@Sun.COM 
97510721SGlenn.Barry@Sun.COM 
97610721SGlenn.Barry@Sun.COM 	/* set the appropriate wrap/unwrap routine for RPCSEC_GSS */
97710721SGlenn.Barry@Sun.COM 	arg->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
97810721SGlenn.Barry@Sun.COM 	arg->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
97910721SGlenn.Barry@Sun.COM 
98010721SGlenn.Barry@Sun.COM 	/* get a dup of rpc msg for taskq thread */
98110721SGlenn.Barry@Sun.COM 	arg->msg = rpc_msg_dup(msg);  /* taskq func must free msg dup */
98210721SGlenn.Barry@Sun.COM 
98310721SGlenn.Barry@Sun.COM 	arg->client_data = client_data;
98410721SGlenn.Barry@Sun.COM 	arg->cr_version = creds.version;
98510721SGlenn.Barry@Sun.COM 	arg->cr_service = creds.service;
98610721SGlenn.Barry@Sun.COM 
987*12553SKaren.Rochford@Sun.COM 	/* We no longer need the xp_xdrin, destroy it all here. */
988*12553SKaren.Rochford@Sun.COM 	XDR_DESTROY(&(rqst->rq_xprt->xp_xdrin));
989*12553SKaren.Rochford@Sun.COM 
99010721SGlenn.Barry@Sun.COM 	/* should be ok to hold clm lock as taskq will have new thread(s) */
99110721SGlenn.Barry@Sun.COM 	ret = ddi_taskq_dispatch(svcrpcsec_gss_init_taskq,
99210721SGlenn.Barry@Sun.COM 	    svcrpcsec_gss_taskq_func, arg, DDI_SLEEP);
99310721SGlenn.Barry@Sun.COM 	if (ret == DDI_FAILURE) {
99410721SGlenn.Barry@Sun.COM 		cmn_err(CE_NOTE, "rpcsec_gss_init: taskq dispatch fail");
99510721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_FAILED;
99610721SGlenn.Barry@Sun.COM 		rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
99710721SGlenn.Barry@Sun.COM 		svc_clone_unlink(arg->rq_xprt);
99810721SGlenn.Barry@Sun.COM 		svc_clone_free(arg->rq_xprt);
99910721SGlenn.Barry@Sun.COM 		kmem_free(arg, sizeof (*arg));
100010721SGlenn.Barry@Sun.COM 		goto error2;
100110721SGlenn.Barry@Sun.COM 	}
100210721SGlenn.Barry@Sun.COM 
100310721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
100410721SGlenn.Barry@Sun.COM 	*no_dispatch = TRUE;
100510721SGlenn.Barry@Sun.COM 	return (AUTH_OK);
100610721SGlenn.Barry@Sun.COM 
100710721SGlenn.Barry@Sun.COM error2:
100810721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
100910721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
101010721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
101110721SGlenn.Barry@Sun.COM 	cmn_err(CE_NOTE, "rpcsec_gss_init: error 0x%x", ret);
101210721SGlenn.Barry@Sun.COM 	return (ret);
101310721SGlenn.Barry@Sun.COM }
101410721SGlenn.Barry@Sun.COM 
101510721SGlenn.Barry@Sun.COM static enum auth_stat
rpcsec_gss_continue_init(struct svc_req * rqst,struct rpc_msg * msg,rpc_gss_creds creds,bool_t * no_dispatch)101610721SGlenn.Barry@Sun.COM rpcsec_gss_continue_init(
101710721SGlenn.Barry@Sun.COM 	struct svc_req		*rqst,
101810721SGlenn.Barry@Sun.COM 	struct rpc_msg		*msg,
101910721SGlenn.Barry@Sun.COM 	rpc_gss_creds		creds,
102010721SGlenn.Barry@Sun.COM 	bool_t			*no_dispatch)
102110721SGlenn.Barry@Sun.COM {
102210721SGlenn.Barry@Sun.COM 	int ret;
102310721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*client_data;
102410721SGlenn.Barry@Sun.COM 	svc_rpc_gss_parms_t	*gss_parms;
102510721SGlenn.Barry@Sun.COM 	rpc_gss_init_res	*retrans_result;
102610721SGlenn.Barry@Sun.COM 
102710721SGlenn.Barry@Sun.COM 	if (creds.ctx_handle.length == 0) {
102810721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
102910721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
103010721SGlenn.Barry@Sun.COM 		return (ret);
103110721SGlenn.Barry@Sun.COM 	}
103210721SGlenn.Barry@Sun.COM 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
103310721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
103410721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
103510721SGlenn.Barry@Sun.COM 		return (ret);
103610721SGlenn.Barry@Sun.COM 	}
103710721SGlenn.Barry@Sun.COM 
103810721SGlenn.Barry@Sun.COM 	mutex_enter(&client_data->clm);
103910721SGlenn.Barry@Sun.COM 	if (client_data->stale) {
104010721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
104110721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
104210721SGlenn.Barry@Sun.COM 		goto error2;
104310721SGlenn.Barry@Sun.COM 	}
104410721SGlenn.Barry@Sun.COM 
104510721SGlenn.Barry@Sun.COM 	/*
104610721SGlenn.Barry@Sun.COM 	 * If context not established, go thru INIT code but with
104710721SGlenn.Barry@Sun.COM 	 * this client handle.
104810721SGlenn.Barry@Sun.COM 	 */
104910721SGlenn.Barry@Sun.COM 	if (!client_data->established) {
105010721SGlenn.Barry@Sun.COM 		mutex_exit(&client_data->clm);
105110721SGlenn.Barry@Sun.COM 		return (rpcsec_gss_init(rqst, msg, creds, no_dispatch,
105210721SGlenn.Barry@Sun.COM 		    client_data));
105310721SGlenn.Barry@Sun.COM 	}
105410721SGlenn.Barry@Sun.COM 
10550Sstevel@tonic-gate 	/*
10560Sstevel@tonic-gate 	 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
10570Sstevel@tonic-gate 	 */
10580Sstevel@tonic-gate 	rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
10590Sstevel@tonic-gate 	rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	/*
10620Sstevel@tonic-gate 	 * Keep copy of parameters we'll need for response, for the
10630Sstevel@tonic-gate 	 * sake of reentrancy (we don't want to look in the context
10640Sstevel@tonic-gate 	 * data because when we are sending a response, another
10650Sstevel@tonic-gate 	 * request may have come in).
10660Sstevel@tonic-gate 	 */
10670Sstevel@tonic-gate 	gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
10680Sstevel@tonic-gate 	gss_parms->established = client_data->established;
10690Sstevel@tonic-gate 	gss_parms->service = creds.service;
10700Sstevel@tonic-gate 	gss_parms->qop_rcvd = (uint_t)client_data->qop;
10710Sstevel@tonic-gate 	gss_parms->context = (void *)client_data->context;
10720Sstevel@tonic-gate 	gss_parms->seq_num = creds.seq_num;
10730Sstevel@tonic-gate 
107410721SGlenn.Barry@Sun.COM 	/*
107510721SGlenn.Barry@Sun.COM 	 * This is an established context. Continue to
107610721SGlenn.Barry@Sun.COM 	 * satisfy retried continue init requests out of
107710721SGlenn.Barry@Sun.COM 	 * the retransmit cache.  Throw away any that don't
107810721SGlenn.Barry@Sun.COM 	 * have a matching xid or the cach is empty.
107910721SGlenn.Barry@Sun.COM 	 * Delete the retransmit cache once the client sends
108010721SGlenn.Barry@Sun.COM 	 * a data request.
108110721SGlenn.Barry@Sun.COM 	 */
108210721SGlenn.Barry@Sun.COM 	if (client_data->retrans_data &&
108310721SGlenn.Barry@Sun.COM 	    (client_data->retrans_data->xid == msg->rm_xid)) {
108410721SGlenn.Barry@Sun.COM 		retrans_result = &client_data->retrans_data->result;
108510721SGlenn.Barry@Sun.COM 		if (set_response_verf(rqst, msg, client_data,
108610721SGlenn.Barry@Sun.COM 		    (uint_t)retrans_result->seq_window)) {
108710721SGlenn.Barry@Sun.COM 			gss_parms->established = FALSE;
10880Sstevel@tonic-gate 			(void) svc_sendreply(rqst->rq_xprt,
108910721SGlenn.Barry@Sun.COM 			    __xdr_rpc_gss_init_res, (caddr_t)retrans_result);
10900Sstevel@tonic-gate 			*no_dispatch = TRUE;
10910Sstevel@tonic-gate 			ASSERT(client_data->ref_cnt > 0);
10920Sstevel@tonic-gate 			client_data->ref_cnt--;
109310721SGlenn.Barry@Sun.COM 		}
109410721SGlenn.Barry@Sun.COM 	}
109510721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
109610721SGlenn.Barry@Sun.COM 
109710721SGlenn.Barry@Sun.COM 	return (AUTH_OK);
109810721SGlenn.Barry@Sun.COM 
109910721SGlenn.Barry@Sun.COM error2:
110010721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
110110721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
110210721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
110310721SGlenn.Barry@Sun.COM 	return (ret);
110410721SGlenn.Barry@Sun.COM }
110510721SGlenn.Barry@Sun.COM 
110610721SGlenn.Barry@Sun.COM static enum auth_stat
rpcsec_gss_data(struct svc_req * rqst,struct rpc_msg * msg,rpc_gss_creds creds,bool_t * no_dispatch)110710721SGlenn.Barry@Sun.COM rpcsec_gss_data(
110810721SGlenn.Barry@Sun.COM 	struct svc_req		*rqst,
110910721SGlenn.Barry@Sun.COM 	struct rpc_msg		*msg,
111010721SGlenn.Barry@Sun.COM 	rpc_gss_creds		creds,
111110721SGlenn.Barry@Sun.COM 	bool_t			*no_dispatch)
111210721SGlenn.Barry@Sun.COM {
111310721SGlenn.Barry@Sun.COM 	int ret;
111410721SGlenn.Barry@Sun.COM 	svc_rpc_gss_parms_t	*gss_parms;
111510721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*client_data;
111610721SGlenn.Barry@Sun.COM 
111710721SGlenn.Barry@Sun.COM 	switch (creds.service) {
111810721SGlenn.Barry@Sun.COM 	case rpc_gss_svc_none:
111910721SGlenn.Barry@Sun.COM 	case rpc_gss_svc_integrity:
112010721SGlenn.Barry@Sun.COM 	case rpc_gss_svc_privacy:
112110721SGlenn.Barry@Sun.COM 		break;
112210721SGlenn.Barry@Sun.COM 	default:
112310721SGlenn.Barry@Sun.COM 		cmn_err(CE_NOTE, "__svcrpcsec_gss: unknown service type=0x%x",
112410721SGlenn.Barry@Sun.COM 		    creds.service);
112510721SGlenn.Barry@Sun.COM 		RPCGSS_LOG(1, "_svcrpcsec_gss: unknown service type: 0x%x\n",
112610721SGlenn.Barry@Sun.COM 		    creds.service);
112710721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
112810721SGlenn.Barry@Sun.COM 		return (ret);
112910721SGlenn.Barry@Sun.COM 	}
113010721SGlenn.Barry@Sun.COM 
113110721SGlenn.Barry@Sun.COM 	if (creds.ctx_handle.length == 0) {
113210721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
113310721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
113410721SGlenn.Barry@Sun.COM 		return (ret);
113510721SGlenn.Barry@Sun.COM 	}
113610721SGlenn.Barry@Sun.COM 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
113710721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
113810721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
113910721SGlenn.Barry@Sun.COM 		return (ret);
114010721SGlenn.Barry@Sun.COM 	}
114110721SGlenn.Barry@Sun.COM 
11420Sstevel@tonic-gate 
114310721SGlenn.Barry@Sun.COM 	mutex_enter(&client_data->clm);
114410721SGlenn.Barry@Sun.COM 	if (!client_data->established) {
114510721SGlenn.Barry@Sun.COM 		ret = AUTH_FAILED;
114610721SGlenn.Barry@Sun.COM 		goto error2;
114710721SGlenn.Barry@Sun.COM 	}
114810721SGlenn.Barry@Sun.COM 	if (client_data->stale) {
114910721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
115010721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
115110721SGlenn.Barry@Sun.COM 		goto error2;
115210721SGlenn.Barry@Sun.COM 	}
115310721SGlenn.Barry@Sun.COM 
115410721SGlenn.Barry@Sun.COM 	/*
115510721SGlenn.Barry@Sun.COM 	 * Once the context is established and there is no more
115610721SGlenn.Barry@Sun.COM 	 * retransmission of last continue init request, it is safe
115710721SGlenn.Barry@Sun.COM 	 * to delete the retransmit cache entry.
115810721SGlenn.Barry@Sun.COM 	 */
115910721SGlenn.Barry@Sun.COM 	if (client_data->retrans_data)
116010721SGlenn.Barry@Sun.COM 		retrans_del(client_data);
116110721SGlenn.Barry@Sun.COM 
116210721SGlenn.Barry@Sun.COM 	/*
116310721SGlenn.Barry@Sun.COM 	 * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
116410721SGlenn.Barry@Sun.COM 	 */
116510721SGlenn.Barry@Sun.COM 	rqst->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
116610721SGlenn.Barry@Sun.COM 	rqst->rq_xprt->xp_auth.svc_ah_private = (caddr_t)client_data;
11670Sstevel@tonic-gate 
116810721SGlenn.Barry@Sun.COM 	/*
116910721SGlenn.Barry@Sun.COM 	 * Keep copy of parameters we'll need for response, for the
117010721SGlenn.Barry@Sun.COM 	 * sake of reentrancy (we don't want to look in the context
117110721SGlenn.Barry@Sun.COM 	 * data because when we are sending a response, another
117210721SGlenn.Barry@Sun.COM 	 * request may have come in).
117310721SGlenn.Barry@Sun.COM 	 */
117410721SGlenn.Barry@Sun.COM 	gss_parms = &rqst->rq_xprt->xp_auth.svc_gss_parms;
117510721SGlenn.Barry@Sun.COM 	gss_parms->established = client_data->established;
117610721SGlenn.Barry@Sun.COM 	gss_parms->service = creds.service;
117710721SGlenn.Barry@Sun.COM 	gss_parms->qop_rcvd = (uint_t)client_data->qop;
117810721SGlenn.Barry@Sun.COM 	gss_parms->context = (void *)client_data->context;
117910721SGlenn.Barry@Sun.COM 	gss_parms->seq_num = creds.seq_num;
118010721SGlenn.Barry@Sun.COM 
118110721SGlenn.Barry@Sun.COM 	/*
118210721SGlenn.Barry@Sun.COM 	 * Context is already established.  Check verifier, and
118310721SGlenn.Barry@Sun.COM 	 * note parameters we will need for response in gss_parms.
118410721SGlenn.Barry@Sun.COM 	 */
118510721SGlenn.Barry@Sun.COM 	if (!check_verf(msg, client_data->context,
118610721SGlenn.Barry@Sun.COM 	    (int *)&gss_parms->qop_rcvd, client_data->u_cred.uid)) {
118710721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
118810721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: check verf failed\n");
118910721SGlenn.Barry@Sun.COM 		goto error2;
119010721SGlenn.Barry@Sun.COM 	}
119110721SGlenn.Barry@Sun.COM 
119210721SGlenn.Barry@Sun.COM 	/*
119310721SGlenn.Barry@Sun.COM 	 *  Check and invoke callback if necessary.
119410721SGlenn.Barry@Sun.COM 	 */
119510721SGlenn.Barry@Sun.COM 	if (!client_data->done_docallback) {
119610721SGlenn.Barry@Sun.COM 		client_data->done_docallback = TRUE;
119710721SGlenn.Barry@Sun.COM 		client_data->qop = gss_parms->qop_rcvd;
119810721SGlenn.Barry@Sun.COM 		client_data->raw_cred.qop = gss_parms->qop_rcvd;
119910721SGlenn.Barry@Sun.COM 		client_data->raw_cred.service = creds.service;
120010721SGlenn.Barry@Sun.COM 		if (!do_callback(rqst, client_data)) {
120110721SGlenn.Barry@Sun.COM 			ret = AUTH_FAILED;
120210721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(1, "_svc_rpcsec_gss:callback failed\n");
120310721SGlenn.Barry@Sun.COM 			goto error2;
12040Sstevel@tonic-gate 		}
12050Sstevel@tonic-gate 	}
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	/*
120810721SGlenn.Barry@Sun.COM 	 * If the context was locked, make sure that the client
120910721SGlenn.Barry@Sun.COM 	 * has not changed QOP.
121010721SGlenn.Barry@Sun.COM 	 */
121110721SGlenn.Barry@Sun.COM 	if (client_data->locked && gss_parms->qop_rcvd != client_data->qop) {
121210721SGlenn.Barry@Sun.COM 		ret = AUTH_BADVERF;
121310721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: can not change qop\n");
121410721SGlenn.Barry@Sun.COM 		goto error2;
121510721SGlenn.Barry@Sun.COM 	}
121610721SGlenn.Barry@Sun.COM 
121710721SGlenn.Barry@Sun.COM 	/*
121810721SGlenn.Barry@Sun.COM 	 * Validate sequence number.
12190Sstevel@tonic-gate 	 */
122010721SGlenn.Barry@Sun.COM 	if (!check_seq(client_data, creds.seq_num, &client_data->stale)) {
122110721SGlenn.Barry@Sun.COM 		if (client_data->stale) {
122210721SGlenn.Barry@Sun.COM 			ret = RPCSEC_GSS_FAILED;
122310721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(1,
122410721SGlenn.Barry@Sun.COM 			    "_svc_rpcsec_gss:check seq failed\n");
122510721SGlenn.Barry@Sun.COM 		} else {
122610721SGlenn.Barry@Sun.COM 			RPCGSS_LOG0(4, "_svc_rpcsec_gss:check seq "
122710721SGlenn.Barry@Sun.COM 			    "failed on good context. Ignoring "
122810721SGlenn.Barry@Sun.COM 			    "request\n");
122910721SGlenn.Barry@Sun.COM 			/*
123010721SGlenn.Barry@Sun.COM 			 * Operational error, drop packet silently.
123110721SGlenn.Barry@Sun.COM 			 * The client will recover after timing out,
123210721SGlenn.Barry@Sun.COM 			 * assuming this is a client error and not
123310721SGlenn.Barry@Sun.COM 			 * a relpay attack.  Don't dispatch.
123410721SGlenn.Barry@Sun.COM 			 */
123510721SGlenn.Barry@Sun.COM 			ret = AUTH_OK;
123610721SGlenn.Barry@Sun.COM 			*no_dispatch = TRUE;
123710721SGlenn.Barry@Sun.COM 		}
123810721SGlenn.Barry@Sun.COM 		goto error2;
123910721SGlenn.Barry@Sun.COM 	}
124010721SGlenn.Barry@Sun.COM 
124110721SGlenn.Barry@Sun.COM 	/*
124210721SGlenn.Barry@Sun.COM 	 * set response verifier
124310721SGlenn.Barry@Sun.COM 	 */
124410721SGlenn.Barry@Sun.COM 	if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
124510721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_FAILED;
124610721SGlenn.Barry@Sun.COM 		client_data->stale = TRUE;
124710721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1,
124810721SGlenn.Barry@Sun.COM 		    "_svc_rpcsec_gss:set response verifier failed\n");
124910721SGlenn.Barry@Sun.COM 		goto error2;
125010721SGlenn.Barry@Sun.COM 	}
125110721SGlenn.Barry@Sun.COM 
125210721SGlenn.Barry@Sun.COM 	/*
125310721SGlenn.Barry@Sun.COM 	 * If context is locked, make sure that the client
125410721SGlenn.Barry@Sun.COM 	 * has not changed the security service.
125510721SGlenn.Barry@Sun.COM 	 */
125610721SGlenn.Barry@Sun.COM 	if (client_data->locked &&
125710721SGlenn.Barry@Sun.COM 	    client_data->raw_cred.service != creds.service) {
125810721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svc_rpcsec_gss: "
125910721SGlenn.Barry@Sun.COM 		    "security service changed.\n");
126010721SGlenn.Barry@Sun.COM 		ret = AUTH_FAILED;
126110721SGlenn.Barry@Sun.COM 		goto error2;
126210721SGlenn.Barry@Sun.COM 	}
126310721SGlenn.Barry@Sun.COM 
126410721SGlenn.Barry@Sun.COM 	/*
126510721SGlenn.Barry@Sun.COM 	 * Set client credentials to raw credential
126610721SGlenn.Barry@Sun.COM 	 * structure in context.  This is okay, since
126710721SGlenn.Barry@Sun.COM 	 * this will not change during the lifetime of
126810721SGlenn.Barry@Sun.COM 	 * the context (so it's MT safe).
126910721SGlenn.Barry@Sun.COM 	 */
127010721SGlenn.Barry@Sun.COM 	rqst->rq_clntcred = (char *)&client_data->raw_cred;
127110721SGlenn.Barry@Sun.COM 
12720Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
127310721SGlenn.Barry@Sun.COM 	return (AUTH_OK);
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate error2:
12760Sstevel@tonic-gate 	ASSERT(client_data->ref_cnt > 0);
12770Sstevel@tonic-gate 	client_data->ref_cnt--;
127810721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
127910721SGlenn.Barry@Sun.COM 	return (ret);
128010721SGlenn.Barry@Sun.COM }
128110721SGlenn.Barry@Sun.COM 
128210721SGlenn.Barry@Sun.COM /*
128310721SGlenn.Barry@Sun.COM  * Note we don't have a client yet to use this routine and test it.
128410721SGlenn.Barry@Sun.COM  */
128510721SGlenn.Barry@Sun.COM static enum auth_stat
rpcsec_gss_destroy(struct svc_req * rqst,rpc_gss_creds creds,bool_t * no_dispatch)128610721SGlenn.Barry@Sun.COM rpcsec_gss_destroy(
128710721SGlenn.Barry@Sun.COM 	struct svc_req		*rqst,
128810721SGlenn.Barry@Sun.COM 	rpc_gss_creds		creds,
128910721SGlenn.Barry@Sun.COM 	bool_t			*no_dispatch)
129010721SGlenn.Barry@Sun.COM {
129110721SGlenn.Barry@Sun.COM 	svc_rpc_gss_data	*client_data;
129210721SGlenn.Barry@Sun.COM 	int ret;
129310721SGlenn.Barry@Sun.COM 
129410721SGlenn.Barry@Sun.COM 	if (creds.ctx_handle.length == 0) {
129510721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no ctx_handle\n");
129610721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
129710721SGlenn.Barry@Sun.COM 		return (ret);
129810721SGlenn.Barry@Sun.COM 	}
129910721SGlenn.Barry@Sun.COM 	if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
130010721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
130110721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: no security context\n");
130210721SGlenn.Barry@Sun.COM 		return (ret);
130310721SGlenn.Barry@Sun.COM 	}
130410721SGlenn.Barry@Sun.COM 
130510721SGlenn.Barry@Sun.COM 	mutex_enter(&client_data->clm);
130610721SGlenn.Barry@Sun.COM 	if (!client_data->established) {
130710721SGlenn.Barry@Sun.COM 		ret = AUTH_FAILED;
130810721SGlenn.Barry@Sun.COM 		goto error2;
130910721SGlenn.Barry@Sun.COM 	}
131010721SGlenn.Barry@Sun.COM 	if (client_data->stale) {
131110721SGlenn.Barry@Sun.COM 		ret = RPCSEC_GSS_NOCRED;
131210721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: client data stale\n");
131310721SGlenn.Barry@Sun.COM 		goto error2;
131410721SGlenn.Barry@Sun.COM 	}
131510721SGlenn.Barry@Sun.COM 
131610721SGlenn.Barry@Sun.COM 	(void) svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
131710721SGlenn.Barry@Sun.COM 	*no_dispatch = TRUE;
131810721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
131910721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
132010721SGlenn.Barry@Sun.COM 	client_data->stale = TRUE;
132110721SGlenn.Barry@Sun.COM 	mutex_exit(&client_data->clm);
132210721SGlenn.Barry@Sun.COM 	return (AUTH_OK);
132310721SGlenn.Barry@Sun.COM 
132410721SGlenn.Barry@Sun.COM error2:
132510721SGlenn.Barry@Sun.COM 	ASSERT(client_data->ref_cnt > 0);
132610721SGlenn.Barry@Sun.COM 	client_data->ref_cnt--;
132710721SGlenn.Barry@Sun.COM 	client_data->stale = TRUE;
13280Sstevel@tonic-gate 	mutex_exit(&client_data->clm);
132910721SGlenn.Barry@Sun.COM 	return (ret);
133010721SGlenn.Barry@Sun.COM }
133110721SGlenn.Barry@Sun.COM 
133210721SGlenn.Barry@Sun.COM /*
133310721SGlenn.Barry@Sun.COM  * Server side authentication for RPCSEC_GSS.
133410721SGlenn.Barry@Sun.COM  */
133510721SGlenn.Barry@Sun.COM enum auth_stat
__svcrpcsec_gss(struct svc_req * rqst,struct rpc_msg * msg,bool_t * no_dispatch)133610721SGlenn.Barry@Sun.COM __svcrpcsec_gss(
133710721SGlenn.Barry@Sun.COM 	struct svc_req		*rqst,
133810721SGlenn.Barry@Sun.COM 	struct rpc_msg		*msg,
133910721SGlenn.Barry@Sun.COM 	bool_t			*no_dispatch)
134010721SGlenn.Barry@Sun.COM {
134110721SGlenn.Barry@Sun.COM 	XDR			xdrs;
134210721SGlenn.Barry@Sun.COM 	rpc_gss_creds		creds;
134310721SGlenn.Barry@Sun.COM 	struct opaque_auth	*cred;
134410721SGlenn.Barry@Sun.COM 	int			ret;
134510721SGlenn.Barry@Sun.COM 
134610721SGlenn.Barry@Sun.COM 	*no_dispatch = FALSE;
134710721SGlenn.Barry@Sun.COM 
134810721SGlenn.Barry@Sun.COM 	/*
134910721SGlenn.Barry@Sun.COM 	 * Initialize response verifier to NULL verifier.  If
135010721SGlenn.Barry@Sun.COM 	 * necessary, this will be changed later.
135110721SGlenn.Barry@Sun.COM 	 */
135210721SGlenn.Barry@Sun.COM 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
135310721SGlenn.Barry@Sun.COM 	rqst->rq_xprt->xp_verf.oa_base = NULL;
135410721SGlenn.Barry@Sun.COM 	rqst->rq_xprt->xp_verf.oa_length = 0;
135510721SGlenn.Barry@Sun.COM 
13560Sstevel@tonic-gate 	/*
135710721SGlenn.Barry@Sun.COM 	 * Pull out and check credential and verifier.
13580Sstevel@tonic-gate 	 */
135910721SGlenn.Barry@Sun.COM 	cred = &msg->rm_call.cb_cred;
136010721SGlenn.Barry@Sun.COM 
136110721SGlenn.Barry@Sun.COM 	if (cred->oa_length == 0) {
136210721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: zero length cred\n");
136310721SGlenn.Barry@Sun.COM 		return (AUTH_BADCRED);
136410721SGlenn.Barry@Sun.COM 	}
136510721SGlenn.Barry@Sun.COM 
136610721SGlenn.Barry@Sun.COM 	xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
136710721SGlenn.Barry@Sun.COM 	bzero((char *)&creds, sizeof (creds));
136810721SGlenn.Barry@Sun.COM 	if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
136910721SGlenn.Barry@Sun.COM 		XDR_DESTROY(&xdrs);
137010721SGlenn.Barry@Sun.COM 		RPCGSS_LOG0(1, "_svcrpcsec_gss: can't decode creds\n");
137110721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
137210721SGlenn.Barry@Sun.COM 		return (AUTH_BADCRED);
137310721SGlenn.Barry@Sun.COM 	}
137410721SGlenn.Barry@Sun.COM 	XDR_DESTROY(&xdrs);
137510721SGlenn.Barry@Sun.COM 
137610721SGlenn.Barry@Sun.COM 	switch (creds.gss_proc) {
137710721SGlenn.Barry@Sun.COM 	case RPCSEC_GSS_INIT:
137810721SGlenn.Barry@Sun.COM 		ret = rpcsec_gss_init(rqst, msg, creds, no_dispatch, NULL);
137910721SGlenn.Barry@Sun.COM 		break;
138010721SGlenn.Barry@Sun.COM 	case RPCSEC_GSS_CONTINUE_INIT:
138110721SGlenn.Barry@Sun.COM 		ret = rpcsec_gss_continue_init(rqst, msg, creds, no_dispatch);
138210721SGlenn.Barry@Sun.COM 		break;
138310721SGlenn.Barry@Sun.COM 	case RPCSEC_GSS_DATA:
138410721SGlenn.Barry@Sun.COM 		ret = rpcsec_gss_data(rqst, msg, creds, no_dispatch);
138510721SGlenn.Barry@Sun.COM 		break;
138610721SGlenn.Barry@Sun.COM 	case RPCSEC_GSS_DESTROY:
138710721SGlenn.Barry@Sun.COM 		ret = rpcsec_gss_destroy(rqst, creds, no_dispatch);
138810721SGlenn.Barry@Sun.COM 		break;
138910721SGlenn.Barry@Sun.COM 	default:
139010721SGlenn.Barry@Sun.COM 		cmn_err(CE_NOTE, "__svcrpcsec_gss: bad proc=%d",
139110721SGlenn.Barry@Sun.COM 		    creds.gss_proc);
139210721SGlenn.Barry@Sun.COM 		ret = AUTH_BADCRED;
139310721SGlenn.Barry@Sun.COM 	}
139410721SGlenn.Barry@Sun.COM 
13950Sstevel@tonic-gate 	if (creds.ctx_handle.length != 0)
13960Sstevel@tonic-gate 		xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
13970Sstevel@tonic-gate 	return (ret);
13980Sstevel@tonic-gate }
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate /*
14010Sstevel@tonic-gate  * Check verifier.  The verifier is the checksum of the RPC header
14020Sstevel@tonic-gate  * upto and including the credentials field.
14030Sstevel@tonic-gate  */
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate /* ARGSUSED */
14060Sstevel@tonic-gate static bool_t
check_verf(struct rpc_msg * msg,gss_ctx_id_t context,int * qop_state,uid_t uid)14070Sstevel@tonic-gate check_verf(struct rpc_msg *msg, gss_ctx_id_t context, int *qop_state, uid_t uid)
14080Sstevel@tonic-gate {
14090Sstevel@tonic-gate 	int			*buf, *tmp;
14100Sstevel@tonic-gate 	char			hdr[128];
14110Sstevel@tonic-gate 	struct opaque_auth	*oa;
14120Sstevel@tonic-gate 	int			len;
14130Sstevel@tonic-gate 	gss_buffer_desc		msg_buf;
14140Sstevel@tonic-gate 	gss_buffer_desc		tok_buf;
14150Sstevel@tonic-gate 	OM_uint32		gssstat, minor_stat;
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	/*
14180Sstevel@tonic-gate 	 * We have to reconstruct the RPC header from the previously
14190Sstevel@tonic-gate 	 * parsed information, since we haven't kept the header intact.
14200Sstevel@tonic-gate 	 */
14215101Spk193450 
14225101Spk193450 	oa = &msg->rm_call.cb_cred;
14235101Spk193450 	if (oa->oa_length > MAX_AUTH_BYTES)
14245101Spk193450 		return (FALSE);
14255101Spk193450 
14265101Spk193450 	/* 8 XDR units from the IXDR macro calls. */
14275101Spk193450 	if (sizeof (hdr) < (8 * BYTES_PER_XDR_UNIT +
14285101Spk193450 	    RNDUP(oa->oa_length)))
14295101Spk193450 		return (FALSE);
14300Sstevel@tonic-gate 	buf = (int *)hdr;
14310Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_xid);
14320Sstevel@tonic-gate 	IXDR_PUT_ENUM(buf, msg->rm_direction);
14330Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
14340Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
14350Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
14360Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
14370Sstevel@tonic-gate 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
14380Sstevel@tonic-gate 	IXDR_PUT_U_INT32(buf, oa->oa_length);
14390Sstevel@tonic-gate 	if (oa->oa_length) {
14400Sstevel@tonic-gate 		len = RNDUP(oa->oa_length);
14410Sstevel@tonic-gate 		tmp = buf;
14420Sstevel@tonic-gate 		buf += len / sizeof (int);
14430Sstevel@tonic-gate 		*(buf - 1) = 0;
14440Sstevel@tonic-gate 		(void) bcopy(oa->oa_base, (caddr_t)tmp, oa->oa_length);
14450Sstevel@tonic-gate 	}
14460Sstevel@tonic-gate 	len = ((char *)buf) - hdr;
14470Sstevel@tonic-gate 	msg_buf.length = len;
14480Sstevel@tonic-gate 	msg_buf.value = hdr;
14490Sstevel@tonic-gate 	oa = &msg->rm_call.cb_verf;
14500Sstevel@tonic-gate 	tok_buf.length = oa->oa_length;
14510Sstevel@tonic-gate 	tok_buf.value = oa->oa_base;
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 	gssstat = kgss_verify(&minor_stat, context, &msg_buf, &tok_buf,
145410721SGlenn.Barry@Sun.COM 	    qop_state);
14550Sstevel@tonic-gate 	if (gssstat != GSS_S_COMPLETE) {
14560Sstevel@tonic-gate 		RPCGSS_LOG(1, "check_verf: kgss_verify status 0x%x\n", gssstat);
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: msg_buf length %d\n", len);
14590Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: msg_buf value 0x%x\n", *(int *)hdr);
14600Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: tok_buf length %ld\n",
146110721SGlenn.Barry@Sun.COM 		    tok_buf.length);
14620Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: tok_buf value 0x%p\n",
146310721SGlenn.Barry@Sun.COM 		    (void *)oa->oa_base);
14640Sstevel@tonic-gate 		RPCGSS_LOG(4, "check_verf: context 0x%p\n", (void *)context);
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 		return (FALSE);
14670Sstevel@tonic-gate 	}
14680Sstevel@tonic-gate 	return (TRUE);
14690Sstevel@tonic-gate }
14700Sstevel@tonic-gate 
147110721SGlenn.Barry@Sun.COM 
14720Sstevel@tonic-gate /*
14730Sstevel@tonic-gate  * Set response verifier.  This is the checksum of the given number.
14740Sstevel@tonic-gate  * (e.g. sequence number or sequence window)
14750Sstevel@tonic-gate  */
14760Sstevel@tonic-gate static bool_t
set_response_verf(rqst,msg,cl,num)14770Sstevel@tonic-gate set_response_verf(rqst, msg, cl, num)
14780Sstevel@tonic-gate 	struct svc_req		*rqst;
14790Sstevel@tonic-gate 	struct rpc_msg		*msg;
14800Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
14810Sstevel@tonic-gate 	uint_t			num;
14820Sstevel@tonic-gate {
14830Sstevel@tonic-gate 	OM_uint32		minor;
14840Sstevel@tonic-gate 	gss_buffer_desc		in_buf, out_buf;
14850Sstevel@tonic-gate 	uint_t			num_net;
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	num_net = (uint_t)htonl(num);
14880Sstevel@tonic-gate 	in_buf.length = sizeof (num);
14890Sstevel@tonic-gate 	in_buf.value = (char *)&num_net;
14900Sstevel@tonic-gate /* XXX uid ? */
149110721SGlenn.Barry@Sun.COM 
14920Sstevel@tonic-gate 	if ((kgss_sign(&minor, cl->context, cl->qop, &in_buf,
14930Sstevel@tonic-gate 				&out_buf)) != GSS_S_COMPLETE)
14940Sstevel@tonic-gate 		return (FALSE);
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
14970Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
14980Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
14990Sstevel@tonic-gate 	bcopy(out_buf.value, rqst->rq_xprt->xp_verf.oa_base, out_buf.length);
15000Sstevel@tonic-gate 	(void) gss_release_buffer(&minor, &out_buf);
15010Sstevel@tonic-gate 	return (TRUE);
15020Sstevel@tonic-gate }
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate /*
15050Sstevel@tonic-gate  * Create client context.
15060Sstevel@tonic-gate  */
15070Sstevel@tonic-gate static svc_rpc_gss_data *
create_client()15080Sstevel@tonic-gate create_client()
15090Sstevel@tonic-gate {
15100Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
15110Sstevel@tonic-gate 	static uint_t		key = 1;
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	client_data = (svc_rpc_gss_data *) kmem_cache_alloc(svc_data_handle,
151410721SGlenn.Barry@Sun.COM 	    KM_SLEEP);
15150Sstevel@tonic-gate 	if (client_data == NULL)
15160Sstevel@tonic-gate 		return (NULL);
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 	/*
15190Sstevel@tonic-gate 	 * set up client data structure
15200Sstevel@tonic-gate 	 */
15210Sstevel@tonic-gate 	client_data->next = NULL;
15220Sstevel@tonic-gate 	client_data->prev = NULL;
15230Sstevel@tonic-gate 	client_data->lru_next = NULL;
15240Sstevel@tonic-gate 	client_data->lru_prev = NULL;
15250Sstevel@tonic-gate 	client_data->client_name.length = 0;
15260Sstevel@tonic-gate 	client_data->client_name.value = NULL;
15270Sstevel@tonic-gate 	client_data->seq_num = 0;
15280Sstevel@tonic-gate 	bzero(client_data->seq_bits, sizeof (client_data->seq_bits));
15290Sstevel@tonic-gate 	client_data->key = 0;
15300Sstevel@tonic-gate 	client_data->cookie = NULL;
15310Sstevel@tonic-gate 	bzero(&client_data->u_cred, sizeof (client_data->u_cred));
15320Sstevel@tonic-gate 	client_data->established = FALSE;
15330Sstevel@tonic-gate 	client_data->locked = FALSE;
15340Sstevel@tonic-gate 	client_data->u_cred_set = 0;
15350Sstevel@tonic-gate 	client_data->context = GSS_C_NO_CONTEXT;
15360Sstevel@tonic-gate 	client_data->expiration = GSS_C_INDEFINITE;
15370Sstevel@tonic-gate 	client_data->deleg = GSS_C_NO_CREDENTIAL;
15380Sstevel@tonic-gate 	client_data->ref_cnt = 1;
15390Sstevel@tonic-gate 	client_data->last_ref_time = gethrestime_sec();
15400Sstevel@tonic-gate 	client_data->qop = GSS_C_QOP_DEFAULT;
15410Sstevel@tonic-gate 	client_data->done_docallback = FALSE;
15420Sstevel@tonic-gate 	client_data->stale = FALSE;
15430Sstevel@tonic-gate 	client_data->retrans_data = NULL;
15440Sstevel@tonic-gate 	bzero(&client_data->raw_cred, sizeof (client_data->raw_cred));
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	/*
15470Sstevel@tonic-gate 	 * The client context handle is a 32-bit key (unsigned int).
15480Sstevel@tonic-gate 	 * The key is incremented until there is no duplicate for it.
15490Sstevel@tonic-gate 	 */
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate 	svc_rpc_gss_cache_stats.total_entries_allocated++;
15520Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
15530Sstevel@tonic-gate 	for (;;) {
15540Sstevel@tonic-gate 		client_data->key = key++;
15550Sstevel@tonic-gate 		if (find_client(client_data->key) == NULL) {
15560Sstevel@tonic-gate 			insert_client(client_data);
15570Sstevel@tonic-gate 			mutex_exit(&ctx_mutex);
15580Sstevel@tonic-gate 			return (client_data);
15590Sstevel@tonic-gate 		}
15600Sstevel@tonic-gate 	}
15610Sstevel@tonic-gate 	/*NOTREACHED*/
15620Sstevel@tonic-gate }
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate /*
15650Sstevel@tonic-gate  * Insert client context into hash list and LRU list.
15660Sstevel@tonic-gate  */
15670Sstevel@tonic-gate static void
insert_client(client_data)15680Sstevel@tonic-gate insert_client(client_data)
15690Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
15700Sstevel@tonic-gate {
15710Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
15720Sstevel@tonic-gate 	int			index = HASH(client_data->key);
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate 	client_data->prev = NULL;
15770Sstevel@tonic-gate 	cl = clients[index];
15780Sstevel@tonic-gate 	if ((client_data->next = cl) != NULL)
15790Sstevel@tonic-gate 		cl->prev = client_data;
15800Sstevel@tonic-gate 	clients[index] = client_data;
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate 	client_data->lru_prev = NULL;
15830Sstevel@tonic-gate 	if ((client_data->lru_next = lru_first) != NULL)
15840Sstevel@tonic-gate 		lru_first->lru_prev = client_data;
15850Sstevel@tonic-gate 	else
15860Sstevel@tonic-gate 		lru_last = client_data;
15870Sstevel@tonic-gate 	lru_first = client_data;
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	num_gss_contexts++;
15900Sstevel@tonic-gate }
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate /*
15930Sstevel@tonic-gate  * Fetch a client, given the client context handle.  Move it to the
15940Sstevel@tonic-gate  * top of the LRU list since this is the most recently used context.
15950Sstevel@tonic-gate  */
15960Sstevel@tonic-gate static svc_rpc_gss_data *
get_client(ctx_handle)15970Sstevel@tonic-gate get_client(ctx_handle)
15980Sstevel@tonic-gate 	gss_buffer_t		ctx_handle;
15990Sstevel@tonic-gate {
16000Sstevel@tonic-gate 	uint_t			key = *(uint_t *)ctx_handle->value;
16010Sstevel@tonic-gate 	svc_rpc_gss_data	*cl;
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 	mutex_enter(&ctx_mutex);
16040Sstevel@tonic-gate 	if ((cl = find_client(key)) != NULL) {
16050Sstevel@tonic-gate 		mutex_enter(&cl->clm);
16060Sstevel@tonic-gate 		if (cl->stale) {
16070Sstevel@tonic-gate 			if (cl->ref_cnt == 0) {
16080Sstevel@tonic-gate 				mutex_exit(&cl->clm);
16090Sstevel@tonic-gate 				destroy_client(cl);
16100Sstevel@tonic-gate 			} else {
16110Sstevel@tonic-gate 				mutex_exit(&cl->clm);
16120Sstevel@tonic-gate 			}
16130Sstevel@tonic-gate 			mutex_exit(&ctx_mutex);
16140Sstevel@tonic-gate 			return (NULL);
16150Sstevel@tonic-gate 		}
16160Sstevel@tonic-gate 		cl->ref_cnt++;
16170Sstevel@tonic-gate 		cl->last_ref_time = gethrestime_sec();
16180Sstevel@tonic-gate 		mutex_exit(&cl->clm);
16190Sstevel@tonic-gate 		if (cl != lru_first) {
16200Sstevel@tonic-gate 			cl->lru_prev->lru_next = cl->lru_next;
16210Sstevel@tonic-gate 			if (cl->lru_next != NULL)
16220Sstevel@tonic-gate 				cl->lru_next->lru_prev = cl->lru_prev;
16230Sstevel@tonic-gate 			else
16240Sstevel@tonic-gate 				lru_last = cl->lru_prev;
16250Sstevel@tonic-gate 			cl->lru_prev = NULL;
16260Sstevel@tonic-gate 			cl->lru_next = lru_first;
16270Sstevel@tonic-gate 			lru_first->lru_prev = cl;
16280Sstevel@tonic-gate 			lru_first = cl;
16290Sstevel@tonic-gate 		}
16300Sstevel@tonic-gate 	}
16310Sstevel@tonic-gate 	mutex_exit(&ctx_mutex);
16320Sstevel@tonic-gate 	return (cl);
16330Sstevel@tonic-gate }
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate /*
16360Sstevel@tonic-gate  * Given the client context handle, find the context corresponding to it.
16370Sstevel@tonic-gate  * Don't change its LRU state since it may not be used.
16380Sstevel@tonic-gate  */
16390Sstevel@tonic-gate static svc_rpc_gss_data *
find_client(key)16400Sstevel@tonic-gate find_client(key)
16410Sstevel@tonic-gate 	uint_t			key;
16420Sstevel@tonic-gate {
16430Sstevel@tonic-gate 	int			index = HASH(key);
16440Sstevel@tonic-gate 	svc_rpc_gss_data	*cl = NULL;
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	for (cl = clients[index]; cl != NULL; cl = cl->next) {
16490Sstevel@tonic-gate 		if (cl->key == key)
16500Sstevel@tonic-gate 			break;
16510Sstevel@tonic-gate 	}
16520Sstevel@tonic-gate 	return (cl);
16530Sstevel@tonic-gate }
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate /*
16560Sstevel@tonic-gate  * Destroy a client context.
16570Sstevel@tonic-gate  */
16580Sstevel@tonic-gate static void
destroy_client(client_data)16590Sstevel@tonic-gate destroy_client(client_data)
16600Sstevel@tonic-gate 	svc_rpc_gss_data	*client_data;
16610Sstevel@tonic-gate {
16620Sstevel@tonic-gate 	OM_uint32		minor;
16630Sstevel@tonic-gate 	int			index = HASH(client_data->key);
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
16660Sstevel@tonic-gate 
16670Sstevel@tonic-gate 	/*
16680Sstevel@tonic-gate 	 * remove from hash list
16690Sstevel@tonic-gate 	 */
16700Sstevel@tonic-gate 	if (client_data->prev == NULL)
16710Sstevel@tonic-gate 		clients[index] = client_data->next;
16720Sstevel@tonic-gate 	else
16730Sstevel@tonic-gate 		client_data->prev->next = client_data->next;
16740Sstevel@tonic-gate 	if (client_data->next != NULL)
16750Sstevel@tonic-gate 		client_data->next->prev = client_data->prev;
16760Sstevel@tonic-gate 
16770Sstevel@tonic-gate 	/*
16780Sstevel@tonic-gate 	 * remove from LRU list
16790Sstevel@tonic-gate 	 */
16800Sstevel@tonic-gate 	if (client_data->lru_prev == NULL)
16810Sstevel@tonic-gate 		lru_first = client_data->lru_next;
16820Sstevel@tonic-gate 	else
16830Sstevel@tonic-gate 		client_data->lru_prev->lru_next = client_data->lru_next;
16840Sstevel@tonic-gate 	if (client_data->lru_next != NULL)
16850Sstevel@tonic-gate 		client_data->lru_next->lru_prev = client_data->lru_prev;
16860Sstevel@tonic-gate 	else
16870Sstevel@tonic-gate 		lru_last = client_data->lru_prev;
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 	/*
16900Sstevel@tonic-gate 	 * If there is a GSS context, clean up GSS state.
16910Sstevel@tonic-gate 	 */
16920Sstevel@tonic-gate 	if (client_data->context != GSS_C_NO_CONTEXT) {
16930Sstevel@tonic-gate 		(void) kgss_delete_sec_context(&minor, &client_data->context,
16940Sstevel@tonic-gate 					NULL);
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 		common_client_data_free(client_data);
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
16990Sstevel@tonic-gate 		    (void) kgss_release_cred(&minor, &client_data->deleg,
17000Sstevel@tonic-gate 				crgetuid(CRED()));
17010Sstevel@tonic-gate 		}
17020Sstevel@tonic-gate 	}
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 	if (client_data->u_cred.gidlist != NULL) {
17050Sstevel@tonic-gate 	    kmem_free((char *)client_data->u_cred.gidlist,
17060Sstevel@tonic-gate 			client_data->u_cred.gidlen * sizeof (gid_t));
17070Sstevel@tonic-gate 	    client_data->u_cred.gidlist = NULL;
17080Sstevel@tonic-gate 	}
17090Sstevel@tonic-gate 	if (client_data->retrans_data != NULL)
17100Sstevel@tonic-gate 		retrans_del(client_data);
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate 	kmem_cache_free(svc_data_handle, client_data);
17130Sstevel@tonic-gate 	num_gss_contexts--;
17140Sstevel@tonic-gate }
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate /*
17170Sstevel@tonic-gate  * Check for expired and stale client contexts.
17180Sstevel@tonic-gate  */
17190Sstevel@tonic-gate static void
sweep_clients(bool_t from_reclaim)17200Sstevel@tonic-gate sweep_clients(bool_t from_reclaim)
17210Sstevel@tonic-gate {
17220Sstevel@tonic-gate 	svc_rpc_gss_data	*cl, *next;
17230Sstevel@tonic-gate 	time_t			last_reference_needed;
17240Sstevel@tonic-gate 	time_t			now = gethrestime_sec();
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 	ASSERT(mutex_owned(&ctx_mutex));
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate 	last_reference_needed = now - (from_reclaim ?
172910721SGlenn.Barry@Sun.COM 	    svc_rpc_gss_active_delta : svc_rpc_gss_inactive_delta);
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 	cl = lru_last;
17320Sstevel@tonic-gate 	while (cl) {
17330Sstevel@tonic-gate 		/*
17340Sstevel@tonic-gate 		 * We assume here that any manipulation of the LRU pointers
17350Sstevel@tonic-gate 		 * and hash bucket pointers are only done when holding the
17360Sstevel@tonic-gate 		 * ctx_mutex.
17370Sstevel@tonic-gate 		 */
17380Sstevel@tonic-gate 		next = cl->lru_prev;
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 		mutex_enter(&cl->clm);
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 		if ((cl->expiration != GSS_C_INDEFINITE &&
17430Sstevel@tonic-gate 		    cl->expiration <= now) || cl->stale ||
17440Sstevel@tonic-gate 		    cl->last_ref_time <= last_reference_needed) {
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 			if ((cl->expiration != GSS_C_INDEFINITE &&
17470Sstevel@tonic-gate 			    cl->expiration <= now) || cl->stale ||
17480Sstevel@tonic-gate 			    (cl->last_ref_time <= last_reference_needed &&
17490Sstevel@tonic-gate 			    cl->ref_cnt == 0)) {
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 				cl->stale = TRUE;
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 				if (cl->ref_cnt == 0) {
17540Sstevel@tonic-gate 					mutex_exit(&cl->clm);
17550Sstevel@tonic-gate 					if (from_reclaim)
17560Sstevel@tonic-gate 						svc_rpc_gss_cache_stats.
17570Sstevel@tonic-gate 						    no_returned_by_reclaim++;
17580Sstevel@tonic-gate 					destroy_client(cl);
17590Sstevel@tonic-gate 				} else
17600Sstevel@tonic-gate 					mutex_exit(&cl->clm);
17610Sstevel@tonic-gate 			} else
17620Sstevel@tonic-gate 				mutex_exit(&cl->clm);
17630Sstevel@tonic-gate 		} else
17640Sstevel@tonic-gate 			mutex_exit(&cl->clm);
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 		cl = next;
17670Sstevel@tonic-gate 	}
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	last_swept = gethrestime_sec();
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate /*
17730Sstevel@tonic-gate  * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
17740Sstevel@tonic-gate  * and write the result to xdrs.
17750Sstevel@tonic-gate  */
17760Sstevel@tonic-gate static bool_t
svc_rpc_gss_wrap(auth,out_xdrs,xdr_func,xdr_ptr)17770Sstevel@tonic-gate svc_rpc_gss_wrap(auth, out_xdrs, xdr_func, xdr_ptr)
17780Sstevel@tonic-gate 	SVCAUTH			*auth;
17790Sstevel@tonic-gate 	XDR			*out_xdrs;
17800Sstevel@tonic-gate 	bool_t			(*xdr_func)();
17810Sstevel@tonic-gate 	caddr_t			xdr_ptr;
17820Sstevel@tonic-gate {
17830Sstevel@tonic-gate 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
178410721SGlenn.Barry@Sun.COM 	bool_t ret;
17850Sstevel@tonic-gate 
17860Sstevel@tonic-gate 	/*
17870Sstevel@tonic-gate 	 * If context is not established, or if neither integrity nor
17880Sstevel@tonic-gate 	 * privacy service is used, don't wrap - just XDR encode.
17890Sstevel@tonic-gate 	 * Otherwise, wrap data using service and QOP parameters.
17900Sstevel@tonic-gate 	 */
17910Sstevel@tonic-gate 	if (!gss_parms->established ||
17920Sstevel@tonic-gate 				gss_parms->service == rpc_gss_svc_none)
17930Sstevel@tonic-gate 		return ((*xdr_func)(out_xdrs, xdr_ptr));
17940Sstevel@tonic-gate 
179510721SGlenn.Barry@Sun.COM 	ret = __rpc_gss_wrap_data(gss_parms->service,
17960Sstevel@tonic-gate 				(OM_uint32)gss_parms->qop_rcvd,
17970Sstevel@tonic-gate 				(gss_ctx_id_t)gss_parms->context,
17980Sstevel@tonic-gate 				gss_parms->seq_num,
179910721SGlenn.Barry@Sun.COM 				out_xdrs, xdr_func, xdr_ptr);
180010721SGlenn.Barry@Sun.COM 	return (ret);
18010Sstevel@tonic-gate }
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate /*
18040Sstevel@tonic-gate  * Decrypt the serialized arguments and XDR decode them.
18050Sstevel@tonic-gate  */
18060Sstevel@tonic-gate static bool_t
svc_rpc_gss_unwrap(auth,in_xdrs,xdr_func,xdr_ptr)18070Sstevel@tonic-gate svc_rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
18080Sstevel@tonic-gate 	SVCAUTH			*auth;
18090Sstevel@tonic-gate 	XDR			*in_xdrs;
18100Sstevel@tonic-gate 	bool_t			(*xdr_func)();
18110Sstevel@tonic-gate 	caddr_t			xdr_ptr;
18120Sstevel@tonic-gate {
18130Sstevel@tonic-gate 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	/*
18160Sstevel@tonic-gate 	 * If context is not established, or if neither integrity nor
18170Sstevel@tonic-gate 	 * privacy service is used, don't unwrap - just XDR decode.
18180Sstevel@tonic-gate 	 * Otherwise, unwrap data.
18190Sstevel@tonic-gate 	 */
18200Sstevel@tonic-gate 	if (!gss_parms->established ||
18210Sstevel@tonic-gate 				gss_parms->service == rpc_gss_svc_none)
18220Sstevel@tonic-gate 		return ((*xdr_func)(in_xdrs, xdr_ptr));
18230Sstevel@tonic-gate 
18240Sstevel@tonic-gate 	return (__rpc_gss_unwrap_data(gss_parms->service,
18250Sstevel@tonic-gate 				(gss_ctx_id_t)gss_parms->context,
18260Sstevel@tonic-gate 				gss_parms->seq_num,
18270Sstevel@tonic-gate 				gss_parms->qop_rcvd,
18280Sstevel@tonic-gate 				in_xdrs, xdr_func, xdr_ptr));
18290Sstevel@tonic-gate }
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate /* ARGSUSED */
18330Sstevel@tonic-gate int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)18340Sstevel@tonic-gate rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
18350Sstevel@tonic-gate {
18360Sstevel@tonic-gate 	return (0);
18370Sstevel@tonic-gate }
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate /*
18400Sstevel@tonic-gate  * Add retransmit entry to the context cache entry for a new xid.
18410Sstevel@tonic-gate  * If there is already an entry, delete it before adding the new one.
18420Sstevel@tonic-gate  */
retrans_add(client,xid,result)18430Sstevel@tonic-gate static void retrans_add(client, xid, result)
18440Sstevel@tonic-gate 	svc_rpc_gss_data *client;
18450Sstevel@tonic-gate 	uint32_t	xid;
18460Sstevel@tonic-gate 	rpc_gss_init_res *result;
18470Sstevel@tonic-gate {
18480Sstevel@tonic-gate 	retrans_entry	*rdata;
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate 	if (client->retrans_data && client->retrans_data->xid == xid)
18510Sstevel@tonic-gate 		return;
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 	rdata = kmem_zalloc(sizeof (*rdata), KM_SLEEP);
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 	if (rdata == NULL)
18560Sstevel@tonic-gate 		return;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	rdata->xid = xid;
18590Sstevel@tonic-gate 	rdata->result = *result;
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 	if (result->token.length != 0) {
18620Sstevel@tonic-gate 		GSS_DUP_BUFFER(rdata->result.token, result->token);
18630Sstevel@tonic-gate 	}
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 	if (client->retrans_data)
18660Sstevel@tonic-gate 		retrans_del(client);
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 	client->retrans_data = rdata;
18690Sstevel@tonic-gate }
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate /*
18720Sstevel@tonic-gate  * Delete the retransmit data from the context cache entry.
18730Sstevel@tonic-gate  */
retrans_del(client)18740Sstevel@tonic-gate static void retrans_del(client)
18750Sstevel@tonic-gate 	svc_rpc_gss_data *client;
18760Sstevel@tonic-gate {
18770Sstevel@tonic-gate 	retrans_entry *rdata;
18780Sstevel@tonic-gate 	OM_uint32 minor_stat;
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	if (client->retrans_data == NULL)
18810Sstevel@tonic-gate 		return;
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 	rdata = client->retrans_data;
18840Sstevel@tonic-gate 	if (rdata->result.token.length != 0) {
18850Sstevel@tonic-gate 	    (void) gss_release_buffer(&minor_stat, &rdata->result.token);
18860Sstevel@tonic-gate 	}
18870Sstevel@tonic-gate 
18880Sstevel@tonic-gate 	kmem_free((caddr_t)rdata, sizeof (*rdata));
18890Sstevel@tonic-gate 	client->retrans_data = NULL;
18900Sstevel@tonic-gate }
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate /*
18930Sstevel@tonic-gate  * This function frees the following fields of svc_rpc_gss_data:
18940Sstevel@tonic-gate  *	client_name, raw_cred.client_principal, raw_cred.mechanism.
18950Sstevel@tonic-gate  */
18960Sstevel@tonic-gate static void
common_client_data_free(svc_rpc_gss_data * client_data)18970Sstevel@tonic-gate common_client_data_free(svc_rpc_gss_data *client_data)
18980Sstevel@tonic-gate {
18990Sstevel@tonic-gate 	if (client_data->client_name.length > 0) {
19000Sstevel@tonic-gate 		(void) gss_release_buffer(NULL, &client_data->client_name);
19010Sstevel@tonic-gate 	}
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 	if (client_data->raw_cred.client_principal) {
19040Sstevel@tonic-gate 		kmem_free((caddr_t)client_data->raw_cred.client_principal,
190510721SGlenn.Barry@Sun.COM 		    client_data->raw_cred.client_principal->len +
190610721SGlenn.Barry@Sun.COM 		    sizeof (int));
19070Sstevel@tonic-gate 		client_data->raw_cred.client_principal = NULL;
19080Sstevel@tonic-gate 	}
19090Sstevel@tonic-gate 
19100Sstevel@tonic-gate 	/*
19110Sstevel@tonic-gate 	 * In the user GSS-API library, mechanism (mech_type returned
19120Sstevel@tonic-gate 	 * by gss_accept_sec_context) is static storage, however
19130Sstevel@tonic-gate 	 * since all the work is done for gss_accept_sec_context under
19140Sstevel@tonic-gate 	 * gssd, what is returned in the kernel, is a copy from the oid
19150Sstevel@tonic-gate 	 * obtained under from gssd, so need to free it when destroying
19160Sstevel@tonic-gate 	 * the client data.
19170Sstevel@tonic-gate 	 */
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate 	if (client_data->raw_cred.mechanism) {
19200Sstevel@tonic-gate 		kgss_free_oid(client_data->raw_cred.mechanism);
19210Sstevel@tonic-gate 		client_data->raw_cred.mechanism = NULL;
19220Sstevel@tonic-gate 	}
19230Sstevel@tonic-gate }
1924