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