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 Date cheaderReadTime; /* time cheader last read in */
40 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
42 #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))
44 static int index_OK();
47 NameHash(aname, ainstance)
49 register char *ainstance;
51 register unsigned int hash;
54 /* stolen directly from the HashString function in the vol package */
56 for (i = strlen(aname), aname += i - 1; i--; aname--)
57 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
58 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
59 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
60 return (hash % HASHSIZE);
63 /* package up seek and write into one procedure for ease of use */
66 kawrite(tt, pos, buff, len)
67 struct ubik_trans *tt;
74 code = ubik_Seek(tt, 0, pos);
77 code = ubik_Write(tt, buff, len);
81 /* same thing for read */
84 karead(tt, pos, buff, len)
85 struct ubik_trans *tt;
92 code = ubik_Seek(tt, 0, pos);
95 code = ubik_Read(tt, buff, len);
99 static struct Lock cheader_lock;
100 static struct Lock keycache_lock;
102 static int maxCachedKeys;
104 static struct cachedKey {
106 int superseded; /* NEVERDATE => this is current key */
108 struct ktc_encryptionKey key;
109 char name[MAXKTCNAMELEN];
110 char inst[MAXKTCNAMELEN];
112 static afs_int32 keyCacheVersion = 0;
114 static afs_int32 maxKeyLifetime;
115 static int dbfixup = 0;
117 init_kadatabase(initFlags)
118 int initFlags; /* same as init_kaprocs (see which) */
120 Lock_Init(&cheader_lock);
121 Lock_Init(&keycache_lock);
125 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
130 maxKeyLifetime = MAXKTCTICKETLIFETIME;
136 /* check that the database has been initialized. Be careful to fail in a safe
137 manner, to avoid bogusly reinitializing the db. */
140 CheckInit(at, db_init)
141 struct ubik_trans *at;
142 int (*db_init) (); /* procedure to call if rebuilding DB */
144 register afs_int32 code;
148 /* Don't read header if not necessary */
149 if (!ubik_CacheUpdate(at))
152 ObtainWriteLock(&cheader_lock);
153 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
155 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
156 sizeof(afs_int32)))) {
158 printf("No data base\n");
160 printf("I/O Error\n");
162 iversion = ntohl(iversion); /* convert to host order */
163 tversion = ntohl(tversion);
164 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
165 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
167 printf("SetupHeader failed\n");
170 cheaderReadTime = time(0);
173 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
174 KADBVERSION, iversion, tversion);
178 ReleaseWriteLock(&cheader_lock);
182 /* if here, we have no version number or the wrong version number in the
184 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
189 if ((db_init == 0) || (code == KAIO))
192 printf("Error discovered in header, rebuilding.\n");
194 /* try to write a good header */
195 memset(&cheader, 0, sizeof(cheader));
196 cheader.version = htonl(KADBVERSION);
197 cheader.checkVersion = htonl(KADBVERSION);
198 cheader.headerSize = htonl(sizeof(cheader));
200 cheader.eofPtr = htonl(sizeof(cheader));
202 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
203 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
204 cheader.admin_accounts = 0;
205 cheader.hashsize = htonl(HASHSIZE);
206 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
208 return KAIO; /* return the error code */
210 return db_init(at); /* initialize the db */
213 /* Allocate a free block of storage for entry, returning address of a new
214 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
217 AllocBlock(at, tentry)
218 register struct ubik_trans *at;
219 struct kaentry *tentry;
221 register afs_int32 code;
224 if (cheader.freePtr) {
225 /* allocate this dude */
226 temp = ntohl(cheader.freePtr);
227 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
229 return 0; /* can't read block */
230 code = set_header_word(at, freePtr, tentry->next);
232 /* hosed, nothing on free list, grow file */
233 temp = ntohl(cheader.eofPtr); /* remember this guy */
234 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
239 code = inc_header_word(at, stats.allocs);
242 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
246 /* Free a block given its index. It must already have been unthreaded.
247 Returns zero for success or an error code on failure. */
251 struct ubik_trans *at;
254 struct kaentry tentry;
257 /* check index just to be on the safe side */
258 if (!index_OK(index))
261 memset(&tentry, 0, sizeof(kaentry));
262 tentry.next = cheader.freePtr;
263 tentry.flags = htonl(KAFFREE);
264 code = set_header_word(at, freePtr, htonl(index));
267 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
271 code = inc_header_word(at, stats.frees);
277 /* Look for a block by name and instance. If found read the block's contents
278 into the area pointed to by tentry and return the block's index. If not
279 found offset is set to zero. If an error is encountered a non-zero code is
283 FindBlock(at, aname, ainstance, toP, tentry)
284 struct ubik_trans *at;
288 struct kaentry *tentry;
290 register afs_int32 i, code;
291 register afs_int32 to;
294 i = NameHash(aname, ainstance);
295 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
296 to = ntohl(tentry->next)) {
297 code = karead(at, to, (char *)tentry, sizeof(kaentry));
300 /* see if the name matches */
301 if (!strcmp(aname, tentry->userID.name)
302 && (ainstance == (char *)0
303 || !strcmp(ainstance, tentry->userID.instance))) {
304 *toP = to; /* found it */
308 *toP = 0; /* no such entry */
312 /* Add a block to the hash table given a pointer to the block and its index.
313 The block is threaded onto the hash table and written to disk. The routine
314 returns zero if there were no errors. */
317 ThreadBlock(at, index, tentry)
318 struct ubik_trans *at;
320 struct kaentry *tentry;
323 int hi; /* hash index */
325 if (!index_OK(index))
327 hi = NameHash(tentry->userID.name, tentry->userID.instance);
328 tentry->next = cheader.nameHash[hi];
329 code = set_header_word(at, nameHash[hi], htonl(index));
332 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
338 /* Remove a block from the hash table. If success return 0, else return an
342 UnthreadBlock(at, aentry)
343 struct ubik_trans *at;
344 struct kaentry *aentry;
346 register afs_int32 i, code;
347 register afs_int32 to;
349 struct kaentry tentry;
351 i = NameHash(aentry->userID.name, aentry->userID.instance);
353 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
354 to = ntohl(tentry.next)) {
355 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
358 /* see if the name matches */
359 if (!strcmp(aentry->userID.name, tentry.userID.name)
360 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
362 if (lo) { /* unthread from last block */
364 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
367 } else { /* unthread from hash table */
368 code = set_header_word(at, nameHash[i], tentry.next);
372 aentry->next = 0; /* just to be sure */
375 lo = DOFFSET(to, &tentry, &tentry.next);
380 /* Given an index to the last block (or zero the first time) read the contents
381 of the next block and return its index. The last argument is a pointer to
382 an estimate of the number of remaining blocks to read out. The remaining
383 count is an estimate because it may include free blocks that are not
384 returned. If there are no more blocks remaining is zero and the returned
385 index is zero. A non-zero index indicates that tentry has been filled with
386 valid data. If an error is encountered the returned index is zero and the
387 remaining count is negative. */
390 NextBlock(at, index, tentry, remaining)
391 struct ubik_trans *at;
393 struct kaentry *tentry;
394 afs_int32 *remaining;
399 if (index == 0) /* get first one */
400 index = sizeof(cheader);
402 if (!index_OK(index)) {
403 *remaining = -1; /* error */
406 index += sizeof(kaentry);
408 /* now search for the first entry that isn't free */
409 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
410 code = karead(at, index, (char *)tentry, sizeof(kaentry));
415 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
416 /* estimate remaining number of entries, not including this one */
417 *remaining = (last - index) / sizeof(kaentry) - 1;
421 *remaining = 0; /* no more entries */
425 /* These are a collections of routines that deal with externally known keys.
426 They maintain a database of key version numbers and the corresponding key
427 and pointer to the user entry. */
430 ka_NewKey(tt, tentryaddr, tentry, key)
431 struct ubik_trans *tt;
432 afs_int32 tentryaddr;
433 struct kaentry *tentry;
434 struct ktc_encryptionKey *key;
436 struct kaOldKeys okeys; /* old keys block */
437 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
438 afs_int32 prevptr, nextprevptr;
441 afs_int32 newkeyver; /* new key version number */
442 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
443 int foundcurrentkey = 0, addednewkey = 0, modified;
445 es_Report("Newkey for %s.%s\n", tentry->userID.name,
446 tentry->userID.instance);
448 newkeyver = ntohl(tentry->key_version) + 1;
449 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
452 /* An entry may have more than one oldkeys blocks. The entry
453 * points to the most current, but all the oldkeys blocks for an
454 * entry are not linked together. All oldkeys blocks for all
455 * entries are linked together off of the header. So we follow
458 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
459 prevptr = nextprevptr, okeysaddr = nextaddr) {
460 /* foreacholdkeysblock */
461 /* Read the oldKeys block */
462 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
466 nextaddr = ntohl(okeys.next);
467 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
469 /* We only want oldkey blocks that belong to this entry */
470 if (ntohl(okeys.entry) != tentryaddr)
473 modified = 0; /* This oldkeys block has not been modified */
474 keyentries = 0; /* Number of valid key entries in the block */
475 for (i = 0; i < NOLDKEYS; i++) {
477 /* Keep count of number of entries found */
478 if (okeys.keys[i].superseded != 0) {
479 oldtotalkeyentries++;
482 /* If we find the entry that is not superseded, then supersede it */
483 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
484 okeys.keys[i].superseded = htonl(now);
487 if (foundcurrentkey) {
489 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
490 tentry->userID.name, tentry->userID.instance));
496 /* If we find an oldkey of the same version or
497 * an old key that has expired, then delete it.
499 if ((ntohl(okeys.keys[i].version) == newkeyver)
500 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
501 okeys.keys[i].superseded = 0;
502 okeys.keys[i].version = htonl(-1);
503 memset(&okeys.keys[i].key, 0,
504 sizeof(struct ktc_encryptionKey));
507 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
508 now - ntohl(okeys.keys[i].superseded),
509 ntohl(okeys.keys[i].version));
512 /* Add our key here if its free */
513 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
514 okeys.keys[i].version = htonl(newkeyver);
515 okeys.keys[i].superseded = htonl(NEVERDATE);
516 memcpy(&okeys.keys[i].key, key,
517 sizeof(struct ktc_encryptionKey));
519 addednewkey = okeysaddr;
522 /* Keep count of number of entries found */
523 if (okeys.keys[i].superseded != 0) {
525 newtotalkeyentries++;
529 /* If we modified the block, write it out */
530 if (modified && keyentries) {
531 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
536 /* If there are no more entries in this oldkeys block, delete it */
537 if (keyentries == 0) {
539 code = set_header_word(tt, kvnoPtr, okeys.next);
542 kawrite(tt, prevptr, (char *)&okeys.next,
547 code = FreeBlock(tt, okeysaddr);
551 nextprevptr = prevptr; /* won't bump prevptr */
553 } /* foreacholdkeysblock */
555 /* If we could not add the key, create a new oldkeys block */
557 /* Allocate and fill in an oldkeys block */
558 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
561 okeys.flags = htonl(KAFOLDKEYS);
562 okeys.entry = htonl(tentryaddr);
563 okeys.keys[0].version = htonl(newkeyver);
564 okeys.keys[0].superseded = htonl(NEVERDATE);
565 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
566 newtotalkeyentries++;
568 /* Thread onto the header's chain of oldkeys */
569 okeys.next = cheader.kvnoPtr;
570 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
574 /* Write the oldkeys block out */
575 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
579 es_Report("New oldkey block allocated at %d\n", addednewkey);
582 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
584 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
585 tentry->userID.name, tentry->userID.instance,
586 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
590 /* Update the tentry. We rely on caller to write it out */
591 tentry->misc.asServer.oldKeys = htonl(addednewkey);
592 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
593 tentry->key_version = htonl(newkeyver);
594 memcpy(&tentry->key, key, sizeof(tentry->key));
596 /* invalidate key caches everywhere */
597 code = inc_header_word(tt, specialKeysVersion);
601 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
607 ka_DelKey(tt, tentryaddr, tentry)
608 struct ubik_trans *tt;
609 afs_int32 tentryaddr;
610 struct kaentry *tentry;
613 struct kaOldKeys okeys; /* old keys block */
614 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
615 afs_int32 prevptr = 0;
618 es_Report("DelKey for %s.%s\n", tentry->userID.name,
619 tentry->userID.instance);
621 /* An entry may have more than one oldkeys blocks. The entry
622 * points to the most current, but all the oldkeys blocks for an
623 * entry are not linked together. All oldkeys blocks for all
624 * entries are linked together off of the header. So we follow
627 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
628 /* foreacholdkeysblock */
629 /* Read the oldKeys block */
630 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
633 nextaddr = ntohl(okeys.next);
635 /* We only want oldkey blocks that belong to this entry */
636 if (ntohl(okeys.entry) != tentryaddr) {
637 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
641 /* Delete the oldkeys block */
644 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
646 code = set_header_word(tt, kvnoPtr, okeys.next);
650 code = FreeBlock(tt, okeysaddr);
653 } /* foreacholdkeysblock */
655 /* Update the tentry. We rely on caller to write it out */
656 tentry->misc.asServer.oldKeys = 0;
657 tentry->misc.asServer.nOldKeys = 0;
659 /* invalidate key caches everywhere */
660 code = inc_header_word(tt, specialKeysVersion);
668 ka_debugKeyCache(info)
669 struct ka_debugInfo *info;
673 memcpy(&info->cheader_lock, &cheader_lock, sizeof(info->cheader_lock));
674 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
676 info->kcVersion = keyCacheVersion;
677 info->kcSize = maxCachedKeys;
679 for (i = 0; i < maxCachedKeys; i++) {
680 if (keyCache[i].used) {
681 if (info->kcUsed < KADEBUGKCINFOSIZE) {
682 int j = info->kcUsed;
683 char principal[sizeof(keyCache[0].name) +
684 sizeof(keyCache[0].inst)];
686 info->kcInfo[j].used = keyCache[i].superseded;
687 info->kcInfo[j].kvno = keyCache[i].kvno;
688 info->kcInfo[j].primary =
689 (keyCache[i].superseded == NEVERDATE);
690 info->kcInfo[j].keycksum = 0;
694 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
695 info->kcInfo[j].keycksum +=
696 ((char *)&keyCache[i].key)[k];
699 strcpy(principal, keyCache[i].name);
700 strcat(principal, ".");
701 strcat(principal, keyCache[i].inst);
702 strncpy(info->kcInfo[j].principal, principal,
703 sizeof(info->kcInfo[0].principal));
710 /* Add a key to the key cache, expanding it if necessary. */
713 ka_Encache(name, inst, kvno, key, superseded)
717 struct ktc_encryptionKey *key;
722 ObtainWriteLock(&keycache_lock);
723 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
724 for (i = 0; i < maxCachedKeys; i++)
725 keyCache[i].used = 0;
728 for (i = 0; i < maxCachedKeys; i++)
729 if (keyCache[i].used == 0) {
731 keyCache[i].kvno = kvno;
732 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
733 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
734 keyCacheVersion = ntohl(cheader.specialKeysVersion);
735 memcpy(&keyCache[i].key, key, sizeof(*key));
736 keyCache[i].superseded = superseded;
737 keyCache[i].used = time(0);
739 ReleaseWriteLock(&keycache_lock);
742 /* i == maxCachedKeys */
744 (struct cachedKey *)realloc(keyCache,
746 2) * sizeof(struct cachedKey));
748 es_Report("Can't realloc keyCache! out of memory?");
753 int j = i; /* initialize new storage */
754 while (j < maxCachedKeys)
755 keyCache[j++].used = 0;
760 /* Look up the key given a principal and a kvno. This is called by GetTicket
761 to get the decryption key for the authenticating ticket. It is also called
762 by the rxkad security module to decrypt admin tickets. The rxkad call is
763 with tt==0, since Rx can't call Ubik. */
766 ka_LookupKvno(tt, name, inst, kvno, key)
767 struct ubik_trans *tt;
771 struct ktc_encryptionKey *key;
776 struct kaentry tentry;
778 struct kaOldKeys okeys;
780 ObtainReadLock(&keycache_lock);
781 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
782 code = KAKEYCACHEINVALID;
784 for (i = 0; i < maxCachedKeys; i++) {
785 if (keyCache[i].used) { /* zero used date means invalid */
786 if ((keyCache[i].kvno == kvno)
787 && (strcmp(keyCache[i].name, name) == 0)
788 && (strcmp(keyCache[i].inst, inst) == 0)) {
789 memcpy(key, &keyCache[i].key, sizeof(*key));
790 keyCache[i].used = time(0);
791 ReleaseReadLock(&keycache_lock);
798 ReleaseReadLock(&keycache_lock);
802 /* we missed in the cache so need to look in the Ubik database */
803 code = FindBlock(tt, name, inst, &to, &tentry);
809 /* first check the current key */
810 if (tentry.key_version == htonl(kvno)) {
811 memcpy(key, &tentry.key, sizeof(*key));
812 ka_Encache(name, inst, kvno, key, NEVERDATE);
815 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
816 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
819 if (ntohl(okeys.entry) == to)
820 for (i = 0; i < NOLDKEYS; i++)
821 if (okeys.keys[i].superseded
822 && (ntohl(okeys.keys[i].version) == kvno)) {
823 memcpy(key, &okeys.keys[i].key, sizeof(*key));
824 ka_Encache(name, inst, kvno, key,
825 ntohl(okeys.keys[i].superseded));
832 /* Look up the primary key and key version for a principal. */
835 ka_LookupKey(tt, name, inst, kvno, key)
836 struct ubik_trans *tt;
839 afs_int32 *kvno; /* returned */
840 struct ktc_encryptionKey *key; /* copied out */
844 struct kaentry tentry;
847 ObtainReadLock(&keycache_lock);
848 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
849 code = KAKEYCACHEINVALID;
851 for (i = 0; i < maxCachedKeys; i++) {
852 if (keyCache[i].used) { /* zero used date means invalid */
853 if ((keyCache[i].superseded == NEVERDATE)
854 && (strcmp(keyCache[i].name, name) == 0)
855 && (strcmp(keyCache[i].inst, inst) == 0)) {
856 memcpy(key, &keyCache[i].key, sizeof(*key));
857 *kvno = keyCache[i].kvno;
858 keyCache[i].used = time(0);
859 ReleaseReadLock(&keycache_lock);
866 ReleaseReadLock(&keycache_lock);
870 /* we missed in the cache so need to look in the Ubik database */
871 code = FindBlock(tt, name, inst, &to, &tentry);
876 memcpy(key, &tentry.key, sizeof(*key));
877 *kvno = ntohl(tentry.key_version);
878 ka_Encache(name, inst, *kvno, key, NEVERDATE);
882 /* This is, hopefully a temporary mechanism to fill the cache will all keys
883 since filling cache misses during rxkad challenge responses will deadlock if
884 Ubik needs to use Rx. */
888 struct ubik_trans *tt;
894 struct ktc_encryptionKey k;
895 struct kaOldKeys okeys;
896 struct kaentry tentry;
898 /* this is a little marginal, but... */
899 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
903 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
904 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
907 /* get name & instance */
909 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
913 /* get all the old keys in this block */
914 for (i = 0; i < NOLDKEYS; i++)
915 if (okeys.keys[i].superseded) {
917 ka_LookupKvno(tt, tentry.userID.name,
918 tentry.userID.instance,
919 ntohl(okeys.keys[i].version), &k);
924 if (++nfound > maxCachedKeys)
925 return KADATABASEINCONSISTENT;
930 update_admin_count(tt, delta)
931 struct ubik_trans *tt;
937 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
938 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
940 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
950 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
951 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
956 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
959 name_instance_legal(name, instance)
965 /* No string checks apply anymore. The international people want to use full 8
966 bit ascii without problems. */
968 code = (strlen(name) < MAXKTCNAMELEN)
969 && (strlen(instance) < MAXKTCNAMELEN);
971 map = LEGALCHARS; /* permitted chars, instance allows <period> */
972 code = (strlen(name) > 0) && string_legal(instance, map)
973 && string_legal(name, map + 1);
976 dynamic_statistics.string_checks++;
981 string_legal(str, map)
988 if (slen >= MAXKTCNAMELEN)
989 return 0; /* with trailing null must fit in data base */
990 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */