9d7710669fb9afc0082f50cd7e6ce92453a15fba
[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 = malloc(maxCachedKeys * sizeof(struct cachedKey));
103     keyCacheVersion = 0;
104     if (initFlags & 4) {
105         maxKeyLifetime = 90;
106     } else {
107         maxKeyLifetime = MAXKTCTICKETLIFETIME;
108     }
109     if (initFlags & 8)
110         dbfixup++;
111 }
112
113 /* check that the database has been initialized.  Be careful to fail in a safe
114    manner, to avoid bogusly reinitializing the db.  */
115 /**
116  * reads in db cache from ubik.
117  *
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
121  *                  rebuild the db)
122  *
123  * @return operation status
124  *   @retval 0 success
125  */
126 static afs_int32
127 UpdateCache(struct ubik_trans *at, void *rock)
128 {
129     int (*db_init) (struct ubik_trans *) = rock;
130     afs_int32 code;
131     afs_int32 iversion;
132     afs_int32 tversion;
133
134     if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
135         || (code =
136             karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
137                    sizeof(afs_int32)))) {
138         if (code == UEOF)
139             printf("No data base\n");
140         else
141             printf("I/O Error\n");
142     } else {
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));
147             if (code) {
148                 printf("SetupHeader failed\n");
149                 code = KAIO;
150             } else {
151                 cheaderReadTime = time(0);
152             }
153         } else {
154             printf("DB version should be %d; Initial = %d; Terminal = %d\n",
155                    KADBVERSION, iversion, tversion);
156             code = KAIO;
157         }
158     }
159     if (code == 0)
160         return 0;
161
162     /* if here, we have no version number or the wrong version number in the
163      * file */
164     if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
165         code = KAEMPTY;
166     else
167         code = KAIO;
168
169     if ((db_init == 0) || (code == KAIO))
170         return code;
171
172     printf("Error discovered in header, rebuilding.\n");
173
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));
179     cheader.freePtr = 0;
180     cheader.eofPtr = htonl(sizeof(cheader));
181     cheader.kvnoPtr = 0;
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));
187     if (code)
188         return KAIO;            /* return the error code */
189
190     return db_init(at);         /* initialize the db */
191 }
192
193 afs_int32
194 CheckInit(struct ubik_trans *at,
195           int (*db_init) (struct ubik_trans *))         /* procedure to call if rebuilding DB */
196 {
197     return ubik_CheckCache(at, UpdateCache, db_init);
198 }
199
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.  */
202
203 afs_int32
204 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
205 {
206     afs_int32 code;
207     afs_int32 temp;
208
209     if (cheader.freePtr) {
210         /* allocate this dude */
211         temp = ntohl(cheader.freePtr);
212         code = karead(at, temp, (char *)tentry, sizeof(kaentry));
213         if (code)
214             return 0;           /* can't read block */
215         code = set_header_word(at, freePtr, tentry->next);
216     } else {
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)));
220     }
221     if (code)
222         return 0;
223
224     code = inc_header_word(at, stats.allocs);
225     if (code)
226         return 0;
227     memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
228     return temp;
229 }
230
231 /* Free a block given its index.  It must already have been unthreaded.
232    Returns zero for success or an error code on failure. */
233
234 afs_int32
235 FreeBlock(struct ubik_trans *at, afs_int32 index)
236 {
237     struct kaentry tentry;
238     int code;
239
240     /* check index just to be on the safe side */
241     if (!index_OK(index))
242         return KABADINDEX;
243
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));
248     if (code)
249         return KAIO;
250     code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
251     if (code)
252         return KAIO;
253
254     code = inc_header_word(at, stats.frees);
255     if (code)
256         return KAIO;
257     return 0;
258 }
259
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
263    returned. */
264
265 afs_int32
266 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP,
267           struct kaentry *tentry)
268 {
269     afs_int32 i, code;
270     afs_int32 to;
271
272     *toP = 0;
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));
277         if (code)
278             return code;
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 */
284             return 0;
285         }
286     }
287     *toP = 0;                   /* no such entry */
288     return 0;
289 }
290
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. */
294
295 afs_int32
296 ThreadBlock(struct ubik_trans *at, afs_int32 index,
297             struct kaentry *tentry)
298 {
299     int code;
300     int hi;                     /* hash index */
301
302     if (!index_OK(index))
303         return KABADINDEX;
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));
307     if (code)
308         return KAIO;
309     code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
310     if (code)
311         return KAIO;
312     return 0;
313 }
314
315 /* Remove a block from the hash table.  If success return 0, else return an
316    error code. */
317
318 afs_int32
319 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
320 {
321     afs_int32 i, code;
322     afs_int32 to;
323     afs_int32 lo;
324     struct kaentry tentry;
325
326     i = NameHash(aentry->userID.name, aentry->userID.instance);
327     lo = 0;
328     for (to = ntohl(cheader.nameHash[i]); to != NULLO;
329          to = ntohl(tentry.next)) {
330         code = karead(at, to, (char *)&tentry, sizeof(kaentry));
331         if (code)
332             return KAIO;
333         /* see if the name matches */
334         if (!strcmp(aentry->userID.name, tentry.userID.name)
335             && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
336             /* found it */
337             if (lo) {           /* unthread from last block */
338                 code =
339                     kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
340                 if (code)
341                     return KAIO;
342             } else {            /* unthread from hash table */
343                 code = set_header_word(at, nameHash[i], tentry.next);
344                 if (code)
345                     return KAIO;
346             }
347             aentry->next = 0;   /* just to be sure */
348             return 0;
349         }
350         lo = DOFFSET(to, &tentry, &tentry.next);
351     }
352     return KANOENT;
353 }
354
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.  */
363
364 afs_int32
365 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry,
366           afs_int32 *remaining)
367 {
368     int code;
369     afs_int32 last;
370
371     if (index == 0)             /* get first one */
372         index = sizeof(cheader);
373     else {
374         if (!index_OK(index)) {
375             *remaining = -1;    /* error */
376             return 0;
377         }
378         index += sizeof(kaentry);
379     }
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));
383         if (code) {
384             *remaining = -1;
385             return 0;
386         }
387         if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
388             /* estimate remaining number of entries, not including this one */
389             *remaining = (last - index) / sizeof(kaentry) - 1;
390             return index;
391         }
392     }
393     *remaining = 0;             /* no more entries */
394     return 0;
395 }
396
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. */
400
401 afs_int32
402 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr,
403           struct kaentry *tentry, struct ktc_encryptionKey *key)
404 {
405     struct kaOldKeys okeys;     /* old keys block */
406     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
407     afs_int32 prevptr, nextprevptr;
408     int code, i;
409     Date now = time(0);
410     afs_int32 newkeyver;        /* new key version number */
411     afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
412     int addednewkey = 0, modified;
413 #ifdef AUTH_DBM_LOG
414     int foundcurrentkey = 0;
415 #endif
416
417     es_Report("Newkey for %s.%s\n", tentry->userID.name,
418               tentry->userID.instance);
419
420     newkeyver = ntohl(tentry->key_version) + 1;
421     if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
422         newkeyver = 1;
423
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
428      * this link.
429      */
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));
435         if (code)
436             return code;
437
438         nextaddr = ntohl(okeys.next);
439         nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
440
441         /* We only want oldkey blocks that belong to this entry */
442         if (ntohl(okeys.entry) != tentryaddr)
443             continue;
444
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++) {
448             /* foreachkey */
449             /* Keep count of number of entries found */
450             if (okeys.keys[i].superseded != 0) {
451                 oldtotalkeyentries++;
452             }
453
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);
457                 modified = 1;
458 #ifdef AUTH_DBM_LOG
459                 if (foundcurrentkey) {
460                     ViceLog(0,
461                             ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
462                              tentry->userID.name, tentry->userID.instance));
463                 }
464                 foundcurrentkey = 1;
465 #endif
466             }
467
468             /* If we find an oldkey of the same version or
469              * an old key that has expired, then delete it.
470              */
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));
477                 modified = 1;
478
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));
482             }
483
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));
490                 modified = 1;
491                 addednewkey = okeysaddr;
492             }
493
494             /* Keep count of number of entries found */
495             if (okeys.keys[i].superseded != 0) {
496                 keyentries++;
497                 newtotalkeyentries++;
498             }
499         }                       /* foreachkey */
500
501         /* If we modified the block, write it out */
502         if (modified && keyentries) {
503             code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
504             if (code)
505                 return code;
506         }
507
508         /* If there are no more entries in this oldkeys block, delete it */
509         if (keyentries == 0) {
510             if (!prevptr) {
511                 code = set_header_word(tt, kvnoPtr, okeys.next);
512             } else {
513                 code =
514                     kawrite(tt, prevptr, (char *)&okeys.next,
515                             sizeof(afs_int32));
516             }
517             if (code)
518                 return code;
519             code = FreeBlock(tt, okeysaddr);
520             if (code)
521                 return code;
522
523             nextprevptr = prevptr;      /* won't bump prevptr */
524         }
525     }                           /* foreacholdkeysblock */
526
527     /* If we could not add the key, create a new oldkeys block */
528     if (!addednewkey) {
529         /* Allocate and fill in an oldkeys block */
530         addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
531         if (!addednewkey)
532             return KACREATEFAIL;
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++;
539
540         /* Thread onto the header's chain of oldkeys */
541         okeys.next = cheader.kvnoPtr;
542         code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
543         if (code)
544             return code;
545
546         /* Write the oldkeys block out */
547         code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
548         if (code)
549             return code;
550
551         es_Report("New oldkey block allocated at %d\n", addednewkey);
552     }
553 #ifdef AUTH_DBM_LOG
554     if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
555         ViceLog(0,
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));
559     }
560 #endif
561
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));
567
568     /* invalidate key caches everywhere */
569     code = inc_header_word(tt, specialKeysVersion);
570     if (code)
571         return code;
572
573     es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
574               newtotalkeyentries);
575     return 0;
576 }
577
578 afs_int32
579 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr,
580           struct kaentry *tentry)
581 {
582     int code;
583     struct kaOldKeys okeys;     /* old keys block */
584     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
585     afs_int32 prevptr = 0;
586
587     es_Report("DelKey for %s.%s\n", tentry->userID.name,
588               tentry->userID.instance);
589
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
594      * this link.
595      */
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));
600         if (code)
601             return code;
602         nextaddr = ntohl(okeys.next);
603
604         /* We only want oldkey blocks that belong to this entry */
605         if (ntohl(okeys.entry) != tentryaddr) {
606             prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
607             continue;
608         }
609
610         /* Delete the oldkeys block */
611         if (prevptr) {
612             code =
613                 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
614         } else {
615             code = set_header_word(tt, kvnoPtr, okeys.next);
616         }
617         if (code)
618             return code;
619         code = FreeBlock(tt, okeysaddr);
620         if (code)
621             return code;
622     }                           /* foreacholdkeysblock */
623
624     /* Update the tentry. We rely on caller to write it out */
625     tentry->misc.asServer.oldKeys = 0;
626     tentry->misc.asServer.nOldKeys = 0;
627
628     /* invalidate key caches everywhere */
629     code = inc_header_word(tt, specialKeysVersion);
630     if (code)
631         return code;
632
633     return 0;
634 }
635
636 void
637 ka_debugKeyCache(struct ka_debugInfo *info)
638 {
639     int i;
640
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));
644
645     info->kcVersion = keyCacheVersion;
646     info->kcSize = maxCachedKeys;
647     info->kcUsed = 0;
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)];
654
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;
660 #if DEBUG_KEY_CACHE
661                 {
662                     int k;
663                     for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
664                         info->kcInfo[j].keycksum +=
665                             ((char *)&keyCache[i].key)[k];
666                 }
667 #endif
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));
673             }
674             info->kcUsed++;
675         }
676     }
677 }
678
679 /* Add a key to the key cache, expanding it if necessary. */
680
681 void
682 ka_Encache(char *name, char *inst, afs_int32 kvno,
683            struct ktc_encryptionKey *key, Date superseded)
684 {
685     int i;
686
687     ObtainWriteLock(&keycache_lock);
688     if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
689         for (i = 0; i < maxCachedKeys; i++)
690             keyCache[i].used = 0;
691     }
692
693     for (i = 0; i < maxCachedKeys; i++)
694         if (keyCache[i].used == 0) {
695           encache:
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);
703
704             ReleaseWriteLock(&keycache_lock);
705             return;
706         }
707     /* i == maxCachedKeys */
708     keyCache = realloc(keyCache, (maxCachedKeys *=2)
709                                   * sizeof(struct cachedKey));
710     if (keyCache == 0) {
711         es_Report("Can't realloc keyCache! out of memory?");
712         exit(123);
713     }
714
715     {
716         int j = i;              /* initialize new storage */
717         while (j < maxCachedKeys)
718             keyCache[j++].used = 0;
719     }
720     goto encache;
721 }
722
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. */
727
728 afs_int32
729 ka_LookupKvno(struct ubik_trans *tt, char *name, char *inst, afs_int32 kvno,
730               struct ktc_encryptionKey *key)
731 {
732     int i;
733     int code = 0;
734     afs_int32 to;
735     struct kaentry tentry;
736     afs_int32 ko;
737     struct kaOldKeys okeys;
738
739     ObtainReadLock(&keycache_lock);
740     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
741         code = KAKEYCACHEINVALID;
742     else {
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);
751                     return 0;
752                 }
753             }
754         }
755         code = KAUNKNOWNKEY;
756     }
757     ReleaseReadLock(&keycache_lock);
758     if (!tt)
759         return code;
760
761     /* we missed in the cache so need to look in the Ubik database */
762     code = FindBlock(tt, name, inst, &to, &tentry);
763     if (code)
764         return code;
765     if (to == 0)
766         return KANOENT;
767
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);
772         return 0;
773     }
774     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
775         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
776         if (code)
777             return KAIO;
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));
785                     return 0;
786                 }
787     }
788     return KAUNKNOWNKEY;
789 }
790
791 /* Look up the primary key and key version for a principal. */
792
793 afs_int32
794 ka_LookupKey(struct ubik_trans *tt,
795              char *name,
796              char *inst,
797              afs_int32 *kvno,                   /* returned */
798              struct ktc_encryptionKey *key)     /* copied out */
799 {
800     int i;
801     afs_int32 to;
802     struct kaentry tentry;
803     afs_int32 code = 0;
804
805     ObtainReadLock(&keycache_lock);
806     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
807         code = KAKEYCACHEINVALID;
808     else {
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);
818                     return 0;
819                 }
820             }
821         }
822         code = KAUNKNOWNKEY;
823     }
824     ReleaseReadLock(&keycache_lock);
825     if (!tt)
826         return code;
827
828     /* we missed in the cache so need to look in the Ubik database */
829     code = FindBlock(tt, name, inst, &to, &tentry);
830     if (code)
831         return code;
832     if (to == 0)
833         return KANOENT;
834     memcpy(key, &tentry.key, sizeof(*key));
835     *kvno = ntohl(tentry.key_version);
836     ka_Encache(name, inst, *kvno, key, NEVERDATE);
837     return 0;
838 }
839
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. */
843
844 afs_int32
845 ka_FillKeyCache(struct ubik_trans *tt)
846 {
847     int nfound;
848     afs_int32 ko;
849     int code;
850     int i;
851     struct ktc_encryptionKey k;
852     struct kaOldKeys okeys;
853     struct kaentry tentry;
854
855     /* this is a little marginal, but... */
856     if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
857         return 0;
858
859     nfound = 0;
860     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
861         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
862         if (code)
863             return KAIO;
864         /* get name & instance */
865         code =
866             karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
867         if (code)
868             return KAIO;
869
870         /* get all the old keys in this block */
871         for (i = 0; i < NOLDKEYS; i++)
872             if (okeys.keys[i].superseded) {
873                 code =
874                     ka_LookupKvno(tt, tentry.userID.name,
875                                   tentry.userID.instance,
876                                   ntohl(okeys.keys[i].version), &k);
877                 if (code)
878                     return code;
879             }
880     }
881     if (++nfound > maxCachedKeys)
882         return KADATABASEINCONSISTENT;
883     return 0;
884 }
885
886 afs_int32
887 update_admin_count(struct ubik_trans *tt, int delta)
888 {
889     afs_int32 to;
890     afs_int32 code;
891
892     cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
893     to = DOFFSET(0, &cheader, &cheader.admin_accounts);
894     code =
895         kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
896     if (code)
897         return KAIO;
898     return 0;
899 }
900
901 static int
902 index_OK(afs_int32 index)
903 {
904     if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
905         || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
906         return 0;
907     return 1;
908 }
909
910 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
911
912 int
913 name_instance_legal(char *name, char *instance)
914 {
915     int code;
916
917 /* No string checks apply anymore.  The international people want to use full 8
918    bit ascii without problems. */
919 #if 1
920     code = (strlen(name) < MAXKTCNAMELEN)
921         && (strlen(instance) < MAXKTCNAMELEN);
922 #else
923     map = LEGALCHARS;           /* permitted chars, instance allows <period> */
924     code = (strlen(name) > 0) && string_legal(instance, map)
925         && string_legal(name, map + 1);
926 #endif
927     if (!code)
928         dynamic_statistics.string_checks++;
929     return code;
930 }
931
932 #if 0
933 static int
934 string_legal(char *str, char *map)
935 {
936     int slen;
937
938     slen = strlen(str);
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 */
942 }
943 #endif
944