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 *
create_cache_file_ch(keylen_t keylen,algtype_t algtype,int sizespec)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
cache_remap_addresses_ch(struct cachekey_header * ch)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 *
remap_cache_file_ch(struct cachekey_header * ch,u_int newrecs)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
print_cache_ch(struct cachekey_header * ch)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
print_cache(keylen_t keylen,algtype_t algtype)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
hashval(uid_t uid)668 hashval(uid_t uid)
669 {
670 return (uid % NUMHASHBUCKETS);
671 }
672
673
674 static void
list_remove(struct cachekey_disklist * item,struct cachekey_disklist ** head,struct cachekey_disklist ** tail,u_int * count)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
list_remove_hash(struct cachekey_disklist * item,struct cachekey_disklist ** head,struct cachekey_disklist ** tail,u_int * count)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
list_insert(struct cachekey_disklist * item,struct cachekey_disklist ** head,struct cachekey_disklist ** tail,u_int * count)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
list_insert_hash(struct cachekey_disklist * item,struct cachekey_disklist ** head,struct cachekey_disklist ** tail,u_int * count)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 *
find_cache_item(struct cachekey_header ** ch,uid_t uid,struct dhkey * public)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 *
cache_insert_ch(struct cachekey_header * ch,uid_t uid,deskeyarray common,des_block key,keybuf3 * public,keybuf3 * secret)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 *
copy_cl_item(struct cachekey_header * ch,struct cachekey_disklist * cd,des_block key)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 *
cache_retrieve_ch(struct cachekey_header * ch,uid_t uid,keybuf3 * public,des_block key)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
cache_remove_ch(struct cachekey_header * ch,uid_t uid,keybuf3 * public)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 *
get_cache_header(keylen_t keylen,algtype_t algtype)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
release_cache_header(struct cachekey * ck)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
create_cache_file(keylen_t keylen,algtype_t algtype,int sizespec)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
cache_insert(keylen_t keylen,algtype_t algtype,uid_t uid,deskeyarray common,des_block key,keybuf3 * public,keybuf3 * secret)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 *
cache_retrieve(keylen_t keylen,algtype_t algtype,uid_t uid,keybuf3 * public,des_block key)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
cache_remove(keylen_t keylen,algtype_t algtype,uid_t uid,keybuf3 * public)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 *
keybuf3_2_dhkey(keybuf3 * hexkey)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