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>
18 #include <afs/afsutil.h>
24 #if !defined(offsetof)
25 #include <stddef.h> /* for definition of offsetof() */
28 extern Date cheaderReadTime; /* time cheader last read in */
30 #define set_header_word(tt,field,value) kawrite ((tt), (offsetof(struct kaheader, field)), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
32 #define inc_header_word(tt,field) kawrite ((tt), (offsetof(struct kaheader, field)), ((cheader.field = (htonl(ntohl(cheader.field)+1))), (char *)&(cheader.field)), sizeof(afs_int32))
34 static int index_OK(afs_int32);
37 NameHash(char *aname, char *ainstance)
42 /* stolen directly from the HashString function in the vol package */
44 for (i = strlen(aname), aname += i - 1; i--; aname--)
45 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
46 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
47 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
48 return (hash % HASHSIZE);
51 /* package up seek and write into one procedure for ease of use */
54 kawrite(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
58 code = ubik_Seek(tt, 0, pos);
61 code = ubik_Write(tt, buff, len);
65 /* same thing for read */
68 karead(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
72 code = ubik_Seek(tt, 0, pos);
75 code = ubik_Read(tt, buff, len);
79 static struct Lock keycache_lock;
81 static int maxCachedKeys;
83 static struct cachedKey {
85 int superseded; /* NEVERDATE => this is current key */
87 struct ktc_encryptionKey key;
88 char name[MAXKTCNAMELEN];
89 char inst[MAXKTCNAMELEN];
91 static afs_int32 keyCacheVersion = 0;
93 static afs_int32 maxKeyLifetime;
94 static int dbfixup = 0;
97 init_kadatabase(int initFlags)
99 Lock_Init(&keycache_lock);
102 keyCache = malloc(maxCachedKeys * sizeof(struct cachedKey));
107 maxKeyLifetime = MAXKTCTICKETLIFETIME;
113 /* check that the database has been initialized. Be careful to fail in a safe
114 manner, to avoid bogusly reinitializing the db. */
116 * reads in db cache from ubik.
118 * @param[in] ut ubik transaction
119 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
120 * will be called on rebuilding the database (or NULL to not
123 * @return operation status
127 UpdateCache(struct ubik_trans *at, void *rock)
129 int (*db_init) (struct ubik_trans *) = rock;
134 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
136 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
137 sizeof(afs_int32)))) {
139 printf("No data base\n");
141 printf("I/O Error\n");
143 iversion = ntohl(iversion); /* convert to host order */
144 tversion = ntohl(tversion);
145 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
146 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
148 printf("SetupHeader failed\n");
151 cheaderReadTime = time(0);
154 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
155 KADBVERSION, iversion, tversion);
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 */
194 CheckInit(struct ubik_trans *at,
195 int (*db_init) (struct ubik_trans *)) /* procedure to call if rebuilding DB */
197 return ubik_CheckCache(at, UpdateCache, db_init);
200 /* Allocate a free block of storage for entry, returning address of a new
201 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
204 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
209 if (cheader.freePtr) {
210 /* allocate this dude */
211 temp = ntohl(cheader.freePtr);
212 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
214 return 0; /* can't read block */
215 code = set_header_word(at, freePtr, tentry->next);
217 /* hosed, nothing on free list, grow file */
218 temp = ntohl(cheader.eofPtr); /* remember this guy */
219 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
224 code = inc_header_word(at, stats.allocs);
227 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
231 /* Free a block given its index. It must already have been unthreaded.
232 Returns zero for success or an error code on failure. */
235 FreeBlock(struct ubik_trans *at, afs_int32 index)
237 struct kaentry tentry;
240 /* check index just to be on the safe side */
241 if (!index_OK(index))
244 memset(&tentry, 0, sizeof(kaentry));
245 tentry.next = cheader.freePtr;
246 tentry.flags = htonl(KAFFREE);
247 code = set_header_word(at, freePtr, htonl(index));
250 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
254 code = inc_header_word(at, stats.frees);
260 /* Look for a block by name and instance. If found read the block's contents
261 into the area pointed to by tentry and return the block's index. If not
262 found offset is set to zero. If an error is encountered a non-zero code is
266 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
267 struct kaentry *tentry)
273 i = NameHash(aname, ainstance);
274 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
275 to = ntohl(tentry->next)) {
276 code = karead(at, to, (char *)tentry, sizeof(kaentry));
279 /* see if the name matches */
280 if (!strcmp(aname, tentry->userID.name)
281 && (ainstance == (char *)0
282 || !strcmp(ainstance, tentry->userID.instance))) {
283 *toP = to; /* found it */
287 *toP = 0; /* no such entry */
291 /* Add a block to the hash table given a pointer to the block and its index.
292 The block is threaded onto the hash table and written to disk. The routine
293 returns zero if there were no errors. */
296 ThreadBlock(struct ubik_trans *at, afs_int32 index,
297 struct kaentry *tentry)
300 int hi; /* hash index */
302 if (!index_OK(index))
304 hi = NameHash(tentry->userID.name, tentry->userID.instance);
305 tentry->next = cheader.nameHash[hi];
306 code = set_header_word(at, nameHash[hi], htonl(index));
309 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
315 /* Remove a block from the hash table. If success return 0, else return an
319 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
324 struct kaentry tentry;
326 i = NameHash(aentry->userID.name, aentry->userID.instance);
328 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
329 to = ntohl(tentry.next)) {
330 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
333 /* see if the name matches */
334 if (!strcmp(aentry->userID.name, tentry.userID.name)
335 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
337 if (lo) { /* unthread from last block */
339 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
342 } else { /* unthread from hash table */
343 code = set_header_word(at, nameHash[i], tentry.next);
347 aentry->next = 0; /* just to be sure */
350 lo = DOFFSET(to, &tentry, &tentry.next);
355 /* Given an index to the last block (or zero the first time) read the contents
356 of the next block and return its index. The last argument is a pointer to
357 an estimate of the number of remaining blocks to read out. The remaining
358 count is an estimate because it may include free blocks that are not
359 returned. If there are no more blocks remaining is zero and the returned
360 index is zero. A non-zero index indicates that tentry has been filled with
361 valid data. If an error is encountered the returned index is zero and the
362 remaining count is negative. */
365 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
366 afs_int32 *remaining)
371 if (index == 0) /* get first one */
372 index = sizeof(cheader);
374 if (!index_OK(index)) {
375 *remaining = -1; /* error */
378 index += sizeof(kaentry);
380 /* now search for the first entry that isn't free */
381 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
382 code = karead(at, index, (char *)tentry, sizeof(kaentry));
387 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
388 /* estimate remaining number of entries, not including this one */
389 *remaining = (last - index) / sizeof(kaentry) - 1;
393 *remaining = 0; /* no more entries */
397 /* These are a collections of routines that deal with externally known keys.
398 They maintain a database of key version numbers and the corresponding key
399 and pointer to the user entry. */
402 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
403 struct kaentry *tentry, struct ktc_encryptionKey *key)
405 struct kaOldKeys okeys; /* old keys block */
406 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
407 afs_int32 prevptr, nextprevptr;
410 afs_int32 newkeyver; /* new key version number */
411 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
412 int addednewkey = 0, modified;
414 int foundcurrentkey = 0;
417 es_Report("Newkey for %s.%s\n", tentry->userID.name,
418 tentry->userID.instance);
420 newkeyver = ntohl(tentry->key_version) + 1;
421 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
424 /* An entry may have more than one oldkeys blocks. The entry
425 * points to the most current, but all the oldkeys blocks for an
426 * entry are not linked together. All oldkeys blocks for all
427 * entries are linked together off of the header. So we follow
430 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
431 prevptr = nextprevptr, okeysaddr = nextaddr) {
432 /* foreacholdkeysblock */
433 /* Read the oldKeys block */
434 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
438 nextaddr = ntohl(okeys.next);
439 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
441 /* We only want oldkey blocks that belong to this entry */
442 if (ntohl(okeys.entry) != tentryaddr)
445 modified = 0; /* This oldkeys block has not been modified */
446 keyentries = 0; /* Number of valid key entries in the block */
447 for (i = 0; i < NOLDKEYS; i++) {
449 /* Keep count of number of entries found */
450 if (okeys.keys[i].superseded != 0) {
451 oldtotalkeyentries++;
454 /* If we find the entry that is not superseded, then supersede it */
455 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
456 okeys.keys[i].superseded = htonl(now);
459 if (foundcurrentkey) {
461 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
462 tentry->userID.name, tentry->userID.instance));
468 /* If we find an oldkey of the same version or
469 * an old key that has expired, then delete it.
471 if ((ntohl(okeys.keys[i].version) == newkeyver)
472 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
473 okeys.keys[i].superseded = 0;
474 okeys.keys[i].version = htonl(-1);
475 memset(&okeys.keys[i].key, 0,
476 sizeof(struct ktc_encryptionKey));
479 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
480 now - ntohl(okeys.keys[i].superseded),
481 ntohl(okeys.keys[i].version));
484 /* Add our key here if its free */
485 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
486 okeys.keys[i].version = htonl(newkeyver);
487 okeys.keys[i].superseded = htonl(NEVERDATE);
488 memcpy(&okeys.keys[i].key, key,
489 sizeof(struct ktc_encryptionKey));
491 addednewkey = okeysaddr;
494 /* Keep count of number of entries found */
495 if (okeys.keys[i].superseded != 0) {
497 newtotalkeyentries++;
501 /* If we modified the block, write it out */
502 if (modified && keyentries) {
503 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
508 /* If there are no more entries in this oldkeys block, delete it */
509 if (keyentries == 0) {
511 code = set_header_word(tt, kvnoPtr, okeys.next);
514 kawrite(tt, prevptr, (char *)&okeys.next,
519 code = FreeBlock(tt, okeysaddr);
523 nextprevptr = prevptr; /* won't bump prevptr */
525 } /* foreacholdkeysblock */
527 /* If we could not add the key, create a new oldkeys block */
529 /* Allocate and fill in an oldkeys block */
530 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
533 okeys.flags = htonl(KAFOLDKEYS);
534 okeys.entry = htonl(tentryaddr);
535 okeys.keys[0].version = htonl(newkeyver);
536 okeys.keys[0].superseded = htonl(NEVERDATE);
537 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
538 newtotalkeyentries++;
540 /* Thread onto the header's chain of oldkeys */
541 okeys.next = cheader.kvnoPtr;
542 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
546 /* Write the oldkeys block out */
547 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
551 es_Report("New oldkey block allocated at %d\n", addednewkey);
554 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
556 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
557 tentry->userID.name, tentry->userID.instance,
558 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
562 /* Update the tentry. We rely on caller to write it out */
563 tentry->misc.asServer.oldKeys = htonl(addednewkey);
564 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
565 tentry->key_version = htonl(newkeyver);
566 memcpy(&tentry->key, key, sizeof(tentry->key));
568 /* invalidate key caches everywhere */
569 code = inc_header_word(tt, specialKeysVersion);
573 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
579 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
580 struct kaentry *tentry)
583 struct kaOldKeys okeys; /* old keys block */
584 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
585 afs_int32 prevptr = 0;
587 es_Report("DelKey for %s.%s\n", tentry->userID.name,
588 tentry->userID.instance);
590 /* An entry may have more than one oldkeys blocks. The entry
591 * points to the most current, but all the oldkeys blocks for an
592 * entry are not linked together. All oldkeys blocks for all
593 * entries are linked together off of the header. So we follow
596 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
597 /* foreacholdkeysblock */
598 /* Read the oldKeys block */
599 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
602 nextaddr = ntohl(okeys.next);
604 /* We only want oldkey blocks that belong to this entry */
605 if (ntohl(okeys.entry) != tentryaddr) {
606 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
610 /* Delete the oldkeys block */
613 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
615 code = set_header_word(tt, kvnoPtr, okeys.next);
619 code = FreeBlock(tt, okeysaddr);
622 } /* foreacholdkeysblock */
624 /* Update the tentry. We rely on caller to write it out */
625 tentry->misc.asServer.oldKeys = 0;
626 tentry->misc.asServer.nOldKeys = 0;
628 /* invalidate key caches everywhere */
629 code = inc_header_word(tt, specialKeysVersion);
637 ka_debugKeyCache(struct ka_debugInfo *info)
641 /* cheader_lock no longer exists */
642 memset(&info->cheader_lock, 0, sizeof(info->cheader_lock));
643 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
645 info->kcVersion = keyCacheVersion;
646 info->kcSize = maxCachedKeys;
648 for (i = 0; i < maxCachedKeys; i++) {
649 if (keyCache[i].used) {
650 if (info->kcUsed < KADEBUGKCINFOSIZE) {
651 int j = info->kcUsed;
652 char principal[sizeof(keyCache[0].name) +
653 sizeof(keyCache[0].inst)];
655 info->kcInfo[j].used = keyCache[i].superseded;
656 info->kcInfo[j].kvno = keyCache[i].kvno;
657 info->kcInfo[j].primary =
658 (keyCache[i].superseded == NEVERDATE);
659 info->kcInfo[j].keycksum = 0;
663 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
664 info->kcInfo[j].keycksum +=
665 ((char *)&keyCache[i].key)[k];
668 strcpy(principal, keyCache[i].name);
669 strcat(principal, ".");
670 strcat(principal, keyCache[i].inst);
671 strncpy(info->kcInfo[j].principal, principal,
672 sizeof(info->kcInfo[0].principal));
679 /* Add a key to the key cache, expanding it if necessary. */
682 ka_Encache(char *name, char *inst, afs_int32 kvno,
683 struct ktc_encryptionKey *key, Date superseded)
687 ObtainWriteLock(&keycache_lock);
688 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
689 for (i = 0; i < maxCachedKeys; i++)
690 keyCache[i].used = 0;
693 for (i = 0; i < maxCachedKeys; i++)
694 if (keyCache[i].used == 0) {
696 keyCache[i].kvno = kvno;
697 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
698 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
699 keyCacheVersion = ntohl(cheader.specialKeysVersion);
700 memcpy(&keyCache[i].key, key, sizeof(*key));
701 keyCache[i].superseded = superseded;
702 keyCache[i].used = time(0);
704 ReleaseWriteLock(&keycache_lock);
707 /* i == maxCachedKeys */
708 keyCache = realloc(keyCache, (maxCachedKeys *=2)
709 * sizeof(struct cachedKey));
711 es_Report("Can't realloc keyCache! out of memory?");
716 int j = i; /* initialize new storage */
717 while (j < maxCachedKeys)
718 keyCache[j++].used = 0;
723 /* Look up the key given a principal and a kvno. This is called by GetTicket
724 to get the decryption key for the authenticating ticket. It is also called
725 by the rxkad security module to decrypt admin tickets. The rxkad call is
726 with tt==0, since Rx can't call Ubik. */
729 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
730 struct ktc_encryptionKey *key)
735 struct kaentry tentry;
737 struct kaOldKeys okeys;
739 ObtainReadLock(&keycache_lock);
740 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
741 code = KAKEYCACHEINVALID;
743 for (i = 0; i < maxCachedKeys; i++) {
744 if (keyCache[i].used) { /* zero used date means invalid */
745 if ((keyCache[i].kvno == kvno)
746 && (strcmp(keyCache[i].name, name) == 0)
747 && (strcmp(keyCache[i].inst, inst) == 0)) {
748 memcpy(key, &keyCache[i].key, sizeof(*key));
749 keyCache[i].used = time(0);
750 ReleaseReadLock(&keycache_lock);
757 ReleaseReadLock(&keycache_lock);
761 /* we missed in the cache so need to look in the Ubik database */
762 code = FindBlock(tt, name, inst, &to, &tentry);
768 /* first check the current key */
769 if (tentry.key_version == htonl(kvno)) {
770 memcpy(key, &tentry.key, sizeof(*key));
771 ka_Encache(name, inst, kvno, key, NEVERDATE);
774 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
775 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
778 if (ntohl(okeys.entry) == to)
779 for (i = 0; i < NOLDKEYS; i++)
780 if (okeys.keys[i].superseded
781 && (ntohl(okeys.keys[i].version) == kvno)) {
782 memcpy(key, &okeys.keys[i].key, sizeof(*key));
783 ka_Encache(name, inst, kvno, key,
784 ntohl(okeys.keys[i].superseded));
791 /* Look up the primary key and key version for a principal. */
794 ka_LookupKey(struct ubik_trans *tt,
797 afs_int32 *kvno, /* returned */
798 struct ktc_encryptionKey *key) /* copied out */
802 struct kaentry tentry;
805 ObtainReadLock(&keycache_lock);
806 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
807 code = KAKEYCACHEINVALID;
809 for (i = 0; i < maxCachedKeys; i++) {
810 if (keyCache[i].used) { /* zero used date means invalid */
811 if ((keyCache[i].superseded == NEVERDATE)
812 && (strcmp(keyCache[i].name, name) == 0)
813 && (strcmp(keyCache[i].inst, inst) == 0)) {
814 memcpy(key, &keyCache[i].key, sizeof(*key));
815 *kvno = keyCache[i].kvno;
816 keyCache[i].used = time(0);
817 ReleaseReadLock(&keycache_lock);
824 ReleaseReadLock(&keycache_lock);
828 /* we missed in the cache so need to look in the Ubik database */
829 code = FindBlock(tt, name, inst, &to, &tentry);
834 memcpy(key, &tentry.key, sizeof(*key));
835 *kvno = ntohl(tentry.key_version);
836 ka_Encache(name, inst, *kvno, key, NEVERDATE);
840 /* This is, hopefully a temporary mechanism to fill the cache will all keys
841 since filling cache misses during rxkad challenge responses will deadlock if
842 Ubik needs to use Rx. */
845 ka_FillKeyCache(struct ubik_trans *tt)
851 struct ktc_encryptionKey k;
852 struct kaOldKeys okeys;
853 struct kaentry tentry;
855 /* this is a little marginal, but... */
856 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
860 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
861 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
864 /* get name & instance */
866 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
870 /* get all the old keys in this block */
871 for (i = 0; i < NOLDKEYS; i++)
872 if (okeys.keys[i].superseded) {
874 ka_LookupKvno(tt, tentry.userID.name,
875 tentry.userID.instance,
876 ntohl(okeys.keys[i].version), &k);
881 if (++nfound > maxCachedKeys)
882 return KADATABASEINCONSISTENT;
887 update_admin_count(struct ubik_trans *tt, int delta)
892 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
893 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
895 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
902 index_OK(afs_int32 index)
904 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
905 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
910 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
913 name_instance_legal(char *name, char *instance)
917 /* No string checks apply anymore. The international people want to use full 8
918 bit ascii without problems. */
920 code = (strlen(name) < MAXKTCNAMELEN)
921 && (strlen(instance) < MAXKTCNAMELEN);
923 map = LEGALCHARS; /* permitted chars, instance allows <period> */
924 code = (strlen(name) > 0) && string_legal(instance, map)
925 && string_legal(name, map + 1);
928 dynamic_statistics.string_checks++;
934 string_legal(char *str, char *map)
939 if (slen >= MAXKTCNAMELEN)
940 return 0; /* with trailing null must fit in data base */
941 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */