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>
26 #include <afs/afsutil.h>
31 extern Date cheaderReadTime; /* time cheader last read in */
33 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
35 #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))
37 static int index_OK();
40 NameHash(aname, ainstance)
42 register char *ainstance;
44 register unsigned int hash;
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(tt, pos, buff, len)
60 struct ubik_trans *tt;
67 code = ubik_Seek(tt, 0, pos);
70 code = ubik_Write(tt, buff, len);
74 /* same thing for read */
77 karead(tt, pos, buff, len)
78 struct ubik_trans *tt;
85 code = ubik_Seek(tt, 0, pos);
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;
111 init_kadatabase(initFlags)
112 int initFlags; /* same as init_kaprocs (see which) */
114 Lock_Init(&cheader_lock);
115 Lock_Init(&keycache_lock);
119 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
124 maxKeyLifetime = MAXKTCTICKETLIFETIME;
130 /* check that the database has been initialized. Be careful to fail in a safe
131 manner, to avoid bogusly reinitializing the db. */
134 CheckInit(at, db_init)
135 struct ubik_trans *at;
136 int (*db_init) (); /* procedure to call if rebuilding DB */
138 register afs_int32 code;
142 /* Don't read header if not necessary */
143 if (!ubik_CacheUpdate(at))
146 ObtainWriteLock(&cheader_lock);
147 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
149 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
150 sizeof(afs_int32)))) {
152 printf("No data base\n");
154 printf("I/O Error\n");
156 iversion = ntohl(iversion); /* convert to host order */
157 tversion = ntohl(tversion);
158 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
159 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
161 printf("SetupHeader failed\n");
164 cheaderReadTime = time(0);
167 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
168 KADBVERSION, iversion, tversion);
172 ReleaseWriteLock(&cheader_lock);
176 /* if here, we have no version number or the wrong version number in the
178 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
183 if ((db_init == 0) || (code == KAIO))
186 printf("Error discovered in header, rebuilding.\n");
188 /* try to write a good header */
189 memset(&cheader, 0, sizeof(cheader));
190 cheader.version = htonl(KADBVERSION);
191 cheader.checkVersion = htonl(KADBVERSION);
192 cheader.headerSize = htonl(sizeof(cheader));
194 cheader.eofPtr = htonl(sizeof(cheader));
196 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
197 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
198 cheader.admin_accounts = 0;
199 cheader.hashsize = htonl(HASHSIZE);
200 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
202 return KAIO; /* return the error code */
204 return db_init(at); /* initialize the db */
207 /* Allocate a free block of storage for entry, returning address of a new
208 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
211 AllocBlock(at, tentry)
212 register struct ubik_trans *at;
213 struct kaentry *tentry;
215 register afs_int32 code;
218 if (cheader.freePtr) {
219 /* allocate this dude */
220 temp = ntohl(cheader.freePtr);
221 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
223 return 0; /* can't read block */
224 code = set_header_word(at, freePtr, tentry->next);
226 /* hosed, nothing on free list, grow file */
227 temp = ntohl(cheader.eofPtr); /* remember this guy */
228 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
233 code = inc_header_word(at, stats.allocs);
236 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
240 /* Free a block given its index. It must already have been unthreaded.
241 Returns zero for success or an error code on failure. */
245 struct ubik_trans *at;
248 struct kaentry tentry;
251 /* check index just to be on the safe side */
252 if (!index_OK(index))
255 memset(&tentry, 0, sizeof(kaentry));
256 tentry.next = cheader.freePtr;
257 tentry.flags = htonl(KAFFREE);
258 code = set_header_word(at, freePtr, htonl(index));
261 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
265 code = inc_header_word(at, stats.frees);
271 /* Look for a block by name and instance. If found read the block's contents
272 into the area pointed to by tentry and return the block's index. If not
273 found offset is set to zero. If an error is encountered a non-zero code is
277 FindBlock(at, aname, ainstance, toP, tentry)
278 struct ubik_trans *at;
282 struct kaentry *tentry;
284 register afs_int32 i, code;
285 register afs_int32 to;
288 i = NameHash(aname, ainstance);
289 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
290 to = ntohl(tentry->next)) {
291 code = karead(at, to, (char *)tentry, sizeof(kaentry));
294 /* see if the name matches */
295 if (!strcmp(aname, tentry->userID.name)
296 && (ainstance == (char *)0
297 || !strcmp(ainstance, tentry->userID.instance))) {
298 *toP = to; /* found it */
302 *toP = 0; /* no such entry */
306 /* Add a block to the hash table given a pointer to the block and its index.
307 The block is threaded onto the hash table and written to disk. The routine
308 returns zero if there were no errors. */
311 ThreadBlock(at, index, tentry)
312 struct ubik_trans *at;
314 struct kaentry *tentry;
317 int hi; /* hash index */
319 if (!index_OK(index))
321 hi = NameHash(tentry->userID.name, tentry->userID.instance);
322 tentry->next = cheader.nameHash[hi];
323 code = set_header_word(at, nameHash[hi], htonl(index));
326 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
332 /* Remove a block from the hash table. If success return 0, else return an
336 UnthreadBlock(at, aentry)
337 struct ubik_trans *at;
338 struct kaentry *aentry;
340 register afs_int32 i, code;
341 register afs_int32 to;
343 struct kaentry tentry;
345 i = NameHash(aentry->userID.name, aentry->userID.instance);
347 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
348 to = ntohl(tentry.next)) {
349 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
352 /* see if the name matches */
353 if (!strcmp(aentry->userID.name, tentry.userID.name)
354 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
356 if (lo) { /* unthread from last block */
358 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
361 } else { /* unthread from hash table */
362 code = set_header_word(at, nameHash[i], tentry.next);
366 aentry->next = 0; /* just to be sure */
369 lo = DOFFSET(to, &tentry, &tentry.next);
374 /* Given an index to the last block (or zero the first time) read the contents
375 of the next block and return its index. The last argument is a pointer to
376 an estimate of the number of remaining blocks to read out. The remaining
377 count is an estimate because it may include free blocks that are not
378 returned. If there are no more blocks remaining is zero and the returned
379 index is zero. A non-zero index indicates that tentry has been filled with
380 valid data. If an error is encountered the returned index is zero and the
381 remaining count is negative. */
384 NextBlock(at, index, tentry, remaining)
385 struct ubik_trans *at;
387 struct kaentry *tentry;
388 afs_int32 *remaining;
393 if (index == 0) /* get first one */
394 index = sizeof(cheader);
396 if (!index_OK(index)) {
397 *remaining = -1; /* error */
400 index += sizeof(kaentry);
402 /* now search for the first entry that isn't free */
403 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
404 code = karead(at, index, (char *)tentry, sizeof(kaentry));
409 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
410 /* estimate remaining number of entries, not including this one */
411 *remaining = (last - index) / sizeof(kaentry) - 1;
415 *remaining = 0; /* no more entries */
419 /* These are a collections of routines that deal with externally known keys.
420 They maintain a database of key version numbers and the corresponding key
421 and pointer to the user entry. */
424 ka_NewKey(tt, tentryaddr, tentry, key)
425 struct ubik_trans *tt;
426 afs_int32 tentryaddr;
427 struct kaentry *tentry;
428 struct ktc_encryptionKey *key;
430 struct kaOldKeys okeys; /* old keys block */
431 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
432 afs_int32 prevptr, nextprevptr;
435 afs_int32 newkeyver; /* new key version number */
436 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
437 int foundcurrentkey = 0, addednewkey = 0, modified;
439 es_Report("Newkey for %s.%s\n", tentry->userID.name,
440 tentry->userID.instance);
442 newkeyver = ntohl(tentry->key_version) + 1;
443 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
446 /* An entry may have more than one oldkeys blocks. The entry
447 * points to the most current, but all the oldkeys blocks for an
448 * entry are not linked together. All oldkeys blocks for all
449 * entries are linked together off of the header. So we follow
452 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
453 prevptr = nextprevptr, okeysaddr = nextaddr) {
454 /* foreacholdkeysblock */
455 /* Read the oldKeys block */
456 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
460 nextaddr = ntohl(okeys.next);
461 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
463 /* We only want oldkey blocks that belong to this entry */
464 if (ntohl(okeys.entry) != tentryaddr)
467 modified = 0; /* This oldkeys block has not been modified */
468 keyentries = 0; /* Number of valid key entries in the block */
469 for (i = 0; i < NOLDKEYS; i++) {
471 /* Keep count of number of entries found */
472 if (okeys.keys[i].superseded != 0) {
473 oldtotalkeyentries++;
476 /* If we find the entry that is not superseded, then supersede it */
477 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
478 okeys.keys[i].superseded = htonl(now);
481 if (foundcurrentkey) {
483 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
484 tentry->userID.name, tentry->userID.instance));
490 /* If we find an oldkey of the same version or
491 * an old key that has expired, then delete it.
493 if ((ntohl(okeys.keys[i].version) == newkeyver)
494 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
495 okeys.keys[i].superseded = 0;
496 okeys.keys[i].version = htonl(-1);
497 memset(&okeys.keys[i].key, 0,
498 sizeof(struct ktc_encryptionKey));
501 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
502 now - ntohl(okeys.keys[i].superseded),
503 ntohl(okeys.keys[i].version));
506 /* Add our key here if its free */
507 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
508 okeys.keys[i].version = htonl(newkeyver);
509 okeys.keys[i].superseded = htonl(NEVERDATE);
510 memcpy(&okeys.keys[i].key, key,
511 sizeof(struct ktc_encryptionKey));
513 addednewkey = okeysaddr;
516 /* Keep count of number of entries found */
517 if (okeys.keys[i].superseded != 0) {
519 newtotalkeyentries++;
523 /* If we modified the block, write it out */
524 if (modified && keyentries) {
525 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
530 /* If there are no more entries in this oldkeys block, delete it */
531 if (keyentries == 0) {
533 code = set_header_word(tt, kvnoPtr, okeys.next);
536 kawrite(tt, prevptr, (char *)&okeys.next,
541 code = FreeBlock(tt, okeysaddr);
545 nextprevptr = prevptr; /* won't bump prevptr */
547 } /* foreacholdkeysblock */
549 /* If we could not add the key, create a new oldkeys block */
551 /* Allocate and fill in an oldkeys block */
552 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
555 okeys.flags = htonl(KAFOLDKEYS);
556 okeys.entry = htonl(tentryaddr);
557 okeys.keys[0].version = htonl(newkeyver);
558 okeys.keys[0].superseded = htonl(NEVERDATE);
559 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
560 newtotalkeyentries++;
562 /* Thread onto the header's chain of oldkeys */
563 okeys.next = cheader.kvnoPtr;
564 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
568 /* Write the oldkeys block out */
569 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
573 es_Report("New oldkey block allocated at %d\n", addednewkey);
576 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
578 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
579 tentry->userID.name, tentry->userID.instance,
580 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
584 /* Update the tentry. We rely on caller to write it out */
585 tentry->misc.asServer.oldKeys = htonl(addednewkey);
586 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
587 tentry->key_version = htonl(newkeyver);
588 memcpy(&tentry->key, key, sizeof(tentry->key));
590 /* invalidate key caches everywhere */
591 code = inc_header_word(tt, specialKeysVersion);
595 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
601 ka_DelKey(tt, tentryaddr, tentry)
602 struct ubik_trans *tt;
603 afs_int32 tentryaddr;
604 struct kaentry *tentry;
607 struct kaOldKeys okeys; /* old keys block */
608 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
609 afs_int32 prevptr = 0;
612 es_Report("DelKey for %s.%s\n", tentry->userID.name,
613 tentry->userID.instance);
615 /* An entry may have more than one oldkeys blocks. The entry
616 * points to the most current, but all the oldkeys blocks for an
617 * entry are not linked together. All oldkeys blocks for all
618 * entries are linked together off of the header. So we follow
621 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
622 /* foreacholdkeysblock */
623 /* Read the oldKeys block */
624 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
627 nextaddr = ntohl(okeys.next);
629 /* We only want oldkey blocks that belong to this entry */
630 if (ntohl(okeys.entry) != tentryaddr) {
631 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
635 /* Delete the oldkeys block */
638 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
640 code = set_header_word(tt, kvnoPtr, okeys.next);
644 code = FreeBlock(tt, okeysaddr);
647 } /* foreacholdkeysblock */
649 /* Update the tentry. We rely on caller to write it out */
650 tentry->misc.asServer.oldKeys = 0;
651 tentry->misc.asServer.nOldKeys = 0;
653 /* invalidate key caches everywhere */
654 code = inc_header_word(tt, specialKeysVersion);
662 ka_debugKeyCache(info)
663 struct ka_debugInfo *info;
667 memcpy(&info->cheader_lock, &cheader_lock, sizeof(info->cheader_lock));
668 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
670 info->kcVersion = keyCacheVersion;
671 info->kcSize = maxCachedKeys;
673 for (i = 0; i < maxCachedKeys; i++) {
674 if (keyCache[i].used) {
675 if (info->kcUsed < KADEBUGKCINFOSIZE) {
676 int j = info->kcUsed;
677 char principal[sizeof(keyCache[0].name) +
678 sizeof(keyCache[0].inst)];
680 info->kcInfo[j].used = keyCache[i].superseded;
681 info->kcInfo[j].kvno = keyCache[i].kvno;
682 info->kcInfo[j].primary =
683 (keyCache[i].superseded == NEVERDATE);
684 info->kcInfo[j].keycksum = 0;
688 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
689 info->kcInfo[j].keycksum +=
690 ((char *)&keyCache[i].key)[k];
693 strcpy(principal, keyCache[i].name);
694 strcat(principal, ".");
695 strcat(principal, keyCache[i].inst);
696 strncpy(info->kcInfo[j].principal, principal,
697 sizeof(info->kcInfo[0].principal));
704 /* Add a key to the key cache, expanding it if necessary. */
707 ka_Encache(name, inst, kvno, key, superseded)
711 struct ktc_encryptionKey *key;
716 ObtainWriteLock(&keycache_lock);
717 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
718 for (i = 0; i < maxCachedKeys; i++)
719 keyCache[i].used = 0;
722 for (i = 0; i < maxCachedKeys; i++)
723 if (keyCache[i].used == 0) {
725 keyCache[i].kvno = kvno;
726 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
727 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
728 keyCacheVersion = ntohl(cheader.specialKeysVersion);
729 memcpy(&keyCache[i].key, key, sizeof(*key));
730 keyCache[i].superseded = superseded;
731 keyCache[i].used = time(0);
733 ReleaseWriteLock(&keycache_lock);
736 /* i == maxCachedKeys */
738 (struct cachedKey *)realloc(keyCache,
740 2) * sizeof(struct cachedKey));
742 es_Report("Can't realloc keyCache! out of memory?");
747 int j = i; /* initialize new storage */
748 while (j < maxCachedKeys)
749 keyCache[j++].used = 0;
754 /* Look up the key given a principal and a kvno. This is called by GetTicket
755 to get the decryption key for the authenticating ticket. It is also called
756 by the rxkad security module to decrypt admin tickets. The rxkad call is
757 with tt==0, since Rx can't call Ubik. */
760 ka_LookupKvno(tt, name, inst, kvno, key)
761 struct ubik_trans *tt;
765 struct ktc_encryptionKey *key;
770 struct kaentry tentry;
772 struct kaOldKeys okeys;
774 ObtainReadLock(&keycache_lock);
775 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
776 code = KAKEYCACHEINVALID;
778 for (i = 0; i < maxCachedKeys; i++) {
779 if (keyCache[i].used) { /* zero used date means invalid */
780 if ((keyCache[i].kvno == kvno)
781 && (strcmp(keyCache[i].name, name) == 0)
782 && (strcmp(keyCache[i].inst, inst) == 0)) {
783 memcpy(key, &keyCache[i].key, sizeof(*key));
784 keyCache[i].used = time(0);
785 ReleaseReadLock(&keycache_lock);
792 ReleaseReadLock(&keycache_lock);
796 /* we missed in the cache so need to look in the Ubik database */
797 code = FindBlock(tt, name, inst, &to, &tentry);
803 /* first check the current key */
804 if (tentry.key_version == htonl(kvno)) {
805 memcpy(key, &tentry.key, sizeof(*key));
806 ka_Encache(name, inst, kvno, key, NEVERDATE);
809 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
810 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
813 if (ntohl(okeys.entry) == to)
814 for (i = 0; i < NOLDKEYS; i++)
815 if (okeys.keys[i].superseded
816 && (ntohl(okeys.keys[i].version) == kvno)) {
817 memcpy(key, &okeys.keys[i].key, sizeof(*key));
818 ka_Encache(name, inst, kvno, key,
819 ntohl(okeys.keys[i].superseded));
826 /* Look up the primary key and key version for a principal. */
829 ka_LookupKey(tt, name, inst, kvno, key)
830 struct ubik_trans *tt;
833 afs_int32 *kvno; /* returned */
834 struct ktc_encryptionKey *key; /* copied out */
838 struct kaentry tentry;
841 ObtainReadLock(&keycache_lock);
842 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
843 code = KAKEYCACHEINVALID;
845 for (i = 0; i < maxCachedKeys; i++) {
846 if (keyCache[i].used) { /* zero used date means invalid */
847 if ((keyCache[i].superseded == NEVERDATE)
848 && (strcmp(keyCache[i].name, name) == 0)
849 && (strcmp(keyCache[i].inst, inst) == 0)) {
850 memcpy(key, &keyCache[i].key, sizeof(*key));
851 *kvno = keyCache[i].kvno;
852 keyCache[i].used = time(0);
853 ReleaseReadLock(&keycache_lock);
860 ReleaseReadLock(&keycache_lock);
864 /* we missed in the cache so need to look in the Ubik database */
865 code = FindBlock(tt, name, inst, &to, &tentry);
870 memcpy(key, &tentry.key, sizeof(*key));
871 *kvno = ntohl(tentry.key_version);
872 ka_Encache(name, inst, *kvno, key, NEVERDATE);
876 /* This is, hopefully a temporary mechanism to fill the cache will all keys
877 since filling cache misses during rxkad challenge responses will deadlock if
878 Ubik needs to use Rx. */
882 struct ubik_trans *tt;
888 struct ktc_encryptionKey k;
889 struct kaOldKeys okeys;
890 struct kaentry tentry;
892 /* this is a little marginal, but... */
893 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
897 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
898 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
901 /* get name & instance */
903 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
907 /* get all the old keys in this block */
908 for (i = 0; i < NOLDKEYS; i++)
909 if (okeys.keys[i].superseded) {
911 ka_LookupKvno(tt, tentry.userID.name,
912 tentry.userID.instance,
913 ntohl(okeys.keys[i].version), &k);
918 if (++nfound > maxCachedKeys)
919 return KADATABASEINCONSISTENT;
924 update_admin_count(tt, delta)
925 struct ubik_trans *tt;
931 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
932 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
934 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
944 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
945 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
950 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
953 name_instance_legal(name, instance)
959 /* No string checks apply anymore. The international people want to use full 8
960 bit ascii without problems. */
962 code = (strlen(name) < MAXKTCNAMELEN)
963 && (strlen(instance) < MAXKTCNAMELEN);
965 map = LEGALCHARS; /* permitted chars, instance allows <period> */
966 code = (strlen(name) > 0) && string_legal(instance, map)
967 && string_legal(name, map + 1);
970 dynamic_statistics.string_checks++;
975 string_legal(str, map)
982 if (slen >= MAXKTCNAMELEN)
983 return 0; /* with trailing null must fit in data base */
984 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */