5d59ba2a73d100e2f77d71c7ef3169206f85eb32
[openafs.git] / src / kauth / kadatabase.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #include <ubik.h>
16 #include <rx/xdr.h>
17 #include <rx/rx.h>
18 #include <afs/afsutil.h>
19
20 #include "kauth.h"
21 #include "kautils.h"
22 #include "kaserver.h"
23
24 #if !defined(offsetof)
25 #include <stddef.h>             /* for definition of offsetof() */
26 #endif
27
28 extern Date cheaderReadTime;    /* time cheader last read in */
29
30 #define set_header_word(tt,field,value) kawrite ((tt), (offsetof(struct kaheader, field)), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
31
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))
33
34 static int index_OK(afs_int32);
35
36 afs_int32
37 NameHash(char *aname, char *ainstance)
38 {
39     unsigned int hash;
40     int i;
41
42     /* stolen directly from the HashString function in the vol package */
43     hash = 0;
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);
49 }
50
51 /* package up seek and write into one procedure for ease of use */
52
53 afs_int32
54 kawrite(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
55 {
56     afs_int32 code;
57
58     code = ubik_Seek(tt, 0, pos);
59     if (code)
60         return code;
61     code = ubik_Write(tt, buff, len);
62     return code;
63 }
64
65 /* same thing for read */
66
67 afs_int32
68 karead(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
69 {
70     afs_int32 code;
71
72     code = ubik_Seek(tt, 0, pos);
73     if (code)
74         return code;
75     code = ubik_Read(tt, buff, len);
76     return code;
77 }
78
79 static struct Lock keycache_lock;
80
81 static int maxCachedKeys;
82
83 static struct cachedKey {
84     Date used;
85     int superseded;             /* NEVERDATE => this is current key */
86     afs_int32 kvno;
87     struct ktc_encryptionKey key;
88     char name[MAXKTCNAMELEN];
89     char inst[MAXKTCNAMELEN];
90 } *keyCache;
91 static afs_int32 keyCacheVersion = 0;
92
93 static afs_int32 maxKeyLifetime;
94 static int dbfixup = 0;
95
96 void
97 init_kadatabase(int initFlags)
98 {
99     Lock_Init(&keycache_lock);
100
101     maxCachedKeys = 10;
102     keyCache =
103         (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
104     keyCacheVersion = 0;
105     if (initFlags & 4) {
106         maxKeyLifetime = 90;
107     } else {
108         maxKeyLifetime = MAXKTCTICKETLIFETIME;
109     }
110     if (initFlags & 8)
111         dbfixup++;
112 }
113
114 /* check that the database has been initialized.  Be careful to fail in a safe
115    manner, to avoid bogusly reinitializing the db.  */
116 /**
117  * reads in db cache from ubik.
118  *
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
122  *                  rebuild the db)
123  *
124  * @return operation status
125  *   @retval 0 success
126  */
127 static afs_int32
128 UpdateCache(struct ubik_trans *at, void *rock)
129 {
130     int (*db_init) (struct ubik_trans *) = rock;
131     afs_int32 code;
132     afs_int32 iversion;
133     afs_int32 tversion;
134
135     if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
136         || (code =
137             karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
138                    sizeof(afs_int32)))) {
139         if (code == UEOF)
140             printf("No data base\n");
141         else
142             printf("I/O Error\n");
143     } else {
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));
148             if (code) {
149                 printf("SetupHeader failed\n");
150                 code = KAIO;
151             } else {
152                 cheaderReadTime = time(0);
153             }
154         } else {
155             printf("DB version should be %d; Initial = %d; Terminal = %d\n",
156                    KADBVERSION, iversion, tversion);
157             code = KAIO;
158         }
159     }
160     if (code == 0)
161         return 0;
162
163     /* if here, we have no version number or the wrong version number in the
164      * file */
165     if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
166         code = KAEMPTY;
167     else
168         code = KAIO;
169
170     if ((db_init == 0) || (code == KAIO))
171         return code;
172
173     printf("Error discovered in header, rebuilding.\n");
174
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));
180     cheader.freePtr = 0;
181     cheader.eofPtr = htonl(sizeof(cheader));
182     cheader.kvnoPtr = 0;
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));
188     if (code)
189         return KAIO;            /* return the error code */
190
191     return db_init(at);         /* initialize the db */
192 }
193
194 afs_int32
195 CheckInit(struct ubik_trans *at,
196           int (*db_init) (struct ubik_trans *))         /* procedure to call if rebuilding DB */
197 {
198     return ubik_CheckCache(at, UpdateCache, db_init);
199 }
200
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.  */
203
204 afs_int32
205 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
206 {
207     afs_int32 code;
208     afs_int32 temp;
209
210     if (cheader.freePtr) {
211         /* allocate this dude */
212         temp = ntohl(cheader.freePtr);
213         code = karead(at, temp, (char *)tentry, sizeof(kaentry));
214         if (code)
215             return 0;           /* can't read block */
216         code = set_header_word(at, freePtr, tentry->next);
217     } else {
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)));
221     }
222     if (code)
223         return 0;
224
225     code = inc_header_word(at, stats.allocs);
226     if (code)
227         return 0;
228     memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
229     return temp;
230 }
231
232 /* Free a block given its index.  It must already have been unthreaded.
233    Returns zero for success or an error code on failure. */
234
235 afs_int32
236 FreeBlock(struct ubik_trans *at, afs_int32 index)
237 {
238     struct kaentry tentry;
239     int code;
240
241     /* check index just to be on the safe side */
242     if (!index_OK(index))
243         return KABADINDEX;
244
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));
249     if (code)
250         return KAIO;
251     code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
252     if (code)
253         return KAIO;
254
255     code = inc_header_word(at, stats.frees);
256     if (code)
257         return KAIO;
258     return 0;
259 }
260
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
264    returned. */
265
266 afs_int32
267 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
268           struct kaentry *tentry)
269 {
270     afs_int32 i, code;
271     afs_int32 to;
272
273     *toP = 0;
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));
278         if (code)
279             return code;
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 */
285             return 0;
286         }
287     }
288     *toP = 0;                   /* no such entry */
289     return 0;
290 }
291
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. */
295
296 afs_int32
297 ThreadBlock(struct ubik_trans *at, afs_int32 index,
298             struct kaentry *tentry)
299 {
300     int code;
301     int hi;                     /* hash index */
302
303     if (!index_OK(index))
304         return KABADINDEX;
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));
308     if (code)
309         return KAIO;
310     code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
311     if (code)
312         return KAIO;
313     return 0;
314 }
315
316 /* Remove a block from the hash table.  If success return 0, else return an
317    error code. */
318
319 afs_int32
320 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
321 {
322     afs_int32 i, code;
323     afs_int32 to;
324     afs_int32 lo;
325     struct kaentry tentry;
326
327     i = NameHash(aentry->userID.name, aentry->userID.instance);
328     lo = 0;
329     for (to = ntohl(cheader.nameHash[i]); to != NULLO;
330          to = ntohl(tentry.next)) {
331         code = karead(at, to, (char *)&tentry, sizeof(kaentry));
332         if (code)
333             return KAIO;
334         /* see if the name matches */
335         if (!strcmp(aentry->userID.name, tentry.userID.name)
336             && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
337             /* found it */
338             if (lo) {           /* unthread from last block */
339                 code =
340                     kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
341                 if (code)
342                     return KAIO;
343             } else {            /* unthread from hash table */
344                 code = set_header_word(at, nameHash[i], tentry.next);
345                 if (code)
346                     return KAIO;
347             }
348             aentry->next = 0;   /* just to be sure */
349             return 0;
350         }
351         lo = DOFFSET(to, &tentry, &tentry.next);
352     }
353     return KANOENT;
354 }
355
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.  */
364
365 afs_int32
366 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
367           afs_int32 *remaining)
368 {
369     int code;
370     afs_int32 last;
371
372     if (index == 0)             /* get first one */
373         index = sizeof(cheader);
374     else {
375         if (!index_OK(index)) {
376             *remaining = -1;    /* error */
377             return 0;
378         }
379         index += sizeof(kaentry);
380     }
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));
384         if (code) {
385             *remaining = -1;
386             return 0;
387         }
388         if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
389             /* estimate remaining number of entries, not including this one */
390             *remaining = (last - index) / sizeof(kaentry) - 1;
391             return index;
392         }
393     }
394     *remaining = 0;             /* no more entries */
395     return 0;
396 }
397
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. */
401
402 afs_int32
403 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
404           struct kaentry *tentry, struct ktc_encryptionKey *key)
405 {
406     struct kaOldKeys okeys;     /* old keys block */
407     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
408     afs_int32 prevptr, nextprevptr;
409     int code, i;
410     Date now = time(0);
411     afs_int32 newkeyver;        /* new key version number */
412     afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
413     int addednewkey = 0, modified;
414 #ifdef AUTH_DBM_LOG
415     int foundcurrentkey = 0;
416 #endif
417
418     es_Report("Newkey for %s.%s\n", tentry->userID.name,
419               tentry->userID.instance);
420
421     newkeyver = ntohl(tentry->key_version) + 1;
422     if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
423         newkeyver = 1;
424
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
429      * this link.
430      */
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));
436         if (code)
437             return code;
438
439         nextaddr = ntohl(okeys.next);
440         nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
441
442         /* We only want oldkey blocks that belong to this entry */
443         if (ntohl(okeys.entry) != tentryaddr)
444             continue;
445
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++) {
449             /* foreachkey */
450             /* Keep count of number of entries found */
451             if (okeys.keys[i].superseded != 0) {
452                 oldtotalkeyentries++;
453             }
454
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);
458                 modified = 1;
459 #ifdef AUTH_DBM_LOG
460                 if (foundcurrentkey) {
461                     ViceLog(0,
462                             ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
463                              tentry->userID.name, tentry->userID.instance));
464                 }
465                 foundcurrentkey = 1;
466 #endif
467             }
468
469             /* If we find an oldkey of the same version or
470              * an old key that has expired, then delete it.
471              */
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));
478                 modified = 1;
479
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));
483             }
484
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));
491                 modified = 1;
492                 addednewkey = okeysaddr;
493             }
494
495             /* Keep count of number of entries found */
496             if (okeys.keys[i].superseded != 0) {
497                 keyentries++;
498                 newtotalkeyentries++;
499             }
500         }                       /* foreachkey */
501
502         /* If we modified the block, write it out */
503         if (modified && keyentries) {
504             code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
505             if (code)
506                 return code;
507         }
508
509         /* If there are no more entries in this oldkeys block, delete it */
510         if (keyentries == 0) {
511             if (!prevptr) {
512                 code = set_header_word(tt, kvnoPtr, okeys.next);
513             } else {
514                 code =
515                     kawrite(tt, prevptr, (char *)&okeys.next,
516                             sizeof(afs_int32));
517             }
518             if (code)
519                 return code;
520             code = FreeBlock(tt, okeysaddr);
521             if (code)
522                 return code;
523
524             nextprevptr = prevptr;      /* won't bump prevptr */
525         }
526     }                           /* foreacholdkeysblock */
527
528     /* If we could not add the key, create a new oldkeys block */
529     if (!addednewkey) {
530         /* Allocate and fill in an oldkeys block */
531         addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
532         if (!addednewkey)
533             return KACREATEFAIL;
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++;
540
541         /* Thread onto the header's chain of oldkeys */
542         okeys.next = cheader.kvnoPtr;
543         code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
544         if (code)
545             return code;
546
547         /* Write the oldkeys block out */
548         code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
549         if (code)
550             return code;
551
552         es_Report("New oldkey block allocated at %d\n", addednewkey);
553     }
554 #ifdef AUTH_DBM_LOG
555     if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
556         ViceLog(0,
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));
560     }
561 #endif
562
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));
568
569     /* invalidate key caches everywhere */
570     code = inc_header_word(tt, specialKeysVersion);
571     if (code)
572         return code;
573
574     es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
575               newtotalkeyentries);
576     return 0;
577 }
578
579 afs_int32
580 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
581           struct kaentry *tentry)
582 {
583     int code;
584     struct kaOldKeys okeys;     /* old keys block */
585     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
586     afs_int32 prevptr = 0;
587
588     es_Report("DelKey for %s.%s\n", tentry->userID.name,
589               tentry->userID.instance);
590
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
595      * this link.
596      */
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));
601         if (code)
602             return code;
603         nextaddr = ntohl(okeys.next);
604
605         /* We only want oldkey blocks that belong to this entry */
606         if (ntohl(okeys.entry) != tentryaddr) {
607             prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
608             continue;
609         }
610
611         /* Delete the oldkeys block */
612         if (prevptr) {
613             code =
614                 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
615         } else {
616             code = set_header_word(tt, kvnoPtr, okeys.next);
617         }
618         if (code)
619             return code;
620         code = FreeBlock(tt, okeysaddr);
621         if (code)
622             return code;
623     }                           /* foreacholdkeysblock */
624
625     /* Update the tentry. We rely on caller to write it out */
626     tentry->misc.asServer.oldKeys = 0;
627     tentry->misc.asServer.nOldKeys = 0;
628
629     /* invalidate key caches everywhere */
630     code = inc_header_word(tt, specialKeysVersion);
631     if (code)
632         return code;
633
634     return 0;
635 }
636
637 void
638 ka_debugKeyCache(struct ka_debugInfo *info)
639 {
640     int i;
641
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));
645
646     info->kcVersion = keyCacheVersion;
647     info->kcSize = maxCachedKeys;
648     info->kcUsed = 0;
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)];
655
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;
661 #if DEBUG_KEY_CACHE
662                 {
663                     int k;
664                     for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
665                         info->kcInfo[j].keycksum +=
666                             ((char *)&keyCache[i].key)[k];
667                 }
668 #endif
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));
674             }
675             info->kcUsed++;
676         }
677     }
678 }
679
680 /* Add a key to the key cache, expanding it if necessary. */
681
682 void
683 ka_Encache(char *name, char *inst, afs_int32 kvno,
684            struct ktc_encryptionKey *key, Date superseded)
685 {
686     int i;
687
688     ObtainWriteLock(&keycache_lock);
689     if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
690         for (i = 0; i < maxCachedKeys; i++)
691             keyCache[i].used = 0;
692     }
693
694     for (i = 0; i < maxCachedKeys; i++)
695         if (keyCache[i].used == 0) {
696           encache:
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);
704
705             ReleaseWriteLock(&keycache_lock);
706             return;
707         }
708     /* i == maxCachedKeys */
709     keyCache = realloc(keyCache, (maxCachedKeys *=2)
710                                   * sizeof(struct cachedKey));
711     if (keyCache == 0) {
712         es_Report("Can't realloc keyCache! out of memory?");
713         exit(123);
714     }
715
716     {
717         int j = i;              /* initialize new storage */
718         while (j < maxCachedKeys)
719             keyCache[j++].used = 0;
720     }
721     goto encache;
722 }
723
724 /* Look up the key given a principal and a kvno.  This is called by GetTicket
725    to get the decryption key for the authenticating ticket.  It is also called
726    by the rxkad security module to decrypt admin tickets.  The rxkad call is
727    with tt==0, since Rx can't call Ubik. */
728
729 afs_int32
730 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
731               struct ktc_encryptionKey *key)
732 {
733     int i;
734     int code = 0;
735     afs_int32 to;
736     struct kaentry tentry;
737     afs_int32 ko;
738     struct kaOldKeys okeys;
739
740     ObtainReadLock(&keycache_lock);
741     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
742         code = KAKEYCACHEINVALID;
743     else {
744         for (i = 0; i < maxCachedKeys; i++) {
745             if (keyCache[i].used) {     /* zero used date means invalid */
746                 if ((keyCache[i].kvno == kvno)
747                     && (strcmp(keyCache[i].name, name) == 0)
748                     && (strcmp(keyCache[i].inst, inst) == 0)) {
749                     memcpy(key, &keyCache[i].key, sizeof(*key));
750                     keyCache[i].used = time(0);
751                     ReleaseReadLock(&keycache_lock);
752                     return 0;
753                 }
754             }
755         }
756         code = KAUNKNOWNKEY;
757     }
758     ReleaseReadLock(&keycache_lock);
759     if (!tt)
760         return code;
761
762     /* we missed in the cache so need to look in the Ubik database */
763     code = FindBlock(tt, name, inst, &to, &tentry);
764     if (code)
765         return code;
766     if (to == 0)
767         return KANOENT;
768
769     /* first check the current key */
770     if (tentry.key_version == htonl(kvno)) {
771         memcpy(key, &tentry.key, sizeof(*key));
772         ka_Encache(name, inst, kvno, key, NEVERDATE);
773         return 0;
774     }
775     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
776         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
777         if (code)
778             return KAIO;
779         if (ntohl(okeys.entry) == to)
780             for (i = 0; i < NOLDKEYS; i++)
781                 if (okeys.keys[i].superseded
782                     && (ntohl(okeys.keys[i].version) == kvno)) {
783                     memcpy(key, &okeys.keys[i].key, sizeof(*key));
784                     ka_Encache(name, inst, kvno, key,
785                                ntohl(okeys.keys[i].superseded));
786                     return 0;
787                 }
788     }
789     return KAUNKNOWNKEY;
790 }
791
792 /* Look up the primary key and key version for a principal. */
793
794 afs_int32
795 ka_LookupKey(struct ubik_trans *tt,
796              char *name,
797              char *inst,
798              afs_int32 *kvno,                   /* returned */
799              struct ktc_encryptionKey *key)     /* copied out */
800 {
801     int i;
802     afs_int32 to;
803     struct kaentry tentry;
804     afs_int32 code = 0;
805
806     ObtainReadLock(&keycache_lock);
807     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
808         code = KAKEYCACHEINVALID;
809     else {
810         for (i = 0; i < maxCachedKeys; i++) {
811             if (keyCache[i].used) {     /* zero used date means invalid */
812                 if ((keyCache[i].superseded == NEVERDATE)
813                     && (strcmp(keyCache[i].name, name) == 0)
814                     && (strcmp(keyCache[i].inst, inst) == 0)) {
815                     memcpy(key, &keyCache[i].key, sizeof(*key));
816                     *kvno = keyCache[i].kvno;
817                     keyCache[i].used = time(0);
818                     ReleaseReadLock(&keycache_lock);
819                     return 0;
820                 }
821             }
822         }
823         code = KAUNKNOWNKEY;
824     }
825     ReleaseReadLock(&keycache_lock);
826     if (!tt)
827         return code;
828
829     /* we missed in the cache so need to look in the Ubik database */
830     code = FindBlock(tt, name, inst, &to, &tentry);
831     if (code)
832         return code;
833     if (to == 0)
834         return KANOENT;
835     memcpy(key, &tentry.key, sizeof(*key));
836     *kvno = ntohl(tentry.key_version);
837     ka_Encache(name, inst, *kvno, key, NEVERDATE);
838     return 0;
839 }
840
841 /* This is, hopefully a temporary mechanism to fill the cache will all keys
842    since filling cache misses during rxkad challenge responses will deadlock if
843    Ubik needs to use Rx. */
844
845 afs_int32
846 ka_FillKeyCache(struct ubik_trans *tt)
847 {
848     int nfound;
849     afs_int32 ko;
850     int code;
851     int i;
852     struct ktc_encryptionKey k;
853     struct kaOldKeys okeys;
854     struct kaentry tentry;
855
856     /* this is a little marginal, but... */
857     if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
858         return 0;
859
860     nfound = 0;
861     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
862         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
863         if (code)
864             return KAIO;
865         /* get name & instance */
866         code =
867             karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
868         if (code)
869             return KAIO;
870
871         /* get all the old keys in this block */
872         for (i = 0; i < NOLDKEYS; i++)
873             if (okeys.keys[i].superseded) {
874                 code =
875                     ka_LookupKvno(tt, tentry.userID.name,
876                                   tentry.userID.instance,
877                                   ntohl(okeys.keys[i].version), &k);
878                 if (code)
879                     return code;
880             }
881     }
882     if (++nfound > maxCachedKeys)
883         return KADATABASEINCONSISTENT;
884     return 0;
885 }
886
887 afs_int32
888 update_admin_count(struct ubik_trans *tt, int delta)
889 {
890     afs_int32 to;
891     afs_int32 code;
892
893     cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
894     to = DOFFSET(0, &cheader, &cheader.admin_accounts);
895     code =
896         kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
897     if (code)
898         return KAIO;
899     return 0;
900 }
901
902 static int
903 index_OK(afs_int32 index)
904 {
905     if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
906         || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
907         return 0;
908     return 1;
909 }
910
911 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
912
913 int
914 name_instance_legal(char *name, char *instance)
915 {
916     int code;
917
918 /* No string checks apply anymore.  The international people want to use full 8
919    bit ascii without problems. */
920 #if 1
921     code = (strlen(name) < MAXKTCNAMELEN)
922         && (strlen(instance) < MAXKTCNAMELEN);
923 #else
924     map = LEGALCHARS;           /* permitted chars, instance allows <period> */
925     code = (strlen(name) > 0) && string_legal(instance, map)
926         && string_legal(name, map + 1);
927 #endif
928     if (!code)
929         dynamic_statistics.string_checks++;
930     return code;
931 }
932
933 #if 0
934 static int
935 string_legal(char *str, char *map)
936 {
937     int slen;
938
939     slen = strlen(str);
940     if (slen >= MAXKTCNAMELEN)
941         return 0;               /* with trailing null must fit in data base */
942     return (slen == strspn(str, map));  /* strspn returns length(str) if all chars in map */
943 }
944 #endif
945