1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1997 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <fcntl.h> 30 #include <unistd.h> 31 #include <sys/mman.h> 32 #include <thread.h> 33 #include <synch.h> 34 #include <stdlib.h> 35 #include <syslog.h> 36 #include <rpc/des_crypt.h> 37 38 #include "keyserv_cache.h" 39 40 struct cachekey { 41 struct cachekey_header *ch; 42 keylen_t keylen; 43 algtype_t algtype; 44 mutex_t mp; 45 struct cachekey *next; 46 }; 47 48 static struct cachekey *cache = 0; 49 static mutex_t cache_lock = DEFAULTMUTEX; 50 static cond_t cache_cv = DEFAULTCV; 51 static u_long cache_refcnt = 0; 52 53 struct skck { 54 des_block common[3]; 55 des_block verifier; /* Checksum */ 56 struct dhkey secret; 57 }; 58 59 struct cachekey_disklist { 60 uid_t uid; 61 struct cachekey_disklist *prev; /* LRU order */ 62 struct cachekey_disklist *next; 63 struct cachekey_disklist *prevhash; /* Hash chain */ 64 struct cachekey_disklist *nexthash; 65 struct dhkey public; 66 /* 67 * Storage for encrypted skck structure here. The length will be 68 * 8 * ( ( ( sizeof(struct skck) - 1 + secret.length ) - 1 ) / 8 + 1 ) 69 */ 70 }; 71 72 /* Length of skck structure for given key length (in bits) */ 73 #define SKCK_LEN(keylen) ALIGN8(sizeof (struct skck) + KEYLEN(keylen)) 74 /* Length of a cachekey_disklist record for given key length (in bits) */ 75 #define CACHEKEY_RECLEN(keylen) ALIGN8(sizeof (struct cachekey_disklist) - 1 + \ 76 KEYLEN(keylen) + SKCK_LEN(keylen)) 77 #define NUMHASHBUCKETS 253 78 #define CHUNK_NUMREC 64 79 80 #define CACHEKEY_HEADER_VERSION 0 81 82 struct cachekey_header { /* First in each key cache file */ 83 u_int version; /* version number of interface */ 84 u_int headerlength; /* size of this header */ 85 keylen_t keylen; /* in bits */ 86 algtype_t algtype; /* algorithm type */ 87 size_t reclength; /* cache file record size in bytes */ 88 int fd; /* file descriptor */ 89 caddr_t address; /* mmap()ed here */ 90 size_t length; /* bytes mapped */ 91 size_t maxsize; /* don't grow beyond this */ 92 u_int inuse_count; 93 struct cachekey_disklist *inuse; /* LRU order */ 94 struct cachekey_disklist *inuse_end; 95 u_int free_count; 96 struct cachekey_disklist *free; 97 struct cachekey_disklist *bucket[NUMHASHBUCKETS]; 98 struct cachekey_disklist array[1]; /* Start of array */ 99 }; 100 101 102 static struct cachekey_header *create_cache_file_ch(keylen_t keylen, 103 algtype_t algtype, 104 int sizespec); 105 106 static struct cachekey_header *remap_cache_file_ch(struct cachekey_header *ch, 107 u_int newrecs); 108 109 static struct cachekey_header *cache_insert_ch(struct cachekey_header *ch, 110 uid_t uid, deskeyarray common, 111 des_block key, 112 keybuf3 *public, 113 keybuf3 *secret); 114 115 static struct cachekey3_list *cache_retrieve_ch(struct cachekey_header *ch, 116 uid_t uid, 117 keybuf3 *public, 118 des_block key); 119 120 static int cache_remove_ch(struct cachekey_header *ch, 121 uid_t uid, 122 keybuf3 *public); 123 124 static struct cachekey *get_cache_header(keylen_t keylen, 125 algtype_t algtype); 126 127 static void release_cache_header(struct cachekey *); 128 129 static int cache_remap_addresses_ch( 130 struct cachekey_header *); 131 132 static struct cachekey_disklist *find_cache_item(struct cachekey_header **, 133 uid_t, struct dhkey *); 134 135 static struct dhkey *keybuf3_2_dhkey(keybuf3 *); 136 137 static u_int hashval(uid_t); 138 139 static void list_remove(struct cachekey_disklist *, 140 struct cachekey_disklist **, 141 struct cachekey_disklist **, 142 u_int *); 143 144 static void list_remove_hash(struct cachekey_disklist *, 145 struct cachekey_disklist **, 146 struct cachekey_disklist **, 147 u_int *); 148 149 static void list_insert(struct cachekey_disklist *, 150 struct cachekey_disklist **, 151 struct cachekey_disklist **, 152 u_int *); 153 154 static void list_insert_hash(struct cachekey_disklist *, 155 struct cachekey_disklist **, 156 struct cachekey_disklist **, 157 u_int *); 158 159 static struct cachekey3_list * copy_cl_item(struct cachekey_header *ch, 160 struct cachekey_disklist *cd, 161 des_block key); 162 163 extern int hex2bin(u_char *, u_char *, int); 164 extern int bin2hex(u_char *, u_char *, int); 165 166 /* 167 * The folowing set of macros implement address validity checking. A valid 168 * address is defined to be either 0, or to fall on a record boundary. In 169 * the latter case, the the difference between the address and the start of 170 * the record array is divisible by the record length. 171 */ 172 #define FILEOFFSET(ckh) ((u_long)(ckh) - \ 173 (u_long)((ckh)->address)) 174 #define ADJUSTEDADDR(addr, ckh) ((u_long)(addr) + FILEOFFSET(ckh)) 175 #define ARRAYOFFSET(addr, ckh) (ADJUSTEDADDR(addr, ckh) - \ 176 (u_long)&((ckh)->array[0])) 177 #define INVALID_ADDRESS(addr, ckh) ((addr == 0) ? 0 : \ 178 (ARRAYOFFSET(addr, ckh) % (ckh)->reclength) != 0) 179 180 /* Add offset to old address */ 181 #define MOVE_ADDR(old, offset) ((old) == 0) ? 0 : \ 182 (void *)((u_long)(old) + (offset)) 183 184 /* Number of records in use or on free list */ 185 #define NUMRECS(ck_header) ((ck_header)->inuse_count + \ 186 (ck_header)->free_count) 187 188 /* Max number of records the mapped file could hold */ 189 #define MAPRECS(ck_header) (((ck_header)->length - \ 190 sizeof (struct cachekey_header)) / \ 191 (ck_header)->reclength) 192 /* Max number of records the file will hold if extended to the maxsize */ 193 #define MAXRECS(ck_header) (((ck_header)->maxsize - \ 194 sizeof (struct cachekey_header)) / \ 195 (ck_header)->reclength) 196 197 198 struct cachekey_header * 199 create_cache_file_ch(keylen_t keylen, algtype_t algtype, int sizespec) 200 { 201 char filename[MAXPATHLEN]; 202 struct cachekey_header *ch; 203 int fd, newfile = 0, i, checkvalid = 1; 204 struct stat statbuf; 205 size_t reclength, length; 206 struct cachekey_header *oldbase = 0; 207 struct cachekey_disklist *cd; 208 size_t maxsize; 209 210 /* Construct cache file name */ 211 if (snprintf(filename, sizeof (filename), "/var/nis/.keyserv_%d-%d", 212 keylen, algtype) > sizeof (filename)) { 213 syslog(LOG_WARNING, 214 "error constructing file name for mech %d-%d", keylen, algtype); 215 return (0); 216 } 217 218 /* Open/create the file */ 219 if ((fd = open(filename, O_RDWR|O_CREAT, 0600)) < 0) { 220 syslog(LOG_WARNING, "cache file open error for mech %d-%d: %m", 221 keylen, algtype); 222 return (0); 223 } 224 225 /* We want exclusive use of the file */ 226 if (lockf(fd, F_LOCK, 0) < 0) { 227 syslog(LOG_WARNING, "cache file lock error for mech %d-%d: %m", 228 keylen, algtype); 229 close(fd); 230 return (0); 231 } 232 233 /* Zero size means a new file */ 234 if (fstat(fd, &statbuf) < 0) { 235 syslog(LOG_WARNING, "cache file fstat error for mech %d-%d: %m", 236 keylen, algtype); 237 close(fd); 238 return (0); 239 } 240 241 reclength = CACHEKEY_RECLEN(keylen); 242 if (sizespec < 0) { 243 /* specifies the number of records in file */ 244 maxsize = ALIGN8(sizeof (struct cachekey_header)) + 245 -sizespec*reclength; 246 } else { 247 /* specifies size of file in MB */ 248 maxsize = sizespec*1024*1024; 249 } 250 length = ALIGN8(sizeof (struct cachekey_header)) + 251 reclength*CHUNK_NUMREC; 252 if (length > maxsize) { 253 /* 254 * First record resides partly in the header, so the length 255 * cannot be allowed to be less than header plus one record. 256 */ 257 if (maxsize > ALIGN8(sizeof (struct cachekey_header)+reclength)) 258 length = maxsize; 259 else { 260 length = ALIGN8(sizeof (struct cachekey_header)+ 261 reclength); 262 maxsize = length; 263 } 264 } 265 266 if (statbuf.st_size == 0) { 267 /* Extend the file if we just created it */ 268 if (ftruncate(fd, length) < 0) { 269 syslog(LOG_WARNING, 270 "cache file ftruncate error for mech %d-%d: %m", 271 keylen, algtype); 272 close(fd); 273 return (0); 274 } 275 newfile = 1; 276 } else { 277 /* 278 * Temporarily mmap the header, to sanity check and obtain 279 * the address where it was mapped the last time. 280 */ 281 if ((ch = (void *)mmap(0, sizeof (struct cachekey_header), 282 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == 283 MAP_FAILED) { 284 syslog(LOG_WARNING, 285 "cache file mmap1 error for mech %d-%d: %m", 286 keylen, algtype); 287 close(fd); 288 return (0); 289 } 290 if (ch->version != CACHEKEY_HEADER_VERSION || 291 ch->headerlength != sizeof (struct cachekey_header) || 292 ch->keylen != keylen || 293 ch->algtype != algtype || 294 ch->reclength != reclength || 295 ch->length < sizeof (struct cachekey_header) || 296 ch->maxsize < ch->length || 297 INVALID_ADDRESS(ch->inuse, ch) || 298 INVALID_ADDRESS(ch->free, ch)) { 299 syslog(LOG_WARNING, 300 "cache file consistency error for mech %d-%d", 301 keylen, algtype); 302 munmap((caddr_t)ch, sizeof (struct cachekey_header)); 303 close(fd); 304 return (0); 305 } 306 oldbase = (void *)ch->address; 307 length = ch->length; 308 if (munmap((caddr_t)ch, sizeof (struct cachekey_header)) < 0) { 309 syslog(LOG_WARNING, 310 "cache file munmap error for mech %d-%d: %m", 311 keylen, algtype); 312 close(fd); 313 return (0); 314 } 315 } 316 317 /* Map the file */ 318 if ((ch = (void *)mmap((caddr_t)oldbase, length, 319 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { 320 syslog(LOG_WARNING, 321 "cache file mmap2 error for mech %d-%d: %m", 322 keylen, algtype); 323 close(fd); 324 return (0); 325 } 326 327 ch->fd = fd; 328 ch->maxsize = maxsize; 329 330 if (newfile) { 331 ch->version = CACHEKEY_HEADER_VERSION; 332 ch->headerlength = sizeof (struct cachekey_header); 333 ch->keylen = keylen; 334 ch->algtype = algtype; 335 ch->reclength = reclength; 336 ch->length = length; 337 ch->address = (caddr_t)ch; 338 ch->inuse_count = 0; 339 ch->inuse = 0; 340 ch->inuse_end = 0; 341 ch->free = 0; 342 ch->free_count = 0; 343 for (i = 0; i < NUMHASHBUCKETS; i++) { 344 ch->bucket[i] = 0; 345 } 346 347 cd = &(ch->array[0]); 348 for (i = 0; i < MAPRECS(ch); 349 i++, cd = MOVE_ADDR(cd, ch->reclength)) { 350 cd->uid = (uid_t)-1; 351 cd->prev = MOVE_ADDR(cd, -(ch->reclength)); 352 cd->next = MOVE_ADDR(cd, +(ch->reclength)); 353 cd->prevhash = 0; 354 cd->nexthash = 0; 355 } 356 /* 357 * Last record next pointer, and first record prev pointer, 358 * are both NULL. 359 */ 360 cd = MOVE_ADDR(cd, -(ch->reclength)); 361 cd->next = 0; 362 cd = &(ch->array[0]); 363 cd->prev = 0; 364 365 ch->free_count = MAPRECS(ch); 366 ch->free = &(ch->array[0]); 367 368 (void) msync((caddr_t)ch, ch->length, MS_SYNC); 369 370 } else if (ch->length > maxsize) { 371 /* File should shrink */ 372 if ((ch = remap_cache_file_ch(ch, MAXRECS(ch))) == 0) { 373 return (0); 374 } 375 checkvalid = 0; 376 } 377 378 /* 379 * cache_remap_addresses() also checks address consistency, so call 380 * it even if the remap is a no-op. However, if we've called 381 * remap_cache_file_ch(), it will have invoked cache_remap_addresses() 382 * already, so we don't have to do that again. 383 */ 384 if (checkvalid && 385 cache_remap_addresses_ch(ch) == 0) { 386 syslog(LOG_WARNING, "cache file invalid for mech %d-%d", 387 keylen, algtype); 388 (void) munmap((caddr_t)ch, ch->length); 389 close(fd); 390 return (0); 391 } 392 393 (void) msync((caddr_t)ch, ch->length, MS_SYNC); 394 395 return (ch); 396 } 397 398 399 static int 400 cache_remap_addresses_ch(struct cachekey_header *ch) 401 { 402 int i; 403 u_long offset; 404 struct cachekey_disklist *cd; 405 406 offset = (u_long)ch - (u_long)ch->address; 407 408 if (INVALID_ADDRESS(ch->inuse, ch) || 409 INVALID_ADDRESS(ch->inuse_end, ch) || 410 INVALID_ADDRESS(ch->free, ch)) { 411 return (0); 412 } 413 414 ch->inuse = MOVE_ADDR(ch->inuse, offset); 415 ch->inuse_end = MOVE_ADDR(ch->inuse_end, offset); 416 ch->free = MOVE_ADDR(ch->free, offset); 417 418 cd = &(ch->array[0]); 419 for (i = 0; i < NUMRECS(ch); i++) { 420 if (INVALID_ADDRESS(cd->prev, ch) || 421 INVALID_ADDRESS(cd->next, ch) || 422 INVALID_ADDRESS(cd->prevhash, ch) || 423 INVALID_ADDRESS(cd->nexthash, ch)) { 424 return (0); 425 } 426 cd->prev = MOVE_ADDR(cd->prev, offset); 427 cd->next = MOVE_ADDR(cd->next, offset); 428 cd->prevhash = MOVE_ADDR(cd->prevhash, offset); 429 cd->nexthash = MOVE_ADDR(cd->nexthash, offset); 430 cd = MOVE_ADDR(cd, ch->reclength); 431 } 432 433 for (i = 0; i < NUMHASHBUCKETS; i++) { 434 if (INVALID_ADDRESS(ch->bucket[i], ch)) { 435 return (0); 436 } 437 ch->bucket[i] = MOVE_ADDR(ch->bucket[i], offset); 438 } 439 440 /* 441 * To prevent disaster if this function is invoked again, we 442 * update ch->address, so that offset will be zero if we do 443 * get called once more, and the mapped file hasn't moved. 444 */ 445 ch->address = (caddr_t)ch; 446 447 return (1); 448 } 449 450 451 /* 452 * Remap cache file with space for 'newrecs' records. The mmap:ed address 453 * may have to move; the new address is returned. 454 */ 455 static struct cachekey_header * 456 remap_cache_file_ch(struct cachekey_header *ch, u_int newrecs) 457 { 458 size_t newsize, oldsize; 459 u_int currecs; 460 int i, fd; 461 struct cachekey_header *newch; 462 caddr_t oldaddr; 463 struct cachekey_disklist *cd = 0; 464 465 if (ch == 0) 466 return (0); 467 468 469 /* 470 * Since the first record partly resides in the cachekey_header, 471 * newrecs cannot be less than 1. 472 */ 473 if (newrecs < 1) 474 newrecs = 1; 475 476 newsize = ALIGN8(sizeof (struct cachekey_header)) + 477 (ch->reclength)*newrecs; 478 currecs = NUMRECS(ch); 479 480 if (newsize > ch->maxsize) { 481 /* Would exceed maximum allowed */ 482 newsize = ch->maxsize; 483 } 484 485 /* Save stuff we need while the file is unmapped */ 486 oldsize = ch->length; 487 oldaddr = (caddr_t)ch; 488 fd = ch->fd; 489 490 if (newsize > ch->length) { 491 /* Extending the file */ 492 cd = &(ch->array[0]); 493 } else if (newsize == ch->length) { 494 /* Already OK */ 495 return (ch); 496 } else { 497 size_t tmpsize; 498 struct cachekey_disklist *fcd; 499 /* 500 * Shrink the file by removing records from the end. 501 * First, we have to make sure the file contains valid 502 * addresses. 503 */ 504 if (cache_remap_addresses_ch(ch) == 0) { 505 syslog(LOG_WARNING, "cache file invalid for mech %d-%d", 506 ch->keylen, ch->algtype); 507 close(ch->fd); 508 munmap((caddr_t)ch, ch->length); 509 return (0); 510 } 511 fcd = MOVE_ADDR(&(ch->array[0]), 512 ch->reclength*(MAPRECS(ch)-1)); 513 tmpsize = (u_long)fcd - (u_long)ch + ch->reclength; 514 while (tmpsize > newsize && fcd > &(ch->array[0])) { 515 if (fcd->uid == (uid_t)-1) { 516 list_remove(fcd, &(ch->free), 0, 517 &(ch->free_count)); 518 } else { 519 list_remove_hash(fcd, 520 &(ch->bucket[hashval(fcd->uid)]), 0, 0); 521 list_remove(fcd, &(ch->inuse), &(ch->inuse_end), 522 &(ch->inuse_count)); 523 } 524 tmpsize -= ch->reclength; 525 fcd = MOVE_ADDR(fcd, -(ch->reclength)); 526 } 527 ch->length = newsize; 528 (void) msync((caddr_t)ch, ch->length, MS_SYNC); 529 } 530 531 /* Unmap the file */ 532 if (munmap((caddr_t)oldaddr, oldsize) < 0) { 533 return (0); 534 } 535 ch = 0; 536 537 /* Truncate/extend it */ 538 if (ftruncate(fd, newsize) < 0) { 539 return (0); 540 } 541 542 /* Map it again */ 543 if ((newch = (void *)mmap(oldaddr, newsize, 544 PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == 545 MAP_FAILED) { 546 return (0); 547 } 548 549 /* Update with new values */ 550 newch->length = newsize; 551 552 if (cache_remap_addresses_ch(newch) == 0) { 553 syslog(LOG_WARNING, "cache file invalid for mech %d-%d", 554 newch->keylen, newch->algtype); 555 newch->length = oldsize; 556 close(newch->fd); 557 munmap((caddr_t)newch, newsize); 558 return (0); 559 } 560 561 /* If extending the file, add new records to the free list */ 562 if (cd != 0) { 563 cd = MOVE_ADDR(&(newch->array[0]), currecs*newch->reclength); 564 for (i = currecs; i < MAPRECS(newch); i++) { 565 cd->uid = (uid_t)-1; 566 list_insert(cd, &(newch->free), 0, 567 &(newch->free_count)); 568 cd = MOVE_ADDR(cd, newch->reclength); 569 } 570 } 571 572 (void) msync(newch->address, newch->length, MS_SYNC); 573 574 return (newch); 575 } 576 577 578 #ifdef DEBUG 579 void 580 print_cache_ch(struct cachekey_header *ch) 581 { 582 int i, inuse, inuse_err, free, free_err; 583 int pb; 584 struct cachekey_disklist *cd; 585 586 printf( 587 "\nkeylen = %d, algtype = %d, version = %d, headerlen = %d, reclen = %d\n", 588 ch->keylen, ch->algtype, ch->version, ch->headerlength, 589 ch->reclength); 590 printf("fd = %d, address = 0x%x, mapped length = %d, maxsize = %d\n", 591 ch->fd, ch->address, ch->length, ch->maxsize); 592 printf("inuse = %d, free = %d\n", ch->inuse_count, ch->free_count); 593 594 printf("Active hash buckets:\n"); 595 596 for (i = 0, inuse = 0, inuse_err = 0; i < NUMHASHBUCKETS; i++) { 597 cd = ch->bucket[i]; 598 pb = -1; 599 if (cd != 0) { 600 pb = 0; 601 printf("\t%d: ", i); 602 } 603 while (cd != 0) { 604 pb++; 605 printf("%d ", cd->uid); 606 if (cd->uid != (uid_t)-1) { 607 inuse++; 608 } else { 609 inuse_err++; 610 } 611 cd = cd->nexthash; 612 } 613 if (pb >= 0) 614 printf(" (%d)\n", pb); 615 } 616 617 printf("\ncounted hash inuse = %d, errors = %d\n", inuse, inuse_err); 618 619 cd = ch->inuse; 620 inuse = inuse_err = 0; 621 while (cd != 0) { 622 if (cd->uid != (uid_t)-1) { 623 inuse++; 624 } else { 625 inuse_err++; 626 } 627 cd = cd->next; 628 } 629 printf("counted LRU inuse = %d, errors = %d\n", inuse, inuse_err); 630 631 cd = ch->free; 632 free = free_err = 0; 633 while (cd != 0) { 634 if (cd->uid == (uid_t)-1) { 635 free++; 636 } else { 637 free_err++; 638 fprintf(stderr, "free = %d, err = %d, cd->uid = %d\n", 639 free, free_err, cd->uid); 640 } 641 cd = cd->next; 642 } 643 printf("counted free = %d, errors = %d\n", free, free_err); 644 } 645 646 void 647 print_cache(keylen_t keylen, algtype_t algtype) 648 { 649 struct cachekey *c; 650 651 if ((c = get_cache_header(keylen, algtype)) == 0) 652 return; 653 654 if (c->ch == 0) { 655 release_cache_header(c); 656 return; 657 } 658 659 print_cache_ch(c->ch); 660 661 release_cache_header(c); 662 } 663 #endif 664 665 666 667 static u_int 668 hashval(uid_t uid) 669 { 670 return (uid % NUMHASHBUCKETS); 671 } 672 673 674 static void 675 list_remove( 676 struct cachekey_disklist *item, 677 struct cachekey_disklist **head, 678 struct cachekey_disklist **tail, 679 u_int *count) 680 { 681 if (item == NULL) return; 682 683 /* Handle previous item, if any */ 684 if (item->prev == 0) 685 *head = item->next; 686 else 687 item->prev->next = item->next; 688 689 /* Take care of the next item, if any */ 690 if (item->next != 0) 691 item->next->prev = item->prev; 692 693 /* Handle tail pointer, if supplied */ 694 if (tail != 0 && *tail == item) 695 *tail = item->prev; 696 697 item->prev = item->next = 0; 698 if (count != 0) 699 (*count)--; 700 } 701 702 703 static void 704 list_remove_hash( 705 struct cachekey_disklist *item, 706 struct cachekey_disklist **head, 707 struct cachekey_disklist **tail, 708 u_int *count) 709 { 710 if (item == NULL) return; 711 712 /* Handle previous item, if any */ 713 if (item->prevhash == 0) 714 *head = item->nexthash; 715 else 716 item->prevhash->nexthash = item->nexthash; 717 718 /* Take care of the next item, if any */ 719 if (item->nexthash != 0) 720 item->nexthash->prevhash = item->prevhash; 721 722 /* Handle tail pointer, if supplied */ 723 if (tail != 0 && *tail == item) 724 *tail = item->prevhash; 725 726 item->prevhash = item->nexthash = 0; 727 if (count != 0) 728 (*count)--; 729 } 730 731 732 static void 733 list_insert( 734 struct cachekey_disklist *item, 735 struct cachekey_disklist **head, 736 struct cachekey_disklist **tail, 737 u_int *count) 738 { 739 if (item == NULL) return; 740 741 /* Insert at tail, if supplied */ 742 if (tail != 0) { 743 item->prev = *tail; 744 if (item->prev != 0) 745 item->prev->next = item; 746 item->next = 0; 747 *tail = item; 748 if (*head == 0) 749 *head = item; 750 } else { 751 item->next = *head; 752 if (item->next != 0) 753 item->next->prev = item; 754 item->prev = 0; 755 *head = item; 756 } 757 if (count != 0) 758 (*count)++; 759 } 760 761 static void 762 list_insert_hash( 763 struct cachekey_disklist *item, 764 struct cachekey_disklist **head, 765 struct cachekey_disklist **tail, 766 u_int *count) 767 { 768 if (item == NULL) return; 769 770 /* Insert at tail, if supplied */ 771 if (tail != 0) { 772 item->prevhash = *tail; 773 if (item->prevhash != 0) 774 item->prevhash->nexthash = item; 775 item->nexthash = 0; 776 *tail = item; 777 if (*head == 0) 778 *head = item; 779 } else { 780 item->nexthash = *head; 781 if (item->nexthash != 0) 782 item->nexthash->prevhash = item; 783 item->prevhash = 0; 784 *head = item; 785 } 786 if (count != 0) 787 (*count)++; 788 } 789 790 791 /* 792 * Find the cache item specified by the header, uid, and public key. If 793 * no such uid/public item exists, return a pointer to an empty record. 794 * In either case, the item returned has been removed from any and all 795 * lists. 796 */ 797 static struct cachekey_disklist * 798 find_cache_item(struct cachekey_header **ch, uid_t uid, struct dhkey *public) 799 { 800 u_int hash; 801 struct cachekey_disklist *cd; 802 803 hash = hashval(uid); 804 805 if ((ch == NULL) || ((*ch) == NULL)) { 806 return (0); 807 } 808 for (cd = (*ch)->bucket[hash]; cd != 0; cd = cd->nexthash) { 809 if (uid == cd->uid && 810 public->length == cd->public.length && 811 memcmp(public->key, cd->public.key, 812 cd->public.length) == 0) { 813 list_remove_hash(cd, &((*ch)->bucket[hash]), 0, 0); 814 list_remove(cd, &((*ch)->inuse), &((*ch)->inuse_end), 815 &((*ch)->inuse_count)); 816 return (cd); 817 } 818 } 819 820 if ((cd = (*ch)->free) != 0) { 821 list_remove(cd, &((*ch)->free), 0, &((*ch)->free_count)); 822 return (cd); 823 } 824 825 /* Try to extend the file by CHUNK_NUMREC records */ 826 if (((*ch) = remap_cache_file_ch(*ch, NUMRECS(*ch)+CHUNK_NUMREC)) == 0) 827 return (0); 828 829 /* If the extend worked, there should now be at least one free record */ 830 if ((cd = (*ch)->free) != 0) { 831 list_remove(cd, &((*ch)->free), 0, &((*ch)->free_count)); 832 return (cd); 833 } 834 835 /* Sacrifice the LRU item, if there is one */ 836 if ((cd = (*ch)->inuse) == 0) 837 return (0); 838 839 /* Extract from hash list */ 840 list_remove_hash(cd, &((*ch)->bucket[hashval(cd->uid)]), 0, 0); 841 /* Extract from LRU list */ 842 list_remove(cd, &((*ch)->inuse), &((*ch)->inuse_end), 843 &((*ch)->inuse_count)); 844 845 return (cd); 846 } 847 848 849 static struct cachekey_header * 850 cache_insert_ch( 851 struct cachekey_header *ch, 852 uid_t uid, 853 deskeyarray common, 854 des_block key, 855 keybuf3 *public, 856 keybuf3 *secret) 857 { 858 struct cachekey_disklist *cd; 859 struct cachekey_header *newch; 860 int i, err; 861 struct skck *skck; 862 des_block ivec; 863 struct dhkey *pk; 864 struct dhkey *sk; 865 866 867 if (ch == 0 || uid == (uid_t)-1) { 868 return (0); 869 } 870 871 if (common.deskeyarray_len > sizeof (skck->common)/sizeof (des_block) || 872 (pk = keybuf3_2_dhkey(public)) == 0 || 873 (sk = keybuf3_2_dhkey(secret)) == 0) { 874 return (0); 875 } 876 877 newch = ch; 878 if ((cd = find_cache_item(&newch, uid, pk)) == 0) { 879 free(pk); 880 free(sk); 881 return (newch); 882 } 883 884 /* 885 * The item may have been free, or may have been the LRU sacrificial 886 * lamb, so reset all fields. 887 */ 888 cd->uid = uid; 889 memcpy(&(cd->public), pk, DHKEYSIZE(pk)); 890 891 skck = MOVE_ADDR(&(cd->public), DHKEYSIZE(pk)); 892 for (i = 0; i < common.deskeyarray_len; i++) { 893 skck->common[i] = common.deskeyarray_val[i]; 894 } 895 skck->verifier = key; 896 memcpy(&(skck->secret), sk, DHKEYSIZE(sk)); 897 free(pk); 898 free(sk); 899 memcpy(ivec.c, key.c, sizeof (key.c)); 900 err = cbc_crypt(key.c, (char *)skck, SKCK_LEN(newch->keylen), 901 DES_ENCRYPT|DES_HW, ivec.c); 902 if (DES_FAILED(err)) { 903 /* Re-insert on free list */ 904 list_insert(cd, &(newch->free), 0, &(newch->free_count)); 905 return (newch); 906 } 907 908 /* Re-insert on hash list */ 909 list_insert_hash(cd, &(newch->bucket[hashval(cd->uid)]), 0, 0); 910 /* Insert at end of LRU list */ 911 list_insert(cd, &(newch->inuse), &(newch->inuse_end), 912 &(newch->inuse_count)); 913 914 (void) msync((caddr_t)newch, newch->length, MS_SYNC); 915 916 return (newch); 917 } 918 919 920 static struct cachekey3_list * 921 copy_cl_item(struct cachekey_header *ch, struct cachekey_disklist *cd, 922 des_block key) { 923 924 struct cachekey3_list *cl; 925 struct skck *skck, *skck_cd; 926 int i, err; 927 des_block ivec; 928 929 /* Allocate the cachekey3_list structure */ 930 if ((cl = malloc(CACHEKEY3_LIST_SIZE(ch->keylen))) == 0) { 931 return (0); 932 } 933 934 /* Allocate skck structure for decryption */ 935 if ((skck = malloc(SKCK_LEN(ch->keylen))) == 0) { 936 free(cl); 937 return (0); 938 } 939 940 /* Decrypt and check verifier */ 941 skck_cd = MOVE_ADDR(&(cd->public), DHKEYSIZE(&(cd->public))); 942 memcpy(skck, skck_cd, SKCK_LEN(ch->keylen)); 943 memcpy(ivec.c, key.c, sizeof (ivec.c)); 944 err = cbc_crypt(key.c, (char *)skck, SKCK_LEN(ch->keylen), 945 DES_DECRYPT|DES_HW, ivec.c); 946 if (DES_FAILED(err)) { 947 free(cl); 948 free(skck); 949 return (0); 950 } 951 if (memcmp(key.c, skck->verifier.c, sizeof (skck->verifier.c)) != 0) { 952 free(cl); 953 free(skck); 954 return (0); 955 } 956 957 /* Everything OK; copy values */ 958 cl->public = MOVE_ADDR(cl, sizeof (struct cachekey3_list)); 959 cl->public->keybuf3_val = MOVE_ADDR(cl->public, sizeof (keybuf3)); 960 cl->secret = MOVE_ADDR(cl->public->keybuf3_val, 961 ALIGN4(2*KEYLEN(ch->keylen)+1)); 962 cl->secret->keybuf3_val = MOVE_ADDR(cl->secret, sizeof (keybuf3)); 963 cl->deskey.deskeyarray_val = 964 MOVE_ADDR(cl->secret->keybuf3_val, 965 ALIGN4(2*KEYLEN(ch->keylen)+1)); 966 bin2hex(cd->public.key, (u_char *)cl->public->keybuf3_val, 967 cd->public.length); 968 cl->public->keybuf3_len = cd->public.length*2+1; 969 970 bin2hex(skck->secret.key, (u_char *)cl->secret->keybuf3_val, 971 skck->secret.length); 972 cl->secret->keybuf3_len = skck->secret.length*2+1; 973 cl->deskey.deskeyarray_len = sizeof (skck->common)/sizeof (des_block); 974 for (i = 0; i < cl->deskey.deskeyarray_len; i++) { 975 cl->deskey.deskeyarray_val[i] = skck->common[i]; 976 } 977 978 cl->refcnt = 0; 979 cl->next = 0; 980 981 free(skck); 982 983 return (cl); 984 985 } 986 987 988 static struct cachekey3_list * 989 cache_retrieve_ch(struct cachekey_header *ch, uid_t uid, keybuf3 *public, 990 des_block key) { 991 992 struct cachekey_disklist *cd; 993 struct cachekey3_list *cl = 0, **cltmp = &cl; 994 u_int hash; 995 struct dhkey *pk = 0; 996 997 if (uid == (uid_t)-1 || 998 (public != 0 && (pk = keybuf3_2_dhkey(public)) == 0)) { 999 return (0); 1000 } 1001 1002 hash = hashval(uid); 1003 1004 for (cd = ch->bucket[hash]; cd != 0; cd = cd->nexthash) { 1005 if (uid == cd->uid) { 1006 /* Match on public key as well ? */ 1007 if (pk != 0) { 1008 if (memcmp(cd->public.key, pk->key, 1009 cd->public.length) != 0) { 1010 /* Keep looking... */ 1011 continue; 1012 } 1013 cl = copy_cl_item(ch, cd, key); 1014 /* Match on public key => nothing more to do */ 1015 break; 1016 } 1017 *cltmp = copy_cl_item(ch, cd, key); 1018 if (*cltmp == 0) { 1019 /* Return what we've got */ 1020 break; 1021 } 1022 cltmp = &((*cltmp)->next); 1023 /* On to the next item */ 1024 } 1025 } 1026 1027 if (pk != 0) 1028 free(pk); 1029 1030 return (cl); 1031 } 1032 1033 1034 /* 1035 * Remove specified item. 'public' == 0 => remove all items for uid. 1036 * Return number of items removed. 1037 */ 1038 static int 1039 cache_remove_ch(struct cachekey_header *ch, uid_t uid, keybuf3 *public) { 1040 1041 struct cachekey_disklist *cd, *cdtmp; 1042 u_int hash; 1043 int match = 0; 1044 struct dhkey *pk = 0; 1045 1046 if (uid == (uid_t)-1 || 1047 (public != 0 && (pk = keybuf3_2_dhkey(public)) == 0)) { 1048 return (0); 1049 } 1050 1051 hash = hashval(uid); 1052 1053 for (cd = ch->bucket[hash]; cd != 0; ) { 1054 if (uid == cd->uid) { 1055 /* Match on public key as well ? */ 1056 if (pk != 0) { 1057 if (memcmp(cd->public.key, pk->key, 1058 cd->public.length) != 0) { 1059 /* Keep looking... */ 1060 continue; 1061 } 1062 match++; 1063 list_remove_hash(cd, &(ch->bucket[hash]), 0, 0); 1064 list_remove(cd, &(ch->inuse), &(ch->inuse_end), 1065 &(ch->inuse_count)); 1066 cd->uid = (uid_t)-1; 1067 list_insert(cd, &(ch->free), 0, 1068 &(ch->free_count)); 1069 /* Match on public key => nothing more to do */ 1070 break; 1071 } 1072 match++; 1073 /* 1074 * XXX: Assume that the order of the hash list remains 1075 * the same after removal of an item. If this isn't 1076 * true, we really should start over from the start 1077 * of the hash bucket. 1078 */ 1079 cdtmp = cd->nexthash; 1080 list_remove_hash(cd, &(ch->bucket[hash]), 0, 0); 1081 list_remove(cd, &(ch->inuse), &(ch->inuse_end), 1082 &(ch->inuse_count)); 1083 cd->uid = (uid_t)-1; 1084 list_insert(cd, &(ch->free), 0, 1085 &(ch->free_count)); 1086 /* On to the next item */ 1087 cd = cdtmp; 1088 } else { 1089 cd = cd->nexthash; 1090 } 1091 } 1092 1093 free(pk); 1094 return (match); 1095 } 1096 1097 1098 #define INCCACHEREFCNT mutex_lock(&cache_lock); \ 1099 cache_refcnt++; \ 1100 mutex_unlock(&cache_lock) 1101 1102 #if !defined(lint) && !defined(__lint) 1103 #define DECCACHEREFCNT mutex_lock(&cache_lock); \ 1104 if (cache_refcnt > 0) \ 1105 if (cache_refcnt-- == 0) (void) cond_broadcast(&cache_cv); \ 1106 mutex_unlock(&cache_lock) 1107 #else 1108 #define DECCACHEREFCNT mutex_lock(&cache_lock); \ 1109 if (cache_refcnt-- == 0) (void) cond_broadcast(&cache_cv); \ 1110 mutex_unlock(&cache_lock) 1111 #endif 1112 1113 /* 1114 * Return the cachekey structure for the specified keylen and algtype. 1115 * When returned, the lock in the structure has been activated. It's the 1116 * responsibility of the caller to unlock it by calling release_cache_header(). 1117 */ 1118 static struct cachekey * 1119 get_cache_header(keylen_t keylen, algtype_t algtype) { 1120 1121 struct cachekey *c; 1122 1123 INCCACHEREFCNT; 1124 1125 for (c = cache; c != 0; c = c->next) { 1126 if (c->keylen == keylen && c->algtype == algtype) { 1127 mutex_lock(&c->mp); 1128 return (c); 1129 } 1130 } 1131 1132 /* Spin until there are no cache readers */ 1133 mutex_lock(&cache_lock); 1134 #if !defined(lint) && !defined(__lint) 1135 if (cache_refcnt > 0) 1136 #endif 1137 cache_refcnt--; 1138 while (cache_refcnt != 0) { 1139 (void) cond_wait(&cache_cv, &cache_lock); 1140 } 1141 1142 if ((c = malloc(sizeof (struct cachekey))) != 0) { 1143 c->ch = 0; 1144 c->keylen = keylen; 1145 c->algtype = algtype; 1146 mutex_init(&c->mp, 0, 0); 1147 c->next = cache; 1148 cache = c; 1149 mutex_lock(&c->mp); 1150 cache_refcnt++; 1151 mutex_unlock(&cache_lock); 1152 return (c); 1153 } 1154 1155 mutex_unlock(&cache_lock); 1156 return (0); 1157 } 1158 1159 1160 static void 1161 release_cache_header(struct cachekey *ck) { 1162 1163 struct cachekey *c; 1164 1165 if (ck == 0) 1166 return; 1167 1168 for (c = cache; c != 0; c = c->next) { 1169 if (c == ck) { 1170 mutex_unlock(&c->mp); 1171 DECCACHEREFCNT; 1172 break; 1173 } 1174 } 1175 } 1176 1177 1178 int 1179 create_cache_file(keylen_t keylen, algtype_t algtype, int sizespec) 1180 { 1181 struct cachekey *c; 1182 int ret; 1183 1184 if ((c = get_cache_header(keylen, algtype)) == 0) 1185 return (0); 1186 1187 if (c->ch != 0) { 1188 /* Already created and opened */ 1189 release_cache_header(c); 1190 return (1); 1191 } 1192 1193 ret = (c->ch = create_cache_file_ch(keylen, algtype, sizespec)) != 0; 1194 release_cache_header(c); 1195 1196 return (ret); 1197 } 1198 1199 1200 int 1201 cache_insert( 1202 keylen_t keylen, 1203 algtype_t algtype, 1204 uid_t uid, 1205 deskeyarray common, 1206 des_block key, 1207 keybuf3 *public, 1208 keybuf3 *secret) 1209 { 1210 struct cachekey *c; 1211 int ret; 1212 1213 if ((c = get_cache_header(keylen, algtype)) == 0) 1214 return (0); 1215 1216 if (c->ch == 0) { 1217 release_cache_header(c); 1218 return (0); 1219 } 1220 1221 ret = (c->ch = 1222 cache_insert_ch(c->ch, uid, common, key, public, secret)) != 0; 1223 1224 release_cache_header(c); 1225 1226 return (ret); 1227 } 1228 1229 1230 struct cachekey3_list * 1231 cache_retrieve( 1232 keylen_t keylen, 1233 algtype_t algtype, 1234 uid_t uid, 1235 keybuf3 *public, 1236 des_block key) 1237 { 1238 struct cachekey *c; 1239 struct cachekey3_list *cl; 1240 1241 if ((c = get_cache_header(keylen, algtype)) == 0) 1242 return (0); 1243 1244 if (c->ch == 0) { 1245 release_cache_header(c); 1246 return (0); 1247 } 1248 1249 cl = cache_retrieve_ch(c->ch, uid, public, key); 1250 1251 release_cache_header(c); 1252 1253 return (cl); 1254 } 1255 1256 int 1257 cache_remove(keylen_t keylen, algtype_t algtype, uid_t uid, keybuf3 *public) 1258 { 1259 struct cachekey *c; 1260 int ret; 1261 1262 if ((c = get_cache_header(keylen, algtype)) == 0) 1263 return (0); 1264 1265 if (c->ch == 0) { 1266 release_cache_header(c); 1267 return (0); 1268 } 1269 1270 ret = cache_remove_ch(c->ch, uid, public); 1271 1272 release_cache_header(c); 1273 1274 return (ret); 1275 } 1276 1277 1278 static struct dhkey * 1279 keybuf3_2_dhkey(keybuf3 *hexkey) 1280 { 1281 struct dhkey *binkey; 1282 1283 /* hexkey->keybuf3_len*4 is the key length in bits */ 1284 if ((binkey = malloc(DHKEYALLOC(hexkey->keybuf3_len*4))) == 0) 1285 return (0); 1286 1287 /* Set to zero to keep dbx and Purify access checking happy */ 1288 memset(binkey, 0, DHKEYALLOC(hexkey->keybuf3_len*4)); 1289 1290 binkey->length = hexkey->keybuf3_len/2; 1291 hex2bin((u_char *)hexkey->keybuf3_val, binkey->key, 1292 (int)binkey->length); 1293 1294 return (binkey); 1295 } 1296