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