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(char *aname, char *ainstance)
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 keycache_lock;
82 static int maxCachedKeys;
84 static struct cachedKey {
86 int superseded; /* NEVERDATE => this is current key */
88 struct ktc_encryptionKey key;
89 char name[MAXKTCNAMELEN];
90 char inst[MAXKTCNAMELEN];
92 static afs_int32 keyCacheVersion = 0;
94 static afs_int32 maxKeyLifetime;
95 static int dbfixup = 0;
98 init_kadatabase(int initFlags)
100 Lock_Init(&keycache_lock);
104 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
109 maxKeyLifetime = MAXKTCTICKETLIFETIME;
115 /* check that the database has been initialized. Be careful to fail in a safe
116 manner, to avoid bogusly reinitializing the db. */
118 * reads in db cache from ubik.
120 * @param[in] ut ubik transaction
121 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
122 * will be called on rebuilding the database (or NULL to not
125 * @return operation status
129 UpdateCache(struct ubik_trans *at, void *rock)
131 int (*db_init) (struct ubik_trans *) = rock;
136 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
138 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
139 sizeof(afs_int32)))) {
141 printf("No data base\n");
143 printf("I/O Error\n");
145 iversion = ntohl(iversion); /* convert to host order */
146 tversion = ntohl(tversion);
147 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
148 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
150 printf("SetupHeader failed\n");
153 cheaderReadTime = time(0);
156 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
157 KADBVERSION, iversion, tversion);
164 /* if here, we have no version number or the wrong version number in the
166 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
171 if ((db_init == 0) || (code == KAIO))
174 printf("Error discovered in header, rebuilding.\n");
176 /* try to write a good header */
177 memset(&cheader, 0, sizeof(cheader));
178 cheader.version = htonl(KADBVERSION);
179 cheader.checkVersion = htonl(KADBVERSION);
180 cheader.headerSize = htonl(sizeof(cheader));
182 cheader.eofPtr = htonl(sizeof(cheader));
184 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
185 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
186 cheader.admin_accounts = 0;
187 cheader.hashsize = htonl(HASHSIZE);
188 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
190 return KAIO; /* return the error code */
192 return db_init(at); /* initialize the db */
196 CheckInit(struct ubik_trans *at,
197 int (*db_init) (struct ubik_trans *)) /* procedure to call if rebuilding DB */
199 return ubik_CheckCache(at, UpdateCache, db_init);
202 /* Allocate a free block of storage for entry, returning address of a new
203 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
206 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
211 if (cheader.freePtr) {
212 /* allocate this dude */
213 temp = ntohl(cheader.freePtr);
214 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
216 return 0; /* can't read block */
217 code = set_header_word(at, freePtr, tentry->next);
219 /* hosed, nothing on free list, grow file */
220 temp = ntohl(cheader.eofPtr); /* remember this guy */
221 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
226 code = inc_header_word(at, stats.allocs);
229 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
233 /* Free a block given its index. It must already have been unthreaded.
234 Returns zero for success or an error code on failure. */
237 FreeBlock(struct ubik_trans *at, afs_int32 index)
239 struct kaentry tentry;
242 /* check index just to be on the safe side */
243 if (!index_OK(index))
246 memset(&tentry, 0, sizeof(kaentry));
247 tentry.next = cheader.freePtr;
248 tentry.flags = htonl(KAFFREE);
249 code = set_header_word(at, freePtr, htonl(index));
252 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
256 code = inc_header_word(at, stats.frees);
262 /* Look for a block by name and instance. If found read the block's contents
263 into the area pointed to by tentry and return the block's index. If not
264 found offset is set to zero. If an error is encountered a non-zero code is
268 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
269 struct kaentry *tentry)
275 i = NameHash(aname, ainstance);
276 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
277 to = ntohl(tentry->next)) {
278 code = karead(at, to, (char *)tentry, sizeof(kaentry));
281 /* see if the name matches */
282 if (!strcmp(aname, tentry->userID.name)
283 && (ainstance == (char *)0
284 || !strcmp(ainstance, tentry->userID.instance))) {
285 *toP = to; /* found it */
289 *toP = 0; /* no such entry */
293 /* Add a block to the hash table given a pointer to the block and its index.
294 The block is threaded onto the hash table and written to disk. The routine
295 returns zero if there were no errors. */
298 ThreadBlock(struct ubik_trans *at, afs_int32 index,
299 struct kaentry *tentry)
302 int hi; /* hash index */
304 if (!index_OK(index))
306 hi = NameHash(tentry->userID.name, tentry->userID.instance);
307 tentry->next = cheader.nameHash[hi];
308 code = set_header_word(at, nameHash[hi], htonl(index));
311 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
317 /* Remove a block from the hash table. If success return 0, else return an
321 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
326 struct kaentry tentry;
328 i = NameHash(aentry->userID.name, aentry->userID.instance);
330 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
331 to = ntohl(tentry.next)) {
332 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
335 /* see if the name matches */
336 if (!strcmp(aentry->userID.name, tentry.userID.name)
337 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
339 if (lo) { /* unthread from last block */
341 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
344 } else { /* unthread from hash table */
345 code = set_header_word(at, nameHash[i], tentry.next);
349 aentry->next = 0; /* just to be sure */
352 lo = DOFFSET(to, &tentry, &tentry.next);
357 /* Given an index to the last block (or zero the first time) read the contents
358 of the next block and return its index. The last argument is a pointer to
359 an estimate of the number of remaining blocks to read out. The remaining
360 count is an estimate because it may include free blocks that are not
361 returned. If there are no more blocks remaining is zero and the returned
362 index is zero. A non-zero index indicates that tentry has been filled with
363 valid data. If an error is encountered the returned index is zero and the
364 remaining count is negative. */
367 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
368 afs_int32 *remaining)
373 if (index == 0) /* get first one */
374 index = sizeof(cheader);
376 if (!index_OK(index)) {
377 *remaining = -1; /* error */
380 index += sizeof(kaentry);
382 /* now search for the first entry that isn't free */
383 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
384 code = karead(at, index, (char *)tentry, sizeof(kaentry));
389 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
390 /* estimate remaining number of entries, not including this one */
391 *remaining = (last - index) / sizeof(kaentry) - 1;
395 *remaining = 0; /* no more entries */
399 /* These are a collections of routines that deal with externally known keys.
400 They maintain a database of key version numbers and the corresponding key
401 and pointer to the user entry. */
404 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
405 struct kaentry *tentry, struct ktc_encryptionKey *key)
407 struct kaOldKeys okeys; /* old keys block */
408 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
409 afs_int32 prevptr, nextprevptr;
412 afs_int32 newkeyver; /* new key version number */
413 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
414 int addednewkey = 0, modified;
416 int foundcurrentkey = 0;
419 es_Report("Newkey for %s.%s\n", tentry->userID.name,
420 tentry->userID.instance);
422 newkeyver = ntohl(tentry->key_version) + 1;
423 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
426 /* An entry may have more than one oldkeys blocks. The entry
427 * points to the most current, but all the oldkeys blocks for an
428 * entry are not linked together. All oldkeys blocks for all
429 * entries are linked together off of the header. So we follow
432 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
433 prevptr = nextprevptr, okeysaddr = nextaddr) {
434 /* foreacholdkeysblock */
435 /* Read the oldKeys block */
436 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
440 nextaddr = ntohl(okeys.next);
441 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
443 /* We only want oldkey blocks that belong to this entry */
444 if (ntohl(okeys.entry) != tentryaddr)
447 modified = 0; /* This oldkeys block has not been modified */
448 keyentries = 0; /* Number of valid key entries in the block */
449 for (i = 0; i < NOLDKEYS; i++) {
451 /* Keep count of number of entries found */
452 if (okeys.keys[i].superseded != 0) {
453 oldtotalkeyentries++;
456 /* If we find the entry that is not superseded, then supersede it */
457 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
458 okeys.keys[i].superseded = htonl(now);
461 if (foundcurrentkey) {
463 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
464 tentry->userID.name, tentry->userID.instance));
470 /* If we find an oldkey of the same version or
471 * an old key that has expired, then delete it.
473 if ((ntohl(okeys.keys[i].version) == newkeyver)
474 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
475 okeys.keys[i].superseded = 0;
476 okeys.keys[i].version = htonl(-1);
477 memset(&okeys.keys[i].key, 0,
478 sizeof(struct ktc_encryptionKey));
481 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
482 now - ntohl(okeys.keys[i].superseded),
483 ntohl(okeys.keys[i].version));
486 /* Add our key here if its free */
487 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
488 okeys.keys[i].version = htonl(newkeyver);
489 okeys.keys[i].superseded = htonl(NEVERDATE);
490 memcpy(&okeys.keys[i].key, key,
491 sizeof(struct ktc_encryptionKey));
493 addednewkey = okeysaddr;
496 /* Keep count of number of entries found */
497 if (okeys.keys[i].superseded != 0) {
499 newtotalkeyentries++;
503 /* If we modified the block, write it out */
504 if (modified && keyentries) {
505 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
510 /* If there are no more entries in this oldkeys block, delete it */
511 if (keyentries == 0) {
513 code = set_header_word(tt, kvnoPtr, okeys.next);
516 kawrite(tt, prevptr, (char *)&okeys.next,
521 code = FreeBlock(tt, okeysaddr);
525 nextprevptr = prevptr; /* won't bump prevptr */
527 } /* foreacholdkeysblock */
529 /* If we could not add the key, create a new oldkeys block */
531 /* Allocate and fill in an oldkeys block */
532 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
535 okeys.flags = htonl(KAFOLDKEYS);
536 okeys.entry = htonl(tentryaddr);
537 okeys.keys[0].version = htonl(newkeyver);
538 okeys.keys[0].superseded = htonl(NEVERDATE);
539 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
540 newtotalkeyentries++;
542 /* Thread onto the header's chain of oldkeys */
543 okeys.next = cheader.kvnoPtr;
544 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
548 /* Write the oldkeys block out */
549 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
553 es_Report("New oldkey block allocated at %d\n", addednewkey);
556 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
558 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
559 tentry->userID.name, tentry->userID.instance,
560 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
564 /* Update the tentry. We rely on caller to write it out */
565 tentry->misc.asServer.oldKeys = htonl(addednewkey);
566 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
567 tentry->key_version = htonl(newkeyver);
568 memcpy(&tentry->key, key, sizeof(tentry->key));
570 /* invalidate key caches everywhere */
571 code = inc_header_word(tt, specialKeysVersion);
575 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
581 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
582 struct kaentry *tentry)
585 struct kaOldKeys okeys; /* old keys block */
586 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
587 afs_int32 prevptr = 0;
589 es_Report("DelKey for %s.%s\n", tentry->userID.name,
590 tentry->userID.instance);
592 /* An entry may have more than one oldkeys blocks. The entry
593 * points to the most current, but all the oldkeys blocks for an
594 * entry are not linked together. All oldkeys blocks for all
595 * entries are linked together off of the header. So we follow
598 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
599 /* foreacholdkeysblock */
600 /* Read the oldKeys block */
601 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
604 nextaddr = ntohl(okeys.next);
606 /* We only want oldkey blocks that belong to this entry */
607 if (ntohl(okeys.entry) != tentryaddr) {
608 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
612 /* Delete the oldkeys block */
615 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
617 code = set_header_word(tt, kvnoPtr, okeys.next);
621 code = FreeBlock(tt, okeysaddr);
624 } /* foreacholdkeysblock */
626 /* Update the tentry. We rely on caller to write it out */
627 tentry->misc.asServer.oldKeys = 0;
628 tentry->misc.asServer.nOldKeys = 0;
630 /* invalidate key caches everywhere */
631 code = inc_header_word(tt, specialKeysVersion);
639 ka_debugKeyCache(struct ka_debugInfo *info)
643 /* cheader_lock no longer exists */
644 memset(&info->cheader_lock, 0, sizeof(info->cheader_lock));
645 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
647 info->kcVersion = keyCacheVersion;
648 info->kcSize = maxCachedKeys;
650 for (i = 0; i < maxCachedKeys; i++) {
651 if (keyCache[i].used) {
652 if (info->kcUsed < KADEBUGKCINFOSIZE) {
653 int j = info->kcUsed;
654 char principal[sizeof(keyCache[0].name) +
655 sizeof(keyCache[0].inst)];
657 info->kcInfo[j].used = keyCache[i].superseded;
658 info->kcInfo[j].kvno = keyCache[i].kvno;
659 info->kcInfo[j].primary =
660 (keyCache[i].superseded == NEVERDATE);
661 info->kcInfo[j].keycksum = 0;
665 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
666 info->kcInfo[j].keycksum +=
667 ((char *)&keyCache[i].key)[k];
670 strcpy(principal, keyCache[i].name);
671 strcat(principal, ".");
672 strcat(principal, keyCache[i].inst);
673 strncpy(info->kcInfo[j].principal, principal,
674 sizeof(info->kcInfo[0].principal));
681 /* Add a key to the key cache, expanding it if necessary. */
684 ka_Encache(char *name, char *inst, afs_int32 kvno,
685 struct ktc_encryptionKey *key, Date superseded)
689 ObtainWriteLock(&keycache_lock);
690 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
691 for (i = 0; i < maxCachedKeys; i++)
692 keyCache[i].used = 0;
695 for (i = 0; i < maxCachedKeys; i++)
696 if (keyCache[i].used == 0) {
698 keyCache[i].kvno = kvno;
699 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
700 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
701 keyCacheVersion = ntohl(cheader.specialKeysVersion);
702 memcpy(&keyCache[i].key, key, sizeof(*key));
703 keyCache[i].superseded = superseded;
704 keyCache[i].used = time(0);
706 ReleaseWriteLock(&keycache_lock);
709 /* i == maxCachedKeys */
711 (struct cachedKey *)realloc(keyCache,
713 2) * sizeof(struct cachedKey));
715 es_Report("Can't realloc keyCache! out of memory?");
720 int j = i; /* initialize new storage */
721 while (j < maxCachedKeys)
722 keyCache[j++].used = 0;
727 /* Look up the key given a principal and a kvno. This is called by GetTicket
728 to get the decryption key for the authenticating ticket. It is also called
729 by the rxkad security module to decrypt admin tickets. The rxkad call is
730 with tt==0, since Rx can't call Ubik. */
733 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
734 struct ktc_encryptionKey *key)
739 struct kaentry tentry;
741 struct kaOldKeys okeys;
743 ObtainReadLock(&keycache_lock);
744 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
745 code = KAKEYCACHEINVALID;
747 for (i = 0; i < maxCachedKeys; i++) {
748 if (keyCache[i].used) { /* zero used date means invalid */
749 if ((keyCache[i].kvno == kvno)
750 && (strcmp(keyCache[i].name, name) == 0)
751 && (strcmp(keyCache[i].inst, inst) == 0)) {
752 memcpy(key, &keyCache[i].key, sizeof(*key));
753 keyCache[i].used = time(0);
754 ReleaseReadLock(&keycache_lock);
761 ReleaseReadLock(&keycache_lock);
765 /* we missed in the cache so need to look in the Ubik database */
766 code = FindBlock(tt, name, inst, &to, &tentry);
772 /* first check the current key */
773 if (tentry.key_version == htonl(kvno)) {
774 memcpy(key, &tentry.key, sizeof(*key));
775 ka_Encache(name, inst, kvno, key, NEVERDATE);
778 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
779 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
782 if (ntohl(okeys.entry) == to)
783 for (i = 0; i < NOLDKEYS; i++)
784 if (okeys.keys[i].superseded
785 && (ntohl(okeys.keys[i].version) == kvno)) {
786 memcpy(key, &okeys.keys[i].key, sizeof(*key));
787 ka_Encache(name, inst, kvno, key,
788 ntohl(okeys.keys[i].superseded));
795 /* Look up the primary key and key version for a principal. */
798 ka_LookupKey(struct ubik_trans *tt,
801 afs_int32 *kvno, /* returned */
802 struct ktc_encryptionKey *key) /* copied out */
806 struct kaentry tentry;
809 ObtainReadLock(&keycache_lock);
810 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
811 code = KAKEYCACHEINVALID;
813 for (i = 0; i < maxCachedKeys; i++) {
814 if (keyCache[i].used) { /* zero used date means invalid */
815 if ((keyCache[i].superseded == NEVERDATE)
816 && (strcmp(keyCache[i].name, name) == 0)
817 && (strcmp(keyCache[i].inst, inst) == 0)) {
818 memcpy(key, &keyCache[i].key, sizeof(*key));
819 *kvno = keyCache[i].kvno;
820 keyCache[i].used = time(0);
821 ReleaseReadLock(&keycache_lock);
828 ReleaseReadLock(&keycache_lock);
832 /* we missed in the cache so need to look in the Ubik database */
833 code = FindBlock(tt, name, inst, &to, &tentry);
838 memcpy(key, &tentry.key, sizeof(*key));
839 *kvno = ntohl(tentry.key_version);
840 ka_Encache(name, inst, *kvno, key, NEVERDATE);
844 /* This is, hopefully a temporary mechanism to fill the cache will all keys
845 since filling cache misses during rxkad challenge responses will deadlock if
846 Ubik needs to use Rx. */
849 ka_FillKeyCache(struct ubik_trans *tt)
855 struct ktc_encryptionKey k;
856 struct kaOldKeys okeys;
857 struct kaentry tentry;
859 /* this is a little marginal, but... */
860 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
864 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
865 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
868 /* get name & instance */
870 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
874 /* get all the old keys in this block */
875 for (i = 0; i < NOLDKEYS; i++)
876 if (okeys.keys[i].superseded) {
878 ka_LookupKvno(tt, tentry.userID.name,
879 tentry.userID.instance,
880 ntohl(okeys.keys[i].version), &k);
885 if (++nfound > maxCachedKeys)
886 return KADATABASEINCONSISTENT;
891 update_admin_count(struct ubik_trans *tt, int delta)
896 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
897 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
899 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
906 index_OK(afs_int32 index)
908 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
909 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
914 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
917 name_instance_legal(char *name, char *instance)
921 /* No string checks apply anymore. The international people want to use full 8
922 bit ascii without problems. */
924 code = (strlen(name) < MAXKTCNAMELEN)
925 && (strlen(instance) < MAXKTCNAMELEN);
927 map = LEGALCHARS; /* permitted chars, instance allows <period> */
928 code = (strlen(name) > 0) && string_legal(instance, map)
929 && string_legal(name, map + 1);
932 dynamic_statistics.string_checks++;
938 string_legal(char *str, char *map)
943 if (slen >= MAXKTCNAMELEN)
944 return 0; /* with trailing null must fit in data base */
945 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */