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