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>
16 #include <sys/types.h>
20 #include <netinet/in.h>
33 #include <afs/afsutil.h>
38 extern struct kaheader cheader;
39 extern Date cheaderReadTime; /* time cheader last read in */
40 extern struct ubik_dbase *KA_dbase;
42 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
44 #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))
46 static int index_OK();
49 NameHash(aname, ainstance)
51 register char *ainstance;
53 register unsigned int hash;
56 /* stolen directly from the HashString function in the vol package */
58 for (i = strlen(aname), aname += i - 1; i--; aname--)
59 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
60 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
61 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
62 return (hash % HASHSIZE);
65 /* package up seek and write into one procedure for ease of use */
68 kawrite(tt, pos, buff, len)
69 struct ubik_trans *tt;
76 code = ubik_Seek(tt, 0, pos);
79 code = ubik_Write(tt, buff, len);
83 /* same thing for read */
86 karead(tt, pos, buff, len)
87 struct ubik_trans *tt;
94 code = ubik_Seek(tt, 0, pos);
97 code = ubik_Read(tt, buff, len);
101 static struct Lock cheader_lock;
102 static struct Lock keycache_lock;
104 static int maxCachedKeys;
106 static struct cachedKey {
108 int superseded; /* NEVERDATE => this is current key */
110 struct ktc_encryptionKey key;
111 char name[MAXKTCNAMELEN];
112 char inst[MAXKTCNAMELEN];
114 static afs_int32 keyCacheVersion = 0;
116 static afs_int32 maxKeyLifetime;
117 static int dbfixup = 0;
119 init_kadatabase(initFlags)
120 int initFlags; /* same as init_kaprocs (see which) */
122 Lock_Init(&cheader_lock);
123 Lock_Init(&keycache_lock);
127 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
132 maxKeyLifetime = MAXKTCTICKETLIFETIME;
138 /* check that the database has been initialized. Be careful to fail in a safe
139 manner, to avoid bogusly reinitializing the db. */
142 CheckInit(at, db_init)
143 struct ubik_trans *at;
144 int (*db_init) (); /* procedure to call if rebuilding DB */
146 register afs_int32 code;
150 /* Don't read header if not necessary */
151 if (!ubik_CacheUpdate(at))
154 ObtainWriteLock(&cheader_lock);
155 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
157 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
158 sizeof(afs_int32)))) {
160 printf("No data base\n");
162 printf("I/O Error\n");
164 iversion = ntohl(iversion); /* convert to host order */
165 tversion = ntohl(tversion);
166 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
167 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
169 printf("SetupHeader failed\n");
172 cheaderReadTime = time(0);
175 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
176 KADBVERSION, iversion, tversion);
180 ReleaseWriteLock(&cheader_lock);
184 /* if here, we have no version number or the wrong version number in the
186 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
191 if ((db_init == 0) || (code == KAIO))
194 printf("Error discovered in header, rebuilding.\n");
196 /* try to write a good header */
197 memset(&cheader, 0, sizeof(cheader));
198 cheader.version = htonl(KADBVERSION);
199 cheader.checkVersion = htonl(KADBVERSION);
200 cheader.headerSize = htonl(sizeof(cheader));
202 cheader.eofPtr = htonl(sizeof(cheader));
204 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
205 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
206 cheader.admin_accounts = 0;
207 cheader.hashsize = htonl(HASHSIZE);
208 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
210 return KAIO; /* return the error code */
212 return db_init(at); /* initialize the db */
215 /* Allocate a free block of storage for entry, returning address of a new
216 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
219 AllocBlock(at, tentry)
220 register struct ubik_trans *at;
221 struct kaentry *tentry;
223 register afs_int32 code;
226 if (cheader.freePtr) {
227 /* allocate this dude */
228 temp = ntohl(cheader.freePtr);
229 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
231 return 0; /* can't read block */
232 code = set_header_word(at, freePtr, tentry->next);
234 /* hosed, nothing on free list, grow file */
235 temp = ntohl(cheader.eofPtr); /* remember this guy */
236 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
241 code = inc_header_word(at, stats.allocs);
244 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
248 /* Free a block given its index. It must already have been unthreaded.
249 Returns zero for success or an error code on failure. */
253 struct ubik_trans *at;
256 struct kaentry tentry;
259 /* check index just to be on the safe side */
260 if (!index_OK(index))
263 memset(&tentry, 0, sizeof(kaentry));
264 tentry.next = cheader.freePtr;
265 tentry.flags = htonl(KAFFREE);
266 code = set_header_word(at, freePtr, htonl(index));
269 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
273 code = inc_header_word(at, stats.frees);
279 /* Look for a block by name and instance. If found read the block's contents
280 into the area pointed to by tentry and return the block's index. If not
281 found offset is set to zero. If an error is encountered a non-zero code is
285 FindBlock(at, aname, ainstance, toP, tentry)
286 struct ubik_trans *at;
290 struct kaentry *tentry;
292 register afs_int32 i, code;
293 register afs_int32 to;
296 i = NameHash(aname, ainstance);
297 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
298 to = ntohl(tentry->next)) {
299 code = karead(at, to, (char *)tentry, sizeof(kaentry));
302 /* see if the name matches */
303 if (!strcmp(aname, tentry->userID.name)
304 && (ainstance == (char *)0
305 || !strcmp(ainstance, tentry->userID.instance))) {
306 *toP = to; /* found it */
310 *toP = 0; /* no such entry */
314 /* Add a block to the hash table given a pointer to the block and its index.
315 The block is threaded onto the hash table and written to disk. The routine
316 returns zero if there were no errors. */
319 ThreadBlock(at, index, tentry)
320 struct ubik_trans *at;
322 struct kaentry *tentry;
325 int hi; /* hash index */
327 if (!index_OK(index))
329 hi = NameHash(tentry->userID.name, tentry->userID.instance);
330 tentry->next = cheader.nameHash[hi];
331 code = set_header_word(at, nameHash[hi], htonl(index));
334 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
340 /* Remove a block from the hash table. If success return 0, else return an
344 UnthreadBlock(at, aentry)
345 struct ubik_trans *at;
346 struct kaentry *aentry;
348 register afs_int32 i, code;
349 register afs_int32 to;
351 struct kaentry tentry;
353 i = NameHash(aentry->userID.name, aentry->userID.instance);
355 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
356 to = ntohl(tentry.next)) {
357 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
360 /* see if the name matches */
361 if (!strcmp(aentry->userID.name, tentry.userID.name)
362 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
364 if (lo) { /* unthread from last block */
366 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
369 } else { /* unthread from hash table */
370 code = set_header_word(at, nameHash[i], tentry.next);
374 aentry->next = 0; /* just to be sure */
377 lo = DOFFSET(to, &tentry, &tentry.next);
382 /* Given an index to the last block (or zero the first time) read the contents
383 of the next block and return its index. The last argument is a pointer to
384 an estimate of the number of remaining blocks to read out. The remaining
385 count is an estimate because it may include free blocks that are not
386 returned. If there are no more blocks remaining is zero and the returned
387 index is zero. A non-zero index indicates that tentry has been filled with
388 valid data. If an error is encountered the returned index is zero and the
389 remaining count is negative. */
392 NextBlock(at, index, tentry, remaining)
393 struct ubik_trans *at;
395 struct kaentry *tentry;
396 afs_int32 *remaining;
401 if (index == 0) /* get first one */
402 index = sizeof(cheader);
404 if (!index_OK(index)) {
405 *remaining = -1; /* error */
408 index += sizeof(kaentry);
410 /* now search for the first entry that isn't free */
411 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
412 code = karead(at, index, (char *)tentry, sizeof(kaentry));
417 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
418 /* estimate remaining number of entries, not including this one */
419 *remaining = (last - index) / sizeof(kaentry) - 1;
423 *remaining = 0; /* no more entries */
427 /* These are a collections of routines that deal with externally known keys.
428 They maintain a database of key version numbers and the corresponding key
429 and pointer to the user entry. */
432 ka_NewKey(tt, tentryaddr, tentry, key)
433 struct ubik_trans *tt;
434 afs_int32 tentryaddr;
435 struct kaentry *tentry;
436 struct ktc_encryptionKey *key;
438 struct kaOldKeys okeys; /* old keys block */
439 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
440 afs_int32 prevptr, nextprevptr;
443 afs_int32 newkeyver; /* new key version number */
444 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
445 int foundcurrentkey = 0, addednewkey = 0, modified;
447 es_Report("Newkey for %s.%s\n", tentry->userID.name,
448 tentry->userID.instance);
450 newkeyver = ntohl(tentry->key_version) + 1;
451 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
454 /* An entry may have more than one oldkeys blocks. The entry
455 * points to the most current, but all the oldkeys blocks for an
456 * entry are not linked together. All oldkeys blocks for all
457 * entries are linked together off of the header. So we follow
460 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
461 prevptr = nextprevptr, okeysaddr = nextaddr) {
462 /* foreacholdkeysblock */
463 /* Read the oldKeys block */
464 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
468 nextaddr = ntohl(okeys.next);
469 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
471 /* We only want oldkey blocks that belong to this entry */
472 if (ntohl(okeys.entry) != tentryaddr)
475 modified = 0; /* This oldkeys block has not been modified */
476 keyentries = 0; /* Number of valid key entries in the block */
477 for (i = 0; i < NOLDKEYS; i++) {
479 /* Keep count of number of entries found */
480 if (okeys.keys[i].superseded != 0) {
481 oldtotalkeyentries++;
484 /* If we find the entry that is not superseded, then supersede it */
485 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
486 okeys.keys[i].superseded = htonl(now);
489 if (foundcurrentkey) {
491 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
492 tentry->userID.name, tentry->userID.instance));
498 /* If we find an oldkey of the same version or
499 * an old key that has expired, then delete it.
501 if ((ntohl(okeys.keys[i].version) == newkeyver)
502 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
503 okeys.keys[i].superseded = 0;
504 okeys.keys[i].version = htonl(-1);
505 memset(&okeys.keys[i].key, 0,
506 sizeof(struct ktc_encryptionKey));
509 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
510 now - ntohl(okeys.keys[i].superseded),
511 ntohl(okeys.keys[i].version));
514 /* Add our key here if its free */
515 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
516 okeys.keys[i].version = htonl(newkeyver);
517 okeys.keys[i].superseded = htonl(NEVERDATE);
518 memcpy(&okeys.keys[i].key, key,
519 sizeof(struct ktc_encryptionKey));
521 addednewkey = okeysaddr;
524 /* Keep count of number of entries found */
525 if (okeys.keys[i].superseded != 0) {
527 newtotalkeyentries++;
531 /* If we modified the block, write it out */
532 if (modified && keyentries) {
533 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
538 /* If there are no more entries in this oldkeys block, delete it */
539 if (keyentries == 0) {
541 code = set_header_word(tt, kvnoPtr, okeys.next);
544 kawrite(tt, prevptr, (char *)&okeys.next,
549 code = FreeBlock(tt, okeysaddr);
553 nextprevptr = prevptr; /* won't bump prevptr */
555 } /* foreacholdkeysblock */
557 /* If we could not add the key, create a new oldkeys block */
559 /* Allocate and fill in an oldkeys block */
560 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
563 okeys.flags = htonl(KAFOLDKEYS);
564 okeys.entry = htonl(tentryaddr);
565 okeys.keys[0].version = htonl(newkeyver);
566 okeys.keys[0].superseded = htonl(NEVERDATE);
567 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
568 newtotalkeyentries++;
570 /* Thread onto the header's chain of oldkeys */
571 okeys.next = cheader.kvnoPtr;
572 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
576 /* Write the oldkeys block out */
577 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
581 es_Report("New oldkey block allocated at %d\n", addednewkey);
584 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
586 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
587 tentry->userID.name, tentry->userID.instance,
588 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
592 /* Update the tentry. We rely on caller to write it out */
593 tentry->misc.asServer.oldKeys = htonl(addednewkey);
594 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
595 tentry->key_version = htonl(newkeyver);
596 memcpy(&tentry->key, key, sizeof(tentry->key));
598 /* invalidate key caches everywhere */
599 code = inc_header_word(tt, specialKeysVersion);
603 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
609 ka_DelKey(tt, tentryaddr, tentry)
610 struct ubik_trans *tt;
611 afs_int32 tentryaddr;
612 struct kaentry *tentry;
615 struct kaOldKeys okeys; /* old keys block */
616 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
617 afs_int32 prevptr = 0;
620 es_Report("DelKey for %s.%s\n", tentry->userID.name,
621 tentry->userID.instance);
623 /* An entry may have more than one oldkeys blocks. The entry
624 * points to the most current, but all the oldkeys blocks for an
625 * entry are not linked together. All oldkeys blocks for all
626 * entries are linked together off of the header. So we follow
629 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
630 /* foreacholdkeysblock */
631 /* Read the oldKeys block */
632 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
635 nextaddr = ntohl(okeys.next);
637 /* We only want oldkey blocks that belong to this entry */
638 if (ntohl(okeys.entry) != tentryaddr) {
639 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
643 /* Delete the oldkeys block */
646 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
648 code = set_header_word(tt, kvnoPtr, okeys.next);
652 code = FreeBlock(tt, okeysaddr);
655 } /* foreacholdkeysblock */
657 /* Update the tentry. We rely on caller to write it out */
658 tentry->misc.asServer.oldKeys = 0;
659 tentry->misc.asServer.nOldKeys = 0;
661 /* invalidate key caches everywhere */
662 code = inc_header_word(tt, specialKeysVersion);
670 ka_debugKeyCache(info)
671 struct ka_debugInfo *info;
675 memcpy(&info->cheader_lock, &cheader_lock, sizeof(info->cheader_lock));
676 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
678 info->kcVersion = keyCacheVersion;
679 info->kcSize = maxCachedKeys;
681 for (i = 0; i < maxCachedKeys; i++) {
682 if (keyCache[i].used) {
683 if (info->kcUsed < KADEBUGKCINFOSIZE) {
684 int j = info->kcUsed;
686 char principal[sizeof(keyCache[0].name) +
687 sizeof(keyCache[0].inst)];
689 info->kcInfo[j].used = keyCache[i].superseded;
690 info->kcInfo[j].kvno = keyCache[i].kvno;
691 info->kcInfo[j].primary =
692 (keyCache[i].superseded == NEVERDATE);
693 info->kcInfo[j].keycksum = 0;
695 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
696 info->kcInfo[j].keycksum += ((char *)&keyCache[i].key)[k];
698 strcpy(principal, keyCache[i].name);
699 strcat(principal, ".");
700 strcat(principal, keyCache[i].inst);
701 strncpy(info->kcInfo[j].principal, principal,
702 sizeof(info->kcInfo[0].principal));
709 /* Add a key to the key cache, expanding it if necessary. */
711 ka_Encache(name, inst, kvno, key, superseded)
715 struct ktc_encryptionKey *key;
720 ObtainWriteLock(&keycache_lock);
721 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
722 for (i = 0; i < maxCachedKeys; i++)
723 keyCache[i].used = 0;
726 for (i = 0; i < maxCachedKeys; i++)
727 if (keyCache[i].used == 0) {
729 keyCache[i].kvno = kvno;
730 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
731 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
732 keyCacheVersion = ntohl(cheader.specialKeysVersion);
733 memcpy(&keyCache[i].key, key, sizeof(*key));
734 keyCache[i].superseded = superseded;
735 keyCache[i].used = time(0);
737 ReleaseWriteLock(&keycache_lock);
740 /* i == maxCachedKeys */
742 (struct cachedKey *)realloc(keyCache,
744 2) * sizeof(struct cachedKey));
746 es_Report("Can't realloc keyCache! out of memory?");
751 int j = i; /* initialize new storage */
752 while (j < maxCachedKeys)
753 keyCache[j++].used = 0;
758 /* Look up the key given a principal and a kvno. This is called by GetTicket
759 to get the decryption key for the authenticating ticket. It is also called
760 by the rxkad security module to decrypt admin tickets. The rxkad call is
761 with tt==0, since Rx can't call Ubik. */
764 ka_LookupKvno(tt, name, inst, kvno, key)
765 struct ubik_trans *tt;
769 struct ktc_encryptionKey *key;
774 struct kaentry tentry;
776 struct kaOldKeys okeys;
778 ObtainReadLock(&keycache_lock);
779 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
780 code = KAKEYCACHEINVALID;
782 for (i = 0; i < maxCachedKeys; i++) {
783 if (keyCache[i].used) { /* zero used date means invalid */
784 if ((keyCache[i].kvno == kvno)
785 && (strcmp(keyCache[i].name, name) == 0)
786 && (strcmp(keyCache[i].inst, inst) == 0)) {
787 memcpy(key, &keyCache[i].key, sizeof(*key));
788 keyCache[i].used = time(0);
789 ReleaseReadLock(&keycache_lock);
796 ReleaseReadLock(&keycache_lock);
800 /* we missed in the cache so need to look in the Ubik database */
801 code = FindBlock(tt, name, inst, &to, &tentry);
807 /* first check the current key */
808 if (tentry.key_version == htonl(kvno)) {
809 memcpy(key, &tentry.key, sizeof(*key));
810 ka_Encache(name, inst, kvno, key, NEVERDATE);
813 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
814 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
817 if (ntohl(okeys.entry) == to)
818 for (i = 0; i < NOLDKEYS; i++)
819 if (okeys.keys[i].superseded
820 && (ntohl(okeys.keys[i].version) == kvno)) {
821 memcpy(key, &okeys.keys[i].key, sizeof(*key));
822 ka_Encache(name, inst, kvno, key,
823 ntohl(okeys.keys[i].superseded));
830 /* Look up the primary key and key version for a principal. */
833 ka_LookupKey(tt, name, inst, kvno, key)
834 struct ubik_trans *tt;
837 afs_int32 *kvno; /* returned */
838 struct ktc_encryptionKey *key; /* copied out */
842 struct kaentry tentry;
845 ObtainReadLock(&keycache_lock);
846 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
847 code = KAKEYCACHEINVALID;
849 for (i = 0; i < maxCachedKeys; i++) {
850 if (keyCache[i].used) { /* zero used date means invalid */
851 if ((keyCache[i].superseded == NEVERDATE)
852 && (strcmp(keyCache[i].name, name) == 0)
853 && (strcmp(keyCache[i].inst, inst) == 0)) {
854 memcpy(key, &keyCache[i].key, sizeof(*key));
855 *kvno = keyCache[i].kvno;
856 keyCache[i].used = time(0);
857 ReleaseReadLock(&keycache_lock);
864 ReleaseReadLock(&keycache_lock);
868 /* we missed in the cache so need to look in the Ubik database */
869 code = FindBlock(tt, name, inst, &to, &tentry);
874 memcpy(key, &tentry.key, sizeof(*key));
875 *kvno = ntohl(tentry.key_version);
876 ka_Encache(name, inst, *kvno, key, NEVERDATE);
880 /* This is, hopefully a temporary mechanism to fill the cache will all keys
881 since filling cache misses during rxkad challenge responses will deadlock if
882 Ubik needs to use Rx. */
886 struct ubik_trans *tt;
892 struct ktc_encryptionKey k;
893 struct kaOldKeys okeys;
894 struct kaentry tentry;
896 /* this is a little marginal, but... */
897 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
901 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
902 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
905 /* get name & instance */
907 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
911 /* get all the old keys in this block */
912 for (i = 0; i < NOLDKEYS; i++)
913 if (okeys.keys[i].superseded) {
915 ka_LookupKvno(tt, tentry.userID.name,
916 tentry.userID.instance,
917 ntohl(okeys.keys[i].version), &k);
922 if (++nfound > maxCachedKeys)
923 return KADATABASEINCONSISTENT;
928 update_admin_count(tt, delta)
929 struct ubik_trans *tt;
935 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
936 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
938 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
948 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
949 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
954 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
957 name_instance_legal(name, instance)
963 /* No string checks apply anymore. The international people want to use full 8
964 bit ascii without problems. */
966 code = (strlen(name) < MAXKTCNAMELEN)
967 && (strlen(instance) < MAXKTCNAMELEN);
969 map = LEGALCHARS; /* permitted chars, instance allows <period> */
970 code = (strlen(name) > 0) && string_legal(instance, map)
971 && string_legal(name, map + 1);
974 dynamic_statistics.string_checks++;
979 string_legal(str, map)
986 if (slen >= MAXKTCNAMELEN)
987 return 0; /* with trailing null must fit in data base */
988 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */