Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / kauth / rebuild.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 <hcrypto/des.h>
16
17 #include <ubik.h>
18 #include <afs/cmd.h>
19 #include <rx/rxkad.h>
20 #include <rx/rxkad_convert.h>
21 #include <afs/com_err.h>
22
23 #include "kauth.h"
24 #include "kautils.h"
25 #include "kaserver.h"
26
27 #define UBIK_HEADERSIZE 64
28 #define UBIK_BUFFERSIZE 1024
29
30 char *whoami = "kadb_check";
31 int fd;
32 FILE *out;
33
34 void badEntry(afs_int32, afs_int32);
35
36 int listuheader, listkheader, listentries, verbose;
37
38 int
39 readUbikHeader(void)
40 {
41 int offset, r;
42 struct ubik_hdr uheader;
43
44 offset = lseek(fd, 0, 0);
45 if (offset != 0) {
46 printf("error: lseek to 0 failed: %d %d\n", offset, errno);
47 return (-1);
48 }
49
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,
54 errno);
55 return (-1);
56 }
57
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);
62
63 if (listuheader) {
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);
69 }
70
71 if (uheader.size != UBIK_HEADERSIZE)
72 printf("Ubik header size is %u (should be %u)\n", uheader.size,
73 UBIK_HEADERSIZE);
74 if (uheader.magic != UBIK_MAGIC)
75 printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
76 UBIK_MAGIC);
77
78 return (0);
79 }
80
81 void
82 PrintHeader(struct kaheader *header)
83 {
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);
97 }
98
99 void
100 PrintEntry(afs_int32 index, struct kaentry *entry)
101 {
102 int i;
103 char Time[100];
104 struct tm *tm_p;
105 time_t tt;
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;
109
110 printf("\n");
111
112 i = (index - sizeof(struct kaheader)) / sizeof(struct kaentry);
113
114 printf("Entry %5d (%u):\n", i, index);
115
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);
120 }
121 printf("\n");
122 }
123
124 printf(" flags = ");
125 if (entry->flags & KAFNORMAL)
126 printf("NORMAL ");
127 if (entry->flags & KAFADMIN)
128 printf("ADMIN ");
129 if (entry->flags & KAFNOTGS)
130 printf("NOTGS ");
131 if (entry->flags & KAFNOSEAL)
132 printf("NOSEAL ");
133 if (entry->flags & KAFNOCPW)
134 printf("NOCPW ");
135
136 if (entry->flags & KAFNEWASSOC)
137 printf("CR-ASSOC ");
138 if (entry->flags & KAFFREE)
139 printf("FREE ");
140 if (entry->flags & KAFOLDKEYS)
141 printf("OLDKEYS ");
142 if (entry->flags & KAFSPECIAL)
143 printf("SPECIAL ");
144 if (entry->flags & KAFASSOCROOT)
145 printf("ROOT-ASSOC ");
146 if (entry->flags & KAFASSOC)
147 printf("AN-ASSOC ");
148 printf("\n");
149
150 printf(" Next = %u\n", entry->next);
151
152 if (entry->flags & KAFFREE)
153 return;
154 if (entry->flags & KAFOLDKEYS)
155 return;
156
157 tt = entry->user_expiration;
158 tm_p = localtime(&tt);
159 if (tm_p)
160 strftime(Time, 100, "%m/%d/%Y %H:%M", tm_p);
161
162 printf(" User Expiration = %s\n",
163 (entry->user_expiration == 0xffffffff) ? "never" : Time);
164
165 printf(" Password Expiration = %u days %s\n",
166 entry->misc_auth_bytes[EXPIRES],
167 (entry->misc_auth_bytes[EXPIRES] ? "" : "(never)"));
168
169 printf(" Password Attempts before lock = ");
170 if (!entry->misc_auth_bytes[ATTEMPTS])
171 printf("unlimited\n");
172 else
173 printf("%d\n", entry->misc_auth_bytes[ATTEMPTS]);
174
175 printf(" Password lockout time = ");
176 if (!entry->misc_auth_bytes[LOCKTIME])
177 printf("unlimited\n");
178 else
179 printf("%.1f min\n", (entry->misc_auth_bytes[LOCKTIME] * 8.5));
180
181 printf(" Is entry locked = %s\n",
182 (entry->misc_auth_bytes[REUSEFLAGS] ==
183 KA_ISLOCKED) ? "yes" : "no");
184
185 printf(" Permit password reuse = %s\n",
186 (!entry->pwsums[0] && !entry->pwsums[1]) ? "yes" : "no");
187
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);
196
197 printf(" Key = ");
198 ka_PrintBytes((char *)&entry->key, sizeof(entry->key));
199 printf("\n");
200
201 /* What about asServer structs and such and misc_ath_bytes */
202 }
203
204 /* ntohEntry - convert back to host-order */
205 void
206 ntohEntry(struct kaentry *entryp)
207 {
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);
218 }
219
220 char principal[64];
221 char *
222 EntryName(struct kaentry *entryp)
223 {
224 char name[32], inst[32];
225
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));
230
231 if (strlen(entryp->userID.instance)) {
232 sprintf(principal, "%s.%s", name, inst);
233 } else {
234 strcpy(principal, name);
235 }
236
237 return (principal);
238 }
239
240 void
241 RebuildEntry(struct kaentry *entryp)
242 {
243 char key[33];
244 char flags[128];
245 char Time[50];
246
247 /* Special entries are not rebuilt */
248 if (entryp->flags & KAFSPECIAL)
249 return;
250
251 fprintf(out, "create -name %s", EntryName(entryp));
252
253 ka_ConvertBytes(key, sizeof(key), (char *)&entryp->key,
254 sizeof(entryp->key));
255 fprintf(out, " -initial_password foo\n");
256
257 strcpy(flags, "");
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");
266
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);
274 }
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));
285 fprintf(out, "\n");
286
287 fprintf(out, "setkey -name %s -new_key %s -kvno %d\n", principal, key,
288 ntohl(entryp->key_version));
289 }
290
291 int
292 CheckHeader(struct kaheader *header)
293 {
294 afs_int32 i, code = 0;
295
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]);
310 }
311 header->checkVersion = ntohl(header->checkVersion);
312
313 if (header->version != header->checkVersion) {
314 code++;
315 fprintf(stderr, "HEADER VERSION MISMATCH: initial %d, final %d\n",
316 header->version, header->checkVersion);
317 }
318 if (header->headerSize != sizeof(struct kaheader)) {
319 code++;
320 fprintf(stderr,
321 "HEADER SIZE WRONG: file indicates %d, should be %" AFS_SIZET_FMT "\n",
322 header->headerSize, sizeof(struct kaheader));
323 }
324 if (header->hashsize != HASHSIZE) {
325 code++;
326 fprintf(stderr, "HASH SIZE WRONG: file indicates %d, should be %d\n",
327 header->hashsize, HASHSIZE);
328 }
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)))) {
333 code++;
334 fprintf(stderr,
335 "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
336 header->headerSize, header->freePtr, header->kvnoPtr,
337 header->eofPtr);
338 }
339
340 /*
341 * fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
342 * (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
343 */
344 return code;
345 }
346
347 afs_int32
348 NameHash(struct kaentry *entryp)
349 {
350 unsigned int hash;
351 int i;
352 char *aname = entryp->userID.name;
353 char *ainstance = entryp->userID.instance;
354
355 /* stolen directly from the HashString function in the vol package */
356 hash = 0;
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);
362 }
363
364 int
365 readDB(afs_int32 offset, void *buffer, afs_int32 size)
366 {
367 afs_int32 code;
368
369 offset += UBIK_HEADERSIZE;
370 code = lseek(fd, offset, SEEK_SET);
371 if (code != offset) {
372 afs_com_err(whoami, errno, "skipping Ubik header");
373 exit(2);
374 }
375 code = read(fd, buffer, size);
376 if (code != size) {
377 afs_com_err(whoami, errno, "reading db got %d bytes", code);
378 exit(3);
379 }
380 return 0;
381 }
382
383 #include "AFS_component_version_number.c"
384
385 static int
386 WorkerBee(struct cmd_syndesc *as, void *arock)
387 {
388 afs_int32 code;
389 char *dbFile;
390 char *outFile;
391 afs_int32 index;
392 struct stat info;
393 struct kaheader header;
394 int nentries, i, j, count;
395 int *entrys;
396 struct kaentry entry;
397
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 */
404
405 if (outFile) {
406 out = fopen(outFile, "w");
407 if (!out) {
408 afs_com_err(whoami, errno, "opening output file %s", outFile);
409 exit(7);
410 }
411 } else
412 out = 0;
413
414 fd = open(dbFile, O_RDONLY, 0);
415 if (fd < 0) {
416 afs_com_err(whoami, errno, "opening database file %s", dbFile);
417 exit(6);
418 }
419 code = fstat(fd, &info);
420 if (code) {
421 afs_com_err(whoami, errno, "stat'ing file %s", dbFile);
422 exit(6);
423 }
424 if ((info.st_size - UBIK_HEADERSIZE) % UBIK_BUFFERSIZE)
425 fprintf(stderr,
426 "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
427 (int) info.st_size, UBIK_BUFFERSIZE, UBIK_HEADERSIZE);
428
429 readUbikHeader();
430
431 readDB(0, &header, sizeof(header));
432 code = CheckHeader(&header);
433 if (listkheader)
434 PrintHeader(&header);
435
436 nentries =
437 (info.st_size -
438 (UBIK_HEADERSIZE + header.headerSize)) / sizeof(struct kaentry);
439 entrys = calloc(nentries, sizeof(int));
440
441 for (i = 0, index = sizeof(header); i < nentries;
442 i++, index += sizeof(struct kaentry)) {
443 readDB(index, &entry, sizeof(entry));
444
445 if (index >= header.eofPtr) {
446 entrys[i] |= 0x8;
447 } else if (listentries) {
448 PrintEntry(index, &entry);
449 }
450
451 if (entry.flags & KAFNORMAL) {
452 entrys[i] |= 0x1; /* user entry */
453
454 if (strlen(entry.userID.name) == 0) {
455 if (verbose)
456 printf("Entry %d has zero length name\n", i);
457 continue;
458 }
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,
462 EntryName(&entry));
463 continue;
464 }
465
466 if (out) {
467 RebuildEntry(&entry);
468 }
469
470 } else if (entry.flags & KAFFREE) {
471 entrys[i] |= 0x2; /* free entry */
472
473 } else if (entry.flags & KAFOLDKEYS) {
474 entrys[i] |= 0x4; /* old keys block */
475 /* Should check the structure of the oldkeys block? */
476
477 } else {
478 if (index < header.eofPtr) {
479 fprintf(stderr, "Entry %d is unrecognizable\n", i);
480 }
481 }
482 }
483
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));
488
489 /* check to see if the name is hashed correctly */
490 i = NameHash(&entry);
491 if (i != j) {
492 fprintf(stderr,
493 "Entry %" AFS_SIZET_FMT ", %s, found in hash chain %d (should be %d)\n",
494 ((index -
495 sizeof(struct kaheader)) / sizeof(struct kaentry)),
496 EntryName(&entry), j, i);
497 }
498
499 /* Is it on another hash chain or circular hash chain */
500 i = (index - header.headerSize) / sizeof(entry);
501 if (entrys[i] & 0x10) {
502 fprintf(stderr,
503 "Entry %d, %s, hash index %d, was found on another hash chain\n",
504 i, EntryName(&entry), j);
505 if (entry.next)
506 fprintf(stderr, "Skipping rest of hash chain %d\n", j);
507 else
508 fprintf(stderr, "No next entry in hash chain %d\n", j);
509 code++;
510 break;
511 }
512 entrys[i] |= 0x10; /* On hash chain */
513 }
514 }
515
516 /* Follow the free pointers */
517 count = 0;
518 for (index = header.freePtr; index; index = entry.next) {
519 readDB(index, &entry, sizeof(entry));
520
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,
525 EntryName(&entry));
526 fprintf(stderr, "Skipping rest of free chain\n");
527 code++;
528 break;
529 }
530 entrys[i] |= 0x20; /* On free chain */
531
532 count++;
533 }
534 if (verbose)
535 printf("Found %d free entries\n", count);
536
537 /* Follow the oldkey blocks */
538 count = 0;
539 for (index = header.kvnoPtr; index; index = entry.next) {
540 readDB(index, &entry, sizeof(entry));
541
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");
548 code++;
549 break;
550 }
551 entrys[i] |= 0x40; /* On free chain */
552
553 count++;
554 }
555 if (verbose)
556 printf("Found %d oldkey blocks\n", count);
557
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
562 * 0x8 --> Past EOF
563 */
564 for (i = 0; i < nentries; i++) {
565 j = entrys[i];
566 if (j & 0x1) { /* user entry */
567 if (!(j & 0x10))
568 badEntry(j, i); /* on hash chain? */
569 else if (j & 0xee)
570 badEntry(j, i); /* anything else? */
571 } else if (j & 0x2) { /* free entry */
572 if (!(j & 0x20))
573 badEntry(j, i); /* on free chain? */
574 else if (j & 0xdd)
575 badEntry(j, i); /* anything else? */
576 } else if (j & 0x4) { /* oldkeys entry */
577 if (!(j & 0x40))
578 badEntry(j, i); /* on oldkeys chain? */
579 else if (j & 0xbb)
580 badEntry(j, i); /* anything else? */
581 } else if (j & 0x8) { /* past eof */
582 if (j & 0xf7)
583 badEntry(j, i); /* anything else? */
584 } else
585 badEntry(j, i); /* anything else? */
586 }
587
588 exit(code != 0);
589 }
590
591 void
592 badEntry(afs_int32 e, afs_int32 i)
593 {
594 int offset;
595 struct kaentry entry;
596
597 offset = i * sizeof(struct kaentry) + sizeof(struct kaheader);
598 readDB(offset, &entry, sizeof(entry));
599
600 fprintf(stderr, "Entry %d, %s, hash index %d, is bad: [", i,
601 EntryName(&entry), NameHash(&entry));
602 if (e & 0x1)
603 fprintf(stderr, " UserEntry");
604 if (e & 0x2)
605 fprintf(stderr, " FreeEntry");
606 if (e & 0x4)
607 fprintf(stderr, " OldkeysEntry");
608 if (e & 0x8)
609 fprintf(stderr, " PastEOF");
610 if (!(e & 0xf))
611 fprintf(stderr, " <NULL>");
612 fprintf(stderr, " ] [");
613 if (e & 0x10)
614 fprintf(stderr, " UserChain");
615 if (e & 0x20)
616 fprintf(stderr, " FreeChain");
617 if (e & 0x40)
618 fprintf(stderr, " OldkeysChain");
619 if (!(e & 0xf0))
620 fprintf(stderr, " <NULL>");
621 fprintf(stderr, " ]\n");
622 }
623
624 int
625 main(int argc, char **argv)
626 {
627 struct cmd_syndesc *ts;
628
629 setlinebuf(stdout);
630
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");
640
641 return cmd_Dispatch(argc, argv);
642 }