1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28*0Sstevel@tonic-gate /* All Rights Reserved */ 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate /* 31*0Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD 32*0Sstevel@tonic-gate * under license from the Regents of the University of California. 33*0Sstevel@tonic-gate */ 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate /* 38*0Sstevel@tonic-gate * svcauth_des.c, server-side des authentication 39*0Sstevel@tonic-gate * 40*0Sstevel@tonic-gate * We insure for the service the following: 41*0Sstevel@tonic-gate * (1) The timestamp microseconds do not exceed 1 million. 42*0Sstevel@tonic-gate * (2) The timestamp plus the window is less than the current time. 43*0Sstevel@tonic-gate * (3) The timestamp is not less than the one previously 44*0Sstevel@tonic-gate * seen in the current session. 45*0Sstevel@tonic-gate * 46*0Sstevel@tonic-gate * It is up to the server to determine if the window size is 47*0Sstevel@tonic-gate * too small. 48*0Sstevel@tonic-gate */ 49*0Sstevel@tonic-gate 50*0Sstevel@tonic-gate #include <sys/types.h> 51*0Sstevel@tonic-gate #include <sys/time.h> 52*0Sstevel@tonic-gate #include <sys/systm.h> 53*0Sstevel@tonic-gate #include <sys/param.h> 54*0Sstevel@tonic-gate #include <sys/stream.h> 55*0Sstevel@tonic-gate #include <sys/stropts.h> 56*0Sstevel@tonic-gate #include <sys/strsubr.h> 57*0Sstevel@tonic-gate #include <sys/tiuser.h> 58*0Sstevel@tonic-gate #include <sys/tihdr.h> 59*0Sstevel@tonic-gate #include <sys/t_kuser.h> 60*0Sstevel@tonic-gate #include <sys/t_lock.h> 61*0Sstevel@tonic-gate #include <sys/debug.h> 62*0Sstevel@tonic-gate #include <sys/kmem.h> 63*0Sstevel@tonic-gate #include <sys/time.h> 64*0Sstevel@tonic-gate #include <sys/cmn_err.h> 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate #include <rpc/types.h> 67*0Sstevel@tonic-gate #include <rpc/xdr.h> 68*0Sstevel@tonic-gate #include <rpc/auth.h> 69*0Sstevel@tonic-gate #include <rpc/auth_des.h> 70*0Sstevel@tonic-gate #include <rpc/rpc_msg.h> 71*0Sstevel@tonic-gate #include <rpc/svc.h> 72*0Sstevel@tonic-gate #include <rpc/svc_auth.h> 73*0Sstevel@tonic-gate #include <rpc/clnt.h> 74*0Sstevel@tonic-gate #include <rpc/des_crypt.h> 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate #define USEC_PER_SEC 1000000 77*0Sstevel@tonic-gate #define BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */) 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate /* 80*0Sstevel@tonic-gate * Cache of conversation keys and some other useful items. 81*0Sstevel@tonic-gate * The hash table size is controled via authdes_cachesz variable. 82*0Sstevel@tonic-gate * The authdes_cachesz has to be the power of 2. 83*0Sstevel@tonic-gate */ 84*0Sstevel@tonic-gate #define AUTHDES_CACHE_TABLE_SZ 1024 85*0Sstevel@tonic-gate static int authdes_cachesz = AUTHDES_CACHE_TABLE_SZ; 86*0Sstevel@tonic-gate #define HASH(key) ((key) & (authdes_cachesz - 1)) 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate /* low water mark for the number of cache entries */ 89*0Sstevel@tonic-gate static int low_cache_entries = 128; 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate struct authdes_cache_entry { 92*0Sstevel@tonic-gate uint32_t nickname; /* nick name id */ 93*0Sstevel@tonic-gate uint32_t window; /* credential lifetime window */ 94*0Sstevel@tonic-gate des_block key; /* conversation key */ 95*0Sstevel@tonic-gate time_t ref_time; /* time referenced previously */ 96*0Sstevel@tonic-gate char *rname; /* client's name */ 97*0Sstevel@tonic-gate caddr_t localcred; /* generic local credential */ 98*0Sstevel@tonic-gate struct authdes_cache_entry *prev, *next; /* hash table linked list */ 99*0Sstevel@tonic-gate struct authdes_cache_entry *lru_prev, *lru_next; /* LRU linked list */ 100*0Sstevel@tonic-gate kmutex_t lock; /* cache entry lock */ 101*0Sstevel@tonic-gate }; 102*0Sstevel@tonic-gate static struct authdes_cache_entry **authdes_cache; /* [authdes_cachesz] */ 103*0Sstevel@tonic-gate static struct authdes_cache_entry *lru_first = NULL; 104*0Sstevel@tonic-gate static struct authdes_cache_entry *lru_last = NULL; 105*0Sstevel@tonic-gate static kmutex_t authdes_lock; /* cache table lock */ 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate static struct kmem_cache *authdes_cache_handle; 108*0Sstevel@tonic-gate static uint32_t Nickname = 0; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_new(char *, 111*0Sstevel@tonic-gate des_block *, uint32_t); 112*0Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_get(uint32_t); 113*0Sstevel@tonic-gate static void authdes_cache_reclaim(void *); 114*0Sstevel@tonic-gate static void sweep_cache(); 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * After 12 hours, check and delete cache entries that have been 118*0Sstevel@tonic-gate * idled for more than 10 hours. 119*0Sstevel@tonic-gate */ 120*0Sstevel@tonic-gate static time_t authdes_sweep_interval = 12*60*60; 121*0Sstevel@tonic-gate static time_t authdes_cache_time = 10*60*60; 122*0Sstevel@tonic-gate static time_t authdes_last_swept = 0; 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate /* 125*0Sstevel@tonic-gate * cache statistics 126*0Sstevel@tonic-gate */ 127*0Sstevel@tonic-gate static int authdes_ncache = 0; /* number of current cached entries */ 128*0Sstevel@tonic-gate static int authdes_ncachehits = 0; /* #times cache hit */ 129*0Sstevel@tonic-gate static int authdes_ncachemisses = 0; /* #times cache missed */ 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate #define NOT_DEAD(ptr) ASSERT((((intptr_t)(ptr)) != 0xdeadbeef)) 132*0Sstevel@tonic-gate #define IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0) 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate /* 135*0Sstevel@tonic-gate * Service side authenticator for AUTH_DES 136*0Sstevel@tonic-gate */ 137*0Sstevel@tonic-gate enum auth_stat 138*0Sstevel@tonic-gate _svcauth_des(struct svc_req *rqst, struct rpc_msg *msg) 139*0Sstevel@tonic-gate { 140*0Sstevel@tonic-gate int32_t *ixdr; 141*0Sstevel@tonic-gate des_block cryptbuf[2]; 142*0Sstevel@tonic-gate struct authdes_cred *cred; 143*0Sstevel@tonic-gate struct authdes_verf verf; 144*0Sstevel@tonic-gate int status; 145*0Sstevel@tonic-gate des_block *sessionkey; 146*0Sstevel@tonic-gate des_block ivec; 147*0Sstevel@tonic-gate uint32_t window, winverf, namelen; 148*0Sstevel@tonic-gate bool_t nick; 149*0Sstevel@tonic-gate struct timeval timestamp, current_time; 150*0Sstevel@tonic-gate struct authdes_cache_entry *nick_entry; 151*0Sstevel@tonic-gate struct area { 152*0Sstevel@tonic-gate struct authdes_cred area_cred; 153*0Sstevel@tonic-gate char area_netname[MAXNETNAMELEN+1]; 154*0Sstevel@tonic-gate } *area; 155*0Sstevel@tonic-gate timestruc_t now; 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 158*0Sstevel@tonic-gate if (authdes_cache == NULL) { 159*0Sstevel@tonic-gate authdes_cache = kmem_zalloc( 160*0Sstevel@tonic-gate sizeof (struct authdes_cache_entry *) * authdes_cachesz, 161*0Sstevel@tonic-gate KM_SLEEP); 162*0Sstevel@tonic-gate } 163*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate /* LINTED pointer alignment */ 166*0Sstevel@tonic-gate area = (struct area *)rqst->rq_clntcred; 167*0Sstevel@tonic-gate cred = (struct authdes_cred *)&area->area_cred; 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate /* 170*0Sstevel@tonic-gate * Get the credential 171*0Sstevel@tonic-gate */ 172*0Sstevel@tonic-gate /* LINTED pointer alignment */ 173*0Sstevel@tonic-gate ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base; 174*0Sstevel@tonic-gate cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind); 175*0Sstevel@tonic-gate switch (cred->adc_namekind) { 176*0Sstevel@tonic-gate case ADN_FULLNAME: 177*0Sstevel@tonic-gate namelen = IXDR_GET_U_INT32(ixdr); 178*0Sstevel@tonic-gate if (namelen > MAXNETNAMELEN) 179*0Sstevel@tonic-gate return (AUTH_BADCRED); 180*0Sstevel@tonic-gate cred->adc_fullname.name = area->area_netname; 181*0Sstevel@tonic-gate bcopy(ixdr, cred->adc_fullname.name, namelen); 182*0Sstevel@tonic-gate cred->adc_fullname.name[namelen] = 0; 183*0Sstevel@tonic-gate ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT); 184*0Sstevel@tonic-gate cred->adc_fullname.key.key.high = (uint32_t)*ixdr++; 185*0Sstevel@tonic-gate cred->adc_fullname.key.key.low = (uint32_t)*ixdr++; 186*0Sstevel@tonic-gate cred->adc_fullname.window = (uint32_t)*ixdr++; 187*0Sstevel@tonic-gate nick = FALSE; 188*0Sstevel@tonic-gate break; 189*0Sstevel@tonic-gate case ADN_NICKNAME: 190*0Sstevel@tonic-gate cred->adc_nickname = (uint32_t)*ixdr++; 191*0Sstevel@tonic-gate nick = TRUE; 192*0Sstevel@tonic-gate break; 193*0Sstevel@tonic-gate default: 194*0Sstevel@tonic-gate return (AUTH_BADCRED); 195*0Sstevel@tonic-gate } 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate /* 198*0Sstevel@tonic-gate * Get the verifier 199*0Sstevel@tonic-gate */ 200*0Sstevel@tonic-gate /* LINTED pointer alignment */ 201*0Sstevel@tonic-gate ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base; 202*0Sstevel@tonic-gate verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++; 203*0Sstevel@tonic-gate verf.adv_xtimestamp.key.low = (uint32_t)*ixdr++; 204*0Sstevel@tonic-gate verf.adv_int_u = (uint32_t)*ixdr++; 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate /* 208*0Sstevel@tonic-gate * Get the conversation key 209*0Sstevel@tonic-gate */ 210*0Sstevel@tonic-gate if (!nick) { /* ADN_FULLNAME */ 211*0Sstevel@tonic-gate sessionkey = &cred->adc_fullname.key; 212*0Sstevel@tonic-gate if (key_decryptsession(cred->adc_fullname.name, sessionkey) != 213*0Sstevel@tonic-gate RPC_SUCCESS) { 214*0Sstevel@tonic-gate return (AUTH_BADCRED); /* key not found */ 215*0Sstevel@tonic-gate } 216*0Sstevel@tonic-gate } else { /* ADN_NICKNAME */ 217*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 218*0Sstevel@tonic-gate if (!(nick_entry = authdes_cache_get(cred->adc_nickname))) { 219*0Sstevel@tonic-gate RPCLOG(1, "_svcauth_des: nickname %d not in the cache\n", 220*0Sstevel@tonic-gate cred->adc_nickname); 221*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 222*0Sstevel@tonic-gate return (AUTH_BADCRED); /* need refresh */ 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate sessionkey = &nick_entry->key; 225*0Sstevel@tonic-gate mutex_enter(&nick_entry->lock); 226*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 227*0Sstevel@tonic-gate } 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate /* 230*0Sstevel@tonic-gate * Decrypt the timestamp 231*0Sstevel@tonic-gate */ 232*0Sstevel@tonic-gate cryptbuf[0] = verf.adv_xtimestamp; 233*0Sstevel@tonic-gate if (!nick) { /* ADN_FULLNAME */ 234*0Sstevel@tonic-gate cryptbuf[1].key.high = cred->adc_fullname.window; 235*0Sstevel@tonic-gate cryptbuf[1].key.low = verf.adv_winverf; 236*0Sstevel@tonic-gate ivec.key.high = ivec.key.low = 0; 237*0Sstevel@tonic-gate status = cbc_crypt((char *)sessionkey, (char *)cryptbuf, 238*0Sstevel@tonic-gate 2 * sizeof (des_block), DES_DECRYPT, (char *)&ivec); 239*0Sstevel@tonic-gate } else { /* ADN_NICKNAME */ 240*0Sstevel@tonic-gate status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 241*0Sstevel@tonic-gate sizeof (des_block), DES_DECRYPT); 242*0Sstevel@tonic-gate } 243*0Sstevel@tonic-gate if (DES_FAILED(status)) { 244*0Sstevel@tonic-gate RPCLOG0(1, "_svcauth_des: decryption failure\n"); 245*0Sstevel@tonic-gate if (nick) { 246*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate return (AUTH_FAILED); /* system error */ 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate /* 252*0Sstevel@tonic-gate * XDR the decrypted timestamp 253*0Sstevel@tonic-gate */ 254*0Sstevel@tonic-gate ixdr = (int32_t *)cryptbuf; 255*0Sstevel@tonic-gate timestamp.tv_sec = IXDR_GET_INT32(ixdr); 256*0Sstevel@tonic-gate timestamp.tv_usec = IXDR_GET_INT32(ixdr); 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate /* 259*0Sstevel@tonic-gate * Check for valid credentials and verifiers. 260*0Sstevel@tonic-gate * They could be invalid because the key was flushed 261*0Sstevel@tonic-gate * out of the cache, and so a new session should begin. 262*0Sstevel@tonic-gate * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case. 263*0Sstevel@tonic-gate */ 264*0Sstevel@tonic-gate if (!nick) { /* ADN_FULLNAME */ 265*0Sstevel@tonic-gate window = IXDR_GET_U_INT32(ixdr); 266*0Sstevel@tonic-gate winverf = IXDR_GET_U_INT32(ixdr); 267*0Sstevel@tonic-gate if (winverf != window - 1) { 268*0Sstevel@tonic-gate RPCLOG(1, "_svcauth_des: window verifier mismatch %d\n", 269*0Sstevel@tonic-gate winverf); 270*0Sstevel@tonic-gate return (AUTH_BADCRED); /* garbled credential */ 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate } else { /* ADN_NICKNAME */ 273*0Sstevel@tonic-gate window = nick_entry->window; 274*0Sstevel@tonic-gate } 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate if (timestamp.tv_usec >= USEC_PER_SEC) { 277*0Sstevel@tonic-gate RPCLOG(1, "_svcauth_des: invalid usecs %ld\n", 278*0Sstevel@tonic-gate timestamp.tv_usec); 279*0Sstevel@tonic-gate /* cached out (bad key), or garbled verifier */ 280*0Sstevel@tonic-gate if (nick) { 281*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF); 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate gethrestime(&now); 287*0Sstevel@tonic-gate current_time.tv_sec = now.tv_sec; 288*0Sstevel@tonic-gate current_time.tv_usec = now.tv_nsec / 1000; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate current_time.tv_sec -= window; /* allow for expiration */ 291*0Sstevel@tonic-gate if (!BEFORE(¤t_time, ×tamp)) { 292*0Sstevel@tonic-gate RPCLOG0(1, "_svcauth_des: timestamp expired\n"); 293*0Sstevel@tonic-gate /* replay, or garbled credential */ 294*0Sstevel@tonic-gate if (nick) { 295*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED); 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate /* 301*0Sstevel@tonic-gate * xdr the timestamp before encrypting 302*0Sstevel@tonic-gate */ 303*0Sstevel@tonic-gate ixdr = (int32_t *)cryptbuf; 304*0Sstevel@tonic-gate IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1); 305*0Sstevel@tonic-gate IXDR_PUT_INT32(ixdr, timestamp.tv_usec); 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate /* 308*0Sstevel@tonic-gate * encrypt the timestamp 309*0Sstevel@tonic-gate */ 310*0Sstevel@tonic-gate status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 311*0Sstevel@tonic-gate sizeof (des_block), DES_ENCRYPT); 312*0Sstevel@tonic-gate if (DES_FAILED(status)) { 313*0Sstevel@tonic-gate RPCLOG0(1, "_svcauth_des: encryption failure\n"); 314*0Sstevel@tonic-gate if (nick) { 315*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 316*0Sstevel@tonic-gate } 317*0Sstevel@tonic-gate return (AUTH_FAILED); /* system error */ 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate verf.adv_xtimestamp = cryptbuf[0]; 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate /* 322*0Sstevel@tonic-gate * If a ADN_FULLNAME, create a new nickname cache entry. 323*0Sstevel@tonic-gate */ 324*0Sstevel@tonic-gate if (!nick) { 325*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 326*0Sstevel@tonic-gate if (!(nick_entry = authdes_cache_new(cred->adc_fullname.name, 327*0Sstevel@tonic-gate sessionkey, window))) { 328*0Sstevel@tonic-gate RPCLOG0(1, "_svcauth_des: can not create new cache entry\n"); 329*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 330*0Sstevel@tonic-gate return (AUTH_FAILED); 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate mutex_enter(&nick_entry->lock); 333*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate verf.adv_nickname = nick_entry->nickname; 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate /* 338*0Sstevel@tonic-gate * Serialize the reply verifier, and update rqst 339*0Sstevel@tonic-gate */ 340*0Sstevel@tonic-gate /* LINTED pointer alignment */ 341*0Sstevel@tonic-gate ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base; 342*0Sstevel@tonic-gate *ixdr++ = (int32_t)verf.adv_xtimestamp.key.high; 343*0Sstevel@tonic-gate *ixdr++ = (int32_t)verf.adv_xtimestamp.key.low; 344*0Sstevel@tonic-gate *ixdr++ = (int32_t)verf.adv_int_u; 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES; 347*0Sstevel@tonic-gate rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base; 348*0Sstevel@tonic-gate rqst->rq_xprt->xp_verf.oa_length = 349*0Sstevel@tonic-gate (uint_t)((char *)ixdr - msg->rm_call.cb_verf.oa_base); 350*0Sstevel@tonic-gate if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) { 351*0Sstevel@tonic-gate RPCLOG0(1, "_svcauth_des: invalid oa length\n"); 352*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 353*0Sstevel@tonic-gate return (AUTH_BADVERF); 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate /* 357*0Sstevel@tonic-gate * We succeeded and finish cooking the credential. 358*0Sstevel@tonic-gate * nicknames are cooked into fullnames 359*0Sstevel@tonic-gate */ 360*0Sstevel@tonic-gate if (!nick) { 361*0Sstevel@tonic-gate cred->adc_nickname = nick_entry->nickname; 362*0Sstevel@tonic-gate cred->adc_fullname.window = window; 363*0Sstevel@tonic-gate } else { /* ADN_NICKNAME */ 364*0Sstevel@tonic-gate cred->adc_namekind = ADN_FULLNAME; 365*0Sstevel@tonic-gate cred->adc_fullname.name = nick_entry->rname; 366*0Sstevel@tonic-gate cred->adc_fullname.key = nick_entry->key; 367*0Sstevel@tonic-gate cred->adc_fullname.window = nick_entry->window; 368*0Sstevel@tonic-gate } 369*0Sstevel@tonic-gate mutex_exit(&nick_entry->lock); 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gate /* 372*0Sstevel@tonic-gate * For every authdes_sweep_interval, delete cache entries that have been 373*0Sstevel@tonic-gate * idled for authdes_cache_time. 374*0Sstevel@tonic-gate */ 375*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 376*0Sstevel@tonic-gate if ((gethrestime_sec() - authdes_last_swept) > authdes_sweep_interval) 377*0Sstevel@tonic-gate sweep_cache(); 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate return (AUTH_OK); /* we made it! */ 382*0Sstevel@tonic-gate } 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate /* 385*0Sstevel@tonic-gate * Initialization upon loading the rpcsec module. 386*0Sstevel@tonic-gate */ 387*0Sstevel@tonic-gate void 388*0Sstevel@tonic-gate svcauthdes_init(void) 389*0Sstevel@tonic-gate { 390*0Sstevel@tonic-gate mutex_init(&authdes_lock, NULL, MUTEX_DEFAULT, NULL); 391*0Sstevel@tonic-gate /* 392*0Sstevel@tonic-gate * Allocate des cache handle 393*0Sstevel@tonic-gate */ 394*0Sstevel@tonic-gate authdes_cache_handle = kmem_cache_create("authdes_cache_handle", 395*0Sstevel@tonic-gate sizeof (struct authdes_cache_entry), 0, NULL, NULL, 396*0Sstevel@tonic-gate authdes_cache_reclaim, NULL, NULL, 0); 397*0Sstevel@tonic-gate } 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate /* 400*0Sstevel@tonic-gate * Final actions upon exiting the rpcsec module. 401*0Sstevel@tonic-gate */ 402*0Sstevel@tonic-gate void 403*0Sstevel@tonic-gate svcauthdes_fini(void) 404*0Sstevel@tonic-gate { 405*0Sstevel@tonic-gate mutex_destroy(&authdes_lock); 406*0Sstevel@tonic-gate kmem_cache_destroy(authdes_cache_handle); 407*0Sstevel@tonic-gate } 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate /* 410*0Sstevel@tonic-gate * Local credential handling stuff. 411*0Sstevel@tonic-gate * NOTE: bsd unix dependent. 412*0Sstevel@tonic-gate * Other operating systems should put something else here. 413*0Sstevel@tonic-gate */ 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate struct bsdcred { 416*0Sstevel@tonic-gate uid_t uid; /* cached uid */ 417*0Sstevel@tonic-gate gid_t gid; /* cached gid */ 418*0Sstevel@tonic-gate short valid; /* valid creds */ 419*0Sstevel@tonic-gate short grouplen; /* length of cached groups */ 420*0Sstevel@tonic-gate int groups[NGROUPS_UMAX]; /* cached groups */ 421*0Sstevel@tonic-gate }; 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate /* 424*0Sstevel@tonic-gate * Map a des credential into a unix cred. 425*0Sstevel@tonic-gate * We cache the credential here so the application does 426*0Sstevel@tonic-gate * not have to make an rpc call every time to interpret 427*0Sstevel@tonic-gate * the credential. 428*0Sstevel@tonic-gate */ 429*0Sstevel@tonic-gate int 430*0Sstevel@tonic-gate kauthdes_getucred(const struct authdes_cred *adc, cred_t *cr) 431*0Sstevel@tonic-gate { 432*0Sstevel@tonic-gate uid_t i_uid; 433*0Sstevel@tonic-gate gid_t i_gid; 434*0Sstevel@tonic-gate int i_grouplen; 435*0Sstevel@tonic-gate struct bsdcred *cred; 436*0Sstevel@tonic-gate struct authdes_cache_entry *nickentry; 437*0Sstevel@tonic-gate 438*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 439*0Sstevel@tonic-gate if (!(nickentry = authdes_cache_get(adc->adc_nickname))) { 440*0Sstevel@tonic-gate RPCLOG0(1, "authdes_getucred: invalid nickname\n"); 441*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 442*0Sstevel@tonic-gate return (0); 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate mutex_enter(&nickentry->lock); 446*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 447*0Sstevel@tonic-gate /* LINTED pointer alignment */ 448*0Sstevel@tonic-gate cred = (struct bsdcred *)nickentry->localcred; 449*0Sstevel@tonic-gate if (!cred->valid) { 450*0Sstevel@tonic-gate /* 451*0Sstevel@tonic-gate * not in cache: lookup 452*0Sstevel@tonic-gate */ 453*0Sstevel@tonic-gate if (netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 454*0Sstevel@tonic-gate &i_grouplen, &cred->groups[0]) != RPC_SUCCESS) { 455*0Sstevel@tonic-gate /* 456*0Sstevel@tonic-gate * Probably a host principal, since at this 457*0Sstevel@tonic-gate * point we have valid keys. Note that later 458*0Sstevel@tonic-gate * if the principal is not in the root list 459*0Sstevel@tonic-gate * for NFS, we will be mapped to that exported 460*0Sstevel@tonic-gate * file system's anonymous user, typically 461*0Sstevel@tonic-gate * NOBODY. keyserv KEY_GETCRED will fail for a 462*0Sstevel@tonic-gate * root-netnames so we assume root here. 463*0Sstevel@tonic-gate * Currently NFS is the only caller of this 464*0Sstevel@tonic-gate * routine. If other RPC services call this 465*0Sstevel@tonic-gate * routine, it is up to that service to 466*0Sstevel@tonic-gate * differentiate between local and remote 467*0Sstevel@tonic-gate * roots. 468*0Sstevel@tonic-gate */ 469*0Sstevel@tonic-gate i_uid = 0; 470*0Sstevel@tonic-gate i_gid = 0; 471*0Sstevel@tonic-gate i_grouplen = 0; 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate RPCLOG0(2, "authdes_getucred: missed ucred cache\n"); 474*0Sstevel@tonic-gate cred->uid = i_uid; 475*0Sstevel@tonic-gate cred->gid = i_gid; 476*0Sstevel@tonic-gate cred->grouplen = (short)i_grouplen; 477*0Sstevel@tonic-gate cred->valid = 1; 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate /* 481*0Sstevel@tonic-gate * cached credentials 482*0Sstevel@tonic-gate */ 483*0Sstevel@tonic-gate if (crsetugid(cr, cred->uid, cred->gid) != 0 || 484*0Sstevel@tonic-gate crsetgroups(cr, cred->grouplen, &cred->groups[0]) != 0) { 485*0Sstevel@tonic-gate mutex_exit(&nickentry->lock); 486*0Sstevel@tonic-gate return (0); 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate mutex_exit(&nickentry->lock); 489*0Sstevel@tonic-gate return (1); 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate 492*0Sstevel@tonic-gate /* 493*0Sstevel@tonic-gate * Create a new cache_entry and put it in authdes_cache table. 494*0Sstevel@tonic-gate * Caller should have already locked the authdes_cache table. 495*0Sstevel@tonic-gate */ 496*0Sstevel@tonic-gate struct authdes_cache_entry * 497*0Sstevel@tonic-gate authdes_cache_new(char *fullname, des_block *sessionkey, uint32_t window) { 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate struct authdes_cache_entry *new, *head; 500*0Sstevel@tonic-gate struct bsdcred *ucred; 501*0Sstevel@tonic-gate int index; 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate if (!(new = kmem_cache_alloc(authdes_cache_handle, KM_SLEEP))) { 504*0Sstevel@tonic-gate return (NULL); 505*0Sstevel@tonic-gate } 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate if (!(new->rname = kmem_alloc(strlen(fullname) + 1, KM_NOSLEEP))) { 508*0Sstevel@tonic-gate kmem_cache_free(authdes_cache_handle, new); 509*0Sstevel@tonic-gate return (NULL); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate if (!(ucred = (struct bsdcred *)kmem_alloc(sizeof (struct bsdcred), 513*0Sstevel@tonic-gate KM_NOSLEEP))) { 514*0Sstevel@tonic-gate kmem_free(new->rname, strlen(fullname) + 1); 515*0Sstevel@tonic-gate kmem_cache_free(authdes_cache_handle, new); 516*0Sstevel@tonic-gate return (NULL); 517*0Sstevel@tonic-gate } 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate (void) strcpy(new->rname, fullname); 520*0Sstevel@tonic-gate ucred->valid = 0; 521*0Sstevel@tonic-gate new->localcred = (caddr_t)ucred; 522*0Sstevel@tonic-gate new->key = *sessionkey; 523*0Sstevel@tonic-gate new->window = window; 524*0Sstevel@tonic-gate new->ref_time = gethrestime_sec(); 525*0Sstevel@tonic-gate new->nickname = Nickname++; 526*0Sstevel@tonic-gate mutex_init(&new->lock, NULL, MUTEX_DEFAULT, NULL); 527*0Sstevel@tonic-gate 528*0Sstevel@tonic-gate /* put new into the hash table */ 529*0Sstevel@tonic-gate index = HASH(new->nickname); 530*0Sstevel@tonic-gate head = authdes_cache[index]; 531*0Sstevel@tonic-gate if ((new->next = head) != NULL) { 532*0Sstevel@tonic-gate head->prev = new; 533*0Sstevel@tonic-gate } 534*0Sstevel@tonic-gate authdes_cache[index] = new; 535*0Sstevel@tonic-gate new->prev = NULL; 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate /* update the LRU list */ 538*0Sstevel@tonic-gate new->lru_prev = NULL; 539*0Sstevel@tonic-gate if ((new->lru_next = lru_first) != NULL) { 540*0Sstevel@tonic-gate lru_first->lru_prev = new; 541*0Sstevel@tonic-gate } else { 542*0Sstevel@tonic-gate lru_last = new; 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate lru_first = new; 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate authdes_ncache++; 547*0Sstevel@tonic-gate return (new); 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate /* 551*0Sstevel@tonic-gate * Get an existing cache entry from authdes_cache table. 552*0Sstevel@tonic-gate * The caller should have locked the authdes_cache table. 553*0Sstevel@tonic-gate */ 554*0Sstevel@tonic-gate struct authdes_cache_entry * 555*0Sstevel@tonic-gate authdes_cache_get(uint32_t nickname) { 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate struct authdes_cache_entry *cur = NULL; 558*0Sstevel@tonic-gate int index = HASH(nickname); 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&authdes_lock)); 561*0Sstevel@tonic-gate for (cur = authdes_cache[index]; cur; cur = cur->next) { 562*0Sstevel@tonic-gate if ((cur->nickname == nickname)) { 563*0Sstevel@tonic-gate /* find it, update the LRU list */ 564*0Sstevel@tonic-gate if (cur != lru_first) { 565*0Sstevel@tonic-gate cur->lru_prev->lru_next = cur->lru_next; 566*0Sstevel@tonic-gate if (cur->lru_next != NULL) { 567*0Sstevel@tonic-gate cur->lru_next->lru_prev = cur->lru_prev; 568*0Sstevel@tonic-gate } else { 569*0Sstevel@tonic-gate lru_last = cur->lru_prev; 570*0Sstevel@tonic-gate } 571*0Sstevel@tonic-gate cur->lru_prev = NULL; 572*0Sstevel@tonic-gate cur->lru_next = lru_first; 573*0Sstevel@tonic-gate lru_first->lru_prev = cur; 574*0Sstevel@tonic-gate lru_first = cur; 575*0Sstevel@tonic-gate } 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate cur->ref_time = gethrestime_sec(); 578*0Sstevel@tonic-gate authdes_ncachehits++; 579*0Sstevel@tonic-gate return (cur); 580*0Sstevel@tonic-gate } 581*0Sstevel@tonic-gate } 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate authdes_ncachemisses++; 584*0Sstevel@tonic-gate return (NULL); 585*0Sstevel@tonic-gate } 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate /* 588*0Sstevel@tonic-gate * authdes_cache_reclaim() is called by the kernel memory allocator 589*0Sstevel@tonic-gate * when memory is low. This routine will reclaim 25% of the least recent 590*0Sstevel@tonic-gate * used cache entries above the low water mark (low_cache_entries). 591*0Sstevel@tonic-gate * If the cache entries have already hit the low water mark, it will 592*0Sstevel@tonic-gate * return 1 cache entry. 593*0Sstevel@tonic-gate */ 594*0Sstevel@tonic-gate /*ARGSUSED*/ 595*0Sstevel@tonic-gate void 596*0Sstevel@tonic-gate authdes_cache_reclaim(void *pdata) { 597*0Sstevel@tonic-gate struct authdes_cache_entry *p; 598*0Sstevel@tonic-gate int n, i; 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate mutex_enter(&authdes_lock); 601*0Sstevel@tonic-gate n = authdes_ncache - low_cache_entries; 602*0Sstevel@tonic-gate n = n > 0 ? n/4 : 1; 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate for (i = 0; i < n; i++) { 605*0Sstevel@tonic-gate if ((p = lru_last) == lru_first) 606*0Sstevel@tonic-gate break; 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gate /* Update the hash linked list */ 609*0Sstevel@tonic-gate if (p->prev == NULL) { 610*0Sstevel@tonic-gate authdes_cache[HASH(p->nickname)] = p->next; 611*0Sstevel@tonic-gate } else { 612*0Sstevel@tonic-gate p->prev->next = p->next; 613*0Sstevel@tonic-gate } 614*0Sstevel@tonic-gate if (p->next != NULL) { 615*0Sstevel@tonic-gate p->next->prev = p->prev; 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate /* update the LRU linked list */ 619*0Sstevel@tonic-gate p->lru_prev->lru_next = NULL; 620*0Sstevel@tonic-gate lru_last = p->lru_prev; 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate kmem_free(p->rname, strlen(p->rname) + 1); 623*0Sstevel@tonic-gate kmem_free(p->localcred, sizeof (struct bsdcred)); 624*0Sstevel@tonic-gate mutex_destroy(&p->lock); 625*0Sstevel@tonic-gate kmem_cache_free(authdes_cache_handle, p); 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate authdes_ncache--; 628*0Sstevel@tonic-gate } 629*0Sstevel@tonic-gate mutex_exit(&authdes_lock); 630*0Sstevel@tonic-gate RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n", 631*0Sstevel@tonic-gate authdes_ncache); 632*0Sstevel@tonic-gate } 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate /* 635*0Sstevel@tonic-gate * Walk through the LRU doubly-linked list and delete the cache 636*0Sstevel@tonic-gate * entries that have not been used for more than authdes_cache_time. 637*0Sstevel@tonic-gate * 638*0Sstevel@tonic-gate * Caller should have locked the cache table. 639*0Sstevel@tonic-gate */ 640*0Sstevel@tonic-gate void 641*0Sstevel@tonic-gate sweep_cache() { 642*0Sstevel@tonic-gate struct authdes_cache_entry *p; 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&authdes_lock)); 645*0Sstevel@tonic-gate while ((p = lru_last) != lru_first) { 646*0Sstevel@tonic-gate IS_ALIGNED(p); 647*0Sstevel@tonic-gate NOT_DEAD(p); 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gate /* 650*0Sstevel@tonic-gate * If the last LRU entry idled less than authdes_cache_time, 651*0Sstevel@tonic-gate * we are done with the sweeping. 652*0Sstevel@tonic-gate */ 653*0Sstevel@tonic-gate if (p->ref_time + authdes_cache_time > gethrestime_sec()) 654*0Sstevel@tonic-gate break; 655*0Sstevel@tonic-gate 656*0Sstevel@tonic-gate /* update the hash linked list */ 657*0Sstevel@tonic-gate if (p->prev == NULL) { 658*0Sstevel@tonic-gate authdes_cache[HASH(p->nickname)] = p->next; 659*0Sstevel@tonic-gate } else { 660*0Sstevel@tonic-gate p->prev->next = p->next; 661*0Sstevel@tonic-gate } 662*0Sstevel@tonic-gate if (p->next != NULL) { 663*0Sstevel@tonic-gate p->next->prev = p->prev; 664*0Sstevel@tonic-gate } 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate /* update the LRU linked list */ 667*0Sstevel@tonic-gate p->lru_prev->lru_next = NULL; 668*0Sstevel@tonic-gate lru_last = p->lru_prev; 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate kmem_free(p->rname, strlen(p->rname) + 1); 671*0Sstevel@tonic-gate kmem_free(p->localcred, sizeof (struct bsdcred)); 672*0Sstevel@tonic-gate mutex_destroy(&p->lock); 673*0Sstevel@tonic-gate kmem_cache_free(authdes_cache_handle, p); 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate authdes_ncache--; 676*0Sstevel@tonic-gate } 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate authdes_last_swept = gethrestime_sec(); 679*0Sstevel@tonic-gate RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n", 680*0Sstevel@tonic-gate authdes_ncache); 681*0Sstevel@tonic-gate } 682