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>
32 #include <afs/afsutil.h>
37 extern struct kaheader cheader;
38 extern Date cheaderReadTime; /* time cheader last read in */
39 extern struct ubik_dbase *KA_dbase;
41 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
43 #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))
45 static int index_OK();
47 afs_int32 NameHash (aname, ainstance)
49 register char *ainstance;
50 { register unsigned int hash;
53 /* stolen directly from the HashString function in the vol package */
55 for (i=strlen(aname), aname += i-1; i--; aname--)
56 hash = (hash*31) + (*((unsigned char *)aname) - 31);
57 for (i=strlen(ainstance), ainstance += i-1; i--; ainstance--)
58 hash = (hash*31) + (*((unsigned char *)ainstance) - 31);
59 return(hash % HASHSIZE);
62 /* package up seek and write into one procedure for ease of use */
64 afs_int32 kawrite (tt, pos, buff, len)
65 struct ubik_trans *tt;
71 code = ubik_Seek(tt, 0, pos);
72 if (code) return code;
73 code = ubik_Write(tt,buff,len);
77 /* same thing for read */
79 afs_int32 karead (tt, pos, buff, len)
80 struct ubik_trans *tt;
86 code = ubik_Seek(tt, 0, pos);
87 if (code) return code;
88 code = ubik_Read(tt, buff, len);
92 static struct Lock cheader_lock;
93 static struct Lock keycache_lock;
95 static int maxCachedKeys;
97 static struct cachedKey {
99 int superseded; /* NEVERDATE => this is current key */
101 struct ktc_encryptionKey key;
102 char name[MAXKTCNAMELEN];
103 char inst[MAXKTCNAMELEN];
105 static afs_int32 keyCacheVersion = 0;
107 static afs_int32 maxKeyLifetime;
108 static int dbfixup = 0;
110 init_kadatabase (initFlags)
111 int initFlags; /* same as init_kaprocs (see which) */
113 Lock_Init (&cheader_lock);
114 Lock_Init (&keycache_lock);
117 keyCache = (struct cachedKey *)malloc (maxCachedKeys * sizeof(struct cachedKey));
122 maxKeyLifetime = MAXKTCTICKETLIFETIME;
124 if (initFlags & 8) dbfixup++;
127 /* check that the database has been initialized. Be careful to fail in a safe
128 manner, to avoid bogusly reinitializing the db. */
130 afs_int32 CheckInit (at, db_init)
131 struct ubik_trans *at;
132 int (*db_init)(); /* procedure to call if rebuilding DB */
133 { register afs_int32 code;
137 /* Don't read header if not necessary */
138 if (!ubik_CacheUpdate (at)) return 0;
140 ObtainWriteLock (&cheader_lock);
141 if ((code = karead (at, 0, (char *) &iversion, sizeof(iversion))) ||
142 (code = karead (at, sizeof(cheader)-sizeof(afs_int32),
143 (char *) &tversion, sizeof(afs_int32)))) {
144 if (code == UEOF) printf ("No data base\n");
145 else printf ("I/O Error\n");
147 iversion = ntohl(iversion); /* convert to host order */
148 tversion = ntohl(tversion);
149 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
150 code = karead(at, 0, (char *) &cheader, sizeof(cheader));
152 printf ("SetupHeader failed\n");
155 cheaderReadTime = time(0);
158 printf ("DB version should be %d; Initial = %d; Terminal = %d\n",
159 KADBVERSION, iversion, tversion);
163 ReleaseWriteLock (&cheader_lock);
164 if (code == 0) return 0;
166 /* if here, we have no version number or the wrong version number in the
168 if ((code == UEOF) || ((iversion == 0) && (tversion == 0))) code = KAEMPTY;
171 if ((db_init == 0) || (code == KAIO)) return code;
173 printf ("Error discovered in header, rebuilding.\n");
175 /* try to write a good header */
176 memset(&cheader, 0, sizeof(cheader));
177 cheader.version = htonl(KADBVERSION);
178 cheader.checkVersion = htonl(KADBVERSION);
179 cheader.headerSize = htonl(sizeof(cheader));
181 cheader.eofPtr = htonl(sizeof(cheader));
183 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
184 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
185 cheader.admin_accounts = 0;
186 cheader.hashsize = htonl(HASHSIZE);
187 code = kawrite(at, 0, (char *) &cheader, sizeof(cheader));
188 if (code) return KAIO; /* return the error code */
190 return db_init (at); /* initialize the db */
193 /* Allocate a free block of storage for entry, returning address of a new
194 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
196 afs_int32 AllocBlock (at, tentry)
197 register struct ubik_trans *at;
198 struct kaentry *tentry;
199 { register afs_int32 code;
202 if (cheader.freePtr) {
203 /* allocate this dude */
204 temp = ntohl(cheader.freePtr);
205 code = karead(at, temp, (char *) tentry, sizeof(kaentry));
206 if (code) return 0; /* can't read block */
207 code = set_header_word (at, freePtr, tentry->next);
210 /* hosed, nothing on free list, grow file */
211 temp = ntohl(cheader.eofPtr); /* remember this guy */
212 code = set_header_word (at, eofPtr, htonl(temp+sizeof(kaentry)));
216 code = inc_header_word (at, stats.allocs);
218 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
222 /* Free a block given its index. It must already have been unthreaded.
223 Returns zero for success or an error code on failure. */
225 afs_int32 FreeBlock (at, index)
226 struct ubik_trans *at;
228 { struct kaentry tentry;
231 /* check index just to be on the safe side */
232 if (!index_OK (index)) return KABADINDEX;
234 memset(&tentry, 0, sizeof(kaentry));
235 tentry.next = cheader.freePtr;
236 tentry.flags = htonl(KAFFREE);
237 code = set_header_word (at, freePtr, htonl(index));
238 if (code) return KAIO;
239 code = kawrite (at, index, (char *)&tentry, sizeof(kaentry));
240 if (code) return KAIO;
242 code = inc_header_word (at, stats.frees);
243 if (code) return KAIO;
247 /* Look for a block by name and instance. If found read the block's contents
248 into the area pointed to by tentry and return the block's index. If not
249 found offset is set to zero. If an error is encountered a non-zero code is
252 afs_int32 FindBlock (at, aname, ainstance, toP, tentry)
253 struct ubik_trans *at;
257 struct kaentry *tentry;
259 register afs_int32 i, code;
260 register afs_int32 to;
263 i = NameHash(aname, ainstance);
264 for (to = ntohl(cheader.nameHash[i]); to != NULLO; to = ntohl(tentry->next)) {
265 code = karead(at, to, (char *) tentry, sizeof(kaentry));
266 if (code) return code;
267 /* see if the name matches */
268 if (!strcmp(aname, tentry->userID.name) &&
269 (ainstance == (char *) 0 || !strcmp(ainstance, tentry->userID.instance))) {
270 *toP = to; /* found it */
274 *toP = 0; /* no such entry */
278 /* Add a block to the hash table given a pointer to the block and its index.
279 The block is threaded onto the hash table and written to disk. The routine
280 returns zero if there were no errors. */
282 afs_int32 ThreadBlock (at, index, tentry)
283 struct ubik_trans *at;
285 struct kaentry *tentry;
287 int hi; /* hash index */
289 if (!index_OK(index)) return KABADINDEX;
290 hi = NameHash (tentry->userID.name, tentry->userID.instance);
291 tentry->next = cheader.nameHash[hi];
292 code = set_header_word (at, nameHash[hi], htonl(index));
293 if (code) return KAIO;
294 code = kawrite (at, index, (char *)tentry, sizeof(kaentry));
295 if (code) return KAIO;
299 /* Remove a block from the hash table. If success return 0, else return an
302 afs_int32 UnthreadBlock (at, aentry)
303 struct ubik_trans *at;
304 struct kaentry *aentry;
306 register afs_int32 i, code;
307 register afs_int32 to;
309 struct kaentry tentry;
311 i = NameHash (aentry->userID.name, aentry->userID.instance);
313 for (to = ntohl(cheader.nameHash[i]); to != NULLO; to = ntohl(tentry.next)) {
314 code = karead(at, to, (char *) &tentry, sizeof(kaentry));
315 if (code) return KAIO;
316 /* see if the name matches */
317 if (!strcmp(aentry->userID.name, tentry.userID.name) &&
318 !strcmp(aentry->userID.instance, tentry.userID.instance)) {
320 if (lo) { /* unthread from last block */
321 code = kawrite (at, lo, (char *)&tentry.next, sizeof(afs_int32));
322 if (code) return KAIO;
324 else { /* unthread from hash table */
325 code = set_header_word (at, nameHash[i], tentry.next);
326 if (code) return KAIO;
328 aentry->next = 0; /* just to be sure */
331 lo = DOFFSET(to, &tentry, &tentry.next);
336 /* Given an index to the last block (or zero the first time) read the contents
337 of the next block and return its index. The last argument is a pointer to
338 an estimate of the number of remaining blocks to read out. The remaining
339 count is an estimate because it may include free blocks that are not
340 returned. If there are no more blocks remaining is zero and the returned
341 index is zero. A non-zero index indicates that tentry has been filled with
342 valid data. If an error is encountered the returned index is zero and the
343 remaining count is negative. */
345 afs_int32 NextBlock (at, index, tentry, remaining)
346 struct ubik_trans *at;
348 struct kaentry *tentry;
349 afs_int32 *remaining;
353 if (index == 0) /* get first one */
354 index = sizeof(cheader);
356 if (!index_OK (index)) {
357 *remaining = -1; /* error */
360 index += sizeof(kaentry);
362 /* now search for the first entry that isn't free */
363 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
364 code = karead (at, index, (char *)tentry, sizeof(kaentry));
369 if (!(ntohl(tentry->flags) & (KAFFREE|KAFOLDKEYS))) {
370 /* estimate remaining number of entries, not including this one */
371 *remaining = (last - index) / sizeof(kaentry) - 1;
375 *remaining = 0; /* no more entries */
379 /* These are a collections of routines that deal with externally known keys.
380 They maintain a database of key version numbers and the corresponding key
381 and pointer to the user entry. */
383 afs_int32 ka_NewKey (tt, tentryaddr, tentry, key)
384 struct ubik_trans *tt;
385 afs_int32 tentryaddr;
386 struct kaentry *tentry;
387 struct ktc_encryptionKey *key;
389 struct kaOldKeys okeys; /* old keys block */
390 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
391 afs_int32 prevptr, nextprevptr;
394 afs_int32 newkeyver; /* new key version number */
395 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
396 int foundcurrentkey = 0, addednewkey = 0, modified;
398 es_Report ("Newkey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
400 newkeyver = ntohl(tentry->key_version) + 1;
401 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
404 /* An entry may have more than one oldkeys blocks. The entry
405 * points to the most current, but all the oldkeys blocks for an
406 * entry are not linked together. All oldkeys blocks for all
407 * entries are linked together off of the header. So we follow
410 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr);
412 prevptr = nextprevptr, okeysaddr = nextaddr) {
413 /* foreacholdkeysblock */
414 /* Read the oldKeys block */
415 code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
416 if (code) return code;
418 nextaddr = ntohl(okeys.next);
419 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
421 /* We only want oldkey blocks that belong to this entry */
422 if (ntohl(okeys.entry) != tentryaddr)
425 modified = 0; /* This oldkeys block has not been modified */
426 keyentries = 0; /* Number of valid key entries in the block */
427 for (i=0; i<NOLDKEYS; i++) {
429 /* Keep count of number of entries found */
430 if (okeys.keys[i].superseded != 0) {
431 oldtotalkeyentries++;
434 /* If we find the entry that is not superseded, then supersede it */
435 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
436 okeys.keys[i].superseded = htonl(now);
439 if (foundcurrentkey) {
440 ViceLog(0,("Warning: Entry %s.%s contains more than one valid key: fixing\n",
441 tentry->userID.name, tentry->userID.instance));
447 /* If we find an oldkey of the same version or
448 * an old key that has expired, then delete it.
450 if ( (ntohl(okeys.keys[i].version) == newkeyver) ||
451 ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime)) ) {
452 okeys.keys[i].superseded = 0;
453 okeys.keys[i].version = htonl(-1);
454 memset(&okeys.keys[i].key, 0, sizeof(struct ktc_encryptionKey));
457 es_Report ("Dropped oldkey %d seconds old with kvno %d\n",
458 now - ntohl(okeys.keys[i].superseded),
459 ntohl(okeys.keys[i].version));
462 /* Add our key here if its free */
463 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
464 okeys.keys[i].version = htonl(newkeyver);
465 okeys.keys[i].superseded = htonl(NEVERDATE);
466 memcpy(&okeys.keys[i].key, key, sizeof(struct ktc_encryptionKey));
468 addednewkey = okeysaddr;
471 /* Keep count of number of entries found */
472 if (okeys.keys[i].superseded != 0) {
474 newtotalkeyentries++;
478 /* If we modified the block, write it out */
479 if (modified && keyentries) {
480 code = kawrite (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
481 if (code) return code;
484 /* If there are no more entries in this oldkeys block, delete it */
485 if (keyentries == 0) {
487 code = set_header_word(tt, kvnoPtr, okeys.next);
489 code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
491 if (code) return code;
492 code = FreeBlock (tt, okeysaddr);
493 if (code) return code;
495 nextprevptr = prevptr; /* won't bump prevptr */
497 } /* foreacholdkeysblock */
499 /* If we could not add the key, create a new oldkeys block */
501 /* Allocate and fill in an oldkeys block */
502 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
503 if (!addednewkey) return KACREATEFAIL;
504 okeys.flags = htonl(KAFOLDKEYS);
505 okeys.entry = htonl(tentryaddr);
506 okeys.keys[0].version = htonl(newkeyver);
507 okeys.keys[0].superseded = htonl(NEVERDATE);
508 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
509 newtotalkeyentries++;
511 /* Thread onto the header's chain of oldkeys */
512 okeys.next = cheader.kvnoPtr;
513 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
514 if (code) return code;
516 /* Write the oldkeys block out */
517 code = kawrite (tt, addednewkey, (char *)&okeys, sizeof(okeys));
518 if (code) return code;
520 es_Report ("New oldkey block allocated at %d\n", addednewkey);
524 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
525 ViceLog(0,("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
526 tentry->userID.name, tentry->userID.instance,
527 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
531 /* Update the tentry. We rely on caller to write it out */
532 tentry->misc.asServer.oldKeys = htonl(addednewkey);
533 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
534 tentry->key_version = htonl(newkeyver);
535 memcpy(&tentry->key, key, sizeof (tentry->key));
537 /* invalidate key caches everywhere */
538 code = inc_header_word (tt, specialKeysVersion);
539 if (code) return code;
541 es_Report ("New kvno is %d, now are %d oldkeys\n",
542 newkeyver, newtotalkeyentries);
546 afs_int32 ka_DelKey (tt, tentryaddr, tentry)
547 struct ubik_trans *tt;
548 afs_int32 tentryaddr;
549 struct kaentry *tentry;
552 struct kaOldKeys okeys; /* old keys block */
553 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
554 afs_int32 prevptr = 0;
557 es_Report ("DelKey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
559 /* An entry may have more than one oldkeys blocks. The entry
560 * points to the most current, but all the oldkeys blocks for an
561 * entry are not linked together. All oldkeys blocks for all
562 * entries are linked together off of the header. So we follow
565 for (okeysaddr=ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr=nextaddr) {
566 /* foreacholdkeysblock */
567 /* Read the oldKeys block */
568 code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
569 if (code) return code;
570 nextaddr = ntohl(okeys.next);
572 /* We only want oldkey blocks that belong to this entry */
573 if (ntohl(okeys.entry) != tentryaddr) {
574 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
578 /* Delete the oldkeys block */
580 code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
582 code = set_header_word(tt, kvnoPtr, okeys.next);
584 if (code) return code;
585 code = FreeBlock (tt, okeysaddr);
586 if (code) return code;
587 } /* foreacholdkeysblock */
589 /* Update the tentry. We rely on caller to write it out */
590 tentry->misc.asServer.oldKeys = 0;
591 tentry->misc.asServer.nOldKeys = 0;
593 /* invalidate key caches everywhere */
594 code = inc_header_word (tt, specialKeysVersion);
595 if (code) return code;
600 void ka_debugKeyCache (info)
601 struct ka_debugInfo *info;
604 memcpy(&info->cheader_lock, &cheader_lock, sizeof (info->cheader_lock));
605 memcpy(&info->keycache_lock, &keycache_lock, sizeof (info->keycache_lock));
607 info->kcVersion = keyCacheVersion;
608 info->kcSize = maxCachedKeys;
610 for (i=0; i<maxCachedKeys; i++) {
611 if (keyCache[i].used) {
612 if (info->kcUsed < KADEBUGKCINFOSIZE) {
613 int j = info->kcUsed;
615 char principal[sizeof(keyCache[0].name) + sizeof(keyCache[0].inst)];
617 info->kcInfo[j].used = keyCache[i].superseded;
618 info->kcInfo[j].kvno = keyCache[i].kvno;
619 info->kcInfo[j].primary = (keyCache[i].superseded == NEVERDATE);
620 info->kcInfo[j].keycksum = 0;
622 for (k=0; k<sizeof(struct ktc_encryptionKey); k++)
623 info->kcInfo[j].keycksum += ((char *)&keyCache[i].key)[k];
625 strcpy (principal, keyCache[i].name);
626 strcat (principal, ".");
627 strcat (principal, keyCache[i].inst);
628 strncpy (info->kcInfo[j].principal, principal,
629 sizeof (info->kcInfo[0].principal));
636 /* Add a key to the key cache, expanding it if necessary. */
638 ka_Encache (name, inst, kvno, key, superseded)
642 struct ktc_encryptionKey *key;
646 ObtainWriteLock (&keycache_lock);
647 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
648 for (i=0; i<maxCachedKeys; i++)
649 keyCache[i].used = 0;
652 for (i=0; i<maxCachedKeys; i++)
653 if (keyCache[i].used == 0) {
655 keyCache[i].kvno = kvno;
656 strncpy (keyCache[i].name, name, sizeof (keyCache[i].name));
657 strncpy (keyCache[i].inst, inst, sizeof (keyCache[i].inst));
658 keyCacheVersion = ntohl(cheader.specialKeysVersion);
659 memcpy(&keyCache[i].key, key, sizeof(*key));
660 keyCache[i].superseded = superseded;
661 keyCache[i].used = time(0);
663 ReleaseWriteLock (&keycache_lock);
666 /* i == maxCachedKeys */
667 keyCache = (struct cachedKey *)realloc
668 (keyCache, (maxCachedKeys*=2) * sizeof(struct cachedKey));
670 es_Report ("Can't realloc keyCache! out of memory?");
674 { int j = i; /* initialize new storage */
675 while (j<maxCachedKeys) keyCache[j++].used = 0;
680 /* Look up the key given a principal and a kvno. This is called by GetTicket
681 to get the decryption key for the authenticating ticket. It is also called
682 by the rxkad security module to decrypt admin tickets. The rxkad call is
683 with tt==0, since Rx can't call Ubik. */
685 afs_int32 ka_LookupKvno (tt, name, inst, kvno, key)
686 struct ubik_trans *tt;
690 struct ktc_encryptionKey *key;
694 struct kaentry tentry;
696 struct kaOldKeys okeys;
698 ObtainReadLock (&keycache_lock);
699 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
700 code = KAKEYCACHEINVALID;
702 for (i=0; i<maxCachedKeys; i++) {
703 if (keyCache[i].used) { /* zero used date means invalid */
704 if ((keyCache[i].kvno == kvno) &&
705 (strcmp(keyCache[i].name, name) == 0) &&
706 (strcmp(keyCache[i].inst, inst) == 0)) {
707 memcpy(key, &keyCache[i].key, sizeof(*key));
708 keyCache[i].used = time(0);
709 ReleaseReadLock (&keycache_lock);
716 ReleaseReadLock (&keycache_lock);
720 /* we missed in the cache so need to look in the Ubik database */
721 code = FindBlock (tt, name, inst, &to, &tentry);
722 if (code) return code;
723 if (to == 0) return KANOENT;
725 /* first check the current key */
726 if (tentry.key_version == htonl(kvno)) {
727 memcpy(key, &tentry.key, sizeof(*key));
728 ka_Encache (name, inst, kvno, key, NEVERDATE);
731 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
732 code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
733 if (code) return KAIO;
734 if (ntohl(okeys.entry) == to)
735 for (i=0; i<NOLDKEYS; i++)
736 if (okeys.keys[i].superseded &&
737 (ntohl(okeys.keys[i].version) == kvno)) {
738 memcpy(key, &okeys.keys[i].key, sizeof(*key));
739 ka_Encache (name, inst, kvno, key,
740 ntohl(okeys.keys[i].superseded));
747 /* Look up the primary key and key version for a principal. */
749 afs_int32 ka_LookupKey (tt, name, inst, kvno, key)
750 struct ubik_trans *tt;
753 afs_int32 *kvno; /* returned */
754 struct ktc_encryptionKey *key; /* copied out */
757 struct kaentry tentry;
760 ObtainReadLock (&keycache_lock);
761 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
762 code = KAKEYCACHEINVALID;
764 for (i=0; i<maxCachedKeys; i++) {
765 if (keyCache[i].used) { /* zero used date means invalid */
766 if ((keyCache[i].superseded == NEVERDATE) &&
767 (strcmp(keyCache[i].name, name) == 0) &&
768 (strcmp(keyCache[i].inst, inst) == 0)){
769 memcpy(key, &keyCache[i].key, sizeof(*key));
770 *kvno = keyCache[i].kvno;
771 keyCache[i].used = time(0);
772 ReleaseReadLock (&keycache_lock);
779 ReleaseReadLock (&keycache_lock);
783 /* we missed in the cache so need to look in the Ubik database */
784 code = FindBlock (tt, name, inst, &to, &tentry);
785 if (code) return code;
786 if (to == 0) return KANOENT;
787 memcpy(key, &tentry.key, sizeof(*key));
788 *kvno = ntohl(tentry.key_version);
789 ka_Encache (name, inst, *kvno, key, NEVERDATE);
793 /* This is, hopefully a temporary mechanism to fill the cache will all keys
794 since filling cache misses during rxkad challenge responses will deadlock if
795 Ubik needs to use Rx. */
797 afs_int32 ka_FillKeyCache (tt)
798 struct ubik_trans *tt;
803 struct ktc_encryptionKey k;
804 struct kaOldKeys okeys;
805 struct kaentry tentry;
807 /* this is a little marginal, but... */
808 if (keyCacheVersion == ntohl(cheader.specialKeysVersion)) return 0;
811 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
812 code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
813 if (code) return KAIO;
814 /* get name & instance */
815 code = karead (tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
816 if (code) return KAIO;
818 /* get all the old keys in this block */
819 for (i=0; i<NOLDKEYS; i++)
820 if (okeys.keys[i].superseded) {
821 code = ka_LookupKvno (tt, tentry.userID.name, tentry.userID.instance,
822 ntohl(okeys.keys[i].version), &k);
823 if (code) return code;
826 if (++nfound > maxCachedKeys) return KADATABASEINCONSISTENT;
830 afs_int32 update_admin_count (tt, delta)
831 struct ubik_trans *tt;
836 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
837 to = DOFFSET (0, &cheader, &cheader.admin_accounts);
838 code = kawrite (tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
839 if (code) return KAIO;
843 static int index_OK (index)
846 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr)) ||
847 ((index - sizeof(cheader)) % sizeof(kaentry) != 0)) return 0;
851 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
853 int name_instance_legal (name, instance)
858 /* No string checks apply anymore. The international people want to use full 8
859 bit ascii without problems. */
861 code = (strlen(name) < MAXKTCNAMELEN) && (strlen(instance) < MAXKTCNAMELEN);
863 map = LEGALCHARS; /* permitted chars, instance allows <period> */
864 code = (strlen (name) > 0) && string_legal (instance, map) && string_legal (name, map+1);
866 if (!code) dynamic_statistics.string_checks++;
870 static int string_legal (str, map)
876 if (slen >= MAXKTCNAMELEN) return 0; /* with trailing null must fit in data base */
877 return (slen == strspn (str, map)); /* strspn returns length(str) if all chars in map */