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