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