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