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;
110 init_kadatabase(initFlags)
111 int initFlags; /* same as init_kaprocs (see which) */
113 Lock_Init(&cheader_lock);
114 Lock_Init(&keycache_lock);
118 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
123 maxKeyLifetime = MAXKTCTICKETLIFETIME;
129 /* check that the database has been initialized. Be careful to fail in a safe
130 manner, to avoid bogusly reinitializing the db. */
133 CheckInit(at, db_init)
134 struct ubik_trans *at;
135 int (*db_init) (); /* procedure to call if rebuilding DB */
137 register afs_int32 code;
141 /* Don't read header if not necessary */
142 if (!ubik_CacheUpdate(at))
145 ObtainWriteLock(&cheader_lock);
146 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
148 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
149 sizeof(afs_int32)))) {
151 printf("No data base\n");
153 printf("I/O Error\n");
155 iversion = ntohl(iversion); /* convert to host order */
156 tversion = ntohl(tversion);
157 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
158 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
160 printf("SetupHeader failed\n");
163 cheaderReadTime = time(0);
166 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
167 KADBVERSION, iversion, tversion);
171 ReleaseWriteLock(&cheader_lock);
175 /* if here, we have no version number or the wrong version number in the
177 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
182 if ((db_init == 0) || (code == KAIO))
185 printf("Error discovered in header, rebuilding.\n");
187 /* try to write a good header */
188 memset(&cheader, 0, sizeof(cheader));
189 cheader.version = htonl(KADBVERSION);
190 cheader.checkVersion = htonl(KADBVERSION);
191 cheader.headerSize = htonl(sizeof(cheader));
193 cheader.eofPtr = htonl(sizeof(cheader));
195 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
196 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
197 cheader.admin_accounts = 0;
198 cheader.hashsize = htonl(HASHSIZE);
199 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
201 return KAIO; /* return the error code */
203 return db_init(at); /* initialize the db */
206 /* Allocate a free block of storage for entry, returning address of a new
207 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
210 AllocBlock(at, tentry)
211 register struct ubik_trans *at;
212 struct kaentry *tentry;
214 register afs_int32 code;
217 if (cheader.freePtr) {
218 /* allocate this dude */
219 temp = ntohl(cheader.freePtr);
220 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
222 return 0; /* can't read block */
223 code = set_header_word(at, freePtr, tentry->next);
225 /* hosed, nothing on free list, grow file */
226 temp = ntohl(cheader.eofPtr); /* remember this guy */
227 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
232 code = inc_header_word(at, stats.allocs);
235 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
239 /* Free a block given its index. It must already have been unthreaded.
240 Returns zero for success or an error code on failure. */
244 struct ubik_trans *at;
247 struct kaentry tentry;
250 /* check index just to be on the safe side */
251 if (!index_OK(index))
254 memset(&tentry, 0, sizeof(kaentry));
255 tentry.next = cheader.freePtr;
256 tentry.flags = htonl(KAFFREE);
257 code = set_header_word(at, freePtr, htonl(index));
260 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
264 code = inc_header_word(at, stats.frees);
270 /* Look for a block by name and instance. If found read the block's contents
271 into the area pointed to by tentry and return the block's index. If not
272 found offset is set to zero. If an error is encountered a non-zero code is
276 FindBlock(at, aname, ainstance, toP, tentry)
277 struct ubik_trans *at;
281 struct kaentry *tentry;
283 register afs_int32 i, code;
284 register afs_int32 to;
287 i = NameHash(aname, ainstance);
288 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
289 to = ntohl(tentry->next)) {
290 code = karead(at, to, (char *)tentry, sizeof(kaentry));
293 /* see if the name matches */
294 if (!strcmp(aname, tentry->userID.name)
295 && (ainstance == (char *)0
296 || !strcmp(ainstance, tentry->userID.instance))) {
297 *toP = to; /* found it */
301 *toP = 0; /* no such entry */
305 /* Add a block to the hash table given a pointer to the block and its index.
306 The block is threaded onto the hash table and written to disk. The routine
307 returns zero if there were no errors. */
310 ThreadBlock(at, index, tentry)
311 struct ubik_trans *at;
313 struct kaentry *tentry;
316 int hi; /* hash index */
318 if (!index_OK(index))
320 hi = NameHash(tentry->userID.name, tentry->userID.instance);
321 tentry->next = cheader.nameHash[hi];
322 code = set_header_word(at, nameHash[hi], htonl(index));
325 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
331 /* Remove a block from the hash table. If success return 0, else return an
335 UnthreadBlock(at, aentry)
336 struct ubik_trans *at;
337 struct kaentry *aentry;
339 register afs_int32 i, code;
340 register afs_int32 to;
342 struct kaentry tentry;
344 i = NameHash(aentry->userID.name, aentry->userID.instance);
346 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
347 to = ntohl(tentry.next)) {
348 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
351 /* see if the name matches */
352 if (!strcmp(aentry->userID.name, tentry.userID.name)
353 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
355 if (lo) { /* unthread from last block */
357 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
360 } else { /* unthread from hash table */
361 code = set_header_word(at, nameHash[i], tentry.next);
365 aentry->next = 0; /* just to be sure */
368 lo = DOFFSET(to, &tentry, &tentry.next);
373 /* Given an index to the last block (or zero the first time) read the contents
374 of the next block and return its index. The last argument is a pointer to
375 an estimate of the number of remaining blocks to read out. The remaining
376 count is an estimate because it may include free blocks that are not
377 returned. If there are no more blocks remaining is zero and the returned
378 index is zero. A non-zero index indicates that tentry has been filled with
379 valid data. If an error is encountered the returned index is zero and the
380 remaining count is negative. */
383 NextBlock(at, index, tentry, remaining)
384 struct ubik_trans *at;
386 struct kaentry *tentry;
387 afs_int32 *remaining;
392 if (index == 0) /* get first one */
393 index = sizeof(cheader);
395 if (!index_OK(index)) {
396 *remaining = -1; /* error */
399 index += sizeof(kaentry);
401 /* now search for the first entry that isn't free */
402 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
403 code = karead(at, index, (char *)tentry, sizeof(kaentry));
408 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
409 /* estimate remaining number of entries, not including this one */
410 *remaining = (last - index) / sizeof(kaentry) - 1;
414 *remaining = 0; /* no more entries */
418 /* These are a collections of routines that deal with externally known keys.
419 They maintain a database of key version numbers and the corresponding key
420 and pointer to the user entry. */
423 ka_NewKey(tt, tentryaddr, tentry, key)
424 struct ubik_trans *tt;
425 afs_int32 tentryaddr;
426 struct kaentry *tentry;
427 struct ktc_encryptionKey *key;
429 struct kaOldKeys okeys; /* old keys block */
430 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
431 afs_int32 prevptr, nextprevptr;
434 afs_int32 newkeyver; /* new key version number */
435 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
436 int foundcurrentkey = 0, addednewkey = 0, modified;
438 es_Report("Newkey for %s.%s\n", tentry->userID.name,
439 tentry->userID.instance);
441 newkeyver = ntohl(tentry->key_version) + 1;
442 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
445 /* An entry may have more than one oldkeys blocks. The entry
446 * points to the most current, but all the oldkeys blocks for an
447 * entry are not linked together. All oldkeys blocks for all
448 * entries are linked together off of the header. So we follow
451 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
452 prevptr = nextprevptr, okeysaddr = nextaddr) {
453 /* foreacholdkeysblock */
454 /* Read the oldKeys block */
455 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
459 nextaddr = ntohl(okeys.next);
460 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
462 /* We only want oldkey blocks that belong to this entry */
463 if (ntohl(okeys.entry) != tentryaddr)
466 modified = 0; /* This oldkeys block has not been modified */
467 keyentries = 0; /* Number of valid key entries in the block */
468 for (i = 0; i < NOLDKEYS; i++) {
470 /* Keep count of number of entries found */
471 if (okeys.keys[i].superseded != 0) {
472 oldtotalkeyentries++;
475 /* If we find the entry that is not superseded, then supersede it */
476 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
477 okeys.keys[i].superseded = htonl(now);
480 if (foundcurrentkey) {
482 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
483 tentry->userID.name, tentry->userID.instance));
489 /* If we find an oldkey of the same version or
490 * an old key that has expired, then delete it.
492 if ((ntohl(okeys.keys[i].version) == newkeyver)
493 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
494 okeys.keys[i].superseded = 0;
495 okeys.keys[i].version = htonl(-1);
496 memset(&okeys.keys[i].key, 0,
497 sizeof(struct ktc_encryptionKey));
500 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
501 now - ntohl(okeys.keys[i].superseded),
502 ntohl(okeys.keys[i].version));
505 /* Add our key here if its free */
506 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
507 okeys.keys[i].version = htonl(newkeyver);
508 okeys.keys[i].superseded = htonl(NEVERDATE);
509 memcpy(&okeys.keys[i].key, key,
510 sizeof(struct ktc_encryptionKey));
512 addednewkey = okeysaddr;
515 /* Keep count of number of entries found */
516 if (okeys.keys[i].superseded != 0) {
518 newtotalkeyentries++;
522 /* If we modified the block, write it out */
523 if (modified && keyentries) {
524 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
529 /* If there are no more entries in this oldkeys block, delete it */
530 if (keyentries == 0) {
532 code = set_header_word(tt, kvnoPtr, okeys.next);
535 kawrite(tt, prevptr, (char *)&okeys.next,
540 code = FreeBlock(tt, okeysaddr);
544 nextprevptr = prevptr; /* won't bump prevptr */
546 } /* foreacholdkeysblock */
548 /* If we could not add the key, create a new oldkeys block */
550 /* Allocate and fill in an oldkeys block */
551 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
554 okeys.flags = htonl(KAFOLDKEYS);
555 okeys.entry = htonl(tentryaddr);
556 okeys.keys[0].version = htonl(newkeyver);
557 okeys.keys[0].superseded = htonl(NEVERDATE);
558 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
559 newtotalkeyentries++;
561 /* Thread onto the header's chain of oldkeys */
562 okeys.next = cheader.kvnoPtr;
563 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
567 /* Write the oldkeys block out */
568 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
572 es_Report("New oldkey block allocated at %d\n", addednewkey);
575 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
577 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
578 tentry->userID.name, tentry->userID.instance,
579 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
583 /* Update the tentry. We rely on caller to write it out */
584 tentry->misc.asServer.oldKeys = htonl(addednewkey);
585 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
586 tentry->key_version = htonl(newkeyver);
587 memcpy(&tentry->key, key, sizeof(tentry->key));
589 /* invalidate key caches everywhere */
590 code = inc_header_word(tt, specialKeysVersion);
594 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
600 ka_DelKey(tt, tentryaddr, tentry)
601 struct ubik_trans *tt;
602 afs_int32 tentryaddr;
603 struct kaentry *tentry;
606 struct kaOldKeys okeys; /* old keys block */
607 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
608 afs_int32 prevptr = 0;
611 es_Report("DelKey for %s.%s\n", tentry->userID.name,
612 tentry->userID.instance);
614 /* An entry may have more than one oldkeys blocks. The entry
615 * points to the most current, but all the oldkeys blocks for an
616 * entry are not linked together. All oldkeys blocks for all
617 * entries are linked together off of the header. So we follow
620 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
621 /* foreacholdkeysblock */
622 /* Read the oldKeys block */
623 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
626 nextaddr = ntohl(okeys.next);
628 /* We only want oldkey blocks that belong to this entry */
629 if (ntohl(okeys.entry) != tentryaddr) {
630 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
634 /* Delete the oldkeys block */
637 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
639 code = set_header_word(tt, kvnoPtr, okeys.next);
643 code = FreeBlock(tt, okeysaddr);
646 } /* foreacholdkeysblock */
648 /* Update the tentry. We rely on caller to write it out */
649 tentry->misc.asServer.oldKeys = 0;
650 tentry->misc.asServer.nOldKeys = 0;
652 /* invalidate key caches everywhere */
653 code = inc_header_word(tt, specialKeysVersion);
661 ka_debugKeyCache(info)
662 struct ka_debugInfo *info;
666 memcpy(&info->cheader_lock, &cheader_lock, sizeof(info->cheader_lock));
667 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
669 info->kcVersion = keyCacheVersion;
670 info->kcSize = maxCachedKeys;
672 for (i = 0; i < maxCachedKeys; i++) {
673 if (keyCache[i].used) {
674 if (info->kcUsed < KADEBUGKCINFOSIZE) {
675 int j = info->kcUsed;
676 char principal[sizeof(keyCache[0].name) +
677 sizeof(keyCache[0].inst)];
679 info->kcInfo[j].used = keyCache[i].superseded;
680 info->kcInfo[j].kvno = keyCache[i].kvno;
681 info->kcInfo[j].primary =
682 (keyCache[i].superseded == NEVERDATE);
683 info->kcInfo[j].keycksum = 0;
687 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
688 info->kcInfo[j].keycksum +=
689 ((char *)&keyCache[i].key)[k];
692 strcpy(principal, keyCache[i].name);
693 strcat(principal, ".");
694 strcat(principal, keyCache[i].inst);
695 strncpy(info->kcInfo[j].principal, principal,
696 sizeof(info->kcInfo[0].principal));
703 /* Add a key to the key cache, expanding it if necessary. */
706 ka_Encache(name, inst, kvno, key, superseded)
710 struct ktc_encryptionKey *key;
715 ObtainWriteLock(&keycache_lock);
716 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
717 for (i = 0; i < maxCachedKeys; i++)
718 keyCache[i].used = 0;
721 for (i = 0; i < maxCachedKeys; i++)
722 if (keyCache[i].used == 0) {
724 keyCache[i].kvno = kvno;
725 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
726 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
727 keyCacheVersion = ntohl(cheader.specialKeysVersion);
728 memcpy(&keyCache[i].key, key, sizeof(*key));
729 keyCache[i].superseded = superseded;
730 keyCache[i].used = time(0);
732 ReleaseWriteLock(&keycache_lock);
735 /* i == maxCachedKeys */
737 (struct cachedKey *)realloc(keyCache,
739 2) * sizeof(struct cachedKey));
741 es_Report("Can't realloc keyCache! out of memory?");
746 int j = i; /* initialize new storage */
747 while (j < maxCachedKeys)
748 keyCache[j++].used = 0;
753 /* Look up the key given a principal and a kvno. This is called by GetTicket
754 to get the decryption key for the authenticating ticket. It is also called
755 by the rxkad security module to decrypt admin tickets. The rxkad call is
756 with tt==0, since Rx can't call Ubik. */
759 ka_LookupKvno(tt, name, inst, kvno, key)
760 struct ubik_trans *tt;
764 struct ktc_encryptionKey *key;
769 struct kaentry tentry;
771 struct kaOldKeys okeys;
773 ObtainReadLock(&keycache_lock);
774 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
775 code = KAKEYCACHEINVALID;
777 for (i = 0; i < maxCachedKeys; i++) {
778 if (keyCache[i].used) { /* zero used date means invalid */
779 if ((keyCache[i].kvno == kvno)
780 && (strcmp(keyCache[i].name, name) == 0)
781 && (strcmp(keyCache[i].inst, inst) == 0)) {
782 memcpy(key, &keyCache[i].key, sizeof(*key));
783 keyCache[i].used = time(0);
784 ReleaseReadLock(&keycache_lock);
791 ReleaseReadLock(&keycache_lock);
795 /* we missed in the cache so need to look in the Ubik database */
796 code = FindBlock(tt, name, inst, &to, &tentry);
802 /* first check the current key */
803 if (tentry.key_version == htonl(kvno)) {
804 memcpy(key, &tentry.key, sizeof(*key));
805 ka_Encache(name, inst, kvno, key, NEVERDATE);
808 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
809 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
812 if (ntohl(okeys.entry) == to)
813 for (i = 0; i < NOLDKEYS; i++)
814 if (okeys.keys[i].superseded
815 && (ntohl(okeys.keys[i].version) == kvno)) {
816 memcpy(key, &okeys.keys[i].key, sizeof(*key));
817 ka_Encache(name, inst, kvno, key,
818 ntohl(okeys.keys[i].superseded));
825 /* Look up the primary key and key version for a principal. */
828 ka_LookupKey(tt, name, inst, kvno, key)
829 struct ubik_trans *tt;
832 afs_int32 *kvno; /* returned */
833 struct ktc_encryptionKey *key; /* copied out */
837 struct kaentry tentry;
840 ObtainReadLock(&keycache_lock);
841 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
842 code = KAKEYCACHEINVALID;
844 for (i = 0; i < maxCachedKeys; i++) {
845 if (keyCache[i].used) { /* zero used date means invalid */
846 if ((keyCache[i].superseded == NEVERDATE)
847 && (strcmp(keyCache[i].name, name) == 0)
848 && (strcmp(keyCache[i].inst, inst) == 0)) {
849 memcpy(key, &keyCache[i].key, sizeof(*key));
850 *kvno = keyCache[i].kvno;
851 keyCache[i].used = time(0);
852 ReleaseReadLock(&keycache_lock);
859 ReleaseReadLock(&keycache_lock);
863 /* we missed in the cache so need to look in the Ubik database */
864 code = FindBlock(tt, name, inst, &to, &tentry);
869 memcpy(key, &tentry.key, sizeof(*key));
870 *kvno = ntohl(tentry.key_version);
871 ka_Encache(name, inst, *kvno, key, NEVERDATE);
875 /* This is, hopefully a temporary mechanism to fill the cache will all keys
876 since filling cache misses during rxkad challenge responses will deadlock if
877 Ubik needs to use Rx. */
881 struct ubik_trans *tt;
887 struct ktc_encryptionKey k;
888 struct kaOldKeys okeys;
889 struct kaentry tentry;
891 /* this is a little marginal, but... */
892 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
896 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
897 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
900 /* get name & instance */
902 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
906 /* get all the old keys in this block */
907 for (i = 0; i < NOLDKEYS; i++)
908 if (okeys.keys[i].superseded) {
910 ka_LookupKvno(tt, tentry.userID.name,
911 tentry.userID.instance,
912 ntohl(okeys.keys[i].version), &k);
917 if (++nfound > maxCachedKeys)
918 return KADATABASEINCONSISTENT;
923 update_admin_count(tt, delta)
924 struct ubik_trans *tt;
930 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
931 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
933 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
943 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
944 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
949 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
952 name_instance_legal(name, instance)
958 /* No string checks apply anymore. The international people want to use full 8
959 bit ascii without problems. */
961 code = (strlen(name) < MAXKTCNAMELEN)
962 && (strlen(instance) < MAXKTCNAMELEN);
964 map = LEGALCHARS; /* permitted chars, instance allows <period> */
965 code = (strlen(name) > 0) && string_legal(instance, map)
966 && string_legal(name, map + 1);
969 dynamic_statistics.string_checks++;
974 string_legal(str, map)
981 if (slen >= MAXKTCNAMELEN)
982 return 0; /* with trailing null must fit in data base */
983 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */