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
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.
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]
23 * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include <sys/types.h>
38 #include <rpc/des_crypt.h>
40 #include "keyserv_cache.h"
43 struct cachekey_header
*ch
;
47 struct cachekey
*next
;
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;
57 des_block verifier
; /* Checksum */
61 struct cachekey_disklist
{
63 struct cachekey_disklist
*prev
; /* LRU order */
64 struct cachekey_disklist
*next
;
65 struct cachekey_disklist
*prevhash
; /* Hash chain */
66 struct cachekey_disklist
*nexthash
;
69 * Storage for encrypted skck structure here. The length will be
70 * 8 * ( ( ( sizeof(struct skck) - 1 + secret.length ) - 1 ) / 8 + 1 )
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
82 #define CACHEKEY_HEADER_VERSION 0
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 */
95 struct cachekey_disklist
*inuse
; /* LRU order */
96 struct cachekey_disklist
*inuse_end
;
98 struct cachekey_disklist
*free
;
99 struct cachekey_disklist
*bucket
[NUMHASHBUCKETS
];
100 struct cachekey_disklist array
[1]; /* Start of array */
104 static struct cachekey_header
*create_cache_file_ch(keylen_t keylen
,
108 static struct cachekey_header
*remap_cache_file_ch(struct cachekey_header
*ch
,
111 static struct cachekey_header
*cache_insert_ch(struct cachekey_header
*ch
,
112 uid_t uid
, deskeyarray common
,
117 static struct cachekey3_list
*cache_retrieve_ch(struct cachekey_header
*ch
,
122 static int cache_remove_ch(struct cachekey_header
*ch
,
126 static struct cachekey
*get_cache_header(keylen_t keylen
,
129 static void release_cache_header(struct cachekey
*);
131 static int cache_remap_addresses_ch(
132 struct cachekey_header
*);
134 static struct cachekey_disklist
*find_cache_item(struct cachekey_header
**,
135 uid_t
, struct dhkey
*);
137 static struct dhkey
*keybuf3_2_dhkey(keybuf3
*);
139 static u_int
hashval(uid_t
);
141 static void list_remove(struct cachekey_disklist
*,
142 struct cachekey_disklist
**,
143 struct cachekey_disklist
**,
146 static void list_remove_hash(struct cachekey_disklist
*,
147 struct cachekey_disklist
**,
148 struct cachekey_disklist
**,
151 static void list_insert(struct cachekey_disklist
*,
152 struct cachekey_disklist
**,
153 struct cachekey_disklist
**,
156 static void list_insert_hash(struct cachekey_disklist
*,
157 struct cachekey_disklist
**,
158 struct cachekey_disklist
**,
161 static struct cachekey3_list
* copy_cl_item(struct cachekey_header
*ch
,
162 struct cachekey_disklist
*cd
,
165 extern int hex2bin(u_char
*, u_char
*, int);
166 extern int bin2hex(u_char
*, u_char
*, int);
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.
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)
182 /* Add offset to old address */
183 #define MOVE_ADDR(old, offset) ((old) == 0) ? 0 : \
184 (void *)((u_long)(old) + (offset))
186 /* Number of records in use or on free list */
187 #define NUMRECS(ck_header) ((ck_header)->inuse_count + \
188 (ck_header)->free_count)
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)
200 struct cachekey_header
*
201 create_cache_file_ch(keylen_t keylen
, algtype_t algtype
, int sizespec
)
203 char filename
[MAXPATHLEN
];
204 struct cachekey_header
*ch
;
205 int fd
, newfile
= 0, i
, checkvalid
= 1;
207 size_t reclength
, length
;
208 struct cachekey_header
*oldbase
= 0;
209 struct cachekey_disklist
*cd
;
212 /* Construct cache file name */
213 if (snprintf(filename
, sizeof (filename
), "/var/nis/.keyserv_%d-%d",
214 keylen
, algtype
) > sizeof (filename
)) {
216 "error constructing file name for mech %d-%d", keylen
, algtype
);
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",
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",
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",
243 reclength
= CACHEKEY_RECLEN(keylen
);
245 /* specifies the number of records in file */
246 maxsize
= ALIGN8(sizeof (struct cachekey_header
)) +
249 /* specifies size of file in MB */
250 maxsize
= sizespec
*1024*1024;
252 length
= ALIGN8(sizeof (struct cachekey_header
)) +
253 reclength
*CHUNK_NUMREC
;
254 if (length
> maxsize
) {
256 * First record resides partly in the header, so the length
257 * cannot be allowed to be less than header plus one record.
259 if (maxsize
> ALIGN8(sizeof (struct cachekey_header
)+reclength
))
262 length
= ALIGN8(sizeof (struct cachekey_header
)+
268 if (statbuf
.st_size
== 0) {
269 /* Extend the file if we just created it */
270 if (ftruncate(fd
, length
) < 0) {
272 "cache file ftruncate error for mech %d-%d: %m",
280 * Temporarily mmap the header, to sanity check and obtain
281 * the address where it was mapped the last time.
283 if ((ch
= (void *)mmap(0, sizeof (struct cachekey_header
),
284 PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0)) ==
287 "cache file mmap1 error for mech %d-%d: %m",
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
)) {
302 "cache file consistency error for mech %d-%d",
304 munmap((caddr_t
)ch
, sizeof (struct cachekey_header
));
308 oldbase
= (void *)ch
->address
;
310 if (munmap((caddr_t
)ch
, sizeof (struct cachekey_header
)) < 0) {
312 "cache file munmap error for mech %d-%d: %m",
320 if ((ch
= (void *)mmap((caddr_t
)oldbase
, length
,
321 PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
323 "cache file mmap2 error for mech %d-%d: %m",
330 ch
->maxsize
= maxsize
;
333 ch
->version
= CACHEKEY_HEADER_VERSION
;
334 ch
->headerlength
= sizeof (struct cachekey_header
);
336 ch
->algtype
= algtype
;
337 ch
->reclength
= reclength
;
339 ch
->address
= (caddr_t
)ch
;
345 for (i
= 0; i
< NUMHASHBUCKETS
; i
++) {
349 cd
= &(ch
->array
[0]);
350 for (i
= 0; i
< MAPRECS(ch
);
351 i
++, cd
= MOVE_ADDR(cd
, ch
->reclength
)) {
353 cd
->prev
= MOVE_ADDR(cd
, -(ch
->reclength
));
354 cd
->next
= MOVE_ADDR(cd
, +(ch
->reclength
));
359 * Last record next pointer, and first record prev pointer,
362 cd
= MOVE_ADDR(cd
, -(ch
->reclength
));
364 cd
= &(ch
->array
[0]);
367 ch
->free_count
= MAPRECS(ch
);
368 ch
->free
= &(ch
->array
[0]);
370 (void) msync((caddr_t
)ch
, ch
->length
, MS_SYNC
);
372 } else if (ch
->length
> maxsize
) {
373 /* File should shrink */
374 if ((ch
= remap_cache_file_ch(ch
, MAXRECS(ch
))) == 0) {
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.
387 cache_remap_addresses_ch(ch
) == 0) {
388 syslog(LOG_WARNING
, "cache file invalid for mech %d-%d",
390 (void) munmap((caddr_t
)ch
, ch
->length
);
395 (void) msync((caddr_t
)ch
, ch
->length
, MS_SYNC
);
402 cache_remap_addresses_ch(struct cachekey_header
*ch
)
406 struct cachekey_disklist
*cd
;
408 offset
= (u_long
)ch
- (u_long
)ch
->address
;
410 if (INVALID_ADDRESS(ch
->inuse
, ch
) ||
411 INVALID_ADDRESS(ch
->inuse_end
, ch
) ||
412 INVALID_ADDRESS(ch
->free
, ch
)) {
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
);
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
)) {
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
);
435 for (i
= 0; i
< NUMHASHBUCKETS
; i
++) {
436 if (INVALID_ADDRESS(ch
->bucket
[i
], ch
)) {
439 ch
->bucket
[i
] = MOVE_ADDR(ch
->bucket
[i
], offset
);
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.
447 ch
->address
= (caddr_t
)ch
;
454 * Remap cache file with space for 'newrecs' records. The mmap:ed address
455 * may have to move; the new address is returned.
457 static struct cachekey_header
*
458 remap_cache_file_ch(struct cachekey_header
*ch
, u_int newrecs
)
460 size_t newsize
, oldsize
;
463 struct cachekey_header
*newch
;
465 struct cachekey_disklist
*cd
= 0;
472 * Since the first record partly resides in the cachekey_header,
473 * newrecs cannot be less than 1.
478 newsize
= ALIGN8(sizeof (struct cachekey_header
)) +
479 (ch
->reclength
)*newrecs
;
480 currecs
= NUMRECS(ch
);
482 if (newsize
> ch
->maxsize
) {
483 /* Would exceed maximum allowed */
484 newsize
= ch
->maxsize
;
487 /* Save stuff we need while the file is unmapped */
488 oldsize
= ch
->length
;
489 oldaddr
= (caddr_t
)ch
;
492 if (newsize
> ch
->length
) {
493 /* Extending the file */
494 cd
= &(ch
->array
[0]);
495 } else if (newsize
== ch
->length
) {
500 struct cachekey_disklist
*fcd
;
502 * Shrink the file by removing records from the end.
503 * First, we have to make sure the file contains valid
506 if (cache_remap_addresses_ch(ch
) == 0) {
507 syslog(LOG_WARNING
, "cache file invalid for mech %d-%d",
508 ch
->keylen
, ch
->algtype
);
510 munmap((caddr_t
)ch
, ch
->length
);
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,
521 list_remove_hash(fcd
,
522 &(ch
->bucket
[hashval(fcd
->uid
)]), 0, 0);
523 list_remove(fcd
, &(ch
->inuse
), &(ch
->inuse_end
),
526 tmpsize
-= ch
->reclength
;
527 fcd
= MOVE_ADDR(fcd
, -(ch
->reclength
));
529 ch
->length
= newsize
;
530 (void) msync((caddr_t
)ch
, ch
->length
, MS_SYNC
);
534 if (munmap((caddr_t
)oldaddr
, oldsize
) < 0) {
539 /* Truncate/extend it */
540 if (ftruncate(fd
, newsize
) < 0) {
545 if ((newch
= (void *)mmap(oldaddr
, newsize
,
546 PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0)) ==
551 /* Update with new values */
552 newch
->length
= newsize
;
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
;
559 munmap((caddr_t
)newch
, newsize
);
563 /* If extending the file, add new records to the free list */
565 cd
= MOVE_ADDR(&(newch
->array
[0]), currecs
*newch
->reclength
);
566 for (i
= currecs
; i
< MAPRECS(newch
); i
++) {
568 list_insert(cd
, &(newch
->free
), 0,
569 &(newch
->free_count
));
570 cd
= MOVE_ADDR(cd
, newch
->reclength
);
574 (void) msync(newch
->address
, newch
->length
, MS_SYNC
);
582 print_cache_ch(struct cachekey_header
*ch
)
584 int i
, inuse
, inuse_err
, free
, free_err
;
586 struct cachekey_disklist
*cd
;
589 "\nkeylen = %d, algtype = %d, version = %d, headerlen = %d, reclen = %d\n",
590 ch
->keylen
, ch
->algtype
, ch
->version
, ch
->headerlength
,
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
);
596 printf("Active hash buckets:\n");
598 for (i
= 0, inuse
= 0, inuse_err
= 0; i
< NUMHASHBUCKETS
; i
++) {
607 printf("%d ", cd
->uid
);
608 if (cd
->uid
!= (uid_t
)-1) {
616 printf(" (%d)\n", pb
);
619 printf("\ncounted hash inuse = %d, errors = %d\n", inuse
, inuse_err
);
622 inuse
= inuse_err
= 0;
624 if (cd
->uid
!= (uid_t
)-1) {
631 printf("counted LRU inuse = %d, errors = %d\n", inuse
, inuse_err
);
636 if (cd
->uid
== (uid_t
)-1) {
640 fprintf(stderr
, "free = %d, err = %d, cd->uid = %d\n",
641 free
, free_err
, cd
->uid
);
645 printf("counted free = %d, errors = %d\n", free
, free_err
);
649 print_cache(keylen_t keylen
, algtype_t algtype
)
653 if ((c
= get_cache_header(keylen
, algtype
)) == 0)
657 release_cache_header(c
);
661 print_cache_ch(c
->ch
);
663 release_cache_header(c
);
672 return (uid
% NUMHASHBUCKETS
);
678 struct cachekey_disklist
*item
,
679 struct cachekey_disklist
**head
,
680 struct cachekey_disklist
**tail
,
683 if (item
== NULL
) return;
685 /* Handle previous item, if any */
689 item
->prev
->next
= item
->next
;
691 /* Take care of the next item, if any */
693 item
->next
->prev
= item
->prev
;
695 /* Handle tail pointer, if supplied */
696 if (tail
!= 0 && *tail
== item
)
699 item
->prev
= item
->next
= 0;
707 struct cachekey_disklist
*item
,
708 struct cachekey_disklist
**head
,
709 struct cachekey_disklist
**tail
,
712 if (item
== NULL
) return;
714 /* Handle previous item, if any */
715 if (item
->prevhash
== 0)
716 *head
= item
->nexthash
;
718 item
->prevhash
->nexthash
= item
->nexthash
;
720 /* Take care of the next item, if any */
721 if (item
->nexthash
!= 0)
722 item
->nexthash
->prevhash
= item
->prevhash
;
724 /* Handle tail pointer, if supplied */
725 if (tail
!= 0 && *tail
== item
)
726 *tail
= item
->prevhash
;
728 item
->prevhash
= item
->nexthash
= 0;
736 struct cachekey_disklist
*item
,
737 struct cachekey_disklist
**head
,
738 struct cachekey_disklist
**tail
,
741 if (item
== NULL
) return;
743 /* Insert at tail, if supplied */
747 item
->prev
->next
= item
;
755 item
->next
->prev
= item
;
765 struct cachekey_disklist
*item
,
766 struct cachekey_disklist
**head
,
767 struct cachekey_disklist
**tail
,
770 if (item
== NULL
) return;
772 /* Insert at tail, if supplied */
774 item
->prevhash
= *tail
;
775 if (item
->prevhash
!= 0)
776 item
->prevhash
->nexthash
= item
;
782 item
->nexthash
= *head
;
783 if (item
->nexthash
!= 0)
784 item
->nexthash
->prevhash
= item
;
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
799 static struct cachekey_disklist
*
800 find_cache_item(struct cachekey_header
**ch
, uid_t uid
, struct dhkey
*public)
803 struct cachekey_disklist
*cd
;
807 if ((ch
== NULL
) || ((*ch
) == NULL
)) {
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
));
822 if ((cd
= (*ch
)->free
) != 0) {
823 list_remove(cd
, &((*ch
)->free
), 0, &((*ch
)->free_count
));
827 /* Try to extend the file by CHUNK_NUMREC records */
828 if (((*ch
) = remap_cache_file_ch(*ch
, NUMRECS(*ch
)+CHUNK_NUMREC
)) == 0)
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
));
837 /* Sacrifice the LRU item, if there is one */
838 if ((cd
= (*ch
)->inuse
) == 0)
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
));
851 static struct cachekey_header
*
853 struct cachekey_header
*ch
,
860 struct cachekey_disklist
*cd
;
861 struct cachekey_header
*newch
;
869 if (ch
== 0 || uid
== (uid_t
)-1) {
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) {
880 if ((cd
= find_cache_item(&newch
, uid
, pk
)) == 0) {
887 * The item may have been free, or may have been the LRU sacrificial
888 * lamb, so reset all fields.
891 memcpy(&(cd
->public), pk
, DHKEYSIZE(pk
));
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
];
897 skck
->verifier
= key
;
898 memcpy(&(skck
->secret
), sk
, DHKEYSIZE(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
));
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
));
916 (void) msync((caddr_t
)newch
, newch
->length
, MS_SYNC
);
922 static struct cachekey3_list
*
923 copy_cl_item(struct cachekey_header
*ch
, struct cachekey_disklist
*cd
,
926 struct cachekey3_list
*cl
;
927 struct skck
*skck
, *skck_cd
;
931 /* Allocate the cachekey3_list structure */
932 if ((cl
= malloc(CACHEKEY3_LIST_SIZE(ch
->keylen
))) == 0) {
936 /* Allocate skck structure for decryption */
937 if ((skck
= malloc(SKCK_LEN(ch
->keylen
))) == 0) {
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
)) {
953 if (memcmp(key
.c
, skck
->verifier
.c
, sizeof (skck
->verifier
.c
)) != 0) {
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
,
970 cl
->public->keybuf3_len
= cd
->public.length
*2+1;
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
];
990 static struct cachekey3_list
*
991 cache_retrieve_ch(struct cachekey_header
*ch
, uid_t uid
, keybuf3
*public,
994 struct cachekey_disklist
*cd
;
995 struct cachekey3_list
*cl
= 0, **cltmp
= &cl
;
997 struct dhkey
*pk
= 0;
999 if (uid
== (uid_t
)-1 ||
1000 (public != 0 && (pk
= keybuf3_2_dhkey(public)) == 0)) {
1004 hash
= hashval(uid
);
1006 for (cd
= ch
->bucket
[hash
]; cd
!= 0; cd
= cd
->nexthash
) {
1007 if (uid
== cd
->uid
) {
1008 /* Match on public key as well ? */
1010 if (memcmp(cd
->public.key
, pk
->key
,
1011 cd
->public.length
) != 0) {
1012 /* Keep looking... */
1015 cl
= copy_cl_item(ch
, cd
, key
);
1016 /* Match on public key => nothing more to do */
1019 *cltmp
= copy_cl_item(ch
, cd
, key
);
1021 /* Return what we've got */
1024 cltmp
= &((*cltmp
)->next
);
1025 /* On to the next item */
1037 * Remove specified item. 'public' == 0 => remove all items for uid.
1038 * Return number of items removed.
1041 cache_remove_ch(struct cachekey_header
*ch
, uid_t uid
, keybuf3
*public) {
1043 struct cachekey_disklist
*cd
, *cdtmp
;
1046 struct dhkey
*pk
= 0;
1048 if (uid
== (uid_t
)-1 ||
1049 (public != 0 && (pk
= keybuf3_2_dhkey(public)) == 0)) {
1053 hash
= hashval(uid
);
1055 for (cd
= ch
->bucket
[hash
]; cd
!= 0; ) {
1056 if (uid
== cd
->uid
) {
1057 /* Match on public key as well ? */
1059 if (memcmp(cd
->public.key
, pk
->key
,
1060 cd
->public.length
) != 0) {
1061 /* Keep looking... */
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,
1071 /* Match on public key => nothing more to do */
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.
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,
1088 /* On to the next item */
1100 #define INCCACHEREFCNT mutex_lock(&cache_lock); \
1102 mutex_unlock(&cache_lock)
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)
1110 #define DECCACHEREFCNT mutex_lock(&cache_lock); \
1111 if (cache_refcnt-- == 0) (void) cond_broadcast(&cache_cv); \
1112 mutex_unlock(&cache_lock)
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().
1120 static struct cachekey
*
1121 get_cache_header(keylen_t keylen
, algtype_t algtype
) {
1127 for (c
= cache
; c
!= 0; c
= c
->next
) {
1128 if (c
->keylen
== keylen
&& c
->algtype
== algtype
) {
1134 /* Spin until there are no cache readers */
1135 mutex_lock(&cache_lock
);
1136 #if !defined(lint) && !defined(__lint)
1137 if (cache_refcnt
> 0)
1140 while (cache_refcnt
!= 0) {
1141 (void) cond_wait(&cache_cv
, &cache_lock
);
1144 if ((c
= malloc(sizeof (struct cachekey
))) != 0) {
1147 c
->algtype
= algtype
;
1148 mutex_init(&c
->mp
, 0, 0);
1153 mutex_unlock(&cache_lock
);
1157 mutex_unlock(&cache_lock
);
1163 release_cache_header(struct cachekey
*ck
) {
1170 for (c
= cache
; c
!= 0; c
= c
->next
) {
1172 mutex_unlock(&c
->mp
);
1181 create_cache_file(keylen_t keylen
, algtype_t algtype
, int sizespec
)
1186 if ((c
= get_cache_header(keylen
, algtype
)) == 0)
1190 /* Already created and opened */
1191 release_cache_header(c
);
1195 ret
= (c
->ch
= create_cache_file_ch(keylen
, algtype
, sizespec
)) != 0;
1196 release_cache_header(c
);
1215 if ((c
= get_cache_header(keylen
, algtype
)) == 0)
1219 release_cache_header(c
);
1224 cache_insert_ch(c
->ch
, uid
, common
, key
, public, secret
)) != 0;
1226 release_cache_header(c
);
1232 struct cachekey3_list
*
1241 struct cachekey3_list
*cl
;
1243 if ((c
= get_cache_header(keylen
, algtype
)) == 0)
1247 release_cache_header(c
);
1251 cl
= cache_retrieve_ch(c
->ch
, uid
, public, key
);
1253 release_cache_header(c
);
1259 cache_remove(keylen_t keylen
, algtype_t algtype
, uid_t uid
, keybuf3
*public)
1264 if ((c
= get_cache_header(keylen
, algtype
)) == 0)
1268 release_cache_header(c
);
1272 ret
= cache_remove_ch(c
->ch
, uid
, public);
1274 release_cache_header(c
);
1280 static struct dhkey
*
1281 keybuf3_2_dhkey(keybuf3
*hexkey
)
1283 struct dhkey
*binkey
;
1285 /* hexkey->keybuf3_len*4 is the key length in bits */
1286 if ((binkey
= malloc(DHKEYALLOC(hexkey
->keybuf3_len
*4))) == 0)
1289 /* Set to zero to keep dbx and Purify access checking happy */
1290 memset(binkey
, 0, DHKEYALLOC(hexkey
->keybuf3_len
*4));
1292 binkey
->length
= hexkey
->keybuf3_len
/2;
1293 hex2bin((u_char
*)hexkey
->keybuf3_val
, binkey
->key
,
1294 (int)binkey
->length
);