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