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