Protect ubik cache accesses
[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
14 #include <sys/types.h>
15 #ifdef AFS_NT40_ENV
16 #include <winsock2.h>
17 #else
18 #include <netinet/in.h>
19 #endif
20 #include <string.h>
21 #include <ubik.h>
22 #include <rx/xdr.h>
23 #include <rx/rx.h>
24 #include <afs/afsutil.h>
25 #include "kauth.h"
26 #include "kautils.h"
27 #include "kaserver.h"
28
29 extern Date cheaderReadTime;    /* time cheader last read in */
30
31 #define set_header_word(tt,field,value) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (value)), (char *)&(cheader.field)), sizeof(afs_int32))
32
33 #define inc_header_word(tt,field) kawrite ((tt), ((char *)&(cheader.field) - (char *)&cheader), ((cheader.field = (htonl(ntohl(cheader.field)+1))), (char *)&(cheader.field)), sizeof(afs_int32))
34
35 static int index_OK(afs_int32);
36
37 afs_int32
38 NameHash(register char *aname, register char *ainstance)
39 {
40     register unsigned int hash;
41     register int i;
42
43     /* stolen directly from the HashString function in the vol package */
44     hash = 0;
45     for (i = strlen(aname), aname += i - 1; i--; aname--)
46         hash = (hash * 31) + (*((unsigned char *)aname) - 31);
47     for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
48         hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
49     return (hash % HASHSIZE);
50 }
51
52 /* package up seek and write into one procedure for ease of use */
53
54 afs_int32
55 kawrite(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
56 {
57     afs_int32 code;
58
59     code = ubik_Seek(tt, 0, pos);
60     if (code)
61         return code;
62     code = ubik_Write(tt, buff, len);
63     return code;
64 }
65
66 /* same thing for read */
67
68 afs_int32
69 karead(struct ubik_trans *tt, afs_int32 pos, char *buff, afs_int32 len)
70 {
71     afs_int32 code;
72
73     code = ubik_Seek(tt, 0, pos);
74     if (code)
75         return code;
76     code = ubik_Read(tt, buff, len);
77     return code;
78 }
79
80 static struct Lock keycache_lock;
81
82 static int maxCachedKeys;
83
84 static struct cachedKey {
85     Date used;
86     int superseded;             /* NEVERDATE => this is current key */
87     afs_int32 kvno;
88     struct ktc_encryptionKey key;
89     char name[MAXKTCNAMELEN];
90     char inst[MAXKTCNAMELEN];
91 } *keyCache;
92 static afs_int32 keyCacheVersion = 0;
93
94 static afs_int32 maxKeyLifetime;
95 static int dbfixup = 0;
96
97 void
98 init_kadatabase(int initFlags)
99 {
100     Lock_Init(&keycache_lock);
101
102     maxCachedKeys = 10;
103     keyCache =
104         (struct cachedKey *)malloc(maxCachedKeys * sizeof(struct cachedKey));
105     keyCacheVersion = 0;
106     if (initFlags & 4) {
107         maxKeyLifetime = 90;
108     } else {
109         maxKeyLifetime = MAXKTCTICKETLIFETIME;
110     }
111     if (initFlags & 8)
112         dbfixup++;
113 }
114
115 /* check that the database has been initialized.  Be careful to fail in a safe
116    manner, to avoid bogusly reinitializing the db.  */
117 /**
118  * reads in db cache from ubik.
119  *
120  * @param[in] ut ubik transaction
121  * @param[in] rock  opaque pointer to an int (*) (struct ubik_trans *), which
122  *                  will be called on rebuilding the database (or NULL to not
123  *                  rebuild the db)
124  *
125  * @return operation status
126  *   @retval 0 success
127  */
128 static afs_int32
129 UpdateCache(struct ubik_trans *at, void *rock)
130 {
131     int (*db_init) (struct ubik_trans *) = rock;
132     afs_int32 code;
133     afs_int32 iversion;
134     afs_int32 tversion;
135
136     if ((code = karead(at, 0, (char *)&iversion, sizeof(iversion)))
137         || (code =
138             karead(at, sizeof(cheader) - sizeof(afs_int32), (char *)&tversion,
139                    sizeof(afs_int32)))) {
140         if (code == UEOF)
141             printf("No data base\n");
142         else
143             printf("I/O Error\n");
144     } else {
145         iversion = ntohl(iversion);     /* convert to host order */
146         tversion = ntohl(tversion);
147         if ((iversion == KADBVERSION) && (tversion == KADBVERSION)) {
148             code = karead(at, 0, (char *)&cheader, sizeof(cheader));
149             if (code) {
150                 printf("SetupHeader failed\n");
151                 code = KAIO;
152             } else {
153                 cheaderReadTime = time(0);
154             }
155         } else {
156             printf("DB version should be %d; Initial = %d; Terminal = %d\n",
157                    KADBVERSION, iversion, tversion);
158             code = KAIO;
159         }
160     }
161     if (code == 0)
162         return 0;
163
164     /* if here, we have no version number or the wrong version number in the
165      * file */
166     if ((code == UEOF) || ((iversion == 0) && (tversion == 0)))
167         code = KAEMPTY;
168     else
169         code = KAIO;
170
171     if ((db_init == 0) || (code == KAIO))
172         return code;
173
174     printf("Error discovered in header, rebuilding.\n");
175
176     /* try to write a good header */
177     memset(&cheader, 0, sizeof(cheader));
178     cheader.version = htonl(KADBVERSION);
179     cheader.checkVersion = htonl(KADBVERSION);
180     cheader.headerSize = htonl(sizeof(cheader));
181     cheader.freePtr = 0;
182     cheader.eofPtr = htonl(sizeof(cheader));
183     cheader.kvnoPtr = 0;
184     cheader.specialKeysVersion = htonl(time(0));        /* anything non-zero will do */
185     cheader.stats.cpws = cheader.stats.allocs = cheader.stats.frees = 0;
186     cheader.admin_accounts = 0;
187     cheader.hashsize = htonl(HASHSIZE);
188     code = kawrite(at, 0, (char *)&cheader, sizeof(cheader));
189     if (code)
190         return KAIO;            /* return the error code */
191
192     return db_init(at);         /* initialize the db */
193 }
194
195 afs_int32
196 CheckInit(struct ubik_trans *at,
197           int (*db_init) (struct ubik_trans *))         /* procedure to call if rebuilding DB */
198 {
199     return ubik_CheckCache(at, UpdateCache, db_init);
200 }
201
202 /* Allocate a free block of storage for entry, returning address of a new
203    zeroed entry.  If zero is returned, a Ubik I/O error can be assumed.  */
204
205 afs_int32
206 AllocBlock(struct ubik_trans *at, struct kaentry *tentry)
207 {
208     register afs_int32 code;
209     afs_int32 temp;
210
211     if (cheader.freePtr) {
212         /* allocate this dude */
213         temp = ntohl(cheader.freePtr);
214         code = karead(at, temp, (char *)tentry, sizeof(kaentry));
215         if (code)
216             return 0;           /* can't read block */
217         code = set_header_word(at, freePtr, tentry->next);
218     } else {
219         /* hosed, nothing on free list, grow file */
220         temp = ntohl(cheader.eofPtr);   /* remember this guy */
221         code = set_header_word(at, eofPtr, htonl(temp + sizeof(kaentry)));
222     }
223     if (code)
224         return 0;
225
226     code = inc_header_word(at, stats.allocs);
227     if (code)
228         return 0;
229     memset(tentry, 0, sizeof(kaentry)); /* zero new entry */
230     return temp;
231 }
232
233 /* Free a block given its index.  It must already have been unthreaded.
234    Returns zero for success or an error code on failure. */
235
236 afs_int32
237 FreeBlock(struct ubik_trans *at, afs_int32 index)
238 {
239     struct kaentry tentry;
240     int code;
241
242     /* check index just to be on the safe side */
243     if (!index_OK(index))
244         return KABADINDEX;
245
246     memset(&tentry, 0, sizeof(kaentry));
247     tentry.next = cheader.freePtr;
248     tentry.flags = htonl(KAFFREE);
249     code = set_header_word(at, freePtr, htonl(index));
250     if (code)
251         return KAIO;
252     code = kawrite(at, index, (char *)&tentry, sizeof(kaentry));
253     if (code)
254         return KAIO;
255
256     code = inc_header_word(at, stats.frees);
257     if (code)
258         return KAIO;
259     return 0;
260 }
261
262 /* Look for a block by name and instance.  If found read the block's contents
263    into the area pointed to by tentry and return the block's index.  If not
264    found offset is set to zero.  If an error is encountered a non-zero code is
265    returned. */
266
267 afs_int32
268 FindBlock(struct ubik_trans *at, char *aname, char *ainstance, afs_int32 *toP, 
269           struct kaentry *tentry)
270 {
271     register afs_int32 i, code;
272     register afs_int32 to;
273
274     *toP = 0;
275     i = NameHash(aname, ainstance);
276     for (to = ntohl(cheader.nameHash[i]); to != NULLO;
277          to = ntohl(tentry->next)) {
278         code = karead(at, to, (char *)tentry, sizeof(kaentry));
279         if (code)
280             return code;
281         /* see if the name matches */
282         if (!strcmp(aname, tentry->userID.name)
283             && (ainstance == (char *)0
284                 || !strcmp(ainstance, tentry->userID.instance))) {
285             *toP = to;          /* found it */
286             return 0;
287         }
288     }
289     *toP = 0;                   /* no such entry */
290     return 0;
291 }
292
293 /* Add a block to the hash table given a pointer to the block and its index.
294    The block is threaded onto the hash table and written to disk.  The routine
295    returns zero if there were no errors. */
296
297 afs_int32
298 ThreadBlock(struct ubik_trans *at, afs_int32 index, 
299             struct kaentry *tentry)
300 {
301     int code;
302     int hi;                     /* hash index */
303
304     if (!index_OK(index))
305         return KABADINDEX;
306     hi = NameHash(tentry->userID.name, tentry->userID.instance);
307     tentry->next = cheader.nameHash[hi];
308     code = set_header_word(at, nameHash[hi], htonl(index));
309     if (code)
310         return KAIO;
311     code = kawrite(at, index, (char *)tentry, sizeof(kaentry));
312     if (code)
313         return KAIO;
314     return 0;
315 }
316
317 /* Remove a block from the hash table.  If success return 0, else return an
318    error code. */
319
320 afs_int32
321 UnthreadBlock(struct ubik_trans *at, struct kaentry *aentry)
322 {
323     register afs_int32 i, code;
324     register afs_int32 to;
325     afs_int32 lo;
326     struct kaentry tentry;
327
328     i = NameHash(aentry->userID.name, aentry->userID.instance);
329     lo = 0;
330     for (to = ntohl(cheader.nameHash[i]); to != NULLO;
331          to = ntohl(tentry.next)) {
332         code = karead(at, to, (char *)&tentry, sizeof(kaentry));
333         if (code)
334             return KAIO;
335         /* see if the name matches */
336         if (!strcmp(aentry->userID.name, tentry.userID.name)
337             && !strcmp(aentry->userID.instance, tentry.userID.instance)) {
338             /* found it */
339             if (lo) {           /* unthread from last block */
340                 code =
341                     kawrite(at, lo, (char *)&tentry.next, sizeof(afs_int32));
342                 if (code)
343                     return KAIO;
344             } else {            /* unthread from hash table */
345                 code = set_header_word(at, nameHash[i], tentry.next);
346                 if (code)
347                     return KAIO;
348             }
349             aentry->next = 0;   /* just to be sure */
350             return 0;
351         }
352         lo = DOFFSET(to, &tentry, &tentry.next);
353     }
354     return KANOENT;
355 }
356
357 /* Given an index to the last block (or zero the first time) read the contents
358    of the next block and return its index.  The last argument is a pointer to
359    an estimate of the number of remaining blocks to read out.  The remaining
360    count is an estimate because it may include free blocks that are not
361    returned.  If there are no more blocks remaining is zero and the returned
362    index is zero.  A non-zero index indicates that tentry has been filled with
363    valid data.  If an error is encountered the returned index is zero and the
364    remaining count is negative.  */
365
366 afs_int32
367 NextBlock(struct ubik_trans *at, afs_int32 index, struct kaentry *tentry, 
368           afs_int32 *remaining)
369 {
370     int code;
371     afs_int32 last;
372
373     if (index == 0)             /* get first one */
374         index = sizeof(cheader);
375     else {
376         if (!index_OK(index)) {
377             *remaining = -1;    /* error */
378             return 0;
379         }
380         index += sizeof(kaentry);
381     }
382     /* now search for the first entry that isn't free */
383     for (last = ntohl(cheader.eofPtr); index < last; index += sizeof(kaentry)) {
384         code = karead(at, index, (char *)tentry, sizeof(kaentry));
385         if (code) {
386             *remaining = -1;
387             return 0;
388         }
389         if (!(ntohl(tentry->flags) & (KAFFREE | KAFOLDKEYS))) {
390             /* estimate remaining number of entries, not including this one */
391             *remaining = (last - index) / sizeof(kaentry) - 1;
392             return index;
393         }
394     }
395     *remaining = 0;             /* no more entries */
396     return 0;
397 }
398
399 /* These are a collections of routines that deal with externally known keys.
400    They maintain a database of key version numbers and the corresponding key
401    and pointer to the user entry. */
402
403 afs_int32
404 ka_NewKey(struct ubik_trans *tt, afs_int32 tentryaddr, 
405           struct kaentry *tentry, struct ktc_encryptionKey *key)
406 {
407     struct kaOldKeys okeys;     /* old keys block */
408     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
409     afs_int32 prevptr, nextprevptr;
410     int code, i;
411     Date now = time(0);
412     afs_int32 newkeyver;        /* new key version number */
413     afs_int32 newtotalkeyentries = 0, oldtotalkeyentries = 0, keyentries;
414     int foundcurrentkey = 0, addednewkey = 0, modified;
415
416     es_Report("Newkey for %s.%s\n", tentry->userID.name,
417               tentry->userID.instance);
418
419     newkeyver = ntohl(tentry->key_version) + 1;
420     if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
421         newkeyver = 1;
422
423     /* An entry may have more than one oldkeys blocks. The entry
424      * points to the most current, but all the oldkeys blocks for an
425      * entry are not linked together. All oldkeys blocks for all
426      * entries are linked together off of the header. So we follow
427      * this link.
428      */
429     for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr;
430          prevptr = nextprevptr, okeysaddr = nextaddr) {
431         /* foreacholdkeysblock */
432         /* Read the oldKeys block */
433         code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
434         if (code)
435             return code;
436
437         nextaddr = ntohl(okeys.next);
438         nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
439
440         /* We only want oldkey blocks that belong to this entry */
441         if (ntohl(okeys.entry) != tentryaddr)
442             continue;
443
444         modified = 0;           /* This oldkeys block has not been modified */
445         keyentries = 0;         /* Number of valid key entries in the block */
446         for (i = 0; i < NOLDKEYS; i++) {
447             /* foreachkey */
448             /* Keep count of number of entries found */
449             if (okeys.keys[i].superseded != 0) {
450                 oldtotalkeyentries++;
451             }
452
453             /* If we find the entry that is not superseded, then supersede it */
454             if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
455                 okeys.keys[i].superseded = htonl(now);
456                 modified = 1;
457 #ifdef AUTH_DBM_LOG
458                 if (foundcurrentkey) {
459                     ViceLog(0,
460                             ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
461                              tentry->userID.name, tentry->userID.instance));
462                 }
463 #endif
464                 foundcurrentkey = 1;
465             }
466
467             /* If we find an oldkey of the same version or
468              * an old key that has expired, then delete it.
469              */
470             if ((ntohl(okeys.keys[i].version) == newkeyver)
471                 || ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime))) {
472                 okeys.keys[i].superseded = 0;
473                 okeys.keys[i].version = htonl(-1);
474                 memset(&okeys.keys[i].key, 0,
475                        sizeof(struct ktc_encryptionKey));
476                 modified = 1;
477
478                 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
479                           now - ntohl(okeys.keys[i].superseded),
480                           ntohl(okeys.keys[i].version));
481             }
482
483             /* Add our key here if its free */
484             if (!addednewkey && (okeys.keys[i].superseded == 0)) {
485                 okeys.keys[i].version = htonl(newkeyver);
486                 okeys.keys[i].superseded = htonl(NEVERDATE);
487                 memcpy(&okeys.keys[i].key, key,
488                        sizeof(struct ktc_encryptionKey));
489                 modified = 1;
490                 addednewkey = okeysaddr;
491             }
492
493             /* Keep count of number of entries found */
494             if (okeys.keys[i].superseded != 0) {
495                 keyentries++;
496                 newtotalkeyentries++;
497             }
498         }                       /* foreachkey */
499
500         /* If we modified the block, write it out */
501         if (modified && keyentries) {
502             code = kawrite(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
503             if (code)
504                 return code;
505         }
506
507         /* If there are no more entries in this oldkeys block, delete it */
508         if (keyentries == 0) {
509             if (!prevptr) {
510                 code = set_header_word(tt, kvnoPtr, okeys.next);
511             } else {
512                 code =
513                     kawrite(tt, prevptr, (char *)&okeys.next,
514                             sizeof(afs_int32));
515             }
516             if (code)
517                 return code;
518             code = FreeBlock(tt, okeysaddr);
519             if (code)
520                 return code;
521
522             nextprevptr = prevptr;      /* won't bump prevptr */
523         }
524     }                           /* foreacholdkeysblock */
525
526     /* If we could not add the key, create a new oldkeys block */
527     if (!addednewkey) {
528         /* Allocate and fill in an oldkeys block */
529         addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
530         if (!addednewkey)
531             return KACREATEFAIL;
532         okeys.flags = htonl(KAFOLDKEYS);
533         okeys.entry = htonl(tentryaddr);
534         okeys.keys[0].version = htonl(newkeyver);
535         okeys.keys[0].superseded = htonl(NEVERDATE);
536         memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
537         newtotalkeyentries++;
538
539         /* Thread onto the header's chain of oldkeys */
540         okeys.next = cheader.kvnoPtr;
541         code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
542         if (code)
543             return code;
544
545         /* Write the oldkeys block out */
546         code = kawrite(tt, addednewkey, (char *)&okeys, sizeof(okeys));
547         if (code)
548             return code;
549
550         es_Report("New oldkey block allocated at %d\n", addednewkey);
551     }
552 #ifdef AUTH_DBM_LOG
553     if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
554         ViceLog(0,
555                 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
556                  tentry->userID.name, tentry->userID.instance,
557                  ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
558     }
559 #endif
560
561     /* Update the tentry. We rely on caller to write it out */
562     tentry->misc.asServer.oldKeys = htonl(addednewkey);
563     tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
564     tentry->key_version = htonl(newkeyver);
565     memcpy(&tentry->key, key, sizeof(tentry->key));
566
567     /* invalidate key caches everywhere */
568     code = inc_header_word(tt, specialKeysVersion);
569     if (code)
570         return code;
571
572     es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver,
573               newtotalkeyentries);
574     return 0;
575 }
576
577 afs_int32
578 ka_DelKey(struct ubik_trans *tt, afs_int32 tentryaddr, 
579           struct kaentry *tentry)
580 {
581     int code;
582     struct kaOldKeys okeys;     /* old keys block */
583     afs_int32 okeysaddr, nextaddr;      /* offset of old keys block */
584     afs_int32 prevptr = 0;
585
586     es_Report("DelKey for %s.%s\n", tentry->userID.name,
587               tentry->userID.instance);
588
589     /* An entry may have more than one oldkeys blocks. The entry
590      * points to the most current, but all the oldkeys blocks for an
591      * entry are not linked together. All oldkeys blocks for all
592      * entries are linked together off of the header. So we follow
593      * this link.
594      */
595     for (okeysaddr = ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr = nextaddr) {
596         /* foreacholdkeysblock */
597         /* Read the oldKeys block */
598         code = karead(tt, okeysaddr, (char *)&okeys, sizeof(okeys));
599         if (code)
600             return code;
601         nextaddr = ntohl(okeys.next);
602
603         /* We only want oldkey blocks that belong to this entry */
604         if (ntohl(okeys.entry) != tentryaddr) {
605             prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
606             continue;
607         }
608
609         /* Delete the oldkeys block */
610         if (prevptr) {
611             code =
612                 kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
613         } else {
614             code = set_header_word(tt, kvnoPtr, okeys.next);
615         }
616         if (code)
617             return code;
618         code = FreeBlock(tt, okeysaddr);
619         if (code)
620             return code;
621     }                           /* foreacholdkeysblock */
622
623     /* Update the tentry. We rely on caller to write it out */
624     tentry->misc.asServer.oldKeys = 0;
625     tentry->misc.asServer.nOldKeys = 0;
626
627     /* invalidate key caches everywhere */
628     code = inc_header_word(tt, specialKeysVersion);
629     if (code)
630         return code;
631
632     return 0;
633 }
634
635 void
636 ka_debugKeyCache(struct ka_debugInfo *info)
637 {
638     int i;
639
640     /* cheader_lock no longer exists */
641     memset(&info->cheader_lock, 0, sizeof(info->cheader_lock));
642     memcpy(&info->keycache_lock, &keycache_lock, sizeof(info->keycache_lock));
643
644     info->kcVersion = keyCacheVersion;
645     info->kcSize = maxCachedKeys;
646     info->kcUsed = 0;
647     for (i = 0; i < maxCachedKeys; i++) {
648         if (keyCache[i].used) {
649             if (info->kcUsed < KADEBUGKCINFOSIZE) {
650                 int j = info->kcUsed;
651                 char principal[sizeof(keyCache[0].name) +
652                                sizeof(keyCache[0].inst)];
653
654                 info->kcInfo[j].used = keyCache[i].superseded;
655                 info->kcInfo[j].kvno = keyCache[i].kvno;
656                 info->kcInfo[j].primary =
657                     (keyCache[i].superseded == NEVERDATE);
658                 info->kcInfo[j].keycksum = 0;
659 #if DEBUG_KEY_CACHE
660                 {
661                     int k;
662                     for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
663                         info->kcInfo[j].keycksum +=
664                             ((char *)&keyCache[i].key)[k];
665                 }
666 #endif
667                 strcpy(principal, keyCache[i].name);
668                 strcat(principal, ".");
669                 strcat(principal, keyCache[i].inst);
670                 strncpy(info->kcInfo[j].principal, principal,
671                         sizeof(info->kcInfo[0].principal));
672             }
673             info->kcUsed++;
674         }
675     }
676 }
677
678 /* Add a key to the key cache, expanding it if necessary. */
679
680 void
681 ka_Encache(char *name, char *inst, afs_int32 kvno, 
682            struct ktc_encryptionKey *key, Date superseded)
683 {
684     int i;
685
686     ObtainWriteLock(&keycache_lock);
687     if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
688         for (i = 0; i < maxCachedKeys; i++)
689             keyCache[i].used = 0;
690     }
691
692     for (i = 0; i < maxCachedKeys; i++)
693         if (keyCache[i].used == 0) {
694           encache:
695             keyCache[i].kvno = kvno;
696             strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
697             strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
698             keyCacheVersion = ntohl(cheader.specialKeysVersion);
699             memcpy(&keyCache[i].key, key, sizeof(*key));
700             keyCache[i].superseded = superseded;
701             keyCache[i].used = time(0);
702
703             ReleaseWriteLock(&keycache_lock);
704             return;
705         }
706     /* i == maxCachedKeys */
707     keyCache =
708         (struct cachedKey *)realloc(keyCache,
709                                     (maxCachedKeys *=
710                                      2) * 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