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