1 2 /* 3 * Copyright (c) 1988 by Sun Microsystems, Inc. 4 */ 5 6 /* 7 * Copyright (c) 2009, Sun Microsystems, Inc. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions are met: 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * - Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * - Neither the name of Sun Microsystems, Inc. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * svcauth_des.c, server-side des authentication 36 * 37 * We insure for the service the following: 38 * (1) The timestamp microseconds do not exceed 1 million. 39 * (2) The timestamp plus the window is less than the current time. 40 * (3) The timestamp is not less than the one previously 41 * seen in the current session. 42 * 43 * It is up to the server to determine if the window size is 44 * too small . 45 * 46 */ 47 #include <wintirpc.h> 48 //#include <pthread.h> 49 #include <reentrant.h> 50 #include <string.h> 51 #include <stdlib.h> 52 #include <stdio.h> 53 //#include <unistd.h> 54 #include <rpc/des_crypt.h> 55 //#include <sys/param.h> 56 //#include <netinet/in.h> 57 #include <rpc/types.h> 58 #include <rpc/xdr.h> 59 #include <rpc/auth.h> 60 #include <rpc/auth_des.h> 61 #include <rpc/svc.h> 62 #include <rpc/rpc_msg.h> 63 #include <rpc/svc_auth.h> 64 #include <libc_private.h> 65 66 extern int key_decryptsession_pk(const char *, netobj *, des_block *); 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(rqst, msg) 107 struct svc_req *rqst; 108 struct rpc_msg *msg; 109 { 110 111 long *ixdr; 112 des_block cryptbuf[2]; 113 struct authdes_cred *cred; 114 struct authdes_verf verf; 115 int status; 116 struct cache_entry *entry; 117 short sid = 0; 118 des_block *sessionkey; 119 des_block ivec; 120 u_int window; 121 struct timeval timestamp; 122 u_long namelen; 123 struct area { 124 struct authdes_cred area_cred; 125 char area_netname[MAXNETNAMELEN+1]; 126 } *area; 127 128 if (authdes_cache == NULL) { 129 cache_init(); 130 } 131 132 area = (struct area *)rqst->rq_clntcred; 133 cred = (struct authdes_cred *)&area->area_cred; 134 135 /* 136 * Get the credential 137 */ 138 ixdr = (long *)msg->rm_call.cb_cred.oa_base; 139 cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind); 140 switch (cred->adc_namekind) { 141 case ADN_FULLNAME: 142 namelen = IXDR_GET_U_LONG(ixdr); 143 if (namelen > MAXNETNAMELEN) { 144 return (AUTH_BADCRED); 145 } 146 cred->adc_fullname.name = area->area_netname; 147 bcopy((char *)ixdr, cred->adc_fullname.name, 148 (u_int)namelen); 149 cred->adc_fullname.name[namelen] = 0; 150 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT); 151 cred->adc_fullname.key.key.high = (u_long)*ixdr++; 152 cred->adc_fullname.key.key.low = (u_long)*ixdr++; 153 cred->adc_fullname.window = (u_long)*ixdr++; 154 break; 155 case ADN_NICKNAME: 156 cred->adc_nickname = (u_long)*ixdr++; 157 break; 158 default: 159 return (AUTH_BADCRED); 160 } 161 162 /* 163 * Get the verifier 164 */ 165 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 166 verf.adv_xtimestamp.key.high = (u_long)*ixdr++; 167 verf.adv_xtimestamp.key.low = (u_long)*ixdr++; 168 verf.adv_int_u = (u_long)*ixdr++; 169 170 171 /* 172 * Get the conversation key 173 */ 174 if (cred->adc_namekind == ADN_FULLNAME) { 175 netobj pkey; 176 char pkey_data[1024]; 177 178 sessionkey = &cred->adc_fullname.key; 179 if (! getpublickey(cred->adc_fullname.name, pkey_data)) { 180 debug("getpublickey"); 181 return(AUTH_BADCRED); 182 } 183 pkey.n_bytes = pkey_data; 184 pkey.n_len = strlen(pkey_data) + 1; 185 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey, 186 sessionkey) < 0) { 187 debug("decryptsessionkey"); 188 return (AUTH_BADCRED); /* key not found */ 189 } 190 } else { /* ADN_NICKNAME */ 191 sid = (short)cred->adc_nickname; 192 if (sid < 0 || sid >= AUTHDES_CACHESZ) { 193 debug("bad nickname"); 194 return (AUTH_BADCRED); /* garbled credential */ 195 } 196 sessionkey = &authdes_cache[sid].key; 197 } 198 199 200 /* 201 * Decrypt the timestamp 202 */ 203 cryptbuf[0] = verf.adv_xtimestamp; 204 if (cred->adc_namekind == ADN_FULLNAME) { 205 cryptbuf[1].key.high = cred->adc_fullname.window; 206 cryptbuf[1].key.low = verf.adv_winverf; 207 ivec.key.high = ivec.key.low = 0; 208 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf, 209 2*sizeof(des_block), DES_DECRYPT | DES_HW, 210 (char *)&ivec); 211 } else { 212 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 213 sizeof(des_block), DES_DECRYPT | DES_HW); 214 } 215 if (DES_FAILED(status)) { 216 debug("decryption failure"); 217 return (AUTH_FAILED); /* system error */ 218 } 219 220 /* 221 * XDR the decrypted timestamp 222 */ 223 ixdr = (long *)cryptbuf; 224 timestamp.tv_sec = IXDR_GET_LONG(ixdr); 225 timestamp.tv_usec = IXDR_GET_LONG(ixdr); 226 227 /* 228 * Check for valid credentials and verifiers. 229 * They could be invalid because the key was flushed 230 * out of the cache, and so a new session should begin. 231 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case. 232 */ 233 { 234 struct timeval current; 235 int nick; 236 int winverf; 237 238 if (cred->adc_namekind == ADN_FULLNAME) { 239 window = IXDR_GET_U_LONG(ixdr); 240 winverf = IXDR_GET_U_LONG(ixdr); 241 if (winverf != window - 1) { 242 debug("window verifier mismatch"); 243 return (AUTH_BADCRED); /* garbled credential */ 244 } 245 sid = cache_spot(sessionkey, cred->adc_fullname.name, 246 ×tamp); 247 if (sid < 0) { 248 debug("replayed credential"); 249 return (AUTH_REJECTEDCRED); /* replay */ 250 } 251 nick = 0; 252 } else { /* ADN_NICKNAME */ 253 window = authdes_cache[sid].window; 254 nick = 1; 255 } 256 257 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) { 258 debug("invalid usecs"); 259 /* cached out (bad key), or garbled verifier */ 260 return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF); 261 } 262 if (nick && BEFORE(×tamp, 263 &authdes_cache[sid].laststamp)) { 264 debug("timestamp before last seen"); 265 return (AUTH_REJECTEDVERF); /* replay */ 266 } 267 (void) gettimeofday(¤t, (struct timezone *)NULL); 268 current.tv_sec -= window; /* allow for expiration */ 269 if (!BEFORE(¤t, ×tamp)) { 270 debug("timestamp expired"); 271 /* replay, or garbled credential */ 272 return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED); 273 } 274 } 275 276 /* 277 * Set up the reply verifier 278 */ 279 verf.adv_nickname = (u_long)sid; 280 281 /* 282 * xdr the timestamp before encrypting 283 */ 284 ixdr = (long *)cryptbuf; 285 IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1); 286 IXDR_PUT_LONG(ixdr, timestamp.tv_usec); 287 288 /* 289 * encrypt the timestamp 290 */ 291 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf, 292 sizeof(des_block), DES_ENCRYPT | DES_HW); 293 if (DES_FAILED(status)) { 294 debug("encryption failure"); 295 return (AUTH_FAILED); /* system error */ 296 } 297 verf.adv_xtimestamp = cryptbuf[0]; 298 299 /* 300 * Serialize the reply verifier, and update rqst 301 */ 302 ixdr = (long *)msg->rm_call.cb_verf.oa_base; 303 *ixdr++ = (long)verf.adv_xtimestamp.key.high; 304 *ixdr++ = (long)verf.adv_xtimestamp.key.low; 305 *ixdr++ = (long)verf.adv_int_u; 306 307 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES; 308 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base; 309 rqst->rq_xprt->xp_verf.oa_length = 310 (char *)ixdr - msg->rm_call.cb_verf.oa_base; 311 312 /* 313 * We succeeded, commit the data to the cache now and 314 * finish cooking the credential. 315 */ 316 entry = &authdes_cache[sid]; 317 entry->laststamp = timestamp; 318 cache_ref(sid); 319 if (cred->adc_namekind == ADN_FULLNAME) { 320 cred->adc_fullname.window = window; 321 cred->adc_nickname = (u_long)sid; /* save nickname */ 322 if (entry->rname != NULL) { 323 mem_free(entry->rname, strlen(entry->rname) + 1); 324 } 325 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name) 326 + 1); 327 if (entry->rname != NULL) { 328 (void) strcpy(entry->rname, cred->adc_fullname.name); 329 } else { 330 debug("out of memory"); 331 } 332 entry->key = *sessionkey; 333 entry->window = window; 334 invalidate(entry->localcred); /* mark any cached cred invalid */ 335 } else { /* ADN_NICKNAME */ 336 /* 337 * nicknames are cooked into fullnames 338 */ 339 cred->adc_namekind = ADN_FULLNAME; 340 cred->adc_fullname.name = entry->rname; 341 cred->adc_fullname.key = entry->key; 342 cred->adc_fullname.window = entry->window; 343 } 344 return (AUTH_OK); /* we made it!*/ 345 } 346 347 348 /* 349 * Initialize the cache 350 */ 351 static void 352 cache_init() 353 { 354 int i; 355 356 authdes_cache = (struct cache_entry *) 357 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ); 358 bzero((char *)authdes_cache, 359 sizeof(struct cache_entry) * AUTHDES_CACHESZ); 360 361 authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ); 362 /* 363 * Initialize the lru list 364 */ 365 for (i = 0; i < AUTHDES_CACHESZ; i++) { 366 authdes_lru[i] = i; 367 } 368 } 369 370 371 /* 372 * Find the lru victim 373 */ 374 static short 375 cache_victim() 376 { 377 return (authdes_lru[AUTHDES_CACHESZ-1]); 378 } 379 380 /* 381 * Note that sid was referenced 382 */ 383 static void 384 cache_ref(sid) 385 short sid; 386 { 387 int i; 388 short curr; 389 short prev; 390 391 prev = authdes_lru[0]; 392 authdes_lru[0] = sid; 393 for (i = 1; prev != sid; i++) { 394 curr = authdes_lru[i]; 395 authdes_lru[i] = prev; 396 prev = curr; 397 } 398 } 399 400 401 /* 402 * Find a spot in the cache for a credential containing 403 * the items given. Return -1 if a replay is detected, otherwise 404 * return the spot in the cache. 405 */ 406 static short 407 cache_spot(key, name, timestamp) 408 des_block *key; 409 char *name; 410 struct timeval *timestamp; 411 { 412 struct cache_entry *cp; 413 int i; 414 u_long hi; 415 416 hi = key->key.high; 417 for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) { 418 if (cp->key.key.high == hi && 419 cp->key.key.low == key->key.low && 420 cp->rname != NULL && 421 bcmp(cp->rname, name, strlen(name) + 1) == 0) { 422 if (BEFORE(timestamp, &cp->laststamp)) { 423 svcauthdes_stats.ncachereplays++; 424 return (-1); /* replay */ 425 } 426 svcauthdes_stats.ncachehits++; 427 return (i); /* refresh */ 428 } 429 } 430 svcauthdes_stats.ncachemisses++; 431 return (cache_victim()); /* new credential */ 432 } 433 434 435 #if (defined(sun) || defined(vax) || defined(__FreeBSD__)) 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(adc, uid, gid, grouplen, groups) 459 struct authdes_cred *adc; 460 uid_t *uid; 461 gid_t *gid; 462 int *grouplen; 463 gid_t *groups; 464 { 465 unsigned sid; 466 int i; 467 uid_t i_uid; 468 gid_t i_gid; 469 int i_grouplen; 470 struct bsdcred *cred; 471 472 sid = adc->adc_nickname; 473 if (sid >= AUTHDES_CACHESZ) { 474 debug("invalid nickname"); 475 return (0); 476 } 477 cred = (struct bsdcred *)authdes_cache[sid].localcred; 478 if (cred == NULL) { 479 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred)); 480 authdes_cache[sid].localcred = (char *)cred; 481 cred->grouplen = INVALID; 482 } 483 if (cred->grouplen == INVALID) { 484 /* 485 * not in cache: lookup 486 */ 487 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 488 &i_grouplen, groups)) 489 { 490 debug("unknown netname"); 491 cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */ 492 return (0); 493 } 494 debug("missed ucred cache"); 495 *uid = cred->uid = i_uid; 496 *gid = cred->gid = i_gid; 497 *grouplen = cred->grouplen = i_grouplen; 498 for (i = i_grouplen - 1; i >= 0; i--) { 499 cred->groups[i] = groups[i]; /* int to short */ 500 } 501 return (1); 502 } else if (cred->grouplen == UNKNOWN) { 503 /* 504 * Already lookup up, but no match found 505 */ 506 return (0); 507 } 508 509 /* 510 * cached credentials 511 */ 512 *uid = cred->uid; 513 *gid = cred->gid; 514 *grouplen = cred->grouplen; 515 for (i = cred->grouplen - 1; i >= 0; i--) { 516 groups[i] = cred->groups[i]; /* short to int */ 517 } 518 return (1); 519 } 520 521 static void 522 invalidate(cred) 523 char *cred; 524 { 525 if (cred == NULL) { 526 return; 527 } 528 ((struct bsdcred *)cred)->grouplen = INVALID; 529 } 530 #endif 531 532