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 /* from: static char sccsid[] = "@(#)svcauth_des.c 2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI"; */ 69 static const char rcsid[] = "$FreeBSD$"; 70 #endif 71 72 #define debug(msg) printf("svcauth_des: %s\n", msg) 73 74 #define USEC_PER_SEC ((u_long) 1000000L) 75 #define BEFORE(t1, t2) timercmp(t1, t2, <) 76 77 /* 78 * LRU cache of conversation keys and some other useful items. 79 */ 80 #define AUTHDES_CACHESZ 64 81 struct cache_entry { 82 des_block key; /* conversation key */ 83 char *rname; /* client's name */ 84 u_int window; /* credential lifetime window */ 85 struct timeval laststamp; /* detect replays of creds */ 86 char *localcred; /* generic local credential */ 87 }; 88 static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */; 89 static short *authdes_lru/* [AUTHDES_CACHESZ] */; 90 91 static void cache_init(); /* initialize the cache */ 92 static short cache_spot(); /* find an entry in the cache */ 93 static void cache_ref(/*short sid*/); /* note that sid was ref'd */ 94 95 static void invalidate(); /* invalidate entry in cache */ 96 97 /* 98 * cache statistics 99 */ 100 static struct { 101 u_long ncachehits; /* times cache hit, and is not replay */ 102 u_long ncachereplays; /* times cache hit, and is replay */ 103 u_long ncachemisses; /* times cache missed */ 104 } svcauthdes_stats; 105 106 /* 107 * Service side authenticator for AUTH_DES 108 */ 109 enum auth_stat 110 _svcauth_des(rqst, msg) 111 register struct svc_req *rqst; 112 register struct rpc_msg *msg; 113 { 114 115 register long *ixdr; 116 des_block cryptbuf[2]; 117 register struct authdes_cred *cred; 118 struct authdes_verf verf; 119 int status; 120 register struct cache_entry *entry; 121 short sid = 0; 122 des_block *sessionkey; 123 des_block ivec; 124 u_int window; 125 struct timeval timestamp; 126 u_long namelen; 127 struct area { 128 struct authdes_cred area_cred; 129 char area_netname[MAXNETNAMELEN+1]; 130 } *area; 131 132 if (authdes_cache == NULL) { 133 cache_init(); 134 } 135 136 area = (struct area *)rqst->rq_clntcred; 137 cred = (struct authdes_cred *)&area->area_cred; 138 139 /* 140 * Get the credential 141 */ 142 ixdr = (long *)msg->rm_call.cb_cred.oa_base; 143 cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind); 144 switch (cred->adc_namekind) { 145 case ADN_FULLNAME: 146 namelen = IXDR_GET_U_LONG(ixdr); 147 if (namelen > MAXNETNAMELEN) { 148 return (AUTH_BADCRED); 149 } 150 cred->adc_fullname.name = area->area_netname; 151 bcopy((char *)ixdr, cred->adc_fullname.name, 152 (u_int)namelen); 153 cred->adc_fullname.name[namelen] = 0; 154 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT); 155 cred->adc_fullname.key.key.high = (u_long)*ixdr++; 156 cred->adc_fullname.key.key.low = (u_long)*ixdr++; 157 cred->adc_fullname.window = (u_long)*ixdr++; 158 break; 159 case ADN_NICKNAME: 160 cred->adc_nickname = (u_long)*ixdr++; 161 break; 162 default: 163 return (AUTH_BADCRED); 164 } 165 166 /* 167 * Get the verifier 168 */ 169 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 170 verf.adv_xtimestamp.key.high = (u_long)*ixdr++; 171 verf.adv_xtimestamp.key.low = (u_long)*ixdr++; 172 verf.adv_int_u = (u_long)*ixdr++; 173 174 175 /* 176 * Get the conversation key 177 */ 178 if (cred->adc_namekind == ADN_FULLNAME) { 179 netobj pkey; 180 char pkey_data[1024]; 181 182 sessionkey = &cred->adc_fullname.key; 183 if (! getpublickey(cred->adc_fullname.name, pkey_data)) { 184 debug("getpublickey"); 185 return(AUTH_BADCRED); 186 } 187 pkey.n_bytes = pkey_data; 188 pkey.n_len = strlen(pkey_data) + 1; 189 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey, 190 sessionkey) < 0) { 191 debug("decryptsessionkey"); 192 return (AUTH_BADCRED); /* key not found */ 193 } 194 } else { /* ADN_NICKNAME */ 195 sid = (short)cred->adc_nickname; 196 if (sid < 0 || sid >= AUTHDES_CACHESZ) { 197 debug("bad nickname"); 198 return (AUTH_BADCRED); /* garbled credential */ 199 } 200 sessionkey = &authdes_cache[sid].key; 201 } 202 203 204 /* 205 * Decrypt the timestamp 206 */ 207 cryptbuf[0] = verf.adv_xtimestamp; 208 if (cred->adc_namekind == ADN_FULLNAME) { 209 cryptbuf[1].key.high = cred->adc_fullname.window; 210 cryptbuf[1].key.low = verf.adv_winverf; 211 ivec.key.high = ivec.key.low = 0; 212 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf, 213 2*sizeof(des_block), DES_DECRYPT | DES_HW, 214 (char *)&ivec); 215 } else { 216 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 217 sizeof(des_block), DES_DECRYPT | DES_HW); 218 } 219 if (DES_FAILED(status)) { 220 debug("decryption failure"); 221 return (AUTH_FAILED); /* system error */ 222 } 223 224 /* 225 * XDR the decrypted timestamp 226 */ 227 ixdr = (long *)cryptbuf; 228 timestamp.tv_sec = IXDR_GET_LONG(ixdr); 229 timestamp.tv_usec = IXDR_GET_LONG(ixdr); 230 231 /* 232 * Check for valid credentials and verifiers. 233 * They could be invalid because the key was flushed 234 * out of the cache, and so a new session should begin. 235 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case. 236 */ 237 { 238 struct timeval current; 239 int nick; 240 int winverf; 241 242 if (cred->adc_namekind == ADN_FULLNAME) { 243 window = IXDR_GET_U_LONG(ixdr); 244 winverf = IXDR_GET_U_LONG(ixdr); 245 if (winverf != window - 1) { 246 debug("window verifier mismatch"); 247 return (AUTH_BADCRED); /* garbled credential */ 248 } 249 sid = cache_spot(sessionkey, cred->adc_fullname.name, 250 ×tamp); 251 if (sid < 0) { 252 debug("replayed credential"); 253 return (AUTH_REJECTEDCRED); /* replay */ 254 } 255 nick = 0; 256 } else { /* ADN_NICKNAME */ 257 window = authdes_cache[sid].window; 258 nick = 1; 259 } 260 261 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) { 262 debug("invalid usecs"); 263 /* cached out (bad key), or garbled verifier */ 264 return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF); 265 } 266 if (nick && BEFORE(×tamp, 267 &authdes_cache[sid].laststamp)) { 268 debug("timestamp before last seen"); 269 return (AUTH_REJECTEDVERF); /* replay */ 270 } 271 (void) gettimeofday(¤t, (struct timezone *)NULL); 272 current.tv_sec -= window; /* allow for expiration */ 273 if (!BEFORE(¤t, ×tamp)) { 274 debug("timestamp expired"); 275 /* replay, or garbled credential */ 276 return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED); 277 } 278 } 279 280 /* 281 * Set up the reply verifier 282 */ 283 verf.adv_nickname = (u_long)sid; 284 285 /* 286 * xdr the timestamp before encrypting 287 */ 288 ixdr = (long *)cryptbuf; 289 IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1); 290 IXDR_PUT_LONG(ixdr, timestamp.tv_usec); 291 292 /* 293 * encrypt the timestamp 294 */ 295 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 296 sizeof(des_block), DES_ENCRYPT | DES_HW); 297 if (DES_FAILED(status)) { 298 debug("encryption failure"); 299 return (AUTH_FAILED); /* system error */ 300 } 301 verf.adv_xtimestamp = cryptbuf[0]; 302 303 /* 304 * Serialize the reply verifier, and update rqst 305 */ 306 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 307 *ixdr++ = (long)verf.adv_xtimestamp.key.high; 308 *ixdr++ = (long)verf.adv_xtimestamp.key.low; 309 *ixdr++ = (long)verf.adv_int_u; 310 311 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES; 312 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base; 313 rqst->rq_xprt->xp_verf.oa_length = 314 (char *)ixdr - msg->rm_call.cb_verf.oa_base; 315 316 /* 317 * We succeeded, commit the data to the cache now and 318 * finish cooking the credential. 319 */ 320 entry = &authdes_cache[sid]; 321 entry->laststamp = timestamp; 322 cache_ref(sid); 323 if (cred->adc_namekind == ADN_FULLNAME) { 324 cred->adc_fullname.window = window; 325 cred->adc_nickname = (u_long)sid; /* save nickname */ 326 if (entry->rname != NULL) { 327 mem_free(entry->rname, strlen(entry->rname) + 1); 328 } 329 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name) 330 + 1); 331 if (entry->rname != NULL) { 332 (void) strcpy(entry->rname, cred->adc_fullname.name); 333 } else { 334 debug("out of memory"); 335 } 336 entry->key = *sessionkey; 337 entry->window = window; 338 invalidate(entry->localcred); /* mark any cached cred invalid */ 339 } else { /* ADN_NICKNAME */ 340 /* 341 * nicknames are cooked into fullnames 342 */ 343 cred->adc_namekind = ADN_FULLNAME; 344 cred->adc_fullname.name = entry->rname; 345 cred->adc_fullname.key = entry->key; 346 cred->adc_fullname.window = entry->window; 347 } 348 return (AUTH_OK); /* we made it!*/ 349 } 350 351 352 /* 353 * Initialize the cache 354 */ 355 static void 356 cache_init() 357 { 358 register int i; 359 360 authdes_cache = (struct cache_entry *) 361 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ); 362 bzero((char *)authdes_cache, 363 sizeof(struct cache_entry) * AUTHDES_CACHESZ); 364 365 authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ); 366 /* 367 * Initialize the lru list 368 */ 369 for (i = 0; i < AUTHDES_CACHESZ; i++) { 370 authdes_lru[i] = i; 371 } 372 } 373 374 375 /* 376 * Find the lru victim 377 */ 378 static short 379 cache_victim() 380 { 381 return (authdes_lru[AUTHDES_CACHESZ-1]); 382 } 383 384 /* 385 * Note that sid was referenced 386 */ 387 static void 388 cache_ref(sid) 389 register short sid; 390 { 391 register int i; 392 register short curr; 393 register short prev; 394 395 prev = authdes_lru[0]; 396 authdes_lru[0] = sid; 397 for (i = 1; prev != sid; i++) { 398 curr = authdes_lru[i]; 399 authdes_lru[i] = prev; 400 prev = curr; 401 } 402 } 403 404 405 /* 406 * Find a spot in the cache for a credential containing 407 * the items given. Return -1 if a replay is detected, otherwise 408 * return the spot in the cache. 409 */ 410 static short 411 cache_spot(key, name, timestamp) 412 register des_block *key; 413 char *name; 414 struct timeval *timestamp; 415 { 416 register struct cache_entry *cp; 417 register int i; 418 register u_long hi; 419 420 hi = key->key.high; 421 for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) { 422 if (cp->key.key.high == hi && 423 cp->key.key.low == key->key.low && 424 cp->rname != NULL && 425 bcmp(cp->rname, name, strlen(name) + 1) == 0) { 426 if (BEFORE(timestamp, &cp->laststamp)) { 427 svcauthdes_stats.ncachereplays++; 428 return (-1); /* replay */ 429 } 430 svcauthdes_stats.ncachehits++; 431 return (i); /* refresh */ 432 } 433 } 434 svcauthdes_stats.ncachemisses++; 435 return (cache_victim()); /* new credential */ 436 } 437 438 439 #if (defined(sun) || defined(vax) || defined(__FreeBSD__)) 440 /* 441 * Local credential handling stuff. 442 * NOTE: bsd unix dependent. 443 * Other operating systems should put something else here. 444 */ 445 #define UNKNOWN -2 /* grouplen, if cached cred is unknown user */ 446 #define INVALID -1 /* grouplen, if cache entry is invalid */ 447 448 struct bsdcred { 449 short uid; /* cached uid */ 450 short gid; /* cached gid */ 451 short grouplen; /* length of cached groups */ 452 short groups[NGROUPS]; /* cached groups */ 453 }; 454 455 /* 456 * Map a des credential into a unix cred. 457 * We cache the credential here so the application does 458 * not have to make an rpc call every time to interpret 459 * the credential. 460 */ 461 int 462 authdes_getucred(adc, uid, gid, grouplen, groups) 463 struct authdes_cred *adc; 464 uid_t *uid; 465 gid_t *gid; 466 int *grouplen; 467 register gid_t *groups; 468 { 469 unsigned sid; 470 register int i; 471 uid_t i_uid; 472 gid_t i_gid; 473 int i_grouplen; 474 struct bsdcred *cred; 475 476 sid = adc->adc_nickname; 477 if (sid >= AUTHDES_CACHESZ) { 478 debug("invalid nickname"); 479 return (0); 480 } 481 cred = (struct bsdcred *)authdes_cache[sid].localcred; 482 if (cred == NULL) { 483 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred)); 484 authdes_cache[sid].localcred = (char *)cred; 485 cred->grouplen = INVALID; 486 } 487 if (cred->grouplen == INVALID) { 488 /* 489 * not in cache: lookup 490 */ 491 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 492 &i_grouplen, groups)) 493 { 494 debug("unknown netname"); 495 cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */ 496 return (0); 497 } 498 debug("missed ucred cache"); 499 *uid = cred->uid = i_uid; 500 *gid = cred->gid = i_gid; 501 *grouplen = cred->grouplen = i_grouplen; 502 for (i = i_grouplen - 1; i >= 0; i--) { 503 cred->groups[i] = groups[i]; /* int to short */ 504 } 505 return (1); 506 } else if (cred->grouplen == UNKNOWN) { 507 /* 508 * Already lookup up, but no match found 509 */ 510 return (0); 511 } 512 513 /* 514 * cached credentials 515 */ 516 *uid = cred->uid; 517 *gid = cred->gid; 518 *grouplen = cred->grouplen; 519 for (i = cred->grouplen - 1; i >= 0; i--) { 520 groups[i] = cred->groups[i]; /* short to int */ 521 } 522 return (1); 523 } 524 525 static void 526 invalidate(cred) 527 char *cred; 528 { 529 if (cred == NULL) { 530 return; 531 } 532 ((struct bsdcred *)cred)->grouplen = INVALID; 533 } 534 #endif 535 536