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