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);
103 (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
108 maxKeyLifetime = MAXKTCTICKETLIFETIME;
114 /* check that the database has been initialized. Be careful to fail in a safe
115 manner, to avoid bogusly reinitializing the db. */
117 * reads in db cache from ubik.
119 * @param[in] ut ubik transaction
120 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
121 * will be called on rebuilding the database (or NULL to not
124 * @return operation status
128 UpdateCache(struct ubik_trans *at, void *rock)
130 int (*db_init) (struct ubik_trans *) = rock;
135 if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
137 karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
138 sizeof(afs_int32)))) {
140 printf("No data base\n");
142 printf("I/O Error\n");
144 iversion = ntohl(iversion); /* convert to host order */
145 tversion = ntohl(tversion);
146 if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
147 code = karead(at, 0, (char *)&cheader, sizeof(cheader));
149 printf("SetupHeader failed\n");
152 cheaderReadTime = time(0);
155 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
156 KADBVERSION, iversion, tversion);
163 /* if here, we have no version number or the wrong version number in the
165 if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
170 if ((db_init == 0) || (code == KAIO))
173 printf("Error discovered in header, rebuilding.\n");
175 /* try to write a good header */
176 memset(&cheader, 0, sizeof(cheader));
177 cheader.version = htonl(KADBVERSION);
178 cheader.checkVersion = htonl(KADBVERSION);
179 cheader.headerSize = htonl(sizeof(cheader));
181 cheader.eofPtr = htonl(sizeof(cheader));
183 cheader.specialKeysVersion = htonl(time(0)); /* anything non-zero will do */
184 cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
185 cheader.admin_accounts = 0;
186 cheader.hashsize = htonl(HASHSIZE);
187 code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
189 return KAIO; /* return the error code */
191 return db_init(at); /* initialize the db */
195 CheckInit(struct ubik_trans *at,
196 int (*db_init) (struct ubik_trans *)) /* procedure to call if rebuilding DB */
198 return ubik_CheckCache(at, UpdateCache, db_init);
201 /* Allocate a free block of storage for entry, returning address of a new
202 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
205 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
210 if (cheader.freePtr) {
211 /* allocate this dude */
212 temp = ntohl(cheader.freePtr);
213 code = karead(at, temp, (char *)tentry, sizeof(kaentry));
215 return 0; /* can't read block */
216 code = set_header_word(at, freePtr, tentry->next);
218 /* hosed, nothing on free list, grow file */
219 temp = ntohl(cheader.eofPtr); /* remember this guy */
220 code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
225 code = inc_header_word(at, stats.allocs);
228 memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
232 /* Free a block given its index. It must already have been unthreaded.
233 Returns zero for success or an error code on failure. */
236 FreeBlock(struct ubik_trans *at, afs_int32 index)
238 struct kaentry tentry;
241 /* check index just to be on the safe side */
242 if (!index_OK(index))
245 memset(&tentry, 0, sizeof(kaentry));
246 tentry.next = cheader.freePtr;
247 tentry.flags = htonl(KAFFREE);
248 code = set_header_word(at, freePtr, htonl(index));
251 code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
255 code = inc_header_word(at, stats.frees);
261 /* Look for a block by name and instance. If found read the block's contents
262 into the area pointed to by tentry and return the block's index. If not
263 found offset is set to zero. If an error is encountered a non-zero code is
267 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
268 struct kaentry *tentry)
274 i = NameHash(aname, ainstance);
275 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
276 to = ntohl(tentry->next)) {
277 code = karead(at, to, (char *)tentry, sizeof(kaentry));
280 /* see if the name matches */
281 if (!strcmp(aname, tentry->userID.name)
282 && (ainstance == (char *)0
283 || !strcmp(ainstance, tentry->userID.instance))) {
284 *toP = to; /* found it */
288 *toP = 0; /* no such entry */
292 /* Add a block to the hash table given a pointer to the block and its index.
293 The block is threaded onto the hash table and written to disk. The routine
294 returns zero if there were no errors. */
297 ThreadBlock(struct ubik_trans *at, afs_int32 index,
298 struct kaentry *tentry)
301 int hi; /* hash index */
303 if (!index_OK(index))
305 hi = NameHash(tentry->userID.name, tentry->userID.instance);
306 tentry->next = cheader.nameHash[hi];
307 code = set_header_word(at, nameHash[hi], htonl(index));
310 code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
316 /* Remove a block from the hash table. If success return 0, else return an
320 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
325 struct kaentry tentry;
327 i = NameHash(aentry->userID.name, aentry->userID.instance);
329 for (to = ntohl(cheader.nameHash[i]); to != NULLO;
330 to = ntohl(tentry.next)) {
331 code = karead(at, to, (char *)&tentry, sizeof(kaentry));
334 /* see if the name matches */
335 if (!strcmp(aentry->userID.name, tentry.userID.name)
336 && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
338 if (lo) { /* unthread from last block */
340 kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
343 } else { /* unthread from hash table */
344 code = set_header_word(at, nameHash[i], tentry.next);
348 aentry->next = 0; /* just to be sure */
351 lo = DOFFSET(to, &tentry, &tentry.next);
356 /* Given an index to the last block (or zero the first time) read the contents
357 of the next block and return its index. The last argument is a pointer to
358 an estimate of the number of remaining blocks to read out. The remaining
359 count is an estimate because it may include free blocks that are not
360 returned. If there are no more blocks remaining is zero and the returned
361 index is zero. A non-zero index indicates that tentry has been filled with
362 valid data. If an error is encountered the returned index is zero and the
363 remaining count is negative. */
366 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
367 afs_int32 *remaining)
372 if (index == 0) /* get first one */
373 index = sizeof(cheader);
375 if (!index_OK(index)) {
376 *remaining = -1; /* error */
379 index += sizeof(kaentry);
381 /* now search for the first entry that isn't free */
382 for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
383 code = karead(at, index, (char *)tentry, sizeof(kaentry));
388 if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
389 /* estimate remaining number of entries, not including this one */
390 *remaining = (last - index) / sizeof(kaentry) - 1;
394 *remaining = 0; /* no more entries */
398 /* These are a collections of routines that deal with externally known keys.
399 They maintain a database of key version numbers and the corresponding key
400 and pointer to the user entry. */
403 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
404 struct kaentry *tentry, struct ktc_encryptionKey *key)
406 struct kaOldKeys okeys; /* old keys block */
407 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
408 afs_int32 prevptr, nextprevptr;
411 afs_int32 newkeyver; /* new key version number */
412 afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
413 int addednewkey = 0, modified;
415 int foundcurrentkey = 0;
418 es_Report("Newkey for %s.%s\n", tentry->userID.name,
419 tentry->userID.instance);
421 newkeyver = ntohl(tentry->key_version) + 1;
422 if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
425 /* An entry may have more than one oldkeys blocks. The entry
426 * points to the most current, but all the oldkeys blocks for an
427 * entry are not linked together. All oldkeys blocks for all
428 * entries are linked together off of the header. So we follow
431 for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
432 prevptr = nextprevptr, okeysaddr = nextaddr) {
433 /* foreacholdkeysblock */
434 /* Read the oldKeys block */
435 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
439 nextaddr = ntohl(okeys.next);
440 nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
442 /* We only want oldkey blocks that belong to this entry */
443 if (ntohl(okeys.entry) != tentryaddr)
446 modified = 0; /* This oldkeys block has not been modified */
447 keyentries = 0; /* Number of valid key entries in the block */
448 for (i = 0; i < NOLDKEYS; i++) {
450 /* Keep count of number of entries found */
451 if (okeys.keys[i].superseded != 0) {
452 oldtotalkeyentries++;
455 /* If we find the entry that is not superseded, then supersede it */
456 if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
457 okeys.keys[i].superseded = htonl(now);
460 if (foundcurrentkey) {
462 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
463 tentry->userID.name, tentry->userID.instance));
469 /* If we find an oldkey of the same version or
470 * an old key that has expired, then delete it.
472 if ((ntohl(okeys.keys[i].version) == newkeyver)
473 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
474 okeys.keys[i].superseded = 0;
475 okeys.keys[i].version = htonl(-1);
476 memset(&okeys.keys[i].key, 0,
477 sizeof(struct ktc_encryptionKey));
480 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
481 now - ntohl(okeys.keys[i].superseded),
482 ntohl(okeys.keys[i].version));
485 /* Add our key here if its free */
486 if (!addednewkey && (okeys.keys[i].superseded == 0)) {
487 okeys.keys[i].version = htonl(newkeyver);
488 okeys.keys[i].superseded = htonl(NEVERDATE);
489 memcpy(&okeys.keys[i].key, key,
490 sizeof(struct ktc_encryptionKey));
492 addednewkey = okeysaddr;
495 /* Keep count of number of entries found */
496 if (okeys.keys[i].superseded != 0) {
498 newtotalkeyentries++;
502 /* If we modified the block, write it out */
503 if (modified && keyentries) {
504 code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
509 /* If there are no more entries in this oldkeys block, delete it */
510 if (keyentries == 0) {
512 code = set_header_word(tt, kvnoPtr, okeys.next);
515 kawrite(tt, prevptr, (char *)&okeys.next,
520 code = FreeBlock(tt, okeysaddr);
524 nextprevptr = prevptr; /* won't bump prevptr */
526 } /* foreacholdkeysblock */
528 /* If we could not add the key, create a new oldkeys block */
530 /* Allocate and fill in an oldkeys block */
531 addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
534 okeys.flags = htonl(KAFOLDKEYS);
535 okeys.entry = htonl(tentryaddr);
536 okeys.keys[0].version = htonl(newkeyver);
537 okeys.keys[0].superseded = htonl(NEVERDATE);
538 memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
539 newtotalkeyentries++;
541 /* Thread onto the header's chain of oldkeys */
542 okeys.next = cheader.kvnoPtr;
543 code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
547 /* Write the oldkeys block out */
548 code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
552 es_Report("New oldkey block allocated at %d\n", addednewkey);
555 if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
557 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
558 tentry->userID.name, tentry->userID.instance,
559 ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
563 /* Update the tentry. We rely on caller to write it out */
564 tentry->misc.asServer.oldKeys = htonl(addednewkey);
565 tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
566 tentry->key_version = htonl(newkeyver);
567 memcpy(&tentry->key, key, sizeof(tentry->key));
569 /* invalidate key caches everywhere */
570 code = inc_header_word(tt, specialKeysVersion);
574 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
580 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
581 struct kaentry *tentry)
584 struct kaOldKeys okeys; /* old keys block */
585 afs_int32 okeysaddr, nextaddr; /* offset of old keys block */
586 afs_int32 prevptr = 0;
588 es_Report("DelKey for %s.%s\n", tentry->userID.name,
589 tentry->userID.instance);
591 /* An entry may have more than one oldkeys blocks. The entry
592 * points to the most current, but all the oldkeys blocks for an
593 * entry are not linked together. All oldkeys blocks for all
594 * entries are linked together off of the header. So we follow
597 for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
598 /* foreacholdkeysblock */
599 /* Read the oldKeys block */
600 code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
603 nextaddr = ntohl(okeys.next);
605 /* We only want oldkey blocks that belong to this entry */
606 if (ntohl(okeys.entry) != tentryaddr) {
607 prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
611 /* Delete the oldkeys block */
614 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
616 code = set_header_word(tt, kvnoPtr, okeys.next);
620 code = FreeBlock(tt, okeysaddr);
623 } /* foreacholdkeysblock */
625 /* Update the tentry. We rely on caller to write it out */
626 tentry->misc.asServer.oldKeys = 0;
627 tentry->misc.asServer.nOldKeys = 0;
629 /* invalidate key caches everywhere */
630 code = inc_header_word(tt, specialKeysVersion);
638 ka_debugKeyCache(struct ka_debugInfo *info)
642 /* cheader_lock no longer exists */
643 memset(&info->cheader_lock, 0, sizeof(info->cheader_lock));
644 memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
646 info->kcVersion = keyCacheVersion;
647 info->kcSize = maxCachedKeys;
649 for (i = 0; i < maxCachedKeys; i++) {
650 if (keyCache[i].used) {
651 if (info->kcUsed < KADEBUGKCINFOSIZE) {
652 int j = info->kcUsed;
653 char principal[sizeof(keyCache[0].name) +
654 sizeof(keyCache[0].inst)];
656 info->kcInfo[j].used = keyCache[i].superseded;
657 info->kcInfo[j].kvno = keyCache[i].kvno;
658 info->kcInfo[j].primary =
659 (keyCache[i].superseded == NEVERDATE);
660 info->kcInfo[j].keycksum = 0;
664 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
665 info->kcInfo[j].keycksum +=
666 ((char *)&keyCache[i].key)[k];
669 strcpy(principal, keyCache[i].name);
670 strcat(principal, ".");
671 strcat(principal, keyCache[i].inst);
672 strncpy(info->kcInfo[j].principal, principal,
673 sizeof(info->kcInfo[0].principal));
680 /* Add a key to the key cache, expanding it if necessary. */
683 ka_Encache(char *name, char *inst, afs_int32 kvno,
684 struct ktc_encryptionKey *key, Date superseded)
688 ObtainWriteLock(&keycache_lock);
689 if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
690 for (i = 0; i < maxCachedKeys; i++)
691 keyCache[i].used = 0;
694 for (i = 0; i < maxCachedKeys; i++)
695 if (keyCache[i].used == 0) {
697 keyCache[i].kvno = kvno;
698 strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
699 strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
700 keyCacheVersion = ntohl(cheader.specialKeysVersion);
701 memcpy(&keyCache[i].key, key, sizeof(*key));
702 keyCache[i].superseded = superseded;
703 keyCache[i].used = time(0);
705 ReleaseWriteLock(&keycache_lock);
708 /* i == maxCachedKeys */
710 (struct cachedKey *)realloc(keyCache,
712 2) * sizeof(struct cachedKey));
714 es_Report("Can't realloc keyCache! out of memory?");
719 int j = i; /* initialize new storage */
720 while (j < maxCachedKeys)
721 keyCache[j++].used = 0;
726 /* Look up the key given a principal and a kvno. This is called by GetTicket
727 to get the decryption key for the authenticating ticket. It is also called
728 by the rxkad security module to decrypt admin tickets. The rxkad call is
729 with tt==0, since Rx can't call Ubik. */
732 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
733 struct ktc_encryptionKey *key)
738 struct kaentry tentry;
740 struct kaOldKeys okeys;
742 ObtainReadLock(&keycache_lock);
743 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
744 code = KAKEYCACHEINVALID;
746 for (i = 0; i < maxCachedKeys; i++) {
747 if (keyCache[i].used) { /* zero used date means invalid */
748 if ((keyCache[i].kvno == kvno)
749 && (strcmp(keyCache[i].name, name) == 0)
750 && (strcmp(keyCache[i].inst, inst) == 0)) {
751 memcpy(key, &keyCache[i].key, sizeof(*key));
752 keyCache[i].used = time(0);
753 ReleaseReadLock(&keycache_lock);
760 ReleaseReadLock(&keycache_lock);
764 /* we missed in the cache so need to look in the Ubik database */
765 code = FindBlock(tt, name, inst, &to, &tentry);
771 /* first check the current key */
772 if (tentry.key_version == htonl(kvno)) {
773 memcpy(key, &tentry.key, sizeof(*key));
774 ka_Encache(name, inst, kvno, key, NEVERDATE);
777 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
778 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
781 if (ntohl(okeys.entry) == to)
782 for (i = 0; i < NOLDKEYS; i++)
783 if (okeys.keys[i].superseded
784 && (ntohl(okeys.keys[i].version) == kvno)) {
785 memcpy(key, &okeys.keys[i].key, sizeof(*key));
786 ka_Encache(name, inst, kvno, key,
787 ntohl(okeys.keys[i].superseded));
794 /* Look up the primary key and key version for a principal. */
797 ka_LookupKey(struct ubik_trans *tt,
800 afs_int32 *kvno, /* returned */
801 struct ktc_encryptionKey *key) /* copied out */
805 struct kaentry tentry;
808 ObtainReadLock(&keycache_lock);
809 if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
810 code = KAKEYCACHEINVALID;
812 for (i = 0; i < maxCachedKeys; i++) {
813 if (keyCache[i].used) { /* zero used date means invalid */
814 if ((keyCache[i].superseded == NEVERDATE)
815 && (strcmp(keyCache[i].name, name) == 0)
816 && (strcmp(keyCache[i].inst, inst) == 0)) {
817 memcpy(key, &keyCache[i].key, sizeof(*key));
818 *kvno = keyCache[i].kvno;
819 keyCache[i].used = time(0);
820 ReleaseReadLock(&keycache_lock);
827 ReleaseReadLock(&keycache_lock);
831 /* we missed in the cache so need to look in the Ubik database */
832 code = FindBlock(tt, name, inst, &to, &tentry);
837 memcpy(key, &tentry.key, sizeof(*key));
838 *kvno = ntohl(tentry.key_version);
839 ka_Encache(name, inst, *kvno, key, NEVERDATE);
843 /* This is, hopefully a temporary mechanism to fill the cache will all keys
844 since filling cache misses during rxkad challenge responses will deadlock if
845 Ubik needs to use Rx. */
848 ka_FillKeyCache(struct ubik_trans *tt)
854 struct ktc_encryptionKey k;
855 struct kaOldKeys okeys;
856 struct kaentry tentry;
858 /* this is a little marginal, but... */
859 if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
863 for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
864 code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
867 /* get name & instance */
869 karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
873 /* get all the old keys in this block */
874 for (i = 0; i < NOLDKEYS; i++)
875 if (okeys.keys[i].superseded) {
877 ka_LookupKvno(tt, tentry.userID.name,
878 tentry.userID.instance,
879 ntohl(okeys.keys[i].version), &k);
884 if (++nfound > maxCachedKeys)
885 return KADATABASEINCONSISTENT;
890 update_admin_count(struct ubik_trans *tt, int delta)
895 cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
896 to = DOFFSET(0, &cheader, &cheader.admin_accounts);
898 kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
905 index_OK(afs_int32 index)
907 if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
908 || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
913 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
916 name_instance_legal(char *name, char *instance)
920 /* No string checks apply anymore. The international people want to use full 8
921 bit ascii without problems. */
923 code = (strlen(name) < MAXKTCNAMELEN)
924 && (strlen(instance) < MAXKTCNAMELEN);
926 map = LEGALCHARS; /* permitted chars, instance allows <period> */
927 code = (strlen(name) > 0) && string_legal(instance, map)
928 && string_legal(name, map + 1);
931 dynamic_statistics.string_checks++;
937 string_legal(char *str, char *map)
942 if (slen >= MAXKTCNAMELEN)
943 return 0; /* with trailing null must fit in data base */
944 return (slen == strspn(str, map)); /* strspn returns length(str) if all chars in map */