9b489a74543819862f23a694434cc8b507e8f19b
[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 =
710         (struct cachedKey *)realloc(keyCache,
711                                     (maxCachedKeys *=
712                                      2) * sizeof(struct cachedKey));
713     if (keyCache == 0) {
714         es_Report("Can't realloc keyCache! out of memory?");
715         exit(123);
716     }
717
718     {
719         int j = i;              /* initialize new storage */
720         while (j < maxCachedKeys)
721             keyCache[j++].used = 0;
722     }
723     goto encache;
724 }
725
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. */
730
731 afs_int32
732 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
733               struct ktc_encryptionKey *key)
734 {
735     int i;
736     int code = 0;
737     afs_int32 to;
738     struct kaentry tentry;
739     afs_int32 ko;
740     struct kaOldKeys okeys;
741
742     ObtainReadLock(&keycache_lock);
743     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
744         code = KAKEYCACHEINVALID;
745     else {
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);
754                     return 0;
755                 }
756             }
757         }
758         code = KAUNKNOWNKEY;
759     }
760     ReleaseReadLock(&keycache_lock);
761     if (!tt)
762         return code;
763
764     /* we missed in the cache so need to look in the Ubik database */
765     code = FindBlock(tt, name, inst, &to, &tentry);
766     if (code)
767         return code;
768     if (to == 0)
769         return KANOENT;
770
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);
775         return 0;
776     }
777     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
778         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
779         if (code)
780             return KAIO;
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));
788                     return 0;
789                 }
790     }
791     return KAUNKNOWNKEY;
792 }
793
794 /* Look up the primary key and key version for a principal. */
795
796 afs_int32
797 ka_LookupKey(struct ubik_trans *tt,
798              char *name,
799              char *inst,
800              afs_int32 *kvno,                   /* returned */
801              struct ktc_encryptionKey *key)     /* copied out */
802 {
803     int i;
804     afs_int32 to;
805     struct kaentry tentry;
806     afs_int32 code = 0;
807
808     ObtainReadLock(&keycache_lock);
809     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
810         code = KAKEYCACHEINVALID;
811     else {
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);
821                     return 0;
822                 }
823             }
824         }
825         code = KAUNKNOWNKEY;
826     }
827     ReleaseReadLock(&keycache_lock);
828     if (!tt)
829         return code;
830
831     /* we missed in the cache so need to look in the Ubik database */
832     code = FindBlock(tt, name, inst, &to, &tentry);
833     if (code)
834         return code;
835     if (to == 0)
836         return KANOENT;
837     memcpy(key, &tentry.key, sizeof(*key));
838     *kvno = ntohl(tentry.key_version);
839     ka_Encache(name, inst, *kvno, key, NEVERDATE);
840     return 0;
841 }
842
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. */
846
847 afs_int32
848 ka_FillKeyCache(struct ubik_trans *tt)
849 {
850     int nfound;
851     afs_int32 ko;
852     int code;
853     int i;
854     struct ktc_encryptionKey k;
855     struct kaOldKeys okeys;
856     struct kaentry tentry;
857
858     /* this is a little marginal, but... */
859     if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
860         return 0;
861
862     nfound = 0;
863     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
864         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
865         if (code)
866             return KAIO;
867         /* get name & instance */
868         code =
869             karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
870         if (code)
871             return KAIO;
872
873         /* get all the old keys in this block */
874         for (i = 0; i < NOLDKEYS; i++)
875             if (okeys.keys[i].superseded) {
876                 code =
877                     ka_LookupKvno(tt, tentry.userID.name,
878                                   tentry.userID.instance,
879                                   ntohl(okeys.keys[i].version), &k);
880                 if (code)
881                     return code;
882             }
883     }
884     if (++nfound > maxCachedKeys)
885         return KADATABASEINCONSISTENT;
886     return 0;
887 }
888
889 afs_int32
890 update_admin_count(struct ubik_trans *tt, int delta)
891 {
892     afs_int32 to;
893     afs_int32 code;
894
895     cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
896     to = DOFFSET(0, &cheader, &cheader.admin_accounts);
897     code =
898         kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
899     if (code)
900         return KAIO;
901     return 0;
902 }
903
904 static int
905 index_OK(afs_int32 index)
906 {
907     if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
908         || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
909         return 0;
910     return 1;
911 }
912
913 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
914
915 int
916 name_instance_legal(char *name, char *instance)
917 {
918     int code;
919
920 /* No string checks apply anymore.  The international people want to use full 8
921    bit ascii without problems. */
922 #if 1
923     code = (strlen(name) < MAXKTCNAMELEN)
924         && (strlen(instance) < MAXKTCNAMELEN);
925 #else
926     map = LEGALCHARS;           /* permitted chars, instance allows <period> */
927     code = (strlen(name) > 0) && string_legal(instance, map)
928         && string_legal(name, map + 1);
929 #endif
930     if (!code)
931         dynamic_statistics.string_checks++;
932     return code;
933 }
934
935 #if 0
936 static int
937 string_legal(char *str, char *map)
938 {
939     int slen;
940
941     slen = strlen(str);
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 */
945 }
946 #endif
947