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>
14 #include <sys/types.h>
18 #include <netinet/in.h>
24 #include <afs/afsutil.h>
29 extern Date cheaderReadTime; /* time cheader last read in */
31 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
33 #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))
35 static int index_OK(afs_int32);
38 NameHash(register char *aname, register char *ainstance)
40 register unsigned int hash;
43 /* stolen directly from the HashString function in the vol package */
45 for (i = strlen(aname), aname += i - 1; i--; aname--)
46 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
47 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
48 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
49 return (hash % HASHSIZE);
52 /* package up seek and write into one procedure for ease of use */
55 kawrite(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
59 code = ubik_Seek(tt, 0, pos);
62 code = ubik_Write(tt, buff, len);
66 /* same thing for read */
69 karead(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
73 code = ubik_Seek(tt, 0, pos);
76 code = ubik_Read(tt, buff, len);
80 static struct Lock cheader_lock;
81 static struct Lock keycache_lock;
83 static int maxCachedKeys;
85 static struct cachedKey {
87 int superseded; /* NEVERDATE => this is current key */
89 struct ktc_encryptionKey key;
90 char name[MAXKTCNAMELEN];
91 char inst[MAXKTCNAMELEN];
93 static afs_int32 keyCacheVersion = 0;
95 static afs_int32 maxKeyLifetime;
96 static int dbfixup = 0;
99 init_kadatabase(int initFlags)
101 Lock_Init(&cheader_lock);
102 Lock_Init(&keycache_lock);
106 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
111 maxKeyLifetime = MAXKTCTICKETLIFETIME;
117 /* check that the database has been initialized. Be careful to fail in a safe
118 manner, to avoid bogusly reinitializing the db. */
121 CheckInit(struct ubik_trans *at,
122 int (*db_init) (struct ubik_trans *)) /* procedure to call if rebuilding DB */
124 register afs_int32 code;
128 /* Don't read header if not necessary */
129 if (!ubik_CacheUpdate(at))
132 ObtainWriteLock(&cheader_lock);
133 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
135 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
136 sizeof(afs_int32)))) {
138 printf("No data base\n");
140 printf("I/O Error\n");
142 iversion = ntohl(iversion); /* convert to host order */
143 tversion = ntohl(tversion);
144 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
145 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
147 printf("SetupHeader failed\n");
150 cheaderReadTime = time(0);
153 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
154 KADBVERSION, iversion, tversion);
158 ReleaseWriteLock(&cheader_lock);
162 /* if here, we have no version number or the wrong version number in the
164 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
169 if ((db_init == 0) || (code == KAIO))
172 printf("Error discovered in header, rebuilding.\n");
174 /* try to write a good header */
175 memset(&cheader, 0, sizeof(cheader));
176 cheader.version = htonl(KADBVERSION);
177 cheader.checkVersion = htonl(KADBVERSION);
178 cheader.headerSize = htonl(sizeof(cheader));
180 cheader.eofPtr = htonl(sizeof(cheader));
182 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
183 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
184 cheader.admin_accounts = 0;
185 cheader.hashsize = htonl(HASHSIZE);
186 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
188 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. */
197 AllocBlock(struct ubik_trans *at, 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));
207 return 0; /* can't read block */
208 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)));
217 code = inc_header_word(at, stats.allocs);
220 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
224 /* Free a block given its index. It must already have been unthreaded.
225 Returns zero for success or an error code on failure. */
228 FreeBlock(struct ubik_trans *at, afs_int32 index)
230 struct kaentry tentry;
233 /* check index just to be on the safe side */
234 if (!index_OK(index))
237 memset(&tentry, 0, sizeof(kaentry));
238 tentry.next = cheader.freePtr;
239 tentry.flags = htonl(KAFFREE);
240 code = set_header_word(at, freePtr, htonl(index));
243 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
247 code = inc_header_word(at, stats.frees);
253 /* Look for a block by name and instance. If found read the block's contents
254 into the area pointed to by tentry and return the block's index. If not
255 found offset is set to zero. If an error is encountered a non-zero code is
259 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
260 struct kaentry *tentry)
262 register afs_int32 i, code;
263 register afs_int32 to;
266 i = NameHash(aname, ainstance);
267 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
268 to = ntohl(tentry->next)) {
269 code = karead(at, to, (char *)tentry, sizeof(kaentry));
272 /* see if the name matches */
273 if (!strcmp(aname, tentry->userID.name)
274 && (ainstance == (char *)0
275 || !strcmp(ainstance, tentry->userID.instance))) {
276 *toP = to; /* found it */
280 *toP = 0; /* no such entry */
284 /* Add a block to the hash table given a pointer to the block and its index.
285 The block is threaded onto the hash table and written to disk. The routine
286 returns zero if there were no errors. */
289 ThreadBlock(struct ubik_trans *at, afs_int32 index,
290 struct kaentry *tentry)
293 int hi; /* hash index */
295 if (!index_OK(index))
297 hi = NameHash(tentry->userID.name, tentry->userID.instance);
298 tentry->next = cheader.nameHash[hi];
299 code = set_header_word(at, nameHash[hi], htonl(index));
302 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
308 /* Remove a block from the hash table. If success return 0, else return an
312 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
314 register afs_int32 i, code;
315 register afs_int32 to;
317 struct kaentry tentry;
319 i = NameHash(aentry->userID.name, aentry->userID.instance);
321 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
322 to = ntohl(tentry.next)) {
323 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
326 /* see if the name matches */
327 if (!strcmp(aentry->userID.name, tentry.userID.name)
328 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
330 if (lo) { /* unthread from last block */
332 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
335 } else { /* unthread from hash table */
336 code = set_header_word(at, nameHash[i], tentry.next);
340 aentry->next = 0; /* just to be sure */
343 lo = DOFFSET(to, &tentry, &tentry.next);
348 /* Given an index to the last block (or zero the first time) read the contents
349 of the next block and return its index. The last argument is a pointer to
350 an estimate of the number of remaining blocks to read out. The remaining
351 count is an estimate because it may include free blocks that are not
352 returned. If there are no more blocks remaining is zero and the returned
353 index is zero. A non-zero index indicates that tentry has been filled with
354 valid data. If an error is encountered the returned index is zero and the
355 remaining count is negative. */
358 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
359 afs_int32 *remaining)
364 if (index == 0) /* get first one */
365 index = sizeof(cheader);
367 if (!index_OK(index)) {
368 *remaining = -1; /* error */
371 index += sizeof(kaentry);
373 /* now search for the first entry that isn't free */
374 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
375 code = karead(at, index, (char *)tentry, sizeof(kaentry));
380 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
381 /* estimate remaining number of entries, not including this one */
382 *remaining = (last - index) / sizeof(kaentry) - 1;
386 *remaining = 0; /* no more entries */
390 /* These are a collections of routines that deal with externally known keys.
391 They maintain a database of key version numbers and the corresponding key
392 and pointer to the user entry. */
395 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
396 struct kaentry *tentry, struct ktc_encryptionKey *key)
398 struct kaOldKeys okeys; /* old keys block */
399 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
400 afs_int32 prevptr, nextprevptr;
403 afs_int32 newkeyver; /* new key version number */
404 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
405 int foundcurrentkey = 0, addednewkey = 0, modified;
407 es_Report("Newkey for %s.%s\n", tentry->userID.name,
408 tentry->userID.instance);
410 newkeyver = ntohl(tentry->key_version) + 1;
411 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
414 /* An entry may have more than one oldkeys blocks. The entry
415 * points to the most current, but all the oldkeys blocks for an
416 * entry are not linked together. All oldkeys blocks for all
417 * entries are linked together off of the header. So we follow
420 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
421 prevptr = nextprevptr, okeysaddr = nextaddr) {
422 /* foreacholdkeysblock */
423 /* Read the oldKeys block */
424 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
428 nextaddr = ntohl(okeys.next);
429 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
431 /* We only want oldkey blocks that belong to this entry */
432 if (ntohl(okeys.entry) != tentryaddr)
435 modified = 0; /* This oldkeys block has not been modified */
436 keyentries = 0; /* Number of valid key entries in the block */
437 for (i = 0; i < NOLDKEYS; i++) {
439 /* Keep count of number of entries found */
440 if (okeys.keys[i].superseded != 0) {
441 oldtotalkeyentries++;
444 /* If we find the entry that is not superseded, then supersede it */
445 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
446 okeys.keys[i].superseded = htonl(now);
449 if (foundcurrentkey) {
451 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
452 tentry->userID.name, tentry->userID.instance));
458 /* If we find an oldkey of the same version or
459 * an old key that has expired, then delete it.
461 if ((ntohl(okeys.keys[i].version) == newkeyver)
462 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
463 okeys.keys[i].superseded = 0;
464 okeys.keys[i].version = htonl(-1);
465 memset(&okeys.keys[i].key, 0,
466 sizeof(struct ktc_encryptionKey));
469 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
470 now - ntohl(okeys.keys[i].superseded),
471 ntohl(okeys.keys[i].version));
474 /* Add our key here if its free */
475 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
476 okeys.keys[i].version = htonl(newkeyver);
477 okeys.keys[i].superseded = htonl(NEVERDATE);
478 memcpy(&okeys.keys[i].key, key,
479 sizeof(struct ktc_encryptionKey));
481 addednewkey = okeysaddr;
484 /* Keep count of number of entries found */
485 if (okeys.keys[i].superseded != 0) {
487 newtotalkeyentries++;
491 /* If we modified the block, write it out */
492 if (modified && keyentries) {
493 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
498 /* If there are no more entries in this oldkeys block, delete it */
499 if (keyentries == 0) {
501 code = set_header_word(tt, kvnoPtr, okeys.next);
504 kawrite(tt, prevptr, (char *)&okeys.next,
509 code = FreeBlock(tt, okeysaddr);
513 nextprevptr = prevptr; /* won't bump prevptr */
515 } /* foreacholdkeysblock */
517 /* If we could not add the key, create a new oldkeys block */
519 /* Allocate and fill in an oldkeys block */
520 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
523 okeys.flags = htonl(KAFOLDKEYS);
524 okeys.entry = htonl(tentryaddr);
525 okeys.keys[0].version = htonl(newkeyver);
526 okeys.keys[0].superseded = htonl(NEVERDATE);
527 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
528 newtotalkeyentries++;
530 /* Thread onto the header's chain of oldkeys */
531 okeys.next = cheader.kvnoPtr;
532 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
536 /* Write the oldkeys block out */
537 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
541 es_Report("New oldkey block allocated at %d\n", addednewkey);
544 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
546 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
547 tentry->userID.name, tentry->userID.instance,
548 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
552 /* Update the tentry. We rely on caller to write it out */
553 tentry->misc.asServer.oldKeys = htonl(addednewkey);
554 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
555 tentry->key_version = htonl(newkeyver);
556 memcpy(&tentry->key, key, sizeof(tentry->key));
558 /* invalidate key caches everywhere */
559 code = inc_header_word(tt, specialKeysVersion);
563 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
569 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
570 struct kaentry *tentry)
573 struct kaOldKeys okeys; /* old keys block */
574 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
575 afs_int32 prevptr = 0;
577 es_Report("DelKey for %s.%s\n", tentry->userID.name,
578 tentry->userID.instance);
580 /* An entry may have more than one oldkeys blocks. The entry
581 * points to the most current, but all the oldkeys blocks for an
582 * entry are not linked together. All oldkeys blocks for all
583 * entries are linked together off of the header. So we follow
586 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
587 /* foreacholdkeysblock */
588 /* Read the oldKeys block */
589 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
592 nextaddr = ntohl(okeys.next);
594 /* We only want oldkey blocks that belong to this entry */
595 if (ntohl(okeys.entry) != tentryaddr) {
596 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
600 /* Delete the oldkeys block */
603 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
605 code = set_header_word(tt, kvnoPtr, okeys.next);
609 code = FreeBlock(tt, okeysaddr);
612 } /* foreacholdkeysblock */
614 /* Update the tentry. We rely on caller to write it out */
615 tentry->misc.asServer.oldKeys = 0;
616 tentry->misc.asServer.nOldKeys = 0;
618 /* invalidate key caches everywhere */
619 code = inc_header_word(tt, specialKeysVersion);
627 ka_debugKeyCache(struct ka_debugInfo *info)
631 memcpy(&info->cheader_lock, &cheader_lock, sizeof(info->cheader_lock));
632 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
634 info->kcVersion = keyCacheVersion;
635 info->kcSize = maxCachedKeys;
637 for (i = 0; i < maxCachedKeys; i++) {
638 if (keyCache[i].used) {
639 if (info->kcUsed < KADEBUGKCINFOSIZE) {
640 int j = info->kcUsed;
641 char principal[sizeof(keyCache[0].name) +
642 sizeof(keyCache[0].inst)];
644 info->kcInfo[j].used = keyCache[i].superseded;
645 info->kcInfo[j].kvno = keyCache[i].kvno;
646 info->kcInfo[j].primary =
647 (keyCache[i].superseded == NEVERDATE);
648 info->kcInfo[j].keycksum = 0;
652 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
653 info->kcInfo[j].keycksum +=
654 ((char *)&keyCache[i].key)[k];
657 strcpy(principal, keyCache[i].name);
658 strcat(principal, ".");
659 strcat(principal, keyCache[i].inst);
660 strncpy(info->kcInfo[j].principal, principal,
661 sizeof(info->kcInfo[0].principal));
668 /* Add a key to the key cache, expanding it if necessary. */
671 ka_Encache(char *name, char *inst, afs_int32 kvno,
672 struct ktc_encryptionKey *key, Date superseded)
676 ObtainWriteLock(&keycache_lock);
677 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
678 for (i = 0; i < maxCachedKeys; i++)
679 keyCache[i].used = 0;
682 for (i = 0; i < maxCachedKeys; i++)
683 if (keyCache[i].used == 0) {
685 keyCache[i].kvno = kvno;
686 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
687 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
688 keyCacheVersion = ntohl(cheader.specialKeysVersion);
689 memcpy(&keyCache[i].key, key, sizeof(*key));
690 keyCache[i].superseded = superseded;
691 keyCache[i].used = time(0);
693 ReleaseWriteLock(&keycache_lock);
696 /* i == maxCachedKeys */
698 (struct cachedKey *)realloc(keyCache,
700 2) * sizeof(struct cachedKey));
702 es_Report("Can't realloc keyCache! out of memory?");
707 int j = i; /* initialize new storage */
708 while (j < maxCachedKeys)
709 keyCache[j++].used = 0;
714 /* Look up the key given a principal and a kvno. This is called by GetTicket
715 to get the decryption key for the authenticating ticket. It is also called
716 by the rxkad security module to decrypt admin tickets. The rxkad call is
717 with tt==0, since Rx can't call Ubik. */
720 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
721 struct ktc_encryptionKey *key)
726 struct kaentry tentry;
728 struct kaOldKeys okeys;
730 ObtainReadLock(&keycache_lock);
731 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
732 code = KAKEYCACHEINVALID;
734 for (i = 0; i < maxCachedKeys; i++) {
735 if (keyCache[i].used) { /* zero used date means invalid */
736 if ((keyCache[i].kvno == kvno)
737 && (strcmp(keyCache[i].name, name) == 0)
738 && (strcmp(keyCache[i].inst, inst) == 0)) {
739 memcpy(key, &keyCache[i].key, sizeof(*key));
740 keyCache[i].used = time(0);
741 ReleaseReadLock(&keycache_lock);
748 ReleaseReadLock(&keycache_lock);
752 /* we missed in the cache so need to look in the Ubik database */
753 code = FindBlock(tt, name, inst, &to, &tentry);
759 /* first check the current key */
760 if (tentry.key_version == htonl(kvno)) {
761 memcpy(key, &tentry.key, sizeof(*key));
762 ka_Encache(name, inst, kvno, key, NEVERDATE);
765 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
766 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
769 if (ntohl(okeys.entry) == to)
770 for (i = 0; i < NOLDKEYS; i++)
771 if (okeys.keys[i].superseded
772 && (ntohl(okeys.keys[i].version) == kvno)) {
773 memcpy(key, &okeys.keys[i].key, sizeof(*key));
774 ka_Encache(name, inst, kvno, key,
775 ntohl(okeys.keys[i].superseded));
782 /* Look up the primary key and key version for a principal. */
785 ka_LookupKey(struct ubik_trans *tt,
788 afs_int32 *kvno, /* returned */
789 struct ktc_encryptionKey *key) /* copied out */
793 struct kaentry tentry;
796 ObtainReadLock(&keycache_lock);
797 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
798 code = KAKEYCACHEINVALID;
800 for (i = 0; i < maxCachedKeys; i++) {
801 if (keyCache[i].used) { /* zero used date means invalid */
802 if ((keyCache[i].superseded == NEVERDATE)
803 && (strcmp(keyCache[i].name, name) == 0)
804 && (strcmp(keyCache[i].inst, inst) == 0)) {
805 memcpy(key, &keyCache[i].key, sizeof(*key));
806 *kvno = keyCache[i].kvno;
807 keyCache[i].used = time(0);
808 ReleaseReadLock(&keycache_lock);
815 ReleaseReadLock(&keycache_lock);
819 /* we missed in the cache so need to look in the Ubik database */
820 code = FindBlock(tt, name, inst, &to, &tentry);
825 memcpy(key, &tentry.key, sizeof(*key));
826 *kvno = ntohl(tentry.key_version);
827 ka_Encache(name, inst, *kvno, key, NEVERDATE);
831 /* This is, hopefully a temporary mechanism to fill the cache will all keys
832 since filling cache misses during rxkad challenge responses will deadlock if
833 Ubik needs to use Rx. */
836 ka_FillKeyCache(struct ubik_trans *tt)
842 struct ktc_encryptionKey k;
843 struct kaOldKeys okeys;
844 struct kaentry tentry;
846 /* this is a little marginal, but... */
847 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
851 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
852 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
855 /* get name & instance */
857 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
861 /* get all the old keys in this block */
862 for (i = 0; i < NOLDKEYS; i++)
863 if (okeys.keys[i].superseded) {
865 ka_LookupKvno(tt, tentry.userID.name,
866 tentry.userID.instance,
867 ntohl(okeys.keys[i].version), &k);
872 if (++nfound > maxCachedKeys)
873 return KADATABASEINCONSISTENT;
878 update_admin_count(struct ubik_trans *tt, int delta)
883 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
884 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
886 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
893 index_OK(afs_int32 index)
895 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
896 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
901 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
904 name_instance_legal(char *name, char *instance)
908 /* No string checks apply anymore. The international people want to use full 8
909 bit ascii without problems. */
911 code = (strlen(name) < MAXKTCNAMELEN)
912 && (strlen(instance) < MAXKTCNAMELEN);
914 map = LEGALCHARS; /* permitted chars, instance allows <period> */
915 code = (strlen(name) > 0) && string_legal(instance, map)
916 && string_legal(name, map + 1);
919 dynamic_statistics.string_checks++;
925 string_legal(char *str, char *map)
930 if (slen >= MAXKTCNAMELEN)
931 return 0; /* with trailing null must fit in data base */
932 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */