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