convert-from-bsd-to-posix-string-and-memory-functions-20010807
[openafs.git] / src / kauth / kadatabase.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$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     memset(&cheader, 0, 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     memset(tentry, 0, 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     memset(&tentry, 0, 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     es_Report ("Newkey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
392
393     newkeyver = ntohl(tentry->key_version) + 1;
394     if ((newkeyver < 1) || (newkeyver >= MAXKAKVNO))
395        newkeyver = 1;
396
397     /* An entry may have more than one oldkeys blocks. The entry
398      * points to the most current, but all the oldkeys blocks for an
399      * entry are not linked together. All oldkeys blocks for all
400      * entries are linked together off of the header. So we follow
401      * this link.
402      */
403     for (prevptr = 0, okeysaddr = ntohl(cheader.kvnoPtr);
404          okeysaddr;
405          prevptr = nextprevptr, okeysaddr = nextaddr) {
406        /* foreacholdkeysblock */
407        /* Read the oldKeys block */
408        code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
409        if (code) return code;
410
411        nextaddr    = ntohl(okeys.next);
412        nextprevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
413
414        /* We only want oldkey blocks that belong to this entry */
415        if (ntohl(okeys.entry) != tentryaddr)
416           continue;
417
418        modified   = 0;    /* This oldkeys block has not been modified */
419        keyentries = 0;    /* Number of valid key entries in the block */
420        for (i=0; i<NOLDKEYS; i++) {
421           /* foreachkey */
422           /* Keep count of number of entries found */
423           if (okeys.keys[i].superseded != 0) {
424              oldtotalkeyentries++;
425           }
426
427           /* If we find the entry that is not superseded, then supersede it */
428           if (ntohl(okeys.keys[i].superseded) == NEVERDATE) {
429              okeys.keys[i].superseded = htonl(now);
430              modified = 1;
431 #ifdef AUTH_DBM_LOG
432              if (foundcurrentkey) {
433                 ViceLog(0,("Warning: Entry %s.%s contains more than one valid key: fixing\n",
434                         tentry->userID.name, tentry->userID.instance));
435              }
436 #endif
437              foundcurrentkey = 1;
438           }
439
440           /* If we find an oldkey of the same version or
441            * an old key that has expired, then delete it.
442            */
443           if ( (ntohl(okeys.keys[i].version) == newkeyver) ||
444                ((now - ntohl(okeys.keys[i].superseded) > maxKeyLifetime)) ) {
445              okeys.keys[i].superseded = 0;
446              okeys.keys[i].version    = htonl(-1);
447              memset(&okeys.keys[i].key, 0, sizeof(struct ktc_encryptionKey));
448              modified = 1;
449
450              es_Report ("Dropped oldkey %d seconds old with kvno %d\n",
451                         now - ntohl(okeys.keys[i].superseded),
452                         ntohl(okeys.keys[i].version));
453           }
454
455           /* Add our key here if its free */
456           if (!addednewkey && (okeys.keys[i].superseded == 0)) {
457              okeys.keys[i].version    = htonl(newkeyver);
458              okeys.keys[i].superseded = htonl(NEVERDATE);
459              memcpy(&okeys.keys[i].key, key, sizeof(struct ktc_encryptionKey));
460              modified = 1;
461              addednewkey = okeysaddr;
462           }
463
464           /* Keep count of number of entries found */
465           if (okeys.keys[i].superseded != 0) {
466              keyentries++;
467              newtotalkeyentries++;
468           }
469        } /* foreachkey */
470
471        /* If we modified the block, write it out */
472        if (modified && keyentries) {
473           code = kawrite (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
474           if (code) return code;
475        }
476
477        /* If there are no more entries in this oldkeys block, delete it */
478        if (keyentries == 0) {
479           if (!prevptr) {
480              code = set_header_word(tt, kvnoPtr, okeys.next);
481           } else {
482              code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
483           }
484           if (code) return code;
485           code = FreeBlock (tt, okeysaddr);
486           if (code) return code;
487
488           nextprevptr = prevptr;   /* won't bump prevptr */
489        }
490     } /* foreacholdkeysblock */
491
492     /* If we could not add the key, create a new oldkeys block */
493     if (!addednewkey) {
494        /* Allocate and fill in an oldkeys block */
495        addednewkey = AllocBlock(tt, (struct kaentry *)&okeys);
496        if (!addednewkey) return KACREATEFAIL;
497        okeys.flags              = htonl(KAFOLDKEYS);
498        okeys.entry              = htonl(tentryaddr);
499        okeys.keys[0].version    = htonl(newkeyver);
500        okeys.keys[0].superseded = htonl(NEVERDATE);
501        memcpy(&okeys.keys[0].key, key, sizeof(struct ktc_encryptionKey));
502        newtotalkeyentries++;
503        
504        /* Thread onto the header's chain of oldkeys */
505        okeys.next               = cheader.kvnoPtr;
506        code = set_header_word(tt, kvnoPtr, htonl(addednewkey));
507        if (code) return code;
508
509        /* Write the oldkeys block out */
510        code = kawrite (tt, addednewkey, (char *)&okeys, sizeof(okeys));
511        if (code) return code;
512
513        es_Report ("New oldkey block allocated at %d\n", addednewkey);
514     }
515
516 #ifdef AUTH_DBM_LOG
517     if (oldtotalkeyentries != ntohl(tentry->misc.asServer.nOldKeys)) {
518        ViceLog(0,("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
519                tentry->userID.name, tentry->userID.instance,
520                ntohl(tentry->misc.asServer.nOldKeys), oldtotalkeyentries));
521     }
522 #endif
523
524     /* Update the tentry. We rely on caller to write it out */
525     tentry->misc.asServer.oldKeys  = htonl(addednewkey);
526     tentry->misc.asServer.nOldKeys = htonl(newtotalkeyentries);
527     tentry->key_version            = htonl(newkeyver);
528     memcpy(&tentry->key, key, sizeof (tentry->key));
529
530     /* invalidate key caches everywhere */
531     code = inc_header_word (tt, specialKeysVersion);
532     if (code) return code;
533
534     es_Report ("New kvno is %d, now are %d oldkeys\n", 
535                newkeyver, newtotalkeyentries);
536     return 0;
537 }
538
539 afs_int32 ka_DelKey (tt, tentryaddr, tentry)
540   struct ubik_trans *tt;
541   afs_int32                  tentryaddr;
542   struct kaentry    *tentry;
543 {
544     int   code, i;
545     struct kaOldKeys okeys;             /* old keys block */
546     afs_int32 okeysaddr, nextaddr;              /* offset of old keys block */
547     afs_int32 prevptr = 0;
548     Date  now = time(0);
549
550     es_Report ("DelKey for %s.%s\n", tentry->userID.name, tentry->userID.instance);
551
552     /* An entry may have more than one oldkeys blocks. The entry
553      * points to the most current, but all the oldkeys blocks for an
554      * entry are not linked together. All oldkeys blocks for all
555      * entries are linked together off of the header. So we follow
556      * this link.
557      */
558     for (okeysaddr=ntohl(cheader.kvnoPtr); okeysaddr; okeysaddr=nextaddr) {
559        /* foreacholdkeysblock */
560        /* Read the oldKeys block */
561        code = karead (tt, okeysaddr, (char *)&okeys, sizeof(okeys));
562        if (code) return code;
563        nextaddr = ntohl(okeys.next);
564
565        /* We only want oldkey blocks that belong to this entry */
566        if (ntohl(okeys.entry) != tentryaddr) {
567           prevptr = DOFFSET(okeysaddr, &okeys, &okeys.next);
568           continue;
569        }
570
571        /* Delete the oldkeys block */
572        if (prevptr) {
573           code = kawrite(tt, prevptr, (char *)&okeys.next, sizeof(afs_int32));
574         } else {
575           code = set_header_word(tt, kvnoPtr, okeys.next);
576        }
577        if (code) return code;
578        code = FreeBlock (tt, okeysaddr);
579        if (code) return code;
580     } /* foreacholdkeysblock */
581
582     /* Update the tentry. We rely on caller to write it out */
583     tentry->misc.asServer.oldKeys  = 0;
584     tentry->misc.asServer.nOldKeys = 0;
585
586     /* invalidate key caches everywhere */
587     code = inc_header_word (tt, specialKeysVersion);
588     if (code) return code;
589
590     return 0;
591 }
592
593 void ka_debugKeyCache (info)
594   struct ka_debugInfo *info;
595 {   int i;
596
597     memcpy(&info->cheader_lock, &cheader_lock, sizeof (info->cheader_lock));
598     memcpy(&info->keycache_lock, &keycache_lock, sizeof (info->keycache_lock));
599
600     info->kcVersion = keyCacheVersion;
601     info->kcSize = maxCachedKeys;
602     info->kcUsed = 0;
603     for (i=0; i<maxCachedKeys; i++) {
604         if (keyCache[i].used) {
605             if (info->kcUsed < KADEBUGKCINFOSIZE) {
606                 int  j = info->kcUsed;
607                 int  k;
608                 char principal[sizeof(keyCache[0].name) + sizeof(keyCache[0].inst)];
609
610                 info->kcInfo[j].used = keyCache[i].superseded;
611                 info->kcInfo[j].kvno = keyCache[i].kvno;
612                 info->kcInfo[j].primary = (keyCache[i].superseded == NEVERDATE);
613                 info->kcInfo[j].keycksum = 0;
614 #if DEBUG_KEY_CACHE
615                 for (k=0; k<sizeof(struct ktc_encryptionKey); k++)
616                     info->kcInfo[j].keycksum += ((char *)&keyCache[i].key)[k];
617 #endif
618                 strcpy (principal, keyCache[i].name);
619                 strcat (principal, ".");
620                 strcat (principal, keyCache[i].inst);
621                 strncpy (info->kcInfo[j].principal, principal,
622                          sizeof (info->kcInfo[0].principal));
623             }
624             info->kcUsed++;
625         }
626     }
627 }
628
629 /* Add a key to the key cache, expanding it if necessary. */
630
631 ka_Encache (name, inst, kvno, key, superseded)
632   char *name;
633   char *inst;
634   afs_int32  kvno;
635   struct ktc_encryptionKey *key;
636   Date  superseded;
637 {   int   i;
638
639     ObtainWriteLock (&keycache_lock);
640     if (keyCacheVersion != ntohl(cheader.specialKeysVersion)) {
641         for (i=0; i<maxCachedKeys; i++)
642             keyCache[i].used = 0;
643     }
644
645     for (i=0; i<maxCachedKeys; i++)
646         if (keyCache[i].used == 0) {
647           encache:
648             keyCache[i].kvno = kvno;
649             strncpy (keyCache[i].name, name, sizeof (keyCache[i].name));
650             strncpy (keyCache[i].inst, inst, sizeof (keyCache[i].inst));
651             keyCacheVersion = ntohl(cheader.specialKeysVersion);
652             memcpy(&keyCache[i].key, key, sizeof(*key));
653             keyCache[i].superseded = superseded;
654             keyCache[i].used = time(0);
655
656             ReleaseWriteLock (&keycache_lock);
657             return;
658         }
659     /* i == maxCachedKeys */
660     keyCache = (struct cachedKey *)realloc
661         (keyCache, (maxCachedKeys*=2) * sizeof(struct cachedKey));
662     if (keyCache == 0) {
663         es_Report ("Can't realloc keyCache! out of memory?");
664         exit (123);
665     }
666
667     {   int j = i;                      /* initialize new storage */
668         while (j<maxCachedKeys) keyCache[j++].used = 0;
669     }
670     goto encache;
671 }
672
673 /* Look up the key given a principal and a kvno.  This is called by GetTicket
674    to get the decryption key for the authenticating ticket.  It is also called
675    by the rxkad security module to decrypt admin tickets.  The rxkad call is
676    with tt==0, since Rx can't call Ubik. */
677
678 afs_int32 ka_LookupKvno (tt, name, inst, kvno, key)
679   struct ubik_trans *tt;
680   char              *name;
681   char              *inst;
682   afs_int32                  kvno;
683   struct ktc_encryptionKey *key;
684 {   int  i;
685     int  code = 0;
686     afs_int32 to;
687     struct kaentry tentry;
688     afs_int32 ko;
689     struct kaOldKeys okeys;
690
691     ObtainReadLock (&keycache_lock);
692     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
693         code = KAKEYCACHEINVALID;
694     else {
695         for (i=0; i<maxCachedKeys; i++) {
696             if (keyCache[i].used) {     /* zero used date means invalid */
697                 if ((keyCache[i].kvno == kvno) &&
698                     (strcmp(keyCache[i].name, name) == 0) &&
699                     (strcmp(keyCache[i].inst, inst) == 0)) {
700                     memcpy(key, &keyCache[i].key, sizeof(*key));
701                     keyCache[i].used = time(0);
702                     ReleaseReadLock (&keycache_lock);
703                     return 0;
704                 }
705             }
706         }
707         code = KAUNKNOWNKEY;
708     }
709     ReleaseReadLock (&keycache_lock);
710     if (!tt)
711        return code;
712
713     /* we missed in the cache so need to look in the Ubik database */
714     code = FindBlock (tt, name, inst, &to, &tentry);
715     if (code) return code;
716     if (to == 0) return KANOENT;
717
718     /* first check the current key */
719     if (tentry.key_version == htonl(kvno)) {
720         memcpy(key, &tentry.key, sizeof(*key));
721         ka_Encache (name, inst, kvno, key, NEVERDATE);
722         return 0;
723     }
724     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
725         code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
726         if (code) return KAIO; 
727         if (ntohl(okeys.entry) == to)
728             for (i=0; i<NOLDKEYS; i++)
729                 if (okeys.keys[i].superseded &&
730                     (ntohl(okeys.keys[i].version) == kvno)) {
731                     memcpy(key, &okeys.keys[i].key, sizeof(*key));
732                     ka_Encache (name, inst, kvno, key,
733                                 ntohl(okeys.keys[i].superseded));
734                     return 0;
735                 }
736     }
737     return KAUNKNOWNKEY;
738 }
739
740 /* Look up the primary key and key version for a principal. */
741
742 afs_int32 ka_LookupKey (tt, name, inst, kvno, key)
743   struct ubik_trans *tt;
744   char              *name;
745   char              *inst;
746   afs_int32                 *kvno;              /* returned */
747   struct ktc_encryptionKey *key;        /* copied out */
748 {   int  i;
749     afs_int32 to;
750     struct kaentry tentry;
751     afs_int32 code = 0;
752
753     ObtainReadLock (&keycache_lock);
754     if (keyCacheVersion != ntohl(cheader.specialKeysVersion))
755         code = KAKEYCACHEINVALID;
756     else {
757         for (i=0; i<maxCachedKeys; i++) {
758             if (keyCache[i].used) {             /* zero used date means invalid */
759                 if ((keyCache[i].superseded == NEVERDATE) &&
760                     (strcmp(keyCache[i].name, name) == 0) &&
761                     (strcmp(keyCache[i].inst, inst) == 0)){
762                     memcpy(key, &keyCache[i].key, sizeof(*key));
763                     *kvno = keyCache[i].kvno;
764                     keyCache[i].used = time(0);
765                     ReleaseReadLock (&keycache_lock);
766                     return 0;
767                 }
768             }
769         }
770         code = KAUNKNOWNKEY;
771     }
772     ReleaseReadLock (&keycache_lock);
773     if (!tt)
774        return code;
775
776     /* we missed in the cache so need to look in the Ubik database */
777     code = FindBlock (tt, name, inst, &to, &tentry);
778     if (code) return code;
779     if (to == 0) return KANOENT;
780     memcpy(key, &tentry.key, sizeof(*key));
781     *kvno = ntohl(tentry.key_version);
782     ka_Encache (name, inst, *kvno, key, NEVERDATE);
783     return 0;
784 }
785
786 /* This is, hopefully a temporary mechanism to fill the cache will all keys
787    since filling cache misses during rxkad challenge responses will deadlock if
788    Ubik needs to use Rx. */
789
790 afs_int32 ka_FillKeyCache (tt)
791   struct ubik_trans *tt;
792 {   int  nfound;
793     afs_int32 ko;
794     int  code;
795     int  i;
796     struct ktc_encryptionKey k;
797     struct kaOldKeys okeys;
798     struct kaentry   tentry;
799
800     /* this is a little marginal, but... */
801     if (keyCacheVersion == ntohl(cheader.specialKeysVersion)) return 0;
802
803     nfound = 0;
804     for (ko = ntohl(cheader.kvnoPtr); ko; ko = ntohl(okeys.next)) {
805         code = karead (tt, ko, (char *)&okeys, sizeof(okeys));
806         if (code) return KAIO;
807         /* get name & instance */
808         code = karead (tt, ntohl(okeys.entry), (char *)&tentry, sizeof(tentry));
809         if (code) return KAIO;
810
811         /* get all the old keys in this block */
812         for (i=0; i<NOLDKEYS; i++)
813             if (okeys.keys[i].superseded) {
814                 code = ka_LookupKvno (tt, tentry.userID.name, tentry.userID.instance,
815                                       ntohl(okeys.keys[i].version), &k);
816                 if (code) return code;
817             }
818     }
819     if (++nfound > maxCachedKeys) return KADATABASEINCONSISTENT;
820     return 0;
821 }
822
823 afs_int32 update_admin_count (tt, delta)
824   struct ubik_trans *tt;
825   int                delta;
826 {   afs_int32 to;
827     afs_int32 code;
828
829     cheader.admin_accounts = htonl(ntohl(cheader.admin_accounts) + delta);
830     to = DOFFSET (0, &cheader, &cheader.admin_accounts);
831     code = kawrite (tt, to, (char *)&cheader.admin_accounts, sizeof(afs_int32));
832     if (code) return KAIO;
833     return 0;
834 }
835
836 static int index_OK (index)
837   afs_int32 index;
838 {
839     if ((index < sizeof(cheader)) || (index >= ntohl(cheader.eofPtr)) ||
840         ((index - sizeof(cheader)) % sizeof(kaentry) != 0)) return 0;
841     return 1;
842 }
843
844 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
845
846 int name_instance_legal (name, instance)
847   char *name;
848   char *instance;
849 {   int code;
850
851 /* No string checks apply anymore.  The international people want to use full 8
852    bit ascii without problems. */
853 #if 1
854     code = (strlen(name) < MAXKTCNAMELEN) && (strlen(instance) < MAXKTCNAMELEN);
855 #else
856     map = LEGALCHARS;                   /* permitted chars, instance allows <period> */
857     code = (strlen (name) > 0) && string_legal (instance, map) && string_legal (name, map+1);
858 #endif
859     if (!code) dynamic_statistics.string_checks++;
860     return code;
861 }
862
863 static int string_legal (str, map)
864   char *str;
865   char *map;
866 {   int slen;
867
868     slen = strlen (str);
869     if (slen >= MAXKTCNAMELEN) return 0; /* with trailing null must fit in data base */
870     return (slen == strspn (str, map)); /* strspn returns length(str) if all chars in map */
871 }