2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
18 #include <afs/afsutil.h>
24 #if !defined(offsetof)
25 #include <stddef.h> /* for definition of offsetof() */
28 extern Date cheaderReadTime
; /* time cheader last read in */
30 #define set_header_word(tt,field,value) \
32 (cheader.field) = (value), \
33 kawrite((tt), ((char *)&(cheader.field) - (char *)&cheader), \
34 (char *)&(cheader.field), sizeof(afs_int32)) \
37 #define inc_header_word(tt,field) kawrite ((tt), (offsetof(struct kaheader, field)), ((cheader.field = (htonl(ntohl(cheader.field)+1))), (char *)&(cheader.field)), sizeof(afs_int32))
39 static int index_OK(afs_int32
);
42 NameHash(char *aname
, char *ainstance
)
47 /* stolen directly from the HashString function in the vol package */
49 for (i
= strlen(aname
), aname
+= i
- 1; i
--; aname
--)
50 hash
= (hash
* 31) + (*((unsigned char *)aname
) - 31);
51 for (i
= strlen(ainstance
), ainstance
+= i
- 1; i
--; ainstance
--)
52 hash
= (hash
* 31) + (*((unsigned char *)ainstance
) - 31);
53 return (hash
% HASHSIZE
);
56 /* package up seek and write into one procedure for ease of use */
59 kawrite(struct ubik_trans
*tt
, afs_int32 pos
, char *buff
, afs_int32 len
)
63 code
= ubik_Seek(tt
, 0, pos
);
66 code
= ubik_Write(tt
, buff
, len
);
70 /* same thing for read */
73 karead(struct ubik_trans
*tt
, afs_int32 pos
, char *buff
, afs_int32 len
)
77 code
= ubik_Seek(tt
, 0, pos
);
80 code
= ubik_Read(tt
, buff
, len
);
84 static struct Lock keycache_lock
;
86 static int maxCachedKeys
;
88 static struct cachedKey
{
90 int superseded
; /* NEVERDATE => this is current key */
92 struct ktc_encryptionKey key
;
93 char name
[MAXKTCNAMELEN
];
94 char inst
[MAXKTCNAMELEN
];
96 static afs_int32 keyCacheVersion
= 0;
98 static afs_int32 maxKeyLifetime
;
99 static int dbfixup
= 0;
102 init_kadatabase(int initFlags
)
104 Lock_Init(&keycache_lock
);
107 keyCache
= malloc(maxCachedKeys
* sizeof(struct cachedKey
));
112 maxKeyLifetime
= MAXKTCTICKETLIFETIME
;
118 /* check that the database has been initialized. Be careful to fail in a safe
119 manner, to avoid bogusly reinitializing the db. */
121 * reads in db cache from ubik.
123 * @param[in] ut ubik transaction
124 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
125 * will be called on rebuilding the database (or NULL to not
128 * @return operation status
132 UpdateCache(struct ubik_trans
*at
, void *rock
)
134 int (*db_init
) (struct ubik_trans
*) = rock
;
139 if ((code
= karead(at
, 0, (char *)&iversion
, sizeof(iversion
)))
141 karead(at
, sizeof(cheader
) - sizeof(afs_int32
), (char *)&tversion
,
142 sizeof(afs_int32
)))) {
144 printf("No data base\n");
146 printf("I/O Error\n");
148 iversion
= ntohl(iversion
); /* convert to host order */
149 tversion
= ntohl(tversion
);
150 if ((iversion
== KADBVERSION
) && (tversion
== KADBVERSION
)) {
151 code
= karead(at
, 0, (char *)&cheader
, sizeof(cheader
));
153 printf("SetupHeader failed\n");
156 cheaderReadTime
= time(0);
159 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
160 KADBVERSION
, iversion
, tversion
);
167 /* if here, we have no version number or the wrong version number in the
169 if ((code
== UEOF
) || ((iversion
== 0) && (tversion
== 0)))
174 if ((db_init
== 0) || (code
== KAIO
))
177 printf("Error discovered in header, rebuilding.\n");
179 /* try to write a good header */
180 memset(&cheader
, 0, sizeof(cheader
));
181 cheader
.version
= htonl(KADBVERSION
);
182 cheader
.checkVersion
= htonl(KADBVERSION
);
183 cheader
.headerSize
= htonl(sizeof(cheader
));
185 cheader
.eofPtr
= htonl(sizeof(cheader
));
187 cheader
.specialKeysVersion
= htonl(time(0)); /* anything non-zero will do */
188 cheader
.stats
.cpws
= cheader
.stats
.allocs
= cheader
.stats
.frees
= 0;
189 cheader
.admin_accounts
= 0;
190 cheader
.hashsize
= htonl(HASHSIZE
);
191 code
= kawrite(at
, 0, (char *)&cheader
, sizeof(cheader
));
193 return KAIO
; /* return the error code */
195 return db_init(at
); /* initialize the db */
199 CheckInit(struct ubik_trans
*at
,
200 int (*db_init
) (struct ubik_trans
*)) /* procedure to call if rebuilding DB */
202 return ubik_CheckCache(at
, UpdateCache
, db_init
);
205 /* Allocate a free block of storage for entry, returning address of a new
206 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
209 AllocBlock(struct ubik_trans
*at
, struct kaentry
*tentry
)
214 if (cheader
.freePtr
) {
215 /* allocate this dude */
216 temp
= ntohl(cheader
.freePtr
);
217 code
= karead(at
, temp
, (char *)tentry
, sizeof(kaentry
));
219 return 0; /* can't read block */
220 code
= set_header_word(at
, freePtr
, tentry
->next
);
222 /* hosed, nothing on free list, grow file */
223 temp
= ntohl(cheader
.eofPtr
); /* remember this guy */
224 code
= set_header_word(at
, eofPtr
, htonl(temp
+ sizeof(kaentry
)));
229 code
= inc_header_word(at
, stats
.allocs
);
232 memset(tentry
, 0, sizeof(kaentry
)); /* zero new entry */
236 /* Free a block given its index. It must already have been unthreaded.
237 Returns zero for success or an error code on failure. */
240 FreeBlock(struct ubik_trans
*at
, afs_int32 index
)
242 struct kaentry tentry
;
245 /* check index just to be on the safe side */
246 if (!index_OK(index
))
249 memset(&tentry
, 0, sizeof(kaentry
));
250 tentry
.next
= cheader
.freePtr
;
251 tentry
.flags
= htonl(KAFFREE
);
252 code
= set_header_word(at
, freePtr
, htonl(index
));
255 code
= kawrite(at
, index
, (char *)&tentry
, sizeof(kaentry
));
259 code
= inc_header_word(at
, stats
.frees
);
265 /* Look for a block by name and instance. If found read the block's contents
266 into the area pointed to by tentry and return the block's index. If not
267 found offset is set to zero. If an error is encountered a non-zero code is
271 FindBlock(struct ubik_trans
*at
, char *aname
, char *ainstance
, afs_int32
*toP
,
272 struct kaentry
*tentry
)
278 i
= NameHash(aname
, ainstance
);
279 for (to
= ntohl(cheader
.nameHash
[i
]); to
!= NULLO
;
280 to
= ntohl(tentry
->next
)) {
281 code
= karead(at
, to
, (char *)tentry
, sizeof(kaentry
));
284 /* see if the name matches */
285 if (!strcmp(aname
, tentry
->userID
.name
)
286 && (ainstance
== (char *)0
287 || !strcmp(ainstance
, tentry
->userID
.instance
))) {
288 *toP
= to
; /* found it */
292 *toP
= 0; /* no such entry */
296 /* Add a block to the hash table given a pointer to the block and its index.
297 The block is threaded onto the hash table and written to disk. The routine
298 returns zero if there were no errors. */
301 ThreadBlock(struct ubik_trans
*at
, afs_int32 index
,
302 struct kaentry
*tentry
)
305 int hi
; /* hash index */
307 if (!index_OK(index
))
309 hi
= NameHash(tentry
->userID
.name
, tentry
->userID
.instance
);
310 tentry
->next
= cheader
.nameHash
[hi
];
311 code
= set_header_word(at
, nameHash
[hi
], htonl(index
));
314 code
= kawrite(at
, index
, (char *)tentry
, sizeof(kaentry
));
320 /* Remove a block from the hash table. If success return 0, else return an
324 UnthreadBlock(struct ubik_trans
*at
, struct kaentry
*aentry
)
329 struct kaentry tentry
;
331 i
= NameHash(aentry
->userID
.name
, aentry
->userID
.instance
);
333 for (to
= ntohl(cheader
.nameHash
[i
]); to
!= NULLO
;
334 to
= ntohl(tentry
.next
)) {
335 code
= karead(at
, to
, (char *)&tentry
, sizeof(kaentry
));
338 /* see if the name matches */
339 if (!strcmp(aentry
->userID
.name
, tentry
.userID
.name
)
340 && !strcmp(aentry
->userID
.instance
, tentry
.userID
.instance
)) {
342 if (lo
) { /* unthread from last block */
344 kawrite(at
, lo
, (char *)&tentry
.next
, sizeof(afs_int32
));
347 } else { /* unthread from hash table */
348 code
= set_header_word(at
, nameHash
[i
], tentry
.next
);
352 aentry
->next
= 0; /* just to be sure */
355 lo
= DOFFSET(to
, &tentry
, &tentry
.next
);
360 /* Given an index to the last block (or zero the first time) read the contents
361 of the next block and return its index. The last argument is a pointer to
362 an estimate of the number of remaining blocks to read out. The remaining
363 count is an estimate because it may include free blocks that are not
364 returned. If there are no more blocks remaining is zero and the returned
365 index is zero. A non-zero index indicates that tentry has been filled with
366 valid data. If an error is encountered the returned index is zero and the
367 remaining count is negative. */
370 NextBlock(struct ubik_trans
*at
, afs_int32 index
, struct kaentry
*tentry
,
371 afs_int32
*remaining
)
376 if (index
== 0) /* get first one */
377 index
= sizeof(cheader
);
379 if (!index_OK(index
)) {
380 *remaining
= -1; /* error */
383 index
+= sizeof(kaentry
);
385 /* now search for the first entry that isn't free */
386 for (last
= ntohl(cheader
.eofPtr
); index
< last
; index
+= sizeof(kaentry
)) {
387 code
= karead(at
, index
, (char *)tentry
, sizeof(kaentry
));
392 if (!(ntohl(tentry
->flags
) & (KAFFREE
| KAFOLDKEYS
))) {
393 /* estimate remaining number of entries, not including this one */
394 *remaining
= (last
- index
) / sizeof(kaentry
) - 1;
398 *remaining
= 0; /* no more entries */
402 /* These are a collections of routines that deal with externally known keys.
403 They maintain a database of key version numbers and the corresponding key
404 and pointer to the user entry. */
407 ka_NewKey(struct ubik_trans
*tt
, afs_int32 tentryaddr
,
408 struct kaentry
*tentry
, struct ktc_encryptionKey
*key
)
410 struct kaOldKeys okeys
; /* old keys block */
411 afs_int32 okeysaddr
, nextaddr
; /* offset of old keys block */
412 afs_int32 prevptr
, nextprevptr
;
415 afs_int32 newkeyver
; /* new key version number */
416 afs_int32 newtotalkeyentries
= 0, oldtotalkeyentries
= 0, keyentries
;
417 int addednewkey
= 0, modified
;
419 int foundcurrentkey
= 0;
422 es_Report("Newkey for %s.%s\n", tentry
->userID
.name
,
423 tentry
->userID
.instance
);
425 newkeyver
= ntohl(tentry
->key_version
) + 1;
426 if ((newkeyver
< 1) || (newkeyver
>= MAXKAKVNO
))
429 /* An entry may have more than one oldkeys blocks. The entry
430 * points to the most current, but all the oldkeys blocks for an
431 * entry are not linked together. All oldkeys blocks for all
432 * entries are linked together off of the header. So we follow
435 for (prevptr
= 0, okeysaddr
= ntohl(cheader
.kvnoPtr
); okeysaddr
;
436 prevptr
= nextprevptr
, okeysaddr
= nextaddr
) {
437 /* foreacholdkeysblock */
438 /* Read the oldKeys block */
439 code
= karead(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
443 nextaddr
= ntohl(okeys
.next
);
444 nextprevptr
= DOFFSET(okeysaddr
, &okeys
, &okeys
.next
);
446 /* We only want oldkey blocks that belong to this entry */
447 if (ntohl(okeys
.entry
) != tentryaddr
)
450 modified
= 0; /* This oldkeys block has not been modified */
451 keyentries
= 0; /* Number of valid key entries in the block */
452 for (i
= 0; i
< NOLDKEYS
; i
++) {
454 /* Keep count of number of entries found */
455 if (okeys
.keys
[i
].superseded
!= 0) {
456 oldtotalkeyentries
++;
459 /* If we find the entry that is not superseded, then supersede it */
460 if (ntohl(okeys
.keys
[i
].superseded
) == NEVERDATE
) {
461 okeys
.keys
[i
].superseded
= htonl(now
);
464 if (foundcurrentkey
) {
466 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
467 tentry
->userID
.name
, tentry
->userID
.instance
));
473 /* If we find an oldkey of the same version or
474 * an old key that has expired, then delete it.
476 if ((ntohl(okeys
.keys
[i
].version
) == newkeyver
)
477 || ((now
- ntohl(okeys
.keys
[i
].superseded
) > maxKeyLifetime
))) {
478 okeys
.keys
[i
].superseded
= 0;
479 okeys
.keys
[i
].version
= htonl(-1);
480 memset(&okeys
.keys
[i
].key
, 0,
481 sizeof(struct ktc_encryptionKey
));
484 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
485 now
- ntohl(okeys
.keys
[i
].superseded
),
486 ntohl(okeys
.keys
[i
].version
));
489 /* Add our key here if its free */
490 if (!addednewkey
&& (okeys
.keys
[i
].superseded
== 0)) {
491 okeys
.keys
[i
].version
= htonl(newkeyver
);
492 okeys
.keys
[i
].superseded
= htonl(NEVERDATE
);
493 memcpy(&okeys
.keys
[i
].key
, key
,
494 sizeof(struct ktc_encryptionKey
));
496 addednewkey
= okeysaddr
;
499 /* Keep count of number of entries found */
500 if (okeys
.keys
[i
].superseded
!= 0) {
502 newtotalkeyentries
++;
506 /* If we modified the block, write it out */
507 if (modified
&& keyentries
) {
508 code
= kawrite(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
513 /* If there are no more entries in this oldkeys block, delete it */
514 if (keyentries
== 0) {
516 code
= set_header_word(tt
, kvnoPtr
, okeys
.next
);
519 kawrite(tt
, prevptr
, (char *)&okeys
.next
,
524 code
= FreeBlock(tt
, okeysaddr
);
528 nextprevptr
= prevptr
; /* won't bump prevptr */
530 } /* foreacholdkeysblock */
532 /* If we could not add the key, create a new oldkeys block */
534 /* Allocate and fill in an oldkeys block */
535 addednewkey
= AllocBlock(tt
, (struct kaentry
*)&okeys
);
538 okeys
.flags
= htonl(KAFOLDKEYS
);
539 okeys
.entry
= htonl(tentryaddr
);
540 okeys
.keys
[0].version
= htonl(newkeyver
);
541 okeys
.keys
[0].superseded
= htonl(NEVERDATE
);
542 memcpy(&okeys
.keys
[0].key
, key
, sizeof(struct ktc_encryptionKey
));
543 newtotalkeyentries
++;
545 /* Thread onto the header's chain of oldkeys */
546 okeys
.next
= cheader
.kvnoPtr
;
547 code
= set_header_word(tt
, kvnoPtr
, htonl(addednewkey
));
551 /* Write the oldkeys block out */
552 code
= kawrite(tt
, addednewkey
, (char *)&okeys
, sizeof(okeys
));
556 es_Report("New oldkey block allocated at %d\n", addednewkey
);
559 if (oldtotalkeyentries
!= ntohl(tentry
->misc
.asServer
.nOldKeys
)) {
561 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
562 tentry
->userID
.name
, tentry
->userID
.instance
,
563 ntohl(tentry
->misc
.asServer
.nOldKeys
), oldtotalkeyentries
));
567 /* Update the tentry. We rely on caller to write it out */
568 tentry
->misc
.asServer
.oldKeys
= htonl(addednewkey
);
569 tentry
->misc
.asServer
.nOldKeys
= htonl(newtotalkeyentries
);
570 tentry
->key_version
= htonl(newkeyver
);
571 memcpy(&tentry
->key
, key
, sizeof(tentry
->key
));
573 /* invalidate key caches everywhere */
574 code
= inc_header_word(tt
, specialKeysVersion
);
578 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver
,
584 ka_DelKey(struct ubik_trans
*tt
, afs_int32 tentryaddr
,
585 struct kaentry
*tentry
)
588 struct kaOldKeys okeys
; /* old keys block */
589 afs_int32 okeysaddr
, nextaddr
; /* offset of old keys block */
590 afs_int32 prevptr
= 0;
592 es_Report("DelKey for %s.%s\n", tentry
->userID
.name
,
593 tentry
->userID
.instance
);
595 /* An entry may have more than one oldkeys blocks. The entry
596 * points to the most current, but all the oldkeys blocks for an
597 * entry are not linked together. All oldkeys blocks for all
598 * entries are linked together off of the header. So we follow
601 for (okeysaddr
= ntohl(cheader
.kvnoPtr
); okeysaddr
; okeysaddr
= nextaddr
) {
602 /* foreacholdkeysblock */
603 /* Read the oldKeys block */
604 code
= karead(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
607 nextaddr
= ntohl(okeys
.next
);
609 /* We only want oldkey blocks that belong to this entry */
610 if (ntohl(okeys
.entry
) != tentryaddr
) {
611 prevptr
= DOFFSET(okeysaddr
, &okeys
, &okeys
.next
);
615 /* Delete the oldkeys block */
618 kawrite(tt
, prevptr
, (char *)&okeys
.next
, sizeof(afs_int32
));
620 code
= set_header_word(tt
, kvnoPtr
, okeys
.next
);
624 code
= FreeBlock(tt
, okeysaddr
);
627 } /* foreacholdkeysblock */
629 /* Update the tentry. We rely on caller to write it out */
630 tentry
->misc
.asServer
.oldKeys
= 0;
631 tentry
->misc
.asServer
.nOldKeys
= 0;
633 /* invalidate key caches everywhere */
634 code
= inc_header_word(tt
, specialKeysVersion
);
642 ka_debugKeyCache(struct ka_debugInfo
*info
)
646 /* cheader_lock no longer exists */
647 memset(&info
->cheader_lock
, 0, sizeof(info
->cheader_lock
));
648 memcpy(&info
->keycache_lock
, &keycache_lock
, sizeof(info
->keycache_lock
));
650 info
->kcVersion
= keyCacheVersion
;
651 info
->kcSize
= maxCachedKeys
;
653 for (i
= 0; i
< maxCachedKeys
; i
++) {
654 if (keyCache
[i
].used
) {
655 if (info
->kcUsed
< KADEBUGKCINFOSIZE
) {
656 int j
= info
->kcUsed
;
657 char principal
[sizeof(keyCache
[0].name
) +
658 sizeof(keyCache
[0].inst
)];
660 info
->kcInfo
[j
].used
= keyCache
[i
].superseded
;
661 info
->kcInfo
[j
].kvno
= keyCache
[i
].kvno
;
662 info
->kcInfo
[j
].primary
=
663 (keyCache
[i
].superseded
== NEVERDATE
);
664 info
->kcInfo
[j
].keycksum
= 0;
668 for (k
= 0; k
< sizeof(struct ktc_encryptionKey
); k
++)
669 info
->kcInfo
[j
].keycksum
+=
670 ((char *)&keyCache
[i
].key
)[k
];
673 strcpy(principal
, keyCache
[i
].name
);
674 strcat(principal
, ".");
675 strcat(principal
, keyCache
[i
].inst
);
676 strncpy(info
->kcInfo
[j
].principal
, principal
,
677 sizeof(info
->kcInfo
[0].principal
));
684 /* Add a key to the key cache, expanding it if necessary. */
687 ka_Encache(char *name
, char *inst
, afs_int32 kvno
,
688 struct ktc_encryptionKey
*key
, Date superseded
)
692 ObtainWriteLock(&keycache_lock
);
693 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
)) {
694 for (i
= 0; i
< maxCachedKeys
; i
++)
695 keyCache
[i
].used
= 0;
698 for (i
= 0; i
< maxCachedKeys
; i
++)
699 if (keyCache
[i
].used
== 0) {
701 keyCache
[i
].kvno
= kvno
;
702 strncpy(keyCache
[i
].name
, name
, sizeof(keyCache
[i
].name
));
703 strncpy(keyCache
[i
].inst
, inst
, sizeof(keyCache
[i
].inst
));
704 keyCacheVersion
= ntohl(cheader
.specialKeysVersion
);
705 memcpy(&keyCache
[i
].key
, key
, sizeof(*key
));
706 keyCache
[i
].superseded
= superseded
;
707 keyCache
[i
].used
= time(0);
709 ReleaseWriteLock(&keycache_lock
);
712 /* i == maxCachedKeys */
713 keyCache
= realloc(keyCache
, (maxCachedKeys
*=2)
714 * sizeof(struct cachedKey
));
716 es_Report("Can't realloc keyCache! out of memory?");
721 int j
= i
; /* initialize new storage */
722 while (j
< maxCachedKeys
)
723 keyCache
[j
++].used
= 0;
728 /* Look up the key given a principal and a kvno. This is called by GetTicket
729 to get the decryption key for the authenticating ticket. It is also called
730 by the rxkad security module to decrypt admin tickets. The rxkad call is
731 with tt==0, since Rx can't call Ubik. */
734 ka_LookupKvno(struct ubik_trans
*tt
, char *name
, char *inst
, afs_int32 kvno
,
735 struct ktc_encryptionKey
*key
)
740 struct kaentry tentry
;
742 struct kaOldKeys okeys
;
744 ObtainReadLock(&keycache_lock
);
745 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
))
746 code
= KAKEYCACHEINVALID
;
748 for (i
= 0; i
< maxCachedKeys
; i
++) {
749 if (keyCache
[i
].used
) { /* zero used date means invalid */
750 if ((keyCache
[i
].kvno
== kvno
)
751 && (strcmp(keyCache
[i
].name
, name
) == 0)
752 && (strcmp(keyCache
[i
].inst
, inst
) == 0)) {
753 memcpy(key
, &keyCache
[i
].key
, sizeof(*key
));
754 keyCache
[i
].used
= time(0);
755 ReleaseReadLock(&keycache_lock
);
762 ReleaseReadLock(&keycache_lock
);
766 /* we missed in the cache so need to look in the Ubik database */
767 code
= FindBlock(tt
, name
, inst
, &to
, &tentry
);
773 /* first check the current key */
774 if (tentry
.key_version
== htonl(kvno
)) {
775 memcpy(key
, &tentry
.key
, sizeof(*key
));
776 ka_Encache(name
, inst
, kvno
, key
, NEVERDATE
);
779 for (ko
= ntohl(cheader
.kvnoPtr
); ko
; ko
= ntohl(okeys
.next
)) {
780 code
= karead(tt
, ko
, (char *)&okeys
, sizeof(okeys
));
783 if (ntohl(okeys
.entry
) == to
)
784 for (i
= 0; i
< NOLDKEYS
; i
++)
785 if (okeys
.keys
[i
].superseded
786 && (ntohl(okeys
.keys
[i
].version
) == kvno
)) {
787 memcpy(key
, &okeys
.keys
[i
].key
, sizeof(*key
));
788 ka_Encache(name
, inst
, kvno
, key
,
789 ntohl(okeys
.keys
[i
].superseded
));
796 /* Look up the primary key and key version for a principal. */
799 ka_LookupKey(struct ubik_trans
*tt
,
802 afs_int32
*kvno
, /* returned */
803 struct ktc_encryptionKey
*key
) /* copied out */
807 struct kaentry tentry
;
810 ObtainReadLock(&keycache_lock
);
811 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
))
812 code
= KAKEYCACHEINVALID
;
814 for (i
= 0; i
< maxCachedKeys
; i
++) {
815 if (keyCache
[i
].used
) { /* zero used date means invalid */
816 if ((keyCache
[i
].superseded
== NEVERDATE
)
817 && (strcmp(keyCache
[i
].name
, name
) == 0)
818 && (strcmp(keyCache
[i
].inst
, inst
) == 0)) {
819 memcpy(key
, &keyCache
[i
].key
, sizeof(*key
));
820 *kvno
= keyCache
[i
].kvno
;
821 keyCache
[i
].used
= time(0);
822 ReleaseReadLock(&keycache_lock
);
829 ReleaseReadLock(&keycache_lock
);
833 /* we missed in the cache so need to look in the Ubik database */
834 code
= FindBlock(tt
, name
, inst
, &to
, &tentry
);
839 memcpy(key
, &tentry
.key
, sizeof(*key
));
840 *kvno
= ntohl(tentry
.key_version
);
841 ka_Encache(name
, inst
, *kvno
, key
, NEVERDATE
);
845 /* This is, hopefully a temporary mechanism to fill the cache will all keys
846 since filling cache misses during rxkad challenge responses will deadlock if
847 Ubik needs to use Rx. */
850 ka_FillKeyCache(struct ubik_trans
*tt
)
856 struct ktc_encryptionKey k
;
857 struct kaOldKeys okeys
;
858 struct kaentry tentry
;
860 /* this is a little marginal, but... */
861 if (keyCacheVersion
== ntohl(cheader
.specialKeysVersion
))
865 for (ko
= ntohl(cheader
.kvnoPtr
); ko
; ko
= ntohl(okeys
.next
)) {
866 code
= karead(tt
, ko
, (char *)&okeys
, sizeof(okeys
));
869 /* get name & instance */
871 karead(tt
, ntohl(okeys
.entry
), (char *)&tentry
, sizeof(tentry
));
875 /* get all the old keys in this block */
876 for (i
= 0; i
< NOLDKEYS
; i
++)
877 if (okeys
.keys
[i
].superseded
) {
879 ka_LookupKvno(tt
, tentry
.userID
.name
,
880 tentry
.userID
.instance
,
881 ntohl(okeys
.keys
[i
].version
), &k
);
886 if (++nfound
> maxCachedKeys
)
887 return KADATABASEINCONSISTENT
;
892 update_admin_count(struct ubik_trans
*tt
, int delta
)
897 cheader
.admin_accounts
= htonl(ntohl(cheader
.admin_accounts
) + delta
);
898 to
= DOFFSET(0, &cheader
, &cheader
.admin_accounts
);
900 kawrite(tt
, to
, (char *)&cheader
.admin_accounts
, sizeof(afs_int32
));
907 index_OK(afs_int32 index
)
909 if ((index
< sizeof(cheader
)) || (index
>= ntohl(cheader
.eofPtr
))
910 || ((index
- sizeof(cheader
)) % sizeof(kaentry
) != 0))
915 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
918 name_instance_legal(char *name
, char *instance
)
922 /* No string checks apply anymore. The international people want to use full 8
923 bit ascii without problems. */
925 code
= (strlen(name
) < MAXKTCNAMELEN
)
926 && (strlen(instance
) < MAXKTCNAMELEN
);
928 map
= LEGALCHARS
; /* permitted chars, instance allows <period> */
929 code
= (strlen(name
) > 0) && string_legal(instance
, map
)
930 && string_legal(name
, map
+ 1);
933 dynamic_statistics
.string_checks
++;
939 string_legal(char *str
, char *map
)
944 if (slen
>= MAXKTCNAMELEN
)
945 return 0; /* with trailing null must fit in data base */
946 return (slen
== strspn(str
, map
)); /* strspn returns length(str) if all chars in map */