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>
15 #include <sys/types.h>
19 #include <netinet/in.h>
25 #include <afs/afsutil.h>
30 extern Date cheaderReadTime; /* time cheader last read in */
32 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
34 #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))
36 static int index_OK(afs_int32);
39 NameHash(char *aname, char *ainstance)
44 /* stolen directly from the HashString function in the vol package */
46 for (i = strlen(aname), aname += i - 1; i--; aname--)
47 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
48 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
49 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
50 return (hash % HASHSIZE);
53 /* package up seek and write into one procedure for ease of use */
56 kawrite(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
60 code = ubik_Seek(tt, 0, pos);
63 code = ubik_Write(tt, buff, len);
67 /* same thing for read */
70 karead(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
74 code = ubik_Seek(tt, 0, pos);
77 code = ubik_Read(tt, buff, len);
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(&keycache_lock);
105 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
110 maxKeyLifetime = MAXKTCTICKETLIFETIME;
116 /* check that the database has been initialized. Be careful to fail in a safe
117 manner, to avoid bogusly reinitializing the db. */
119 * reads in db cache from ubik.
121 * @param[in] ut ubik transaction
122 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
123 * will be called on rebuilding the database (or NULL to not
126 * @return operation status
130 UpdateCache(struct ubik_trans *at, void *rock)
132 int (*db_init) (struct ubik_trans *) = rock;
137 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
139 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
140 sizeof(afs_int32)))) {
142 printf("No data base\n");
144 printf("I/O Error\n");
146 iversion = ntohl(iversion); /* convert to host order */
147 tversion = ntohl(tversion);
148 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
149 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
151 printf("SetupHeader failed\n");
154 cheaderReadTime = time(0);
157 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
158 KADBVERSION, iversion, tversion);
165 /* if here, we have no version number or the wrong version number in the
167 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
172 if ((db_init == 0) || (code == KAIO))
175 printf("Error discovered in header, rebuilding.\n");
177 /* try to write a good header */
178 memset(&cheader, 0, sizeof(cheader));
179 cheader.version = htonl(KADBVERSION);
180 cheader.checkVersion = htonl(KADBVERSION);
181 cheader.headerSize = htonl(sizeof(cheader));
183 cheader.eofPtr = htonl(sizeof(cheader));
185 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
186 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
187 cheader.admin_accounts = 0;
188 cheader.hashsize = htonl(HASHSIZE);
189 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
191 return KAIO; /* return the error code */
193 return db_init(at); /* initialize the db */
197 CheckInit(struct ubik_trans *at,
198 int (*db_init) (struct ubik_trans *)) /* procedure to call if rebuilding DB */
200 return ubik_CheckCache(at, UpdateCache, db_init);
203 /* Allocate a free block of storage for entry, returning address of a new
204 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
207 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
212 if (cheader.freePtr) {
213 /* allocate this dude */
214 temp = ntohl(cheader.freePtr);
215 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
217 return 0; /* can't read block */
218 code = set_header_word(at, freePtr, tentry->next);
220 /* hosed, nothing on free list, grow file */
221 temp = ntohl(cheader.eofPtr); /* remember this guy */
222 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
227 code = inc_header_word(at, stats.allocs);
230 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
234 /* Free a block given its index. It must already have been unthreaded.
235 Returns zero for success or an error code on failure. */
238 FreeBlock(struct ubik_trans *at, afs_int32 index)
240 struct kaentry tentry;
243 /* check index just to be on the safe side */
244 if (!index_OK(index))
247 memset(&tentry, 0, sizeof(kaentry));
248 tentry.next = cheader.freePtr;
249 tentry.flags = htonl(KAFFREE);
250 code = set_header_word(at, freePtr, htonl(index));
253 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
257 code = inc_header_word(at, stats.frees);
263 /* Look for a block by name and instance. If found read the block's contents
264 into the area pointed to by tentry and return the block's index. If not
265 found offset is set to zero. If an error is encountered a non-zero code is
269 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
270 struct kaentry *tentry)
276 i = NameHash(aname, ainstance);
277 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
278 to = ntohl(tentry->next)) {
279 code = karead(at, to, (char *)tentry, sizeof(kaentry));
282 /* see if the name matches */
283 if (!strcmp(aname, tentry->userID.name)
284 && (ainstance == (char *)0
285 || !strcmp(ainstance, tentry->userID.instance))) {
286 *toP = to; /* found it */
290 *toP = 0; /* no such entry */
294 /* Add a block to the hash table given a pointer to the block and its index.
295 The block is threaded onto the hash table and written to disk. The routine
296 returns zero if there were no errors. */
299 ThreadBlock(struct ubik_trans *at, afs_int32 index,
300 struct kaentry *tentry)
303 int hi; /* hash index */
305 if (!index_OK(index))
307 hi = NameHash(tentry->userID.name, tentry->userID.instance);
308 tentry->next = cheader.nameHash[hi];
309 code = set_header_word(at, nameHash[hi], htonl(index));
312 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
318 /* Remove a block from the hash table. If success return 0, else return an
322 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
327 struct kaentry tentry;
329 i = NameHash(aentry->userID.name, aentry->userID.instance);
331 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
332 to = ntohl(tentry.next)) {
333 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
336 /* see if the name matches */
337 if (!strcmp(aentry->userID.name, tentry.userID.name)
338 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
340 if (lo) { /* unthread from last block */
342 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
345 } else { /* unthread from hash table */
346 code = set_header_word(at, nameHash[i], tentry.next);
350 aentry->next = 0; /* just to be sure */
353 lo = DOFFSET(to, &tentry, &tentry.next);
358 /* Given an index to the last block (or zero the first time) read the contents
359 of the next block and return its index. The last argument is a pointer to
360 an estimate of the number of remaining blocks to read out. The remaining
361 count is an estimate because it may include free blocks that are not
362 returned. If there are no more blocks remaining is zero and the returned
363 index is zero. A non-zero index indicates that tentry has been filled with
364 valid data. If an error is encountered the returned index is zero and the
365 remaining count is negative. */
368 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
369 afs_int32 *remaining)
374 if (index == 0) /* get first one */
375 index = sizeof(cheader);
377 if (!index_OK(index)) {
378 *remaining = -1; /* error */
381 index += sizeof(kaentry);
383 /* now search for the first entry that isn't free */
384 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
385 code = karead(at, index, (char *)tentry, sizeof(kaentry));
390 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
391 /* estimate remaining number of entries, not including this one */
392 *remaining = (last - index) / sizeof(kaentry) - 1;
396 *remaining = 0; /* no more entries */
400 /* These are a collections of routines that deal with externally known keys.
401 They maintain a database of key version numbers and the corresponding key
402 and pointer to the user entry. */
405 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
406 struct kaentry *tentry, struct ktc_encryptionKey *key)
408 struct kaOldKeys okeys; /* old keys block */
409 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
410 afs_int32 prevptr, nextprevptr;
413 afs_int32 newkeyver; /* new key version number */
414 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
415 int addednewkey = 0, modified;
417 int foundcurrentkey = 0;
420 es_Report("Newkey for %s.%s\n", tentry->userID.name,
421 tentry->userID.instance);
423 newkeyver = ntohl(tentry->key_version) + 1;
424 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
427 /* An entry may have more than one oldkeys blocks. The entry
428 * points to the most current, but all the oldkeys blocks for an
429 * entry are not linked together. All oldkeys blocks for all
430 * entries are linked together off of the header. So we follow
433 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
434 prevptr = nextprevptr, okeysaddr = nextaddr) {
435 /* foreacholdkeysblock */
436 /* Read the oldKeys block */
437 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
441 nextaddr = ntohl(okeys.next);
442 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
444 /* We only want oldkey blocks that belong to this entry */
445 if (ntohl(okeys.entry) != tentryaddr)
448 modified = 0; /* This oldkeys block has not been modified */
449 keyentries = 0; /* Number of valid key entries in the block */
450 for (i = 0; i < NOLDKEYS; i++) {
452 /* Keep count of number of entries found */
453 if (okeys.keys[i].superseded != 0) {
454 oldtotalkeyentries++;
457 /* If we find the entry that is not superseded, then supersede it */
458 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
459 okeys.keys[i].superseded = htonl(now);
462 if (foundcurrentkey) {
464 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
465 tentry->userID.name, tentry->userID.instance));
471 /* If we find an oldkey of the same version or
472 * an old key that has expired, then delete it.
474 if ((ntohl(okeys.keys[i].version) == newkeyver)
475 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
476 okeys.keys[i].superseded = 0;
477 okeys.keys[i].version = htonl(-1);
478 memset(&okeys.keys[i].key, 0,
479 sizeof(struct ktc_encryptionKey));
482 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
483 now - ntohl(okeys.keys[i].superseded),
484 ntohl(okeys.keys[i].version));
487 /* Add our key here if its free */
488 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
489 okeys.keys[i].version = htonl(newkeyver);
490 okeys.keys[i].superseded = htonl(NEVERDATE);
491 memcpy(&okeys.keys[i].key, key,
492 sizeof(struct ktc_encryptionKey));
494 addednewkey = okeysaddr;
497 /* Keep count of number of entries found */
498 if (okeys.keys[i].superseded != 0) {
500 newtotalkeyentries++;
504 /* If we modified the block, write it out */
505 if (modified && keyentries) {
506 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
511 /* If there are no more entries in this oldkeys block, delete it */
512 if (keyentries == 0) {
514 code = set_header_word(tt, kvnoPtr, okeys.next);
517 kawrite(tt, prevptr, (char *)&okeys.next,
522 code = FreeBlock(tt, okeysaddr);
526 nextprevptr = prevptr; /* won't bump prevptr */
528 } /* foreacholdkeysblock */
530 /* If we could not add the key, create a new oldkeys block */
532 /* Allocate and fill in an oldkeys block */
533 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
536 okeys.flags = htonl(KAFOLDKEYS);
537 okeys.entry = htonl(tentryaddr);
538 okeys.keys[0].version = htonl(newkeyver);
539 okeys.keys[0].superseded = htonl(NEVERDATE);
540 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
541 newtotalkeyentries++;
543 /* Thread onto the header's chain of oldkeys */
544 okeys.next = cheader.kvnoPtr;
545 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
549 /* Write the oldkeys block out */
550 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
554 es_Report("New oldkey block allocated at %d\n", addednewkey);
557 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
559 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
560 tentry->userID.name, tentry->userID.instance,
561 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
565 /* Update the tentry. We rely on caller to write it out */
566 tentry->misc.asServer.oldKeys = htonl(addednewkey);
567 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
568 tentry->key_version = htonl(newkeyver);
569 memcpy(&tentry->key, key, sizeof(tentry->key));
571 /* invalidate key caches everywhere */
572 code = inc_header_word(tt, specialKeysVersion);
576 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
582 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
583 struct kaentry *tentry)
586 struct kaOldKeys okeys; /* old keys block */
587 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
588 afs_int32 prevptr = 0;
590 es_Report("DelKey for %s.%s\n", tentry->userID.name,
591 tentry->userID.instance);
593 /* An entry may have more than one oldkeys blocks. The entry
594 * points to the most current, but all the oldkeys blocks for an
595 * entry are not linked together. All oldkeys blocks for all
596 * entries are linked together off of the header. So we follow
599 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
600 /* foreacholdkeysblock */
601 /* Read the oldKeys block */
602 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
605 nextaddr = ntohl(okeys.next);
607 /* We only want oldkey blocks that belong to this entry */
608 if (ntohl(okeys.entry) != tentryaddr) {
609 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
613 /* Delete the oldkeys block */
616 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
618 code = set_header_word(tt, kvnoPtr, okeys.next);
622 code = FreeBlock(tt, okeysaddr);
625 } /* foreacholdkeysblock */
627 /* Update the tentry. We rely on caller to write it out */
628 tentry->misc.asServer.oldKeys = 0;
629 tentry->misc.asServer.nOldKeys = 0;
631 /* invalidate key caches everywhere */
632 code = inc_header_word(tt, specialKeysVersion);
640 ka_debugKeyCache(struct ka_debugInfo *info)
644 /* cheader_lock no longer exists */
645 memset(&info->cheader_lock, 0, sizeof(info->cheader_lock));
646 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
648 info->kcVersion = keyCacheVersion;
649 info->kcSize = maxCachedKeys;
651 for (i = 0; i < maxCachedKeys; i++) {
652 if (keyCache[i].used) {
653 if (info->kcUsed < KADEBUGKCINFOSIZE) {
654 int j = info->kcUsed;
655 char principal[sizeof(keyCache[0].name) +
656 sizeof(keyCache[0].inst)];
658 info->kcInfo[j].used = keyCache[i].superseded;
659 info->kcInfo[j].kvno = keyCache[i].kvno;
660 info->kcInfo[j].primary =
661 (keyCache[i].superseded == NEVERDATE);
662 info->kcInfo[j].keycksum = 0;
666 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
667 info->kcInfo[j].keycksum +=
668 ((char *)&keyCache[i].key)[k];
671 strcpy(principal, keyCache[i].name);
672 strcat(principal, ".");
673 strcat(principal, keyCache[i].inst);
674 strncpy(info->kcInfo[j].principal, principal,
675 sizeof(info->kcInfo[0].principal));
682 /* Add a key to the key cache, expanding it if necessary. */
685 ka_Encache(char *name, char *inst, afs_int32 kvno,
686 struct ktc_encryptionKey *key, Date superseded)
690 ObtainWriteLock(&keycache_lock);
691 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
692 for (i = 0; i < maxCachedKeys; i++)
693 keyCache[i].used = 0;
696 for (i = 0; i < maxCachedKeys; i++)
697 if (keyCache[i].used == 0) {
699 keyCache[i].kvno = kvno;
700 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
701 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
702 keyCacheVersion = ntohl(cheader.specialKeysVersion);
703 memcpy(&keyCache[i].key, key, sizeof(*key));
704 keyCache[i].superseded = superseded;
705 keyCache[i].used = time(0);
707 ReleaseWriteLock(&keycache_lock);
710 /* i == maxCachedKeys */
712 (struct cachedKey *)realloc(keyCache,
714 2) * sizeof(struct cachedKey));
716 es_Report("Can't realloc keyCache! out of memory?");
721 int j = i; /* initialize new storage */
722 while (j < maxCachedKeys)
723 keyCache[j++].used = 0;
728 /* Look up the key given a principal and a kvno. This is called by GetTicket
729 to get the decryption key for the authenticating ticket. It is also called
730 by the rxkad security module to decrypt admin tickets. The rxkad call is
731 with tt==0, since Rx can't call Ubik. */
734 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
735 struct ktc_encryptionKey *key)
740 struct kaentry tentry;
742 struct kaOldKeys okeys;
744 ObtainReadLock(&keycache_lock);
745 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
746 code = KAKEYCACHEINVALID;
748 for (i = 0; i < maxCachedKeys; i++) {
749 if (keyCache[i].used) { /* zero used date means invalid */
750 if ((keyCache[i].kvno == kvno)
751 && (strcmp(keyCache[i].name, name) == 0)
752 && (strcmp(keyCache[i].inst, inst) == 0)) {
753 memcpy(key, &keyCache[i].key, sizeof(*key));
754 keyCache[i].used = time(0);
755 ReleaseReadLock(&keycache_lock);
762 ReleaseReadLock(&keycache_lock);
766 /* we missed in the cache so need to look in the Ubik database */
767 code = FindBlock(tt, name, inst, &to, &tentry);
773 /* first check the current key */
774 if (tentry.key_version == htonl(kvno)) {
775 memcpy(key, &tentry.key, sizeof(*key));
776 ka_Encache(name, inst, kvno, key, NEVERDATE);
779 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
780 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
783 if (ntohl(okeys.entry) == to)
784 for (i = 0; i < NOLDKEYS; i++)
785 if (okeys.keys[i].superseded
786 && (ntohl(okeys.keys[i].version) == kvno)) {
787 memcpy(key, &okeys.keys[i].key, sizeof(*key));
788 ka_Encache(name, inst, kvno, key,
789 ntohl(okeys.keys[i].superseded));
796 /* Look up the primary key and key version for a principal. */
799 ka_LookupKey(struct ubik_trans *tt,
802 afs_int32 *kvno, /* returned */
803 struct ktc_encryptionKey *key) /* copied out */
807 struct kaentry tentry;
810 ObtainReadLock(&keycache_lock);
811 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
812 code = KAKEYCACHEINVALID;
814 for (i = 0; i < maxCachedKeys; i++) {
815 if (keyCache[i].used) { /* zero used date means invalid */
816 if ((keyCache[i].superseded == NEVERDATE)
817 && (strcmp(keyCache[i].name, name) == 0)
818 && (strcmp(keyCache[i].inst, inst) == 0)) {
819 memcpy(key, &keyCache[i].key, sizeof(*key));
820 *kvno = keyCache[i].kvno;
821 keyCache[i].used = time(0);
822 ReleaseReadLock(&keycache_lock);
829 ReleaseReadLock(&keycache_lock);
833 /* we missed in the cache so need to look in the Ubik database */
834 code = FindBlock(tt, name, inst, &to, &tentry);
839 memcpy(key, &tentry.key, sizeof(*key));
840 *kvno = ntohl(tentry.key_version);
841 ka_Encache(name, inst, *kvno, key, NEVERDATE);
845 /* This is, hopefully a temporary mechanism to fill the cache will all keys
846 since filling cache misses during rxkad challenge responses will deadlock if
847 Ubik needs to use Rx. */
850 ka_FillKeyCache(struct ubik_trans *tt)
856 struct ktc_encryptionKey k;
857 struct kaOldKeys okeys;
858 struct kaentry tentry;
860 /* this is a little marginal, but... */
861 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
865 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
866 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
869 /* get name & instance */
871 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
875 /* get all the old keys in this block */
876 for (i = 0; i < NOLDKEYS; i++)
877 if (okeys.keys[i].superseded) {
879 ka_LookupKvno(tt, tentry.userID.name,
880 tentry.userID.instance,
881 ntohl(okeys.keys[i].version), &k);
886 if (++nfound > maxCachedKeys)
887 return KADATABASEINCONSISTENT;
892 update_admin_count(struct ubik_trans *tt, int delta)
897 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
898 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
900 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
907 index_OK(afs_int32 index)
909 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
910 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
915 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
918 name_instance_legal(char *name, char *instance)
922 /* No string checks apply anymore. The international people want to use full 8
923 bit ascii without problems. */
925 code = (strlen(name) < MAXKTCNAMELEN)
926 && (strlen(instance) < MAXKTCNAMELEN);
928 map = LEGALCHARS; /* permitted chars, instance allows <period> */
929 code = (strlen(name) > 0) && string_legal(instance, map)
930 && string_legal(name, map + 1);
933 dynamic_statistics.string_checks++;
939 string_legal(char *str, char *map)
944 if (slen >= MAXKTCNAMELEN)
945 return 0; /* with trailing null must fit in data base */
946 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */