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>
15 #include <hcrypto/des.h>
20 #include <rx/rxkad_convert.h>
21 #include <afs/com_err.h>
27 #define UBIK_HEADERSIZE 64
28 #define UBIK_BUFFERSIZE 1024
30 char *whoami
= "kadb_check";
34 void badEntry(afs_int32
, afs_int32
);
36 int listuheader
, listkheader
, listentries
, verbose
;
42 struct ubik_hdr uheader
;
44 offset
= lseek(fd
, 0, 0);
46 printf("error: lseek to 0 failed: %d %d\n", offset
, errno
);
50 /* now read the info */
51 r
= read(fd
, &uheader
, sizeof(uheader
));
52 if (r
!= sizeof(uheader
)) {
53 printf("error: read of %" AFS_SIZET_FMT
" bytes failed: %d %d\n", sizeof(uheader
), r
,
58 uheader
.magic
= ntohl(uheader
.magic
);
59 uheader
.size
= ntohs(uheader
.size
);
60 uheader
.version
.epoch
= ntohl(uheader
.version
.epoch
);
61 uheader
.version
.counter
= ntohl(uheader
.version
.counter
);
64 printf("Ubik Header\n");
65 printf(" Magic = 0x%x\n", uheader
.magic
);
66 printf(" Size = %u\n", uheader
.size
);
67 printf(" Version.epoch = %u\n", uheader
.version
.epoch
);
68 printf(" Version.counter = %u\n", uheader
.version
.counter
);
71 if (uheader
.size
!= UBIK_HEADERSIZE
)
72 printf("Ubik header size is %u (should be %u)\n", uheader
.size
,
74 if (uheader
.magic
!= UBIK_MAGIC
)
75 printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader
.magic
,
82 PrintHeader(struct kaheader
*header
)
84 printf("Version = %d\n", header
->version
);
85 printf("HeaderSize = %d\n", header
->headerSize
);
86 printf("Free Ptr = %u\n", header
->freePtr
);
87 printf("EOF Ptr = %u\n", header
->eofPtr
);
88 printf("Kvno Ptr = %u\n", header
->kvnoPtr
);
89 printf("SpecialKeysVersion changed = %d\n", header
->specialKeysVersion
);
90 printf("# admin accounts = %d\n", header
->admin_accounts
);
91 printf("HashSize = %d\n", header
->hashsize
);
92 printf("Check Version = %d\n", header
->checkVersion
);
93 printf("stats.minorVersion = %d\n", header
->stats
.minor_version
);
94 printf("stats.AllocBlock calls = %d\n", header
->stats
.allocs
);
95 printf("stats.FreeBlock calls = %d\n", header
->stats
.frees
);
96 printf("stats.cpw commands = %d\n", header
->stats
.cpws
);
100 PrintEntry(afs_int32 index
, struct kaentry
*entry
)
106 time_t modification_time
= entry
->modification_time
;
107 time_t change_password_time
= entry
->change_password_time
;
108 time_t max_ticket_lifetime
= entry
->max_ticket_lifetime
;
112 i
= (index
- sizeof(struct kaheader
)) / sizeof(struct kaentry
);
114 printf("Entry %5d (%u):\n", i
, index
);
116 if (entry
->flags
& KAFNORMAL
) {
117 printf(" Name = %s", entry
->userID
.name
);
118 if (strlen(entry
->userID
.instance
) > 0) {
119 printf(".%s", entry
->userID
.instance
);
125 if (entry
->flags
& KAFNORMAL
)
127 if (entry
->flags
& KAFADMIN
)
129 if (entry
->flags
& KAFNOTGS
)
131 if (entry
->flags
& KAFNOSEAL
)
133 if (entry
->flags
& KAFNOCPW
)
136 if (entry
->flags
& KAFNEWASSOC
)
138 if (entry
->flags
& KAFFREE
)
140 if (entry
->flags
& KAFOLDKEYS
)
142 if (entry
->flags
& KAFSPECIAL
)
144 if (entry
->flags
& KAFASSOCROOT
)
145 printf("ROOT-ASSOC ");
146 if (entry
->flags
& KAFASSOC
)
150 printf(" Next = %u\n", entry
->next
);
152 if (entry
->flags
& KAFFREE
)
154 if (entry
->flags
& KAFOLDKEYS
)
157 tt
= entry
->user_expiration
;
158 tm_p
= localtime(&tt
);
160 strftime(Time
, 100, "%m/%d/%Y %H:%M", tm_p
);
162 printf(" User Expiration = %s\n",
163 (entry
->user_expiration
== 0xffffffff) ? "never" : Time
);
165 printf(" Password Expiration = %u days %s\n",
166 entry
->misc_auth_bytes
[EXPIRES
],
167 (entry
->misc_auth_bytes
[EXPIRES
] ? "" : "(never)"));
169 printf(" Password Attempts before lock = ");
170 if (!entry
->misc_auth_bytes
[ATTEMPTS
])
171 printf("unlimited\n");
173 printf("%d\n", entry
->misc_auth_bytes
[ATTEMPTS
]);
175 printf(" Password lockout time = ");
176 if (!entry
->misc_auth_bytes
[LOCKTIME
])
177 printf("unlimited\n");
179 printf("%.1f min\n", (entry
->misc_auth_bytes
[LOCKTIME
] * 8.5));
181 printf(" Is entry locked = %s\n",
182 (entry
->misc_auth_bytes
[REUSEFLAGS
] ==
183 KA_ISLOCKED
) ? "yes" : "no");
185 printf(" Permit password reuse = %s\n",
186 (!entry
->pwsums
[0] && !entry
->pwsums
[1]) ? "yes" : "no");
188 printf(" Mod Time = %u: %s", entry
->modification_time
,
189 ctime(&modification_time
));
190 printf(" Mod ID = %u\n", entry
->modification_id
);
191 printf(" Change Password Time = %u: %s", entry
->change_password_time
,
192 ctime(&change_password_time
));
193 printf(" Ticket lifetime = %u: %s", entry
->max_ticket_lifetime
,
194 ctime(&max_ticket_lifetime
));
195 printf(" Key Version = %d\n", entry
->key_version
);
198 ka_PrintBytes((char *)&entry
->key
, sizeof(entry
->key
));
201 /* What about asServer structs and such and misc_ath_bytes */
204 /* ntohEntry - convert back to host-order */
206 ntohEntry(struct kaentry
*entryp
)
208 entryp
->flags
= ntohl(entryp
->flags
);
209 entryp
->next
= ntohl(entryp
->next
);
210 entryp
->user_expiration
= ntohl(entryp
->user_expiration
);
211 entryp
->modification_time
= ntohl(entryp
->modification_time
);
212 entryp
->modification_id
= ntohl(entryp
->modification_id
);
213 entryp
->change_password_time
= ntohl(entryp
->change_password_time
);
214 entryp
->max_ticket_lifetime
= ntohl(entryp
->max_ticket_lifetime
);
215 entryp
->key_version
= ntohl(entryp
->key_version
);
216 entryp
->misc
.asServer
.nOldKeys
= ntohl(entryp
->misc
.asServer
.nOldKeys
);
217 entryp
->misc
.asServer
.oldKeys
= ntohl(entryp
->misc
.asServer
.oldKeys
);
222 EntryName(struct kaentry
*entryp
)
224 char name
[32], inst
[32];
226 ka_ConvertBytes(name
, sizeof(name
), entryp
->userID
.name
,
227 strlen(entryp
->userID
.name
));
228 ka_ConvertBytes(inst
, sizeof(inst
), entryp
->userID
.instance
,
229 strlen(entryp
->userID
.instance
));
231 if (strlen(entryp
->userID
.instance
)) {
232 sprintf(principal
, "%s.%s", name
, inst
);
234 strcpy(principal
, name
);
241 RebuildEntry(struct kaentry
*entryp
)
247 /* Special entries are not rebuilt */
248 if (entryp
->flags
& KAFSPECIAL
)
251 fprintf(out
, "create -name %s", EntryName(entryp
));
253 ka_ConvertBytes(key
, sizeof(key
), (char *)&entryp
->key
,
254 sizeof(entryp
->key
));
255 fprintf(out
, " -initial_password foo\n");
258 if (entryp
->flags
& KAFADMIN
)
259 strcat(flags
, "+ADMIN");
260 if (entryp
->flags
& KAFNOTGS
)
261 strcat(flags
, "+NOTGS");
262 if (entryp
->flags
& KAFNOSEAL
)
263 strcat(flags
, "+NOSEAL");
264 if (entryp
->flags
& KAFNOCPW
)
265 strcat(flags
, "+NOCPW");
267 fprintf(out
, "setfields -name %s", principal
);
268 if (strcmp(flags
, "") != 0)
269 fprintf(out
, " -flags %s", &flags
[1]);
270 if (entryp
->user_expiration
!= 0xffffffff) {
271 time_t tt
= entryp
->user_expiration
;
272 strftime(Time
, 50, "%m/%d/%Y %H:%M",localtime(&tt
));
273 fprintf(out
, " -expiration '%s'", Time
);
275 fprintf(out
, " -lifetime %u", entryp
->max_ticket_lifetime
);
276 if (entryp
->misc_auth_bytes
[EXPIRES
])
277 fprintf(out
, " -pwexpires %u", entryp
->misc_auth_bytes
[EXPIRES
]);
278 if (entryp
->pwsums
[0] || entryp
->pwsums
[1])
279 fprintf(out
, " -reuse no");
280 if (entryp
->misc_auth_bytes
[ATTEMPTS
])
281 fprintf(out
, " -attempts %u", entryp
->misc_auth_bytes
[ATTEMPTS
]);
282 if (entryp
->misc_auth_bytes
[LOCKTIME
])
283 fprintf(out
, " -locktime %d",
284 (int)(entryp
->misc_auth_bytes
[LOCKTIME
] * 8.5));
287 fprintf(out
, "setkey -name %s -new_key %s -kvno %d\n", principal
, key
,
288 ntohl(entryp
->key_version
));
292 CheckHeader(struct kaheader
*header
)
294 afs_int32 i
, code
= 0;
296 header
->version
= ntohl(header
->version
);
297 header
->headerSize
= ntohl(header
->headerSize
);
298 header
->freePtr
= ntohl(header
->freePtr
);
299 header
->eofPtr
= ntohl(header
->eofPtr
);
300 header
->kvnoPtr
= ntohl(header
->kvnoPtr
);
301 header
->stats
.minor_version
= ntohl(header
->stats
.minor_version
);
302 header
->stats
.allocs
= ntohl(header
->stats
.allocs
);
303 header
->stats
.frees
= ntohl(header
->stats
.frees
);
304 header
->stats
.cpws
= ntohl(header
->stats
.cpws
);
305 header
->admin_accounts
= ntohl(header
->admin_accounts
);
306 header
->specialKeysVersion
= ntohl(header
->specialKeysVersion
);
307 header
->hashsize
= ntohl(header
->hashsize
);
308 for (i
= 0; i
< HASHSIZE
; i
++) {
309 header
->nameHash
[i
] = ntohl(header
->nameHash
[i
]);
311 header
->checkVersion
= ntohl(header
->checkVersion
);
313 if (header
->version
!= header
->checkVersion
) {
315 fprintf(stderr
, "HEADER VERSION MISMATCH: initial %d, final %d\n",
316 header
->version
, header
->checkVersion
);
318 if (header
->headerSize
!= sizeof(struct kaheader
)) {
321 "HEADER SIZE WRONG: file indicates %d, should be %" AFS_SIZET_FMT
"\n",
322 header
->headerSize
, sizeof(struct kaheader
));
324 if (header
->hashsize
!= HASHSIZE
) {
326 fprintf(stderr
, "HASH SIZE WRONG: file indicates %d, should be %d\n",
327 header
->hashsize
, HASHSIZE
);
329 if ((header
->kvnoPtr
&& ((header
->kvnoPtr
< header
->headerSize
)
330 || (header
->eofPtr
< header
->freePtr
)))
331 || (header
->freePtr
&& ((header
->freePtr
< header
->headerSize
)
332 || (header
->eofPtr
< header
->kvnoPtr
)))) {
335 "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
336 header
->headerSize
, header
->freePtr
, header
->kvnoPtr
,
341 * fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
342 * (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
348 NameHash(struct kaentry
*entryp
)
352 char *aname
= entryp
->userID
.name
;
353 char *ainstance
= entryp
->userID
.instance
;
355 /* stolen directly from the HashString function in the vol package */
357 for (i
= strlen(aname
), aname
+= i
- 1; i
--; aname
--)
358 hash
= (hash
* 31) + (*((unsigned char *)aname
) - 31);
359 for (i
= strlen(ainstance
), ainstance
+= i
- 1; i
--; ainstance
--)
360 hash
= (hash
* 31) + (*((unsigned char *)ainstance
) - 31);
361 return (hash
% HASHSIZE
);
365 readDB(afs_int32 offset
, void *buffer
, afs_int32 size
)
369 offset
+= UBIK_HEADERSIZE
;
370 code
= lseek(fd
, offset
, SEEK_SET
);
371 if (code
!= offset
) {
372 afs_com_err(whoami
, errno
, "skipping Ubik header");
375 code
= read(fd
, buffer
, size
);
377 afs_com_err(whoami
, errno
, "reading db got %d bytes", code
);
383 #include "AFS_component_version_number.c"
386 WorkerBee(struct cmd_syndesc
*as
, void *arock
)
393 struct kaheader header
;
394 int nentries
, i
, j
, count
;
396 struct kaentry entry
;
398 dbFile
= as
->parms
[0].items
->data
; /* -database */
399 listuheader
= (as
->parms
[1].items
? 1 : 0); /* -uheader */
400 listkheader
= (as
->parms
[2].items
? 1 : 0); /* -kheader */
401 listentries
= (as
->parms
[3].items
? 1 : 0); /* -entries */
402 verbose
= (as
->parms
[4].items
? 1 : 0); /* -verbose */
403 outFile
= (as
->parms
[5].items
? as
->parms
[5].items
->data
: NULL
); /* -rebuild */
406 out
= fopen(outFile
, "w");
408 afs_com_err(whoami
, errno
, "opening output file %s", outFile
);
414 fd
= open(dbFile
, O_RDONLY
, 0);
416 afs_com_err(whoami
, errno
, "opening database file %s", dbFile
);
419 code
= fstat(fd
, &info
);
421 afs_com_err(whoami
, errno
, "stat'ing file %s", dbFile
);
424 if ((info
.st_size
- UBIK_HEADERSIZE
) % UBIK_BUFFERSIZE
)
426 "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
427 (int) info
.st_size
, UBIK_BUFFERSIZE
, UBIK_HEADERSIZE
);
431 readDB(0, &header
, sizeof(header
));
432 code
= CheckHeader(&header
);
434 PrintHeader(&header
);
438 (UBIK_HEADERSIZE
+ header
.headerSize
)) / sizeof(struct kaentry
);
439 entrys
= calloc(nentries
, sizeof(int));
441 for (i
= 0, index
= sizeof(header
); i
< nentries
;
442 i
++, index
+= sizeof(struct kaentry
)) {
443 readDB(index
, &entry
, sizeof(entry
));
445 if (index
>= header
.eofPtr
) {
447 } else if (listentries
) {
448 PrintEntry(index
, &entry
);
451 if (entry
.flags
& KAFNORMAL
) {
452 entrys
[i
] |= 0x1; /* user entry */
454 if (strlen(entry
.userID
.name
) == 0) {
456 printf("Entry %d has zero length name\n", i
);
459 if (!DES_check_key_parity(ktc_to_cblock(&entry
.key
))
460 || DES_is_weak_key(ktc_to_cblock(&entry
.key
))) {
461 fprintf(stderr
, "Entry %d, %s, has bad key\n", i
,
467 RebuildEntry(&entry
);
470 } else if (entry
.flags
& KAFFREE
) {
471 entrys
[i
] |= 0x2; /* free entry */
473 } else if (entry
.flags
& KAFOLDKEYS
) {
474 entrys
[i
] |= 0x4; /* old keys block */
475 /* Should check the structure of the oldkeys block? */
478 if (index
< header
.eofPtr
) {
479 fprintf(stderr
, "Entry %d is unrecognizable\n", i
);
484 /* Follow the hash chains */
485 for (j
= 0; j
< HASHSIZE
; j
++) {
486 for (index
= header
.nameHash
[j
]; index
; index
= entry
.next
) {
487 readDB(index
, &entry
, sizeof(entry
));
489 /* check to see if the name is hashed correctly */
490 i
= NameHash(&entry
);
493 "Entry %" AFS_SIZET_FMT
", %s, found in hash chain %d (should be %d)\n",
495 sizeof(struct kaheader
)) / sizeof(struct kaentry
)),
496 EntryName(&entry
), j
, i
);
499 /* Is it on another hash chain or circular hash chain */
500 i
= (index
- header
.headerSize
) / sizeof(entry
);
501 if (entrys
[i
] & 0x10) {
503 "Entry %d, %s, hash index %d, was found on another hash chain\n",
504 i
, EntryName(&entry
), j
);
506 fprintf(stderr
, "Skipping rest of hash chain %d\n", j
);
508 fprintf(stderr
, "No next entry in hash chain %d\n", j
);
512 entrys
[i
] |= 0x10; /* On hash chain */
516 /* Follow the free pointers */
518 for (index
= header
.freePtr
; index
; index
= entry
.next
) {
519 readDB(index
, &entry
, sizeof(entry
));
521 /* Is it on another chain or circular free chain */
522 i
= (index
- header
.headerSize
) / sizeof(entry
);
523 if (entrys
[i
] & 0x20) {
524 fprintf(stderr
, "Entry %d, %s, already found on free chain\n", i
,
526 fprintf(stderr
, "Skipping rest of free chain\n");
530 entrys
[i
] |= 0x20; /* On free chain */
535 printf("Found %d free entries\n", count
);
537 /* Follow the oldkey blocks */
539 for (index
= header
.kvnoPtr
; index
; index
= entry
.next
) {
540 readDB(index
, &entry
, sizeof(entry
));
542 /* Is it on another chain or circular free chain */
543 i
= (index
- header
.headerSize
) / sizeof(entry
);
544 if (entrys
[i
] & 0x40) {
545 fprintf(stderr
, "Entry %d, %s, already found on olkeys chain\n",
546 i
, EntryName(&entry
));
547 fprintf(stderr
, "Skipping rest of oldkeys chain\n");
551 entrys
[i
] |= 0x40; /* On free chain */
556 printf("Found %d oldkey blocks\n", count
);
558 /* Now recheck all the blocks and see if they are allocated correctly
559 * 0x1 --> User Entry 0x10 --> On hash chain
560 * 0x2 --> Free Entry 0x20 --> On Free chain
561 * 0x4 --> OldKeys Entry 0x40 --> On Oldkeys chain
564 for (i
= 0; i
< nentries
; i
++) {
566 if (j
& 0x1) { /* user entry */
568 badEntry(j
, i
); /* on hash chain? */
570 badEntry(j
, i
); /* anything else? */
571 } else if (j
& 0x2) { /* free entry */
573 badEntry(j
, i
); /* on free chain? */
575 badEntry(j
, i
); /* anything else? */
576 } else if (j
& 0x4) { /* oldkeys entry */
578 badEntry(j
, i
); /* on oldkeys chain? */
580 badEntry(j
, i
); /* anything else? */
581 } else if (j
& 0x8) { /* past eof */
583 badEntry(j
, i
); /* anything else? */
585 badEntry(j
, i
); /* anything else? */
592 badEntry(afs_int32 e
, afs_int32 i
)
595 struct kaentry entry
;
597 offset
= i
* sizeof(struct kaentry
) + sizeof(struct kaheader
);
598 readDB(offset
, &entry
, sizeof(entry
));
600 fprintf(stderr
, "Entry %d, %s, hash index %d, is bad: [", i
,
601 EntryName(&entry
), NameHash(&entry
));
603 fprintf(stderr
, " UserEntry");
605 fprintf(stderr
, " FreeEntry");
607 fprintf(stderr
, " OldkeysEntry");
609 fprintf(stderr
, " PastEOF");
611 fprintf(stderr
, " <NULL>");
612 fprintf(stderr
, " ] [");
614 fprintf(stderr
, " UserChain");
616 fprintf(stderr
, " FreeChain");
618 fprintf(stderr
, " OldkeysChain");
620 fprintf(stderr
, " <NULL>");
621 fprintf(stderr
, " ]\n");
625 main(int argc
, char **argv
)
627 struct cmd_syndesc
*ts
;
631 ts
= cmd_CreateSyntax(NULL
, WorkerBee
, NULL
, 0, "KADB check");
632 cmd_AddParm(ts
, "-database", CMD_SINGLE
, CMD_REQUIRED
, "kadb_file");
633 cmd_AddParm(ts
, "-uheader", CMD_FLAG
, CMD_OPTIONAL
,
634 "Display UBIK header");
635 cmd_AddParm(ts
, "-kheader", CMD_FLAG
, CMD_OPTIONAL
,
636 "Display KADB header");
637 cmd_AddParm(ts
, "-entries", CMD_FLAG
, CMD_OPTIONAL
, "Display entries");
638 cmd_AddParm(ts
, "-verbose", CMD_FLAG
, CMD_OPTIONAL
, "verbose");
639 cmd_AddParm(ts
, "-rebuild", CMD_SINGLE
, CMD_OPTIONAL
, "out_file");
641 return cmd_Dispatch(argc
, argv
);