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 <afs/com_err.h>
26 #define UBIK_HEADERSIZE 64
27 #define UBIK_BUFFERSIZE 1024
29 char *whoami = "kadb_check";
33 void badEntry(afs_int32, afs_int32);
35 int listuheader, listkheader, listentries, verbose;
41 struct ubik_hdr uheader;
43 offset = lseek(fd, 0, 0);
45 printf("error: lseek to 0 failed: %d %d\n", offset, errno);
49 /* now read the info */
50 r = read(fd, &uheader, sizeof(uheader));
51 if (r != sizeof(uheader)) {
52 printf("error: read of %" AFS_SIZET_FMT " bytes failed: %d %d\n", sizeof(uheader), r,
57 uheader.magic = ntohl(uheader.magic);
58 uheader.size = ntohl(uheader.size);
59 uheader.version.epoch = ntohl(uheader.version.epoch);
60 uheader.version.counter = ntohl(uheader.version.counter);
63 printf("Ubik Header\n");
64 printf(" Magic = 0x%x\n", uheader.magic);
65 printf(" Size = %u\n", uheader.size);
66 printf(" Version.epoch = %u\n", uheader.version.epoch);
67 printf(" Version.counter = %u\n", uheader.version.counter);
70 if (uheader.size != UBIK_HEADERSIZE)
71 printf("Ubik header size is %u (should be %u)\n", uheader.size,
73 if (uheader.magic != UBIK_MAGIC)
74 printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
81 PrintHeader(struct kaheader *header)
83 printf("Version = %d\n", header->version);
84 printf("HeaderSize = %d\n", header->headerSize);
85 printf("Free Ptr = %u\n", header->freePtr);
86 printf("EOF Ptr = %u\n", header->eofPtr);
87 printf("Kvno Ptr = %u\n", header->kvnoPtr);
88 printf("SpecialKeysVersion changed = %d\n", header->specialKeysVersion);
89 printf("# admin accounts = %d\n", header->admin_accounts);
90 printf("HashSize = %d\n", header->hashsize);
91 printf("Check Version = %d\n", header->checkVersion);
92 printf("stats.minorVersion = %d\n", header->stats.minor_version);
93 printf("stats.AllocBlock calls = %d\n", header->stats.allocs);
94 printf("stats.FreeBlock calls = %d\n", header->stats.frees);
95 printf("stats.cpw commands = %d\n", header->stats.cpws);
99 PrintEntry(afs_int32 index, struct kaentry *entry)
105 time_t modification_time = entry->modification_time;
106 time_t change_password_time = entry->change_password_time;
107 time_t max_ticket_lifetime = entry->max_ticket_lifetime;
111 i = (index - sizeof(struct kaheader)) / sizeof(struct kaentry);
113 printf("Entry %5d (%u):\n", i, index);
115 if (entry->flags & KAFNORMAL) {
116 printf(" Name = %s", entry->userID.name);
117 if (strlen(entry->userID.instance) > 0) {
118 printf(".%s", entry->userID.instance);
124 if (entry->flags & KAFNORMAL)
126 if (entry->flags & KAFADMIN)
128 if (entry->flags & KAFNOTGS)
130 if (entry->flags & KAFNOSEAL)
132 if (entry->flags & KAFNOCPW)
135 if (entry->flags & KAFNEWASSOC)
137 if (entry->flags & KAFFREE)
139 if (entry->flags & KAFOLDKEYS)
141 if (entry->flags & KAFSPECIAL)
143 if (entry->flags & KAFASSOCROOT)
144 printf("ROOT-ASSOC ");
145 if (entry->flags & KAFASSOC)
149 printf(" Next = %u\n", entry->next);
151 if (entry->flags & KAFFREE)
153 if (entry->flags & KAFOLDKEYS)
156 tt = entry->user_expiration;
157 tm_p = localtime(&tt);
159 strftime(Time, 100, "%m/%d/%Y %H:%M", tm_p);
161 printf(" User Expiration = %s\n",
162 (entry->user_expiration == 0xffffffff) ? "never" : Time);
164 printf(" Password Expiration = %u days %s\n",
165 entry->misc_auth_bytes[EXPIRES],
166 (entry->misc_auth_bytes[EXPIRES] ? "" : "(never)"));
168 printf(" Password Attempts before lock = ");
169 if (!entry->misc_auth_bytes[ATTEMPTS])
170 printf("unlimited\n");
172 printf("%d\n", entry->misc_auth_bytes[ATTEMPTS]);
174 printf(" Password lockout time = ");
175 if (!entry->misc_auth_bytes[LOCKTIME])
176 printf("unlimited\n");
178 printf("%.1f min\n", (entry->misc_auth_bytes[LOCKTIME] * 8.5));
180 printf(" Is entry locked = %s\n",
181 (entry->misc_auth_bytes[REUSEFLAGS] ==
182 KA_ISLOCKED) ? "yes" : "no");
184 printf(" Permit password reuse = %s\n",
185 (!entry->pwsums[0] && !entry->pwsums[1]) ? "yes" : "no");
187 printf(" Mod Time = %u: %s", entry->modification_time,
188 ctime(&modification_time));
189 printf(" Mod ID = %u\n", entry->modification_id);
190 printf(" Change Password Time = %u: %s", entry->change_password_time,
191 ctime(&change_password_time));
192 printf(" Ticket lifetime = %u: %s", entry->max_ticket_lifetime,
193 ctime(&max_ticket_lifetime));
194 printf(" Key Version = %d\n", entry->key_version);
197 ka_PrintBytes((char *)&entry->key, sizeof(entry->key));
200 /* What about asServer structs and such and misc_ath_bytes */
203 /* ntohEntry - convert back to host-order */
205 ntohEntry(struct kaentry *entryp)
207 entryp->flags = ntohl(entryp->flags);
208 entryp->next = ntohl(entryp->next);
209 entryp->user_expiration = ntohl(entryp->user_expiration);
210 entryp->modification_time = ntohl(entryp->modification_time);
211 entryp->modification_id = ntohl(entryp->modification_id);
212 entryp->change_password_time = ntohl(entryp->change_password_time);
213 entryp->max_ticket_lifetime = ntohl(entryp->max_ticket_lifetime);
214 entryp->key_version = ntohl(entryp->key_version);
215 entryp->misc.asServer.nOldKeys = ntohl(entryp->misc.asServer.nOldKeys);
216 entryp->misc.asServer.oldKeys = ntohl(entryp->misc.asServer.oldKeys);
221 EntryName(struct kaentry *entryp)
223 char name[32], inst[32];
225 ka_ConvertBytes(name, sizeof(name), entryp->userID.name,
226 strlen(entryp->userID.name));
227 ka_ConvertBytes(inst, sizeof(inst), entryp->userID.instance,
228 strlen(entryp->userID.instance));
230 if (strlen(entryp->userID.instance)) {
231 sprintf(principal, "%s.%s", name, inst);
233 strcpy(principal, name);
240 RebuildEntry(struct kaentry *entryp)
246 /* Special entries are not rebuilt */
247 if (entryp->flags & KAFSPECIAL)
250 fprintf(out, "create -name %s", EntryName(entryp));
252 ka_ConvertBytes(key, sizeof(key), (char *)&entryp->key,
253 sizeof(entryp->key));
254 fprintf(out, " -initial_password foo\n");
257 if (entryp->flags & KAFADMIN)
258 strcat(flags, "+ADMIN");
259 if (entryp->flags & KAFNOTGS)
260 strcat(flags, "+NOTGS");
261 if (entryp->flags & KAFNOSEAL)
262 strcat(flags, "+NOSEAL");
263 if (entryp->flags & KAFNOCPW)
264 strcat(flags, "+NOCPW");
266 fprintf(out, "setfields -name %s", principal);
267 if (strcmp(flags, "") != 0)
268 fprintf(out, " -flags %s", &flags[1]);
269 if (entryp->user_expiration != 0xffffffff) {
270 time_t tt = entryp->user_expiration;
271 strftime(Time, 50, "%m/%d/%Y %H:%M",localtime(&tt));
272 fprintf(out, " -expiration '%s'", Time);
274 fprintf(out, " -lifetime %u", entryp->max_ticket_lifetime);
275 if (entryp->misc_auth_bytes[EXPIRES])
276 fprintf(out, " -pwexpires %u", entryp->misc_auth_bytes[EXPIRES]);
277 if (entryp->pwsums[0] || entryp->pwsums[1])
278 fprintf(out, " -reuse no");
279 if (entryp->misc_auth_bytes[ATTEMPTS])
280 fprintf(out, " -attempts %u", entryp->misc_auth_bytes[ATTEMPTS]);
281 if (entryp->misc_auth_bytes[LOCKTIME])
282 fprintf(out, " -locktime %d",
283 (int)(entryp->misc_auth_bytes[LOCKTIME] * 8.5));
286 fprintf(out, "setkey -name %s -new_key %s -kvno %d\n", principal, key,
287 ntohl(entryp->key_version));
291 CheckHeader(struct kaheader *header)
293 afs_int32 i, code = 0;
295 header->version = ntohl(header->version);
296 header->headerSize = ntohl(header->headerSize);
297 header->freePtr = ntohl(header->freePtr);
298 header->eofPtr = ntohl(header->eofPtr);
299 header->kvnoPtr = ntohl(header->kvnoPtr);
300 header->stats.minor_version = ntohl(header->stats.minor_version);
301 header->stats.allocs = ntohl(header->stats.allocs);
302 header->stats.frees = ntohl(header->stats.frees);
303 header->stats.cpws = ntohl(header->stats.cpws);
304 header->admin_accounts = ntohl(header->admin_accounts);
305 header->specialKeysVersion = ntohl(header->specialKeysVersion);
306 header->hashsize = ntohl(header->hashsize);
307 for (i = 0; i < HASHSIZE; i++) {
308 header->nameHash[i] = ntohl(header->nameHash[i]);
310 header->checkVersion = ntohl(header->checkVersion);
312 if (header->version != header->checkVersion) {
314 fprintf(stderr, "HEADER VERSION MISMATCH: initial %d, final %d\n",
315 header->version, header->checkVersion);
317 if (header->headerSize != sizeof(struct kaheader)) {
320 "HEADER SIZE WRONG: file indicates %d, should be %" AFS_SIZET_FMT "\n",
321 header->headerSize, sizeof(struct kaheader));
323 if (header->hashsize != HASHSIZE) {
325 fprintf(stderr, "HASH SIZE WRONG: file indicates %d, should be %d\n",
326 header->hashsize, HASHSIZE);
328 if ((header->kvnoPtr && ((header->kvnoPtr < header->headerSize)
329 || (header->eofPtr < header->freePtr)))
330 || (header->freePtr && ((header->freePtr < header->headerSize)
331 || (header->eofPtr < header->kvnoPtr)))) {
334 "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
335 header->headerSize, header->freePtr, header->kvnoPtr,
340 * fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
341 * (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
347 NameHash(struct kaentry *entryp)
351 char *aname = entryp->userID.name;
352 char *ainstance = entryp->userID.instance;
354 /* stolen directly from the HashString function in the vol package */
356 for (i = strlen(aname), aname += i - 1; i--; aname--)
357 hash = (hash * 31) + (*((unsigned char *)aname) - 31);
358 for (i = strlen(ainstance), ainstance += i - 1; i--; ainstance--)
359 hash = (hash * 31) + (*((unsigned char *)ainstance) - 31);
360 return (hash % HASHSIZE);
364 readDB(afs_int32 offset, void *buffer, afs_int32 size)
368 offset += UBIK_HEADERSIZE;
369 code = lseek(fd, offset, SEEK_SET);
370 if (code != offset) {
371 afs_com_err(whoami, errno, "skipping Ubik header");
374 code = read(fd, buffer, size);
376 afs_com_err(whoami, errno, "reading db got %d bytes", code);
382 #include "AFS_component_version_number.c"
385 WorkerBee(struct cmd_syndesc *as, void *arock)
392 struct kaheader header;
393 int nentries, i, j, count;
395 struct kaentry entry;
397 dbFile = as->parms[0].items->data; /* -database */
398 listuheader = (as->parms[1].items ? 1 : 0); /* -uheader */
399 listkheader = (as->parms[2].items ? 1 : 0); /* -kheader */
400 listentries = (as->parms[3].items ? 1 : 0); /* -entries */
401 verbose = (as->parms[4].items ? 1 : 0); /* -verbose */
402 outFile = (as->parms[5].items ? as->parms[5].items->data : NULL); /* -rebuild */
405 out = fopen(outFile, "w");
407 afs_com_err(whoami, errno, "opening output file %s", outFile);
413 fd = open(dbFile, O_RDONLY, 0);
415 afs_com_err(whoami, errno, "opening database file %s", dbFile);
418 code = fstat(fd, &info);
420 afs_com_err(whoami, errno, "stat'ing file %s", dbFile);
423 if ((info.st_size - UBIK_HEADERSIZE) % UBIK_BUFFERSIZE)
425 "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
426 (int) info.st_size, UBIK_BUFFERSIZE, UBIK_HEADERSIZE);
430 readDB(0, &header, sizeof(header));
431 code = CheckHeader(&header);
433 PrintHeader(&header);
437 (UBIK_HEADERSIZE + header.headerSize)) / sizeof(struct kaentry);
438 entrys = (int *)malloc(nentries * sizeof(int));
439 memset(entrys, 0, 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, "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);