1 2 /* 3 * Copyright (c) 1988 by Sun Microsystems, Inc. 4 */ 5 6 /* 7 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 8 * unrestricted use provided that this legend is included on all tape 9 * media and as a part of the software program in whole or part. Users 10 * may copy or modify Sun RPC without charge, but are not authorized 11 * to license or distribute it to anyone else except as part of a product or 12 * program developed by the user. 13 * 14 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 15 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 17 * 18 * Sun RPC is provided with no support and without any obligation on the 19 * part of Sun Microsystems, Inc. to assist in its use, correction, 20 * modification or enhancement. 21 * 22 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 23 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 24 * OR ANY PART THEREOF. 25 * 26 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 27 * or profits or other special, indirect and consequential damages, even if 28 * Sun has been advised of the possibility of such damages. 29 * 30 * Sun Microsystems, Inc. 31 * 2550 Garcia Avenue 32 * Mountain View, California 94043 33 */ 34 35 /* 36 * svcauth_des.c, server-side des authentication 37 * 38 * We insure for the service the following: 39 * (1) The timestamp microseconds do not exceed 1 million. 40 * (2) The timestamp plus the window is less than the current time. 41 * (3) The timestamp is not less than the one previously 42 * seen in the current session. 43 * 44 * It is up to the server to determine if the window size is 45 * too small . 46 * 47 */ 48 49 #include "namespace.h" 50 #include "reentrant.h" 51 #include <string.h> 52 #include <stdlib.h> 53 #include <stdio.h> 54 #include <unistd.h> 55 #include <rpc/des_crypt.h> 56 #include <sys/param.h> 57 #include <netinet/in.h> 58 #include <rpc/types.h> 59 #include <rpc/xdr.h> 60 #include <rpc/auth.h> 61 #include <rpc/auth_des.h> 62 #include <rpc/svc.h> 63 #include <rpc/rpc_msg.h> 64 #include <rpc/svc_auth.h> 65 #include "libc_private.h" 66 67 #if defined(LIBC_SCCS) && !defined(lint) 68 static char sccsid[] = "@(#)svcauth_des.c 2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI"; 69 #endif 70 #include <sys/cdefs.h> 71 __FBSDID("$FreeBSD$"); 72 73 extern int key_decryptsession_pk(const char *, netobj *, des_block *); 74 75 #define debug(msg) printf("svcauth_des: %s\n", msg) 76 77 #define USEC_PER_SEC ((u_long) 1000000L) 78 #define BEFORE(t1, t2) timercmp(t1, t2, <) 79 80 /* 81 * LRU cache of conversation keys and some other useful items. 82 */ 83 #define AUTHDES_CACHESZ 64 84 struct cache_entry { 85 des_block key; /* conversation key */ 86 char *rname; /* client's name */ 87 u_int window; /* credential lifetime window */ 88 struct timeval laststamp; /* detect replays of creds */ 89 char *localcred; /* generic local credential */ 90 }; 91 static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */; 92 static short *authdes_lru/* [AUTHDES_CACHESZ] */; 93 94 static void cache_init(); /* initialize the cache */ 95 static short cache_spot(); /* find an entry in the cache */ 96 static void cache_ref(/*short sid*/); /* note that sid was ref'd */ 97 98 static void invalidate(); /* invalidate entry in cache */ 99 100 /* 101 * cache statistics 102 */ 103 static struct { 104 u_long ncachehits; /* times cache hit, and is not replay */ 105 u_long ncachereplays; /* times cache hit, and is replay */ 106 u_long ncachemisses; /* times cache missed */ 107 } svcauthdes_stats; 108 109 /* 110 * Service side authenticator for AUTH_DES 111 */ 112 enum auth_stat 113 _svcauth_des(rqst, msg) 114 struct svc_req *rqst; 115 struct rpc_msg *msg; 116 { 117 118 long *ixdr; 119 des_block cryptbuf[2]; 120 struct authdes_cred *cred; 121 struct authdes_verf verf; 122 int status; 123 struct cache_entry *entry; 124 short sid = 0; 125 des_block *sessionkey; 126 des_block ivec; 127 u_int window; 128 struct timeval timestamp; 129 u_long namelen; 130 struct area { 131 struct authdes_cred area_cred; 132 char area_netname[MAXNETNAMELEN+1]; 133 } *area; 134 135 if (authdes_cache == NULL) { 136 cache_init(); 137 } 138 139 area = (struct area *)rqst->rq_clntcred; 140 cred = (struct authdes_cred *)&area->area_cred; 141 142 /* 143 * Get the credential 144 */ 145 ixdr = (long *)msg->rm_call.cb_cred.oa_base; 146 cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind); 147 switch (cred->adc_namekind) { 148 case ADN_FULLNAME: 149 namelen = IXDR_GET_U_LONG(ixdr); 150 if (namelen > MAXNETNAMELEN) { 151 return (AUTH_BADCRED); 152 } 153 cred->adc_fullname.name = area->area_netname; 154 bcopy((char *)ixdr, cred->adc_fullname.name, 155 (u_int)namelen); 156 cred->adc_fullname.name[namelen] = 0; 157 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT); 158 cred->adc_fullname.key.key.high = (u_long)*ixdr++; 159 cred->adc_fullname.key.key.low = (u_long)*ixdr++; 160 cred->adc_fullname.window = (u_long)*ixdr++; 161 break; 162 case ADN_NICKNAME: 163 cred->adc_nickname = (u_long)*ixdr++; 164 break; 165 default: 166 return (AUTH_BADCRED); 167 } 168 169 /* 170 * Get the verifier 171 */ 172 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 173 verf.adv_xtimestamp.key.high = (u_long)*ixdr++; 174 verf.adv_xtimestamp.key.low = (u_long)*ixdr++; 175 verf.adv_int_u = (u_long)*ixdr++; 176 177 178 /* 179 * Get the conversation key 180 */ 181 if (cred->adc_namekind == ADN_FULLNAME) { 182 netobj pkey; 183 char pkey_data[1024]; 184 185 sessionkey = &cred->adc_fullname.key; 186 if (! getpublickey(cred->adc_fullname.name, pkey_data)) { 187 debug("getpublickey"); 188 return(AUTH_BADCRED); 189 } 190 pkey.n_bytes = pkey_data; 191 pkey.n_len = strlen(pkey_data) + 1; 192 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey, 193 sessionkey) < 0) { 194 debug("decryptsessionkey"); 195 return (AUTH_BADCRED); /* key not found */ 196 } 197 } else { /* ADN_NICKNAME */ 198 sid = (short)cred->adc_nickname; 199 if (sid < 0 || sid >= AUTHDES_CACHESZ) { 200 debug("bad nickname"); 201 return (AUTH_BADCRED); /* garbled credential */ 202 } 203 sessionkey = &authdes_cache[sid].key; 204 } 205 206 207 /* 208 * Decrypt the timestamp 209 */ 210 cryptbuf[0] = verf.adv_xtimestamp; 211 if (cred->adc_namekind == ADN_FULLNAME) { 212 cryptbuf[1].key.high = cred->adc_fullname.window; 213 cryptbuf[1].key.low = verf.adv_winverf; 214 ivec.key.high = ivec.key.low = 0; 215 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf, 216 2*sizeof(des_block), DES_DECRYPT | DES_HW, 217 (char *)&ivec); 218 } else { 219 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 220 sizeof(des_block), DES_DECRYPT | DES_HW); 221 } 222 if (DES_FAILED(status)) { 223 debug("decryption failure"); 224 return (AUTH_FAILED); /* system error */ 225 } 226 227 /* 228 * XDR the decrypted timestamp 229 */ 230 ixdr = (long *)cryptbuf; 231 timestamp.tv_sec = IXDR_GET_LONG(ixdr); 232 timestamp.tv_usec = IXDR_GET_LONG(ixdr); 233 234 /* 235 * Check for valid credentials and verifiers. 236 * They could be invalid because the key was flushed 237 * out of the cache, and so a new session should begin. 238 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case. 239 */ 240 { 241 struct timeval current; 242 int nick; 243 int winverf; 244 245 if (cred->adc_namekind == ADN_FULLNAME) { 246 window = IXDR_GET_U_LONG(ixdr); 247 winverf = IXDR_GET_U_LONG(ixdr); 248 if (winverf != window - 1) { 249 debug("window verifier mismatch"); 250 return (AUTH_BADCRED); /* garbled credential */ 251 } 252 sid = cache_spot(sessionkey, cred->adc_fullname.name, 253 ×tamp); 254 if (sid < 0) { 255 debug("replayed credential"); 256 return (AUTH_REJECTEDCRED); /* replay */ 257 } 258 nick = 0; 259 } else { /* ADN_NICKNAME */ 260 window = authdes_cache[sid].window; 261 nick = 1; 262 } 263 264 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) { 265 debug("invalid usecs"); 266 /* cached out (bad key), or garbled verifier */ 267 return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF); 268 } 269 if (nick && BEFORE(×tamp, 270 &authdes_cache[sid].laststamp)) { 271 debug("timestamp before last seen"); 272 return (AUTH_REJECTEDVERF); /* replay */ 273 } 274 (void)gettimeofday(¤t, NULL); 275 current.tv_sec -= window; /* allow for expiration */ 276 if (!BEFORE(¤t, ×tamp)) { 277 debug("timestamp expired"); 278 /* replay, or garbled credential */ 279 return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED); 280 } 281 } 282 283 /* 284 * Set up the reply verifier 285 */ 286 verf.adv_nickname = (u_long)sid; 287 288 /* 289 * xdr the timestamp before encrypting 290 */ 291 ixdr = (long *)cryptbuf; 292 IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1); 293 IXDR_PUT_LONG(ixdr, timestamp.tv_usec); 294 295 /* 296 * encrypt the timestamp 297 */ 298 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 299 sizeof(des_block), DES_ENCRYPT | DES_HW); 300 if (DES_FAILED(status)) { 301 debug("encryption failure"); 302 return (AUTH_FAILED); /* system error */ 303 } 304 verf.adv_xtimestamp = cryptbuf[0]; 305 306 /* 307 * Serialize the reply verifier, and update rqst 308 */ 309 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 310 *ixdr++ = (long)verf.adv_xtimestamp.key.high; 311 *ixdr++ = (long)verf.adv_xtimestamp.key.low; 312 *ixdr++ = (long)verf.adv_int_u; 313 314 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES; 315 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base; 316 rqst->rq_xprt->xp_verf.oa_length = 317 (char *)ixdr - msg->rm_call.cb_verf.oa_base; 318 319 /* 320 * We succeeded, commit the data to the cache now and 321 * finish cooking the credential. 322 */ 323 entry = &authdes_cache[sid]; 324 entry->laststamp = timestamp; 325 cache_ref(sid); 326 if (cred->adc_namekind == ADN_FULLNAME) { 327 cred->adc_fullname.window = window; 328 cred->adc_nickname = (u_long)sid; /* save nickname */ 329 if (entry->rname != NULL) { 330 mem_free(entry->rname, strlen(entry->rname) + 1); 331 } 332 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name) 333 + 1); 334 if (entry->rname != NULL) { 335 (void) strcpy(entry->rname, cred->adc_fullname.name); 336 } else { 337 debug("out of memory"); 338 } 339 entry->key = *sessionkey; 340 entry->window = window; 341 invalidate(entry->localcred); /* mark any cached cred invalid */ 342 } else { /* ADN_NICKNAME */ 343 /* 344 * nicknames are cooked into fullnames 345 */ 346 cred->adc_namekind = ADN_FULLNAME; 347 cred->adc_fullname.name = entry->rname; 348 cred->adc_fullname.key = entry->key; 349 cred->adc_fullname.window = entry->window; 350 } 351 return (AUTH_OK); /* we made it!*/ 352 } 353 354 355 /* 356 * Initialize the cache 357 */ 358 static void 359 cache_init() 360 { 361 int i; 362 363 authdes_cache = (struct cache_entry *) 364 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ); 365 bzero((char *)authdes_cache, 366 sizeof(struct cache_entry) * AUTHDES_CACHESZ); 367 368 authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ); 369 /* 370 * Initialize the lru list 371 */ 372 for (i = 0; i < AUTHDES_CACHESZ; i++) { 373 authdes_lru[i] = i; 374 } 375 } 376 377 378 /* 379 * Find the lru victim 380 */ 381 static short 382 cache_victim() 383 { 384 return (authdes_lru[AUTHDES_CACHESZ-1]); 385 } 386 387 /* 388 * Note that sid was referenced 389 */ 390 static void 391 cache_ref(sid) 392 short sid; 393 { 394 int i; 395 short curr; 396 short prev; 397 398 prev = authdes_lru[0]; 399 authdes_lru[0] = sid; 400 for (i = 1; prev != sid; i++) { 401 curr = authdes_lru[i]; 402 authdes_lru[i] = prev; 403 prev = curr; 404 } 405 } 406 407 408 /* 409 * Find a spot in the cache for a credential containing 410 * the items given. Return -1 if a replay is detected, otherwise 411 * return the spot in the cache. 412 */ 413 static short 414 cache_spot(key, name, timestamp) 415 des_block *key; 416 char *name; 417 struct timeval *timestamp; 418 { 419 struct cache_entry *cp; 420 int i; 421 u_long hi; 422 423 hi = key->key.high; 424 for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) { 425 if (cp->key.key.high == hi && 426 cp->key.key.low == key->key.low && 427 cp->rname != NULL && 428 bcmp(cp->rname, name, strlen(name) + 1) == 0) { 429 if (BEFORE(timestamp, &cp->laststamp)) { 430 svcauthdes_stats.ncachereplays++; 431 return (-1); /* replay */ 432 } 433 svcauthdes_stats.ncachehits++; 434 return (i); /* refresh */ 435 } 436 } 437 svcauthdes_stats.ncachemisses++; 438 return (cache_victim()); /* new credential */ 439 } 440 441 442 #if (defined(sun) || defined(vax) || defined(__FreeBSD__)) 443 /* 444 * Local credential handling stuff. 445 * NOTE: bsd unix dependent. 446 * Other operating systems should put something else here. 447 */ 448 #define UNKNOWN -2 /* grouplen, if cached cred is unknown user */ 449 #define INVALID -1 /* grouplen, if cache entry is invalid */ 450 451 struct bsdcred { 452 uid_t uid; /* cached uid */ 453 gid_t gid; /* cached gid */ 454 int grouplen; /* length of cached groups */ 455 gid_t groups[NGRPS]; /* cached groups */ 456 }; 457 458 /* 459 * Map a des credential into a unix cred. 460 * We cache the credential here so the application does 461 * not have to make an rpc call every time to interpret 462 * the credential. 463 */ 464 int 465 authdes_getucred(adc, uid, gid, grouplen, groups) 466 struct authdes_cred *adc; 467 uid_t *uid; 468 gid_t *gid; 469 int *grouplen; 470 gid_t *groups; 471 { 472 unsigned sid; 473 int i; 474 uid_t i_uid; 475 gid_t i_gid; 476 int i_grouplen; 477 struct bsdcred *cred; 478 479 sid = adc->adc_nickname; 480 if (sid >= AUTHDES_CACHESZ) { 481 debug("invalid nickname"); 482 return (0); 483 } 484 cred = (struct bsdcred *)authdes_cache[sid].localcred; 485 if (cred == NULL) { 486 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred)); 487 authdes_cache[sid].localcred = (char *)cred; 488 cred->grouplen = INVALID; 489 } 490 if (cred->grouplen == INVALID) { 491 /* 492 * not in cache: lookup 493 */ 494 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 495 &i_grouplen, groups)) 496 { 497 debug("unknown netname"); 498 cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */ 499 return (0); 500 } 501 debug("missed ucred cache"); 502 *uid = cred->uid = i_uid; 503 *gid = cred->gid = i_gid; 504 *grouplen = cred->grouplen = i_grouplen; 505 for (i = i_grouplen - 1; i >= 0; i--) { 506 cred->groups[i] = groups[i]; /* int to short */ 507 } 508 return (1); 509 } else if (cred->grouplen == UNKNOWN) { 510 /* 511 * Already lookup up, but no match found 512 */ 513 return (0); 514 } 515 516 /* 517 * cached credentials 518 */ 519 *uid = cred->uid; 520 *gid = cred->gid; 521 *grouplen = cred->grouplen; 522 for (i = cred->grouplen - 1; i >= 0; i--) { 523 groups[i] = cred->groups[i]; /* short to int */ 524 } 525 return (1); 526 } 527 528 static void 529 invalidate(cred) 530 char *cred; 531 { 532 if (cred == NULL) { 533 return; 534 } 535 ((struct bsdcred *)cred)->grouplen = INVALID; 536 } 537 #endif 538 539