reindent-20030715
[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, i;
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                 int k;
686                 char principal[sizeof(keyCache[0].name) +
687                                sizeof(keyCache[0].inst)];
688
689                 info->kcInfo[j].used = keyCache[i].superseded;
690                 info->kcInfo[j].kvno = keyCache[i].kvno;
691                 info->kcInfo[j].primary =
692                     (keyCache[i].superseded == NEVERDATE);
693                 info->kcInfo[j].keycksum = 0;
694 #if DEBUG_KEY_CACHE
695                 for (k = 0; k < sizeof(struct ktc_encryptionKey); k++)
696                     info->kcInfo[j].keycksum += ((char *)&keyCache[i].key)[k];
697 #endif
698                 strcpy(principal, keyCache[i].name);
699                 strcat(principal, ".");
700                 strcat(principal, keyCache[i].inst);
701                 strncpy(info->kcInfo[j].principal, principal,
702                         sizeof(info->kcInfo[0].principal));
703             }
704             info->kcUsed++;
705         }
706     }
707 }
708
709 /* Add a key to the key cache, expanding it if necessary. */
710
711 ka_Encache(name, inst, kvno, key, superseded)
712      char *name;
713      char *inst;
714      afs_int32 kvno;
715      struct ktc_encryptionKey *key;
716      Date superseded;
717 {
718     int i;
719
720     ObtainWriteLock(&keycache_lock);
721     if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
722         for (i = 0; i < maxCachedKeys; i++)
723             keyCache[i].used = 0;
724     }
725
726     for (i = 0; i < maxCachedKeys; i++)
727         if (keyCache[i].used == 0) {
728           encache:
729             keyCache[i].kvno = kvno;
730             strncpy(keyCache[i].name, name, sizeof(keyCache[i].name));
731             strncpy(keyCache[i].inst, inst, sizeof(keyCache[i].inst));
732             keyCacheVersion = ntohl(cheader.specialKeysVersion);
733             memcpy(&keyCache[i].key, key, sizeof(*key));
734             keyCache[i].superseded = superseded;
735             keyCache[i].used = time(0);
736
737             ReleaseWriteLock(&keycache_lock);
738             return;
739         }
740     /* i == maxCachedKeys */
741     keyCache =
742         (struct cachedKey *)realloc(keyCache,
743                                     (maxCachedKeys *=
744                                      2) * sizeof(struct cachedKey));
745     if (keyCache == 0) {
746         es_Report("Can't realloc keyCache! out of memory?");
747         exit(123);
748     }
749
750     {
751         int j = i;              /* initialize new storage */
752         while (j < maxCachedKeys)
753             keyCache[j++].used = 0;
754     }
755     goto encache;
756 }
757
758 /* Look up the key given a principal and a kvno.  This is called by GetTicket
759    to get the decryption key for the authenticating ticket.  It is also called
760    by the rxkad security module to decrypt admin tickets.  The rxkad call is
761    with tt==0, since Rx can't call Ubik. */
762
763 afs_int32
764 ka_LookupKvno(tt, name, inst, kvno, key)
765      struct ubik_trans *tt;
766      char *name;
767      char *inst;
768      afs_int32 kvno;
769      struct ktc_encryptionKey *key;
770 {
771     int i;
772     int code = 0;
773     afs_int32 to;
774     struct kaentry tentry;
775     afs_int32 ko;
776     struct kaOldKeys okeys;
777
778     ObtainReadLock(&keycache_lock);
779     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
780         code = KAKEYCACHEINVALID;
781     else {
782         for (i = 0; i < maxCachedKeys; i++) {
783             if (keyCache[i].used) {     /* zero used date means invalid */
784                 if ((keyCache[i].kvno == kvno)
785                     && (strcmp(keyCache[i].name, name) == 0)
786                     && (strcmp(keyCache[i].inst, inst) == 0)) {
787                     memcpy(key, &keyCache[i].key, sizeof(*key));
788                     keyCache[i].used = time(0);
789                     ReleaseReadLock(&keycache_lock);
790                     return 0;
791                 }
792             }
793         }
794         code = KAUNKNOWNKEY;
795     }
796     ReleaseReadLock(&keycache_lock);
797     if (!tt)
798         return code;
799
800     /* we missed in the cache so need to look in the Ubik database */
801     code = FindBlock(tt, name, inst, &to, &tentry);
802     if (code)
803         return code;
804     if (to == 0)
805         return KANOENT;
806
807     /* first check the current key */
808     if (tentry.key_version == htonl(kvno)) {
809         memcpy(key, &tentry.key, sizeof(*key));
810         ka_Encache(name, inst, kvno, key, NEVERDATE);
811         return 0;
812     }
813     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
814         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
815         if (code)
816             return KAIO;
817         if (ntohl(okeys.entry) == to)
818             for (i = 0; i < NOLDKEYS; i++)
819                 if (okeys.keys[i].superseded
820                     && (ntohl(okeys.keys[i].version) == kvno)) {
821                     memcpy(key, &okeys.keys[i].key, sizeof(*key));
822                     ka_Encache(name, inst, kvno, key,
823                                ntohl(okeys.keys[i].superseded));
824                     return 0;
825                 }
826     }
827     return KAUNKNOWNKEY;
828 }
829
830 /* Look up the primary key and key version for a principal. */
831
832 afs_int32
833 ka_LookupKey(tt, name, inst, kvno, key)
834      struct ubik_trans *tt;
835      char *name;
836      char *inst;
837      afs_int32 *kvno;           /* returned */
838      struct ktc_encryptionKey *key;     /* copied out */
839 {
840     int i;
841     afs_int32 to;
842     struct kaentry tentry;
843     afs_int32 code = 0;
844
845     ObtainReadLock(&keycache_lock);
846     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
847         code = KAKEYCACHEINVALID;
848     else {
849         for (i = 0; i < maxCachedKeys; i++) {
850             if (keyCache[i].used) {     /* zero used date means invalid */
851                 if ((keyCache[i].superseded == NEVERDATE)
852                     && (strcmp(keyCache[i].name, name) == 0)
853                     && (strcmp(keyCache[i].inst, inst) == 0)) {
854                     memcpy(key, &keyCache[i].key, sizeof(*key));
855                     *kvno = keyCache[i].kvno;
856                     keyCache[i].used = time(0);
857                     ReleaseReadLock(&keycache_lock);
858                     return 0;
859                 }
860             }
861         }
862         code = KAUNKNOWNKEY;
863     }
864     ReleaseReadLock(&keycache_lock);
865     if (!tt)
866         return code;
867
868     /* we missed in the cache so need to look in the Ubik database */
869     code = FindBlock(tt, name, inst, &to, &tentry);
870     if (code)
871         return code;
872     if (to == 0)
873         return KANOENT;
874     memcpy(key, &tentry.key, sizeof(*key));
875     *kvno = ntohl(tentry.key_version);
876     ka_Encache(name, inst, *kvno, key, NEVERDATE);
877     return 0;
878 }
879
880 /* This is, hopefully a temporary mechanism to fill the cache will all keys
881    since filling cache misses during rxkad challenge responses will deadlock if
882    Ubik needs to use Rx. */
883
884 afs_int32
885 ka_FillKeyCache(tt)
886      struct ubik_trans *tt;
887 {
888     int nfound;
889     afs_int32 ko;
890     int code;
891     int i;
892     struct ktc_encryptionKey k;
893     struct kaOldKeys okeys;
894     struct kaentry tentry;
895
896     /* this is a little marginal, but... */
897     if (keyCacheVersion == ntohl(cheader.specialKeysVersion))
898         return 0;
899
900     nfound = 0;
901     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
902         code = karead(tt, ko, (char *)&okeys, sizeof(okeys));
903         if (code)
904             return KAIO;
905         /* get name & instance */
906         code =
907             karead(tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
908         if (code)
909             return KAIO;
910
911         /* get all the old keys in this block */
912         for (i = 0; i < NOLDKEYS; i++)
913             if (okeys.keys[i].superseded) {
914                 code =
915                     ka_LookupKvno(tt, tentry.userID.name,
916                                   tentry.userID.instance,
917                                   ntohl(okeys.keys[i].version), &k);
918                 if (code)
919                     return code;
920             }
921     }
922     if (++nfound > maxCachedKeys)
923         return KADATABASEINCONSISTENT;
924     return 0;
925 }
926
927 afs_int32
928 update_admin_count(tt, delta)
929      struct ubik_trans *tt;
930      int delta;
931 {
932     afs_int32 to;
933     afs_int32 code;
934
935     cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
936     to = DOFFSET(0, &cheader, &cheader.admin_accounts);
937     code =
938         kawrite(tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
939     if (code)
940         return KAIO;
941     return 0;
942 }
943
944 static int
945 index_OK(index)
946      afs_int32 index;
947 {
948     if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr))
949         || ((index - sizeof(cheader)) % sizeof(kaentry) != 0))
950         return 0;
951     return 1;
952 }
953
954 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
955
956 int
957 name_instance_legal(name, instance)
958      char *name;
959      char *instance;
960 {
961     int code;
962
963 /* No string checks apply anymore.  The international people want to use full 8
964    bit ascii without problems. */
965 #if 1
966     code = (strlen(name) < MAXKTCNAMELEN)
967         && (strlen(instance) < MAXKTCNAMELEN);
968 #else
969     map = LEGALCHARS;           /* permitted chars, instance allows <period> */
970     code = (strlen(name) > 0) && string_legal(instance, map)
971         && string_legal(name, map + 1);
972 #endif
973     if (!code)
974         dynamic_statistics.string_checks++;
975     return code;
976 }
977
978 static int
979 string_legal(str, map)
980      char *str;
981      char *map;
982 {
983     int slen;
984
985     slen = strlen(str);
986     if (slen >= MAXKTCNAMELEN)
987         return 0;               /* with trailing null must fit in data base */
988     return (slen == strspn(str, map));  /* strspn returns length(str) if all chars in map */
989 }