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