2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
14 #include <sys/types.h>
18 #include <netinet/in.h>
24 #include <afs/afsutil.h>
29 #if !defined(offsetof)
30 #include <stddef.h> /* for definition of offsetof() */
33 extern Date cheaderReadTime
; /* time cheader last read in */
35 #define set_header_word(tt,field,value) \
37 (cheader.field) = (value), \
38 kawrite((tt), ((char *)&(cheader.field) - (char *)&cheader), \
39 (char *)&(cheader.field), sizeof(afs_int32)) \
42 #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))
44 static int index_OK(afs_int32
);
47 NameHash(char *aname
, char *ainstance
)
52 /* stolen directly from the HashString function in the vol package */
54 for (i
= strlen(aname
), aname
+= i
- 1; i
--; aname
--)
55 hash
= (hash
* 31) + (*((unsigned char *)aname
) - 31);
56 for (i
= strlen(ainstance
), ainstance
+= i
- 1; i
--; ainstance
--)
57 hash
= (hash
* 31) + (*((unsigned char *)ainstance
) - 31);
58 return (hash
% HASHSIZE
);
61 /* package up seek and write into one procedure for ease of use */
64 kawrite(struct ubik_trans
*tt
, afs_int32 pos
, char *buff
, afs_int32 len
)
68 code
= ubik_Seek(tt
, 0, pos
);
71 code
= ubik_Write(tt
, buff
, len
);
75 /* same thing for read */
78 karead(struct ubik_trans
*tt
, afs_int32 pos
, char *buff
, afs_int32 len
)
82 code
= ubik_Seek(tt
, 0, pos
);
85 code
= ubik_Read(tt
, buff
, len
);
89 static struct Lock keycache_lock
;
91 static int maxCachedKeys
;
93 static struct cachedKey
{
95 int superseded
; /* NEVERDATE => this is current key */
97 struct ktc_encryptionKey key
;
98 char name
[MAXKTCNAMELEN
];
99 char inst
[MAXKTCNAMELEN
];
101 static afs_int32 keyCacheVersion
= 0;
103 static afs_int32 maxKeyLifetime
;
104 static int dbfixup
= 0;
107 init_kadatabase(int initFlags
)
109 Lock_Init(&keycache_lock
);
113 (struct cachedKey
*)malloc(maxCachedKeys
* sizeof(struct cachedKey
));
118 maxKeyLifetime
= MAXKTCTICKETLIFETIME
;
124 /* check that the database has been initialized. Be careful to fail in a safe
125 manner, to avoid bogusly reinitializing the db. */
127 * reads in db cache from ubik.
129 * @param[in] ut ubik transaction
130 * @param[in] rock opaque pointer to an int (*) (struct ubik_trans *), which
131 * will be called on rebuilding the database (or NULL to not
134 * @return operation status
138 UpdateCache(struct ubik_trans
*at
, void *rock
)
140 int (*db_init
) (struct ubik_trans
*) = rock
;
145 if ((code
= karead(at
, 0, (char *)&iversion
, sizeof(iversion
)))
147 karead(at
, sizeof(cheader
) - sizeof(afs_int32
), (char *)&tversion
,
148 sizeof(afs_int32
)))) {
150 printf("No data base\n");
152 printf("I/O Error\n");
154 iversion
= ntohl(iversion
); /* convert to host order */
155 tversion
= ntohl(tversion
);
156 if ((iversion
== KADBVERSION
) && (tversion
== KADBVERSION
)) {
157 code
= karead(at
, 0, (char *)&cheader
, sizeof(cheader
));
159 printf("SetupHeader failed\n");
162 cheaderReadTime
= time(0);
165 printf("DB version should be %d; Initial = %d; Terminal = %d\n",
166 KADBVERSION
, iversion
, tversion
);
173 /* if here, we have no version number or the wrong version number in the
175 if ((code
== UEOF
) || ((iversion
== 0) && (tversion
== 0)))
180 if ((db_init
== 0) || (code
== KAIO
))
183 printf("Error discovered in header, rebuilding.\n");
185 /* try to write a good header */
186 memset(&cheader
, 0, sizeof(cheader
));
187 cheader
.version
= htonl(KADBVERSION
);
188 cheader
.checkVersion
= htonl(KADBVERSION
);
189 cheader
.headerSize
= htonl(sizeof(cheader
));
191 cheader
.eofPtr
= htonl(sizeof(cheader
));
193 cheader
.specialKeysVersion
= htonl(time(0)); /* anything non-zero will do */
194 cheader
.stats
.cpws
= cheader
.stats
.allocs
= cheader
.stats
.frees
= 0;
195 cheader
.admin_accounts
= 0;
196 cheader
.hashsize
= htonl(HASHSIZE
);
197 code
= kawrite(at
, 0, (char *)&cheader
, sizeof(cheader
));
199 return KAIO
; /* return the error code */
201 return db_init(at
); /* initialize the db */
205 CheckInit(struct ubik_trans
*at
,
206 int (*db_init
) (struct ubik_trans
*)) /* procedure to call if rebuilding DB */
208 return ubik_CheckCache(at
, UpdateCache
, db_init
);
211 /* Allocate a free block of storage for entry, returning address of a new
212 zeroed entry. If zero is returned, a Ubik I/O error can be assumed. */
215 AllocBlock(struct ubik_trans
*at
, struct kaentry
*tentry
)
220 if (cheader
.freePtr
) {
221 /* allocate this dude */
222 temp
= ntohl(cheader
.freePtr
);
223 code
= karead(at
, temp
, (char *)tentry
, sizeof(kaentry
));
225 return 0; /* can't read block */
226 code
= set_header_word(at
, freePtr
, tentry
->next
);
228 /* hosed, nothing on free list, grow file */
229 temp
= ntohl(cheader
.eofPtr
); /* remember this guy */
230 code
= set_header_word(at
, eofPtr
, htonl(temp
+ sizeof(kaentry
)));
235 code
= inc_header_word(at
, stats
.allocs
);
238 memset(tentry
, 0, sizeof(kaentry
)); /* zero new entry */
242 /* Free a block given its index. It must already have been unthreaded.
243 Returns zero for success or an error code on failure. */
246 FreeBlock(struct ubik_trans
*at
, afs_int32 index
)
248 struct kaentry tentry
;
251 /* check index just to be on the safe side */
252 if (!index_OK(index
))
255 memset(&tentry
, 0, sizeof(kaentry
));
256 tentry
.next
= cheader
.freePtr
;
257 tentry
.flags
= htonl(KAFFREE
);
258 code
= set_header_word(at
, freePtr
, htonl(index
));
261 code
= kawrite(at
, index
, (char *)&tentry
, sizeof(kaentry
));
265 code
= inc_header_word(at
, stats
.frees
);
271 /* Look for a block by name and instance. If found read the block's contents
272 into the area pointed to by tentry and return the block's index. If not
273 found offset is set to zero. If an error is encountered a non-zero code is
277 FindBlock(struct ubik_trans
*at
, char *aname
, char *ainstance
, afs_int32
*toP
,
278 struct kaentry
*tentry
)
284 i
= NameHash(aname
, ainstance
);
285 for (to
= ntohl(cheader
.nameHash
[i
]); to
!= NULLO
;
286 to
= ntohl(tentry
->next
)) {
287 code
= karead(at
, to
, (char *)tentry
, sizeof(kaentry
));
290 /* see if the name matches */
291 if (!strcmp(aname
, tentry
->userID
.name
)
292 && (ainstance
== (char *)0
293 || !strcmp(ainstance
, tentry
->userID
.instance
))) {
294 *toP
= to
; /* found it */
298 *toP
= 0; /* no such entry */
302 /* Add a block to the hash table given a pointer to the block and its index.
303 The block is threaded onto the hash table and written to disk. The routine
304 returns zero if there were no errors. */
307 ThreadBlock(struct ubik_trans
*at
, afs_int32 index
,
308 struct kaentry
*tentry
)
311 int hi
; /* hash index */
313 if (!index_OK(index
))
315 hi
= NameHash(tentry
->userID
.name
, tentry
->userID
.instance
);
316 tentry
->next
= cheader
.nameHash
[hi
];
317 code
= set_header_word(at
, nameHash
[hi
], htonl(index
));
320 code
= kawrite(at
, index
, (char *)tentry
, sizeof(kaentry
));
326 /* Remove a block from the hash table. If success return 0, else return an
330 UnthreadBlock(struct ubik_trans
*at
, struct kaentry
*aentry
)
335 struct kaentry tentry
;
337 i
= NameHash(aentry
->userID
.name
, aentry
->userID
.instance
);
339 for (to
= ntohl(cheader
.nameHash
[i
]); to
!= NULLO
;
340 to
= ntohl(tentry
.next
)) {
341 code
= karead(at
, to
, (char *)&tentry
, sizeof(kaentry
));
344 /* see if the name matches */
345 if (!strcmp(aentry
->userID
.name
, tentry
.userID
.name
)
346 && !strcmp(aentry
->userID
.instance
, tentry
.userID
.instance
)) {
348 if (lo
) { /* unthread from last block */
350 kawrite(at
, lo
, (char *)&tentry
.next
, sizeof(afs_int32
));
353 } else { /* unthread from hash table */
354 code
= set_header_word(at
, nameHash
[i
], tentry
.next
);
358 aentry
->next
= 0; /* just to be sure */
361 lo
= DOFFSET(to
, &tentry
, &tentry
.next
);
366 /* Given an index to the last block (or zero the first time) read the contents
367 of the next block and return its index. The last argument is a pointer to
368 an estimate of the number of remaining blocks to read out. The remaining
369 count is an estimate because it may include free blocks that are not
370 returned. If there are no more blocks remaining is zero and the returned
371 index is zero. A non-zero index indicates that tentry has been filled with
372 valid data. If an error is encountered the returned index is zero and the
373 remaining count is negative. */
376 NextBlock(struct ubik_trans
*at
, afs_int32 index
, struct kaentry
*tentry
,
377 afs_int32
*remaining
)
382 if (index
== 0) /* get first one */
383 index
= sizeof(cheader
);
385 if (!index_OK(index
)) {
386 *remaining
= -1; /* error */
389 index
+= sizeof(kaentry
);
391 /* now search for the first entry that isn't free */
392 for (last
= ntohl(cheader
.eofPtr
); index
< last
; index
+= sizeof(kaentry
)) {
393 code
= karead(at
, index
, (char *)tentry
, sizeof(kaentry
));
398 if (!(ntohl(tentry
->flags
) & (KAFFREE
| KAFOLDKEYS
))) {
399 /* estimate remaining number of entries, not including this one */
400 *remaining
= (last
- index
) / sizeof(kaentry
) - 1;
404 *remaining
= 0; /* no more entries */
408 /* These are a collections of routines that deal with externally known keys.
409 They maintain a database of key version numbers and the corresponding key
410 and pointer to the user entry. */
413 ka_NewKey(struct ubik_trans
*tt
, afs_int32 tentryaddr
,
414 struct kaentry
*tentry
, struct ktc_encryptionKey
*key
)
416 struct kaOldKeys okeys
; /* old keys block */
417 afs_int32 okeysaddr
, nextaddr
; /* offset of old keys block */
418 afs_int32 prevptr
, nextprevptr
;
421 afs_int32 newkeyver
; /* new key version number */
422 afs_int32 newtotalkeyentries
= 0, oldtotalkeyentries
= 0, keyentries
;
423 int addednewkey
= 0, modified
;
425 int foundcurrentkey
= 0;
429 es_Report("Newkey for %s.%s\n", tentry
->userID
.name
,
430 tentry
->userID
.instance
);
432 newkeyver
= ntohl(tentry
->key_version
) + 1;
433 if ((newkeyver
< 1) || (newkeyver
>= MAXKAKVNO
))
436 /* An entry may have more than one oldkeys blocks. The entry
437 * points to the most current, but all the oldkeys blocks for an
438 * entry are not linked together. All oldkeys blocks for all
439 * entries are linked together off of the header. So we follow
442 for (prevptr
= 0, okeysaddr
= ntohl(cheader
.kvnoPtr
); okeysaddr
;
443 prevptr
= nextprevptr
, okeysaddr
= nextaddr
) {
444 /* foreacholdkeysblock */
445 /* Read the oldKeys block */
446 code
= karead(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
450 nextaddr
= ntohl(okeys
.next
);
451 nextprevptr
= DOFFSET(okeysaddr
, &okeys
, &okeys
.next
);
453 /* We only want oldkey blocks that belong to this entry */
454 if (ntohl(okeys
.entry
) != tentryaddr
)
457 modified
= 0; /* This oldkeys block has not been modified */
458 keyentries
= 0; /* Number of valid key entries in the block */
459 for (i
= 0; i
< NOLDKEYS
; i
++) {
461 /* Keep count of number of entries found */
462 if (okeys
.keys
[i
].superseded
!= 0) {
463 oldtotalkeyentries
++;
466 /* If we find the entry that is not superseded, then supersede it */
467 if (ntohl(okeys
.keys
[i
].superseded
) == NEVERDATE
) {
468 okeys
.keys
[i
].superseded
= htonl(now
);
471 if (foundcurrentkey
) {
473 ("Warning: Entry %s.%s contains more than one valid key: fixing\n",
474 tentry
->userID
.name
, tentry
->userID
.instance
));
480 /* If we find an oldkey of the same version or
481 * an old key that has expired, then delete it.
483 if ((ntohl(okeys
.keys
[i
].version
) == newkeyver
)
484 || ((now
- ntohl(okeys
.keys
[i
].superseded
) > maxKeyLifetime
))) {
485 okeys
.keys
[i
].superseded
= 0;
486 okeys
.keys
[i
].version
= htonl(-1);
487 memset(&okeys
.keys
[i
].key
, 0,
488 sizeof(struct ktc_encryptionKey
));
491 es_Report("Dropped oldkey %d seconds old with kvno %d\n",
492 now
- ntohl(okeys
.keys
[i
].superseded
),
493 ntohl(okeys
.keys
[i
].version
));
496 /* Add our key here if its free */
497 if (!addednewkey
&& (okeys
.keys
[i
].superseded
== 0)) {
498 okeys
.keys
[i
].version
= htonl(newkeyver
);
499 okeys
.keys
[i
].superseded
= htonl(NEVERDATE
);
500 memcpy(&okeys
.keys
[i
].key
, key
,
501 sizeof(struct ktc_encryptionKey
));
503 addednewkey
= okeysaddr
;
506 /* Keep count of number of entries found */
507 if (okeys
.keys
[i
].superseded
!= 0) {
509 newtotalkeyentries
++;
513 /* If we modified the block, write it out */
514 if (modified
&& keyentries
) {
515 code
= kawrite(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
520 /* If there are no more entries in this oldkeys block, delete it */
521 if (keyentries
== 0) {
523 code
= set_header_word(tt
, kvnoPtr
, okeys
.next
);
526 kawrite(tt
, prevptr
, (char *)&okeys
.next
,
531 code
= FreeBlock(tt
, okeysaddr
);
535 nextprevptr
= prevptr
; /* won't bump prevptr */
537 } /* foreacholdkeysblock */
539 /* If we could not add the key, create a new oldkeys block */
541 /* Allocate and fill in an oldkeys block */
542 addednewkey
= AllocBlock(tt
, (struct kaentry
*)&okeys
);
545 okeys
.flags
= htonl(KAFOLDKEYS
);
546 okeys
.entry
= htonl(tentryaddr
);
547 okeys
.keys
[0].version
= htonl(newkeyver
);
548 okeys
.keys
[0].superseded
= htonl(NEVERDATE
);
549 memcpy(&okeys
.keys
[0].key
, key
, sizeof(struct ktc_encryptionKey
));
550 newtotalkeyentries
++;
552 /* Thread onto the header's chain of oldkeys */
553 okeys
.next
= cheader
.kvnoPtr
;
554 code
= set_header_word(tt
, kvnoPtr
, htonl(addednewkey
));
558 /* Write the oldkeys block out */
559 code
= kawrite(tt
, addednewkey
, (char *)&okeys
, sizeof(okeys
));
563 es_Report("New oldkey block allocated at %d\n", addednewkey
);
566 if (oldtotalkeyentries
!= ntohl(tentry
->misc
.asServer
.nOldKeys
)) {
568 ("Warning: Entry %s.%s reports %d oldkeys, found %d: fixing\n",
569 tentry
->userID
.name
, tentry
->userID
.instance
,
570 ntohl(tentry
->misc
.asServer
.nOldKeys
), oldtotalkeyentries
));
574 /* Update the tentry. We rely on caller to write it out */
575 tentry
->misc
.asServer
.oldKeys
= htonl(addednewkey
);
576 tentry
->misc
.asServer
.nOldKeys
= htonl(newtotalkeyentries
);
577 tentry
->key_version
= htonl(newkeyver
);
578 memcpy(&tentry
->key
, key
, sizeof(tentry
->key
));
580 /* invalidate key caches everywhere */
581 code
= inc_header_word(tt
, specialKeysVersion
);
585 es_Report("New kvno is %d, now are %d oldkeys\n", newkeyver
,
591 ka_DelKey(struct ubik_trans
*tt
, afs_int32 tentryaddr
,
592 struct kaentry
*tentry
)
595 struct kaOldKeys okeys
; /* old keys block */
596 afs_int32 okeysaddr
, nextaddr
; /* offset of old keys block */
597 afs_int32 prevptr
= 0;
599 es_Report("DelKey for %s.%s\n", tentry
->userID
.name
,
600 tentry
->userID
.instance
);
602 /* An entry may have more than one oldkeys blocks. The entry
603 * points to the most current, but all the oldkeys blocks for an
604 * entry are not linked together. All oldkeys blocks for all
605 * entries are linked together off of the header. So we follow
608 for (okeysaddr
= ntohl(cheader
.kvnoPtr
); okeysaddr
; okeysaddr
= nextaddr
) {
609 /* foreacholdkeysblock */
610 /* Read the oldKeys block */
611 code
= karead(tt
, okeysaddr
, (char *)&okeys
, sizeof(okeys
));
614 nextaddr
= ntohl(okeys
.next
);
616 /* We only want oldkey blocks that belong to this entry */
617 if (ntohl(okeys
.entry
) != tentryaddr
) {
618 prevptr
= DOFFSET(okeysaddr
, &okeys
, &okeys
.next
);
622 /* Delete the oldkeys block */
625 kawrite(tt
, prevptr
, (char *)&okeys
.next
, sizeof(afs_int32
));
627 code
= set_header_word(tt
, kvnoPtr
, okeys
.next
);
631 code
= FreeBlock(tt
, okeysaddr
);
634 } /* foreacholdkeysblock */
636 /* Update the tentry. We rely on caller to write it out */
637 tentry
->misc
.asServer
.oldKeys
= 0;
638 tentry
->misc
.asServer
.nOldKeys
= 0;
640 /* invalidate key caches everywhere */
641 code
= inc_header_word(tt
, specialKeysVersion
);
649 ka_debugKeyCache(struct ka_debugInfo
*info
)
653 /* cheader_lock no longer exists */
654 memset(&info
->cheader_lock
, 0, sizeof(info
->cheader_lock
));
655 memcpy(&info
->keycache_lock
, &keycache_lock
, sizeof(info
->keycache_lock
));
657 info
->kcVersion
= keyCacheVersion
;
658 info
->kcSize
= maxCachedKeys
;
660 for (i
= 0; i
< maxCachedKeys
; i
++) {
661 if (keyCache
[i
].used
) {
662 if (info
->kcUsed
< KADEBUGKCINFOSIZE
) {
663 int j
= info
->kcUsed
;
664 char principal
[sizeof(keyCache
[0].name
) +
665 sizeof(keyCache
[0].inst
)];
667 info
->kcInfo
[j
].used
= keyCache
[i
].superseded
;
668 info
->kcInfo
[j
].kvno
= keyCache
[i
].kvno
;
669 info
->kcInfo
[j
].primary
=
670 (keyCache
[i
].superseded
== NEVERDATE
);
671 info
->kcInfo
[j
].keycksum
= 0;
675 for (k
= 0; k
< sizeof(struct ktc_encryptionKey
); k
++)
676 info
->kcInfo
[j
].keycksum
+=
677 ((char *)&keyCache
[i
].key
)[k
];
680 strcpy(principal
, keyCache
[i
].name
);
681 strcat(principal
, ".");
682 strcat(principal
, keyCache
[i
].inst
);
683 strncpy(info
->kcInfo
[j
].principal
, principal
,
684 sizeof(info
->kcInfo
[0].principal
));
691 /* Add a key to the key cache, expanding it if necessary. */
694 ka_Encache(char *name
, char *inst
, afs_int32 kvno
,
695 struct ktc_encryptionKey
*key
, Date superseded
)
699 ObtainWriteLock(&keycache_lock
);
700 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
)) {
701 for (i
= 0; i
< maxCachedKeys
; i
++)
702 keyCache
[i
].used
= 0;
705 for (i
= 0; i
< maxCachedKeys
; i
++)
706 if (keyCache
[i
].used
== 0) {
708 keyCache
[i
].kvno
= kvno
;
709 strncpy(keyCache
[i
].name
, name
, sizeof(keyCache
[i
].name
));
710 strncpy(keyCache
[i
].inst
, inst
, sizeof(keyCache
[i
].inst
));
711 keyCacheVersion
= ntohl(cheader
.specialKeysVersion
);
712 memcpy(&keyCache
[i
].key
, key
, sizeof(*key
));
713 keyCache
[i
].superseded
= superseded
;
714 keyCache
[i
].used
= time(0);
716 ReleaseWriteLock(&keycache_lock
);
719 /* i == maxCachedKeys */
721 (struct cachedKey
*)realloc(keyCache
,
723 2) * sizeof(struct cachedKey
));
725 es_Report("Can't realloc keyCache! out of memory?");
730 int j
= i
; /* initialize new storage */
731 while (j
< maxCachedKeys
)
732 keyCache
[j
++].used
= 0;
737 /* Look up the key given a principal and a kvno. This is called by GetTicket
738 to get the decryption key for the authenticating ticket. It is also called
739 by the rxkad security module to decrypt admin tickets. The rxkad call is
740 with tt==0, since Rx can't call Ubik. */
743 ka_LookupKvno(struct ubik_trans
*tt
, char *name
, char *inst
, afs_int32 kvno
,
744 struct ktc_encryptionKey
*key
)
749 struct kaentry tentry
;
751 struct kaOldKeys okeys
;
753 ObtainReadLock(&keycache_lock
);
754 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
))
755 code
= KAKEYCACHEINVALID
;
757 for (i
= 0; i
< maxCachedKeys
; i
++) {
758 if (keyCache
[i
].used
) { /* zero used date means invalid */
759 if ((keyCache
[i
].kvno
== kvno
)
760 && (strcmp(keyCache
[i
].name
, name
) == 0)
761 && (strcmp(keyCache
[i
].inst
, inst
) == 0)) {
762 memcpy(key
, &keyCache
[i
].key
, sizeof(*key
));
763 keyCache
[i
].used
= time(0);
764 ReleaseReadLock(&keycache_lock
);
771 ReleaseReadLock(&keycache_lock
);
775 /* we missed in the cache so need to look in the Ubik database */
776 code
= FindBlock(tt
, name
, inst
, &to
, &tentry
);
782 /* first check the current key */
783 if (tentry
.key_version
== htonl(kvno
)) {
784 memcpy(key
, &tentry
.key
, sizeof(*key
));
785 ka_Encache(name
, inst
, kvno
, key
, NEVERDATE
);
788 for (ko
= ntohl(cheader
.kvnoPtr
); ko
; ko
= ntohl(okeys
.next
)) {
789 code
= karead(tt
, ko
, (char *)&okeys
, sizeof(okeys
));
792 if (ntohl(okeys
.entry
) == to
)
793 for (i
= 0; i
< NOLDKEYS
; i
++)
794 if (okeys
.keys
[i
].superseded
795 && (ntohl(okeys
.keys
[i
].version
) == kvno
)) {
796 memcpy(key
, &okeys
.keys
[i
].key
, sizeof(*key
));
797 ka_Encache(name
, inst
, kvno
, key
,
798 ntohl(okeys
.keys
[i
].superseded
));
805 /* Look up the primary key and key version for a principal. */
808 ka_LookupKey(struct ubik_trans
*tt
,
811 afs_int32
*kvno
, /* returned */
812 struct ktc_encryptionKey
*key
) /* copied out */
816 struct kaentry tentry
;
819 ObtainReadLock(&keycache_lock
);
820 if (keyCacheVersion
!= ntohl(cheader
.specialKeysVersion
))
821 code
= KAKEYCACHEINVALID
;
823 for (i
= 0; i
< maxCachedKeys
; i
++) {
824 if (keyCache
[i
].used
) { /* zero used date means invalid */
825 if ((keyCache
[i
].superseded
== NEVERDATE
)
826 && (strcmp(keyCache
[i
].name
, name
) == 0)
827 && (strcmp(keyCache
[i
].inst
, inst
) == 0)) {
828 memcpy(key
, &keyCache
[i
].key
, sizeof(*key
));
829 *kvno
= keyCache
[i
].kvno
;
830 keyCache
[i
].used
= time(0);
831 ReleaseReadLock(&keycache_lock
);
838 ReleaseReadLock(&keycache_lock
);
842 /* we missed in the cache so need to look in the Ubik database */
843 code
= FindBlock(tt
, name
, inst
, &to
, &tentry
);
848 memcpy(key
, &tentry
.key
, sizeof(*key
));
849 *kvno
= ntohl(tentry
.key_version
);
850 ka_Encache(name
, inst
, *kvno
, key
, NEVERDATE
);
854 /* This is, hopefully a temporary mechanism to fill the cache will all keys
855 since filling cache misses during rxkad challenge responses will deadlock if
856 Ubik needs to use Rx. */
859 ka_FillKeyCache(struct ubik_trans
*tt
)
865 struct ktc_encryptionKey k
;
866 struct kaOldKeys okeys
;
867 struct kaentry tentry
;
869 /* this is a little marginal, but... */
870 if (keyCacheVersion
== ntohl(cheader
.specialKeysVersion
))
874 for (ko
= ntohl(cheader
.kvnoPtr
); ko
; ko
= ntohl(okeys
.next
)) {
875 code
= karead(tt
, ko
, (char *)&okeys
, sizeof(okeys
));
878 /* get name & instance */
880 karead(tt
, ntohl(okeys
.entry
), (char *)&tentry
, sizeof(tentry
));
884 /* get all the old keys in this block */
885 for (i
= 0; i
< NOLDKEYS
; i
++)
886 if (okeys
.keys
[i
].superseded
) {
888 ka_LookupKvno(tt
, tentry
.userID
.name
,
889 tentry
.userID
.instance
,
890 ntohl(okeys
.keys
[i
].version
), &k
);
895 if (++nfound
> maxCachedKeys
)
896 return KADATABASEINCONSISTENT
;
901 update_admin_count(struct ubik_trans
*tt
, int delta
)
906 cheader
.admin_accounts
= htonl(ntohl(cheader
.admin_accounts
) + delta
);
907 to
= DOFFSET(0, &cheader
, &cheader
.admin_accounts
);
909 kawrite(tt
, to
, (char *)&cheader
.admin_accounts
, sizeof(afs_int32
));
916 index_OK(afs_int32 index
)
918 if ((index
< sizeof(cheader
)) || (index
>= ntohl(cheader
.eofPtr
))
919 || ((index
- sizeof(cheader
)) % sizeof(kaentry
) != 0))
924 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
927 name_instance_legal(char *name
, char *instance
)
931 /* No string checks apply anymore. The international people want to use full 8
932 bit ascii without problems. */
934 code
= (strlen(name
) < MAXKTCNAMELEN
)
935 && (strlen(instance
) < MAXKTCNAMELEN
);
937 map
= LEGALCHARS
; /* permitted chars, instance allows <period> */
938 code
= (strlen(name
) > 0) && string_legal(instance
, map
)
939 && string_legal(name
, map
+ 1);
942 dynamic_statistics
.string_checks
++;
948 string_legal(char *str
, char *map
)
953 if (slen
>= MAXKTCNAMELEN
)
954 return 0; /* with trailing null must fit in data base */
955 return (slen
== strspn(str
, map
)); /* strspn returns length(str) if all chars in map */