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