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