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