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