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>
15 #include <sys/types.h>
19 #include <netinet/in.h>
25 #include <afs/afsutil.h>
30 extern struct kaheader cheader;
31 extern Date cheaderReadTime; /* time cheader last read in */
32 extern struct ubik_dbase *KA_dbase;
34 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
36 #define inc_header_word(tt,field) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (htonl(ntohl(cheader.field)+1))), (char *)&(cheader.field)), sizeof(afs_int32))
38 static int index_OK();
40 afs_int32 NameHash (aname, ainstance)
42 register char *ainstance;
43 { register unsigned int hash;
46 /* stolen directly from the HashString function in the vol package */
48 for (i=strlen(aname), aname += i-1; i--; aname--)
49 hash = (hash*31) + (*((unsigned char *)aname) - 31);
50 for (i=strlen(ainstance), ainstance += i-1; i--; ainstance--)
51 hash = (hash*31) + (*((unsigned char *)ainstance) - 31);
52 return(hash % HASHSIZE);
55 /* package up seek and write into one procedure for ease of use */
57 afs_int32 kawrite (tt, pos, buff, len)
58 struct ubik_trans *tt;
64 code = ubik_Seek(tt, 0, pos);
65 if (code) return code;
66 code = ubik_Write(tt,buff,len);
70 /* same thing for read */
72 afs_int32 karead (tt, pos, buff, len)
73 struct ubik_trans *tt;
79 code = ubik_Seek(tt, 0, pos);
80 if (code) return code;
81 code = ubik_Read(tt, buff, len);
85 static struct Lock cheader_lock;
86 static struct Lock keycache_lock;
88 static int maxCachedKeys;
90 static struct cachedKey {
92 int superseded; /* NEVERDATE => this is current key */
94 struct ktc_encryptionKey key;
95 char name[MAXKTCNAMELEN];
96 char inst[MAXKTCNAMELEN];
98 static afs_int32 keyCacheVersion = 0;
100 static afs_int32 maxKeyLifetime;
101 static int dbfixup = 0;
103 init_kadatabase (initFlags)
104 int initFlags; /* same as init_kaprocs (see which) */
106 Lock_Init (&cheader_lock);
107 Lock_Init (&keycache_lock);
110 keyCache = (struct cachedKey *)malloc (maxCachedKeys * sizeof(struct cachedKey));
115 maxKeyLifetime = MAXKTCTICKETLIFETIME;
117 if (initFlags & 8) dbfixup++;
120 /* check that the database has been initialized. Be careful to fail in a safe
121 manner, to avoid bogusly reinitializing the db. */
123 afs_int32 CheckInit (at, db_init)
124 struct ubik_trans *at;
125 int (*db_init)(); /* procedure to call if rebuilding DB */
126 { register afs_int32 code;
130 /* Don't read header if not necessary */
131 if (!ubik_CacheUpdate (at)) return 0;
133 ObtainWriteLock (&cheader_lock);
134 if ((code = karead (at, 0, (char *) &iversion, sizeof(iversion))) ||
135 (code = karead (at, sizeof(cheader)-sizeof(afs_int32),
136 (char *) &tversion, sizeof(afs_int32)))) {
137 if (code == UEOF) printf ("No data base\n");
138 else printf ("I/O Error\n");
140 iversion = ntohl(iversion); /* convert to host order */
141 tversion = ntohl(tversion);
142 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
143 code = karead(at, 0, (char *) &cheader, sizeof(cheader));
145 printf ("SetupHeader failed\n");
148 cheaderReadTime = time(0);
151 printf ("DB version should be %d; Initial = %d; Terminal = %d\n",
152 KADBVERSION, iversion, tversion);
156 ReleaseWriteLock (&cheader_lock);
157 if (code == 0) return 0;
159 /* if here, we have no version number or the wrong version number in the
161 if ((code == UEOF) || ((iversion == 0) && (tversion == 0))) code = KAEMPTY;
164 if ((db_init == 0) || (code == KAIO)) return code;
166 printf ("Error discovered in header, rebuilding.\n");
168 /* try to write a good header */
169 memset(&cheader, 0, sizeof(cheader));
170 cheader.version = htonl(KADBVERSION);
171 cheader.checkVersion = htonl(KADBVERSION);
172 cheader.headerSize = htonl(sizeof(cheader));
174 cheader.eofPtr = htonl(sizeof(cheader));
176 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
177 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
178 cheader.admin_accounts = 0;
179 cheader.hashsize = htonl(HASHSIZE);
180 code = kawrite(at, 0, (char *) &cheader, sizeof(cheader));
181 if (code) return KAIO; /* return the error code */
183 return db_init (at); /* initialize the db */
186 /* Allocate a free block of storage for entry, returning address of a new
187 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
189 afs_int32 AllocBlock (at, tentry)
190 register struct ubik_trans *at;
191 struct kaentry *tentry;
192 { register afs_int32 code;
195 if (cheader.freePtr) {
196 /* allocate this dude */
197 temp = ntohl(cheader.freePtr);
198 code = karead(at, temp, (char *) tentry, sizeof(kaentry));
199 if (code) return 0; /* can't read block */
200 code = set_header_word (at, freePtr, tentry->next);
203 /* hosed, nothing on free list, grow file */
204 temp = ntohl(cheader.eofPtr); /* remember this guy */
205 code = set_header_word (at, eofPtr, htonl(temp+sizeof(kaentry)));
209 code = inc_header_word (at, stats.allocs);
211 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
215 /* Free a block given its index. It must already have been unthreaded.
216 Returns zero for success or an error code on failure. */
218 afs_int32 FreeBlock (at, index)
219 struct ubik_trans *at;
221 { struct kaentry tentry;
224 /* check index just to be on the safe side */
225 if (!index_OK (index)) return KABADINDEX;
227 memset(&tentry, 0, sizeof(kaentry));
228 tentry.next = cheader.freePtr;
229 tentry.flags = htonl(KAFFREE);
230 code = set_header_word (at, freePtr, htonl(index));
231 if (code) return KAIO;
232 code = kawrite (at, index, (char *)&tentry, sizeof(kaentry));
233 if (code) return KAIO;
235 code = inc_header_word (at, stats.frees);
236 if (code) return KAIO;
240 /* Look for a block by name and instance. If found read the block's contents
241 into the area pointed to by tentry and return the block's index. If not
242 found offset is set to zero. If an error is encountered a non-zero code is
245 afs_int32 FindBlock (at, aname, ainstance, toP, tentry)
246 struct ubik_trans *at;
250 struct kaentry *tentry;
252 register afs_int32 i, code;
253 register afs_int32 to;
256 i = NameHash(aname, ainstance);
257 for (to = ntohl(cheader.nameHash[i]); to != NULLO; to = ntohl(tentry->next)) {
258 code = karead(at, to, (char *) tentry, sizeof(kaentry));
259 if (code) return code;
260 /* see if the name matches */
261 if (!strcmp(aname, tentry->userID.name) &&
262 (ainstance == (char *) 0 || !strcmp(ainstance, tentry->userID.instance))) {
263 *toP = to; /* found it */
267 *toP = 0; /* no such entry */
271 /* Add a block to the hash table given a pointer to the block and its index.
272 The block is threaded onto the hash table and written to disk. The routine
273 returns zero if there were no errors. */
275 afs_int32 ThreadBlock (at, index, tentry)
276 struct ubik_trans *at;
278 struct kaentry *tentry;
280 int hi; /* hash index */
282 if (!index_OK(index)) return KABADINDEX;
283 hi = NameHash (tentry->userID.name, tentry->userID.instance);
284 tentry->next = cheader.nameHash[hi];
285 code = set_header_word (at, nameHash[hi], htonl(index));
286 if (code) return KAIO;
287 code = kawrite (at, index, (char *)tentry, sizeof(kaentry));
288 if (code) return KAIO;
292 /* Remove a block from the hash table. If success return 0, else return an
295 afs_int32 UnthreadBlock (at, aentry)
296 struct ubik_trans *at;
297 struct kaentry *aentry;
299 register afs_int32 i, code;
300 register afs_int32 to;
302 struct kaentry tentry;
304 i = NameHash (aentry->userID.name, aentry->userID.instance);
306 for (to = ntohl(cheader.nameHash[i]); to != NULLO; to = ntohl(tentry.next)) {
307 code = karead(at, to, (char *) &tentry, sizeof(kaentry));
308 if (code) return KAIO;
309 /* see if the name matches */
310 if (!strcmp(aentry->userID.name, tentry.userID.name) &&
311 !strcmp(aentry->userID.instance, tentry.userID.instance)) {
313 if (lo) { /* unthread from last block */
314 code = kawrite (at, lo, (char *)&tentry.next, sizeof(afs_int32));
315 if (code) return KAIO;
317 else { /* unthread from hash table */
318 code = set_header_word (at, nameHash[i], tentry.next);
319 if (code) return KAIO;
321 aentry->next = 0; /* just to be sure */
324 lo = DOFFSET(to, &tentry, &tentry.next);
329 /* Given an index to the last block (or zero the first time) read the contents
330 of the next block and return its index. The last argument is a pointer to
331 an estimate of the number of remaining blocks to read out. The remaining
332 count is an estimate because it may include free blocks that are not
333 returned. If there are no more blocks remaining is zero and the returned
334 index is zero. A non-zero index indicates that tentry has been filled with
335 valid data. If an error is encountered the returned index is zero and the
336 remaining count is negative. */
338 afs_int32 NextBlock (at, index, tentry, remaining)
339 struct ubik_trans *at;
341 struct kaentry *tentry;
342 afs_int32 *remaining;
346 if (index == 0) /* get first one */
347 index = sizeof(cheader);
349 if (!index_OK (index)) {
350 *remaining = -1; /* error */
353 index += sizeof(kaentry);
355 /* now search for the first entry that isn't free */
356 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
357 code = karead (at, index, (char *)tentry, sizeof(kaentry));
362 if (!(ntohl(tentry->flags) & (KAFFREE|KAFOLDKEYS))) {
363 /* estimate remaining number of entries, not including this one */
364 *remaining = (last - index) / sizeof(kaentry) - 1;
368 *remaining = 0; /* no more entries */
372 /* These are a collections of routines that deal with externally known keys.
373 They maintain a database of key version numbers and the corresponding key
374 and pointer to the user entry. */
376 afs_int32 ka_NewKey (tt, tentryaddr, tentry, key)
377 struct ubik_trans *tt;
378 afs_int32 tentryaddr;
379 struct kaentry *tentry;
380 struct ktc_encryptionKey *key;
382 struct kaOldKeys okeys; /* old keys block */
383 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
384 afs_int32 prevptr, nextprevptr;
387 afs_int32 newkeyver; /* new key version number */
388 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
389 int foundcurrentkey = 0, addednewkey = 0, modified;
391 es_Report ("Newkey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
393 newkeyver = ntohl(tentry->key_version) + 1;
394 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
397 /* An entry may have more than one oldkeys blocks. The entry
398 * points to the most current, but all the oldkeys blocks for an
399 * entry are not linked together. All oldkeys blocks for all
400 * entries are linked together off of the header. So we follow
403 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr);
405 prevptr = nextprevptr, okeysaddr = nextaddr) {
406 /* foreacholdkeysblock */
407 /* Read the oldKeys block */
408 code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
409 if (code) return code;
411 nextaddr = ntohl(okeys.next);
412 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
414 /* We only want oldkey blocks that belong to this entry */
415 if (ntohl(okeys.entry) != tentryaddr)
418 modified = 0; /* This oldkeys block has not been modified */
419 keyentries = 0; /* Number of valid key entries in the block */
420 for (i=0; i<NOLDKEYS; i++) {
422 /* Keep count of number of entries found */
423 if (okeys.keys[i].superseded != 0) {
424 oldtotalkeyentries++;
427 /* If we find the entry that is not superseded, then supersede it */
428 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
429 okeys.keys[i].superseded = htonl(now);
432 if (foundcurrentkey) {
433 ViceLog(0,("Warning: Entry %s.%s contains more than one valid key: fixing\n",
434 tentry->userID.name, tentry->userID.instance));
440 /* If we find an oldkey of the same version or
441 * an old key that has expired, then delete it.
443 if ( (ntohl(okeys.keys[i].version) == newkeyver) ||
444 ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime)) ) {
445 okeys.keys[i].superseded = 0;
446 okeys.keys[i].version = htonl(-1);
447 memset(&okeys.keys[i].key, 0, sizeof(struct ktc_encryptionKey));
450 es_Report ("Dropped oldkey %d seconds old with kvno %d\n",
451 now - ntohl(okeys.keys[i].superseded),
452 ntohl(okeys.keys[i].version));
455 /* Add our key here if its free */
456 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
457 okeys.keys[i].version = htonl(newkeyver);
458 okeys.keys[i].superseded = htonl(NEVERDATE);
459 memcpy(&okeys.keys[i].key, key, sizeof(struct ktc_encryptionKey));
461 addednewkey = okeysaddr;
464 /* Keep count of number of entries found */
465 if (okeys.keys[i].superseded != 0) {
467 newtotalkeyentries++;
471 /* If we modified the block, write it out */
472 if (modified && keyentries) {
473 code = kawrite (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
474 if (code) return code;
477 /* If there are no more entries in this oldkeys block, delete it */
478 if (keyentries == 0) {
480 code = set_header_word(tt, kvnoPtr, okeys.next);
482 code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
484 if (code) return code;
485 code = FreeBlock (tt, okeysaddr);
486 if (code) return code;
488 nextprevptr = prevptr; /* won't bump prevptr */
490 } /* foreacholdkeysblock */
492 /* If we could not add the key, create a new oldkeys block */
494 /* Allocate and fill in an oldkeys block */
495 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
496 if (!addednewkey) return KACREATEFAIL;
497 okeys.flags = htonl(KAFOLDKEYS);
498 okeys.entry = htonl(tentryaddr);
499 okeys.keys[0].version = htonl(newkeyver);
500 okeys.keys[0].superseded = htonl(NEVERDATE);
501 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
502 newtotalkeyentries++;
504 /* Thread onto the header's chain of oldkeys */
505 okeys.next = cheader.kvnoPtr;
506 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
507 if (code) return code;
509 /* Write the oldkeys block out */
510 code = kawrite (tt, addednewkey, (char *)&okeys, sizeof(okeys));
511 if (code) return code;
513 es_Report ("New oldkey block allocated at %d\n", addednewkey);
517 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
518 ViceLog(0,("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
519 tentry->userID.name, tentry->userID.instance,
520 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
524 /* Update the tentry. We rely on caller to write it out */
525 tentry->misc.asServer.oldKeys = htonl(addednewkey);
526 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
527 tentry->key_version = htonl(newkeyver);
528 memcpy(&tentry->key, key, sizeof (tentry->key));
530 /* invalidate key caches everywhere */
531 code = inc_header_word (tt, specialKeysVersion);
532 if (code) return code;
534 es_Report ("New kvno is %d, now are %d oldkeys\n",
535 newkeyver, newtotalkeyentries);
539 afs_int32 ka_DelKey (tt, tentryaddr, tentry)
540 struct ubik_trans *tt;
541 afs_int32 tentryaddr;
542 struct kaentry *tentry;
545 struct kaOldKeys okeys; /* old keys block */
546 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
547 afs_int32 prevptr = 0;
550 es_Report ("DelKey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
552 /* An entry may have more than one oldkeys blocks. The entry
553 * points to the most current, but all the oldkeys blocks for an
554 * entry are not linked together. All oldkeys blocks for all
555 * entries are linked together off of the header. So we follow
558 for (okeysaddr=ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr=nextaddr) {
559 /* foreacholdkeysblock */
560 /* Read the oldKeys block */
561 code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
562 if (code) return code;
563 nextaddr = ntohl(okeys.next);
565 /* We only want oldkey blocks that belong to this entry */
566 if (ntohl(okeys.entry) != tentryaddr) {
567 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
571 /* Delete the oldkeys block */
573 code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
575 code = set_header_word(tt, kvnoPtr, okeys.next);
577 if (code) return code;
578 code = FreeBlock (tt, okeysaddr);
579 if (code) return code;
580 } /* foreacholdkeysblock */
582 /* Update the tentry. We rely on caller to write it out */
583 tentry->misc.asServer.oldKeys = 0;
584 tentry->misc.asServer.nOldKeys = 0;
586 /* invalidate key caches everywhere */
587 code = inc_header_word (tt, specialKeysVersion);
588 if (code) return code;
593 void ka_debugKeyCache (info)
594 struct ka_debugInfo *info;
597 memcpy(&info->cheader_lock, &cheader_lock, sizeof (info->cheader_lock));
598 memcpy(&info->keycache_lock, &keycache_lock, sizeof (info->keycache_lock));
600 info->kcVersion = keyCacheVersion;
601 info->kcSize = maxCachedKeys;
603 for (i=0; i<maxCachedKeys; i++) {
604 if (keyCache[i].used) {
605 if (info->kcUsed < KADEBUGKCINFOSIZE) {
606 int j = info->kcUsed;
608 char principal[sizeof(keyCache[0].name) + sizeof(keyCache[0].inst)];
610 info->kcInfo[j].used = keyCache[i].superseded;
611 info->kcInfo[j].kvno = keyCache[i].kvno;
612 info->kcInfo[j].primary = (keyCache[i].superseded == NEVERDATE);
613 info->kcInfo[j].keycksum = 0;
615 for (k=0; k<sizeof(struct ktc_encryptionKey); k++)
616 info->kcInfo[j].keycksum += ((char *)&keyCache[i].key)[k];
618 strcpy (principal, keyCache[i].name);
619 strcat (principal, ".");
620 strcat (principal, keyCache[i].inst);
621 strncpy (info->kcInfo[j].principal, principal,
622 sizeof (info->kcInfo[0].principal));
629 /* Add a key to the key cache, expanding it if necessary. */
631 ka_Encache (name, inst, kvno, key, superseded)
635 struct ktc_encryptionKey *key;
639 ObtainWriteLock (&keycache_lock);
640 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
641 for (i=0; i<maxCachedKeys; i++)
642 keyCache[i].used = 0;
645 for (i=0; i<maxCachedKeys; i++)
646 if (keyCache[i].used == 0) {
648 keyCache[i].kvno = kvno;
649 strncpy (keyCache[i].name, name, sizeof (keyCache[i].name));
650 strncpy (keyCache[i].inst, inst, sizeof (keyCache[i].inst));
651 keyCacheVersion = ntohl(cheader.specialKeysVersion);
652 memcpy(&keyCache[i].key, key, sizeof(*key));
653 keyCache[i].superseded = superseded;
654 keyCache[i].used = time(0);
656 ReleaseWriteLock (&keycache_lock);
659 /* i == maxCachedKeys */
660 keyCache = (struct cachedKey *)realloc
661 (keyCache, (maxCachedKeys*=2) * sizeof(struct cachedKey));
663 es_Report ("Can't realloc keyCache! out of memory?");
667 { int j = i; /* initialize new storage */
668 while (j<maxCachedKeys) keyCache[j++].used = 0;
673 /* Look up the key given a principal and a kvno. This is called by GetTicket
674 to get the decryption key for the authenticating ticket. It is also called
675 by the rxkad security module to decrypt admin tickets. The rxkad call is
676 with tt==0, since Rx can't call Ubik. */
678 afs_int32 ka_LookupKvno (tt, name, inst, kvno, key)
679 struct ubik_trans *tt;
683 struct ktc_encryptionKey *key;
687 struct kaentry tentry;
689 struct kaOldKeys okeys;
691 ObtainReadLock (&keycache_lock);
692 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
693 code = KAKEYCACHEINVALID;
695 for (i=0; i<maxCachedKeys; i++) {
696 if (keyCache[i].used) { /* zero used date means invalid */
697 if ((keyCache[i].kvno == kvno) &&
698 (strcmp(keyCache[i].name, name) == 0) &&
699 (strcmp(keyCache[i].inst, inst) == 0)) {
700 memcpy(key, &keyCache[i].key, sizeof(*key));
701 keyCache[i].used = time(0);
702 ReleaseReadLock (&keycache_lock);
709 ReleaseReadLock (&keycache_lock);
713 /* we missed in the cache so need to look in the Ubik database */
714 code = FindBlock (tt, name, inst, &to, &tentry);
715 if (code) return code;
716 if (to == 0) return KANOENT;
718 /* first check the current key */
719 if (tentry.key_version == htonl(kvno)) {
720 memcpy(key, &tentry.key, sizeof(*key));
721 ka_Encache (name, inst, kvno, key, NEVERDATE);
724 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
725 code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
726 if (code) return KAIO;
727 if (ntohl(okeys.entry) == to)
728 for (i=0; i<NOLDKEYS; i++)
729 if (okeys.keys[i].superseded &&
730 (ntohl(okeys.keys[i].version) == kvno)) {
731 memcpy(key, &okeys.keys[i].key, sizeof(*key));
732 ka_Encache (name, inst, kvno, key,
733 ntohl(okeys.keys[i].superseded));
740 /* Look up the primary key and key version for a principal. */
742 afs_int32 ka_LookupKey (tt, name, inst, kvno, key)
743 struct ubik_trans *tt;
746 afs_int32 *kvno; /* returned */
747 struct ktc_encryptionKey *key; /* copied out */
750 struct kaentry tentry;
753 ObtainReadLock (&keycache_lock);
754 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
755 code = KAKEYCACHEINVALID;
757 for (i=0; i<maxCachedKeys; i++) {
758 if (keyCache[i].used) { /* zero used date means invalid */
759 if ((keyCache[i].superseded == NEVERDATE) &&
760 (strcmp(keyCache[i].name, name) == 0) &&
761 (strcmp(keyCache[i].inst, inst) == 0)){
762 memcpy(key, &keyCache[i].key, sizeof(*key));
763 *kvno = keyCache[i].kvno;
764 keyCache[i].used = time(0);
765 ReleaseReadLock (&keycache_lock);
772 ReleaseReadLock (&keycache_lock);
776 /* we missed in the cache so need to look in the Ubik database */
777 code = FindBlock (tt, name, inst, &to, &tentry);
778 if (code) return code;
779 if (to == 0) return KANOENT;
780 memcpy(key, &tentry.key, sizeof(*key));
781 *kvno = ntohl(tentry.key_version);
782 ka_Encache (name, inst, *kvno, key, NEVERDATE);
786 /* This is, hopefully a temporary mechanism to fill the cache will all keys
787 since filling cache misses during rxkad challenge responses will deadlock if
788 Ubik needs to use Rx. */
790 afs_int32 ka_FillKeyCache (tt)
791 struct ubik_trans *tt;
796 struct ktc_encryptionKey k;
797 struct kaOldKeys okeys;
798 struct kaentry tentry;
800 /* this is a little marginal, but... */
801 if (keyCacheVersion == ntohl(cheader.specialKeysVersion)) return 0;
804 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
805 code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
806 if (code) return KAIO;
807 /* get name & instance */
808 code = karead (tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
809 if (code) return KAIO;
811 /* get all the old keys in this block */
812 for (i=0; i<NOLDKEYS; i++)
813 if (okeys.keys[i].superseded) {
814 code = ka_LookupKvno (tt, tentry.userID.name, tentry.userID.instance,
815 ntohl(okeys.keys[i].version), &k);
816 if (code) return code;
819 if (++nfound > maxCachedKeys) return KADATABASEINCONSISTENT;
823 afs_int32 update_admin_count (tt, delta)
824 struct ubik_trans *tt;
829 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
830 to = DOFFSET (0, &cheader, &cheader.admin_accounts);
831 code = kawrite (tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
832 if (code) return KAIO;
836 static int index_OK (index)
839 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr)) ||
840 ((index - sizeof(cheader)) % sizeof(kaentry) != 0)) return 0;
844 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
846 int name_instance_legal (name, instance)
851 /* No string checks apply anymore. The international people want to use full 8
852 bit ascii without problems. */
854 code = (strlen(name) < MAXKTCNAMELEN) && (strlen(instance) < MAXKTCNAMELEN);
856 map = LEGALCHARS; /* permitted chars, instance allows <period> */
857 code = (strlen (name) > 0) && string_legal (instance, map) && string_legal (name, map+1);
859 if (!code) dynamic_statistics.string_checks++;
863 static int string_legal (str, map)
869 if (slen >= MAXKTCNAMELEN) return 0; /* with trailing null must fit in data base */
870 return (slen == strspn (str, map)); /* strspn returns length(str) if all chars in map */