6794d9d42a58d2072d44cd4f4bb7fda7759d81b5
[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 <afs/com_err.h>
21
22 #include "kauth.h"
23 #include "kautils.h"
24 #include "kaserver.h"
25
26 #define UBIK_HEADERSIZE 64
27 #define UBIK_BUFFERSIZE 1024
28
29 char *whoami = "kadb_check";
30 int fd;
31 FILE *out;
32
33 void badEntry(afs_int32, afs_int32);
34
35 int listuheader, listkheader, listentries, verbose;
36
37 int
38 readUbikHeader(void)
39 {
40     int offset, r;
41     struct ubik_hdr uheader;
42
43     offset = lseek(fd, 0, 0);
44     if (offset != 0) {
45         printf("error: lseek to 0 failed: %d %d\n", offset, errno);
46         return (-1);
47     }
48
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,
53                errno);
54         return (-1);
55     }
56
57     uheader.magic = ntohl(uheader.magic);
58     uheader.size = ntohs(uheader.size);
59     uheader.version.epoch = ntohl(uheader.version.epoch);
60     uheader.version.counter = ntohl(uheader.version.counter);
61
62     if (listuheader) {
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);
68     }
69
70     if (uheader.size != UBIK_HEADERSIZE)
71         printf("Ubik header size is %u (should be %u)\n", uheader.size,
72                UBIK_HEADERSIZE);
73     if (uheader.magic != UBIK_MAGIC)
74         printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
75                UBIK_MAGIC);
76
77     return (0);
78 }
79
80 void
81 PrintHeader(struct kaheader *header)
82 {
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);
96 }
97
98 void
99 PrintEntry(afs_int32 index, struct kaentry *entry)
100 {
101     int i;
102     char Time[100];
103     struct tm *tm_p;
104     time_t tt;
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;
108
109     printf("\n");
110
111     i = (index - sizeof(struct kaheader)) / sizeof(struct kaentry);
112
113     printf("Entry %5d (%u):\n", i, index);
114
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);
119         }
120         printf("\n");
121     }
122
123     printf("   flags = ");
124     if (entry->flags & KAFNORMAL)
125         printf("NORMAL ");
126     if (entry->flags & KAFADMIN)
127         printf("ADMIN ");
128     if (entry->flags & KAFNOTGS)
129         printf("NOTGS ");
130     if (entry->flags & KAFNOSEAL)
131         printf("NOSEAL ");
132     if (entry->flags & KAFNOCPW)
133         printf("NOCPW ");
134
135     if (entry->flags & KAFNEWASSOC)
136         printf("CR-ASSOC ");
137     if (entry->flags & KAFFREE)
138         printf("FREE ");
139     if (entry->flags & KAFOLDKEYS)
140         printf("OLDKEYS ");
141     if (entry->flags & KAFSPECIAL)
142         printf("SPECIAL ");
143     if (entry->flags & KAFASSOCROOT)
144         printf("ROOT-ASSOC ");
145     if (entry->flags & KAFASSOC)
146         printf("AN-ASSOC ");
147     printf("\n");
148
149     printf("   Next = %u\n", entry->next);
150
151     if (entry->flags & KAFFREE)
152         return;
153     if (entry->flags & KAFOLDKEYS)
154         return;
155
156     tt = entry->user_expiration;
157     tm_p = localtime(&tt);
158     if (tm_p)
159         strftime(Time, 100, "%m/%d/%Y %H:%M", tm_p);
160
161     printf("   User Expiration = %s\n",
162            (entry->user_expiration == 0xffffffff) ? "never" : Time);
163
164     printf("   Password Expiration = %u days %s\n",
165            entry->misc_auth_bytes[EXPIRES],
166            (entry->misc_auth_bytes[EXPIRES] ? "" : "(never)"));
167
168     printf("   Password Attempts before lock = ");
169     if (!entry->misc_auth_bytes[ATTEMPTS])
170         printf("unlimited\n");
171     else
172         printf("%d\n", entry->misc_auth_bytes[ATTEMPTS]);
173
174     printf("   Password lockout time = ");
175     if (!entry->misc_auth_bytes[LOCKTIME])
176         printf("unlimited\n");
177     else
178         printf("%.1f min\n", (entry->misc_auth_bytes[LOCKTIME] * 8.5));
179
180     printf("   Is entry locked = %s\n",
181            (entry->misc_auth_bytes[REUSEFLAGS] ==
182             KA_ISLOCKED) ? "yes" : "no");
183
184     printf("   Permit password reuse = %s\n",
185            (!entry->pwsums[0] && !entry->pwsums[1]) ? "yes" : "no");
186
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);
195
196     printf("   Key = ");
197     ka_PrintBytes((char *)&entry->key, sizeof(entry->key));
198     printf("\n");
199
200     /* What about asServer structs and such and misc_ath_bytes */
201 }
202
203 /* ntohEntry - convert back to host-order */
204 void
205 ntohEntry(struct kaentry *entryp)
206 {
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);
217 }
218
219 char principal[64];
220 char *
221 EntryName(struct kaentry *entryp)
222 {
223     char name[32], inst[32];
224
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));
229
230     if (strlen(entryp->userID.instance)) {
231         sprintf(principal, "%s.%s", name, inst);
232     } else {
233         strcpy(principal, name);
234     }
235
236     return (principal);
237 }
238
239 void
240 RebuildEntry(struct kaentry *entryp)
241 {
242     char key[33];
243     char flags[128];
244     char Time[50];
245
246     /* Special entries are not rebuilt */
247     if (entryp->flags & KAFSPECIAL)
248         return;
249
250     fprintf(out, "create    -name %s", EntryName(entryp));
251
252     ka_ConvertBytes(key, sizeof(key), (char *)&entryp->key,
253                     sizeof(entryp->key));
254     fprintf(out, " -initial_password foo\n");
255
256     strcpy(flags, "");
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");
265
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);
273     }
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));
284     fprintf(out, "\n");
285
286     fprintf(out, "setkey    -name %s -new_key %s -kvno %d\n", principal, key,
287             ntohl(entryp->key_version));
288 }
289
290 int
291 CheckHeader(struct kaheader *header)
292 {
293     afs_int32 i, code = 0;
294
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]);
309     }
310     header->checkVersion = ntohl(header->checkVersion);
311
312     if (header->version != header->checkVersion) {
313         code++;
314         fprintf(stderr, "HEADER VERSION MISMATCH: initial %d, final %d\n",
315                 header->version, header->checkVersion);
316     }
317     if (header->headerSize != sizeof(struct kaheader)) {
318         code++;
319         fprintf(stderr,
320                 "HEADER SIZE WRONG: file indicates %d, should be %" AFS_SIZET_FMT "\n",
321                 header->headerSize, sizeof(struct kaheader));
322     }
323     if (header->hashsize != HASHSIZE) {
324         code++;
325         fprintf(stderr, "HASH SIZE WRONG: file indicates %d, should be %d\n",
326                 header->hashsize, HASHSIZE);
327     }
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)))) {
332         code++;
333         fprintf(stderr,
334                 "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
335                 header->headerSize, header->freePtr, header->kvnoPtr,
336                 header->eofPtr);
337     }
338
339 /*
340  *  fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
341  *          (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
342  */
343     return code;
344 }
345
346 afs_int32
347 NameHash(struct kaentry *entryp)
348 {
349     unsigned int hash;
350     int i;
351     char *aname = entryp->userID.name;
352     char *ainstance = entryp->userID.instance;
353
354     /* stolen directly from the HashString function in the vol package */
355     hash = 0;
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);
361 }
362
363 int
364 readDB(afs_int32 offset, void *buffer, afs_int32 size)
365 {
366     afs_int32 code;
367
368     offset += UBIK_HEADERSIZE;
369     code = lseek(fd, offset, SEEK_SET);
370     if (code != offset) {
371         afs_com_err(whoami, errno, "skipping Ubik header");
372         exit(2);
373     }
374     code = read(fd, buffer, size);
375     if (code != size) {
376         afs_com_err(whoami, errno, "reading db got %d bytes", code);
377         exit(3);
378     }
379     return 0;
380 }
381
382 #include "AFS_component_version_number.c"
383
384 static int
385 WorkerBee(struct cmd_syndesc *as, void *arock)
386 {
387     afs_int32 code;
388     char *dbFile;
389     char *outFile;
390     afs_int32 index;
391     struct stat info;
392     struct kaheader header;
393     int nentries, i, j, count;
394     int *entrys;
395     struct kaentry entry;
396
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  */
403
404     if (outFile) {
405         out = fopen(outFile, "w");
406         if (!out) {
407             afs_com_err(whoami, errno, "opening output file %s", outFile);
408             exit(7);
409         }
410     } else
411         out = 0;
412
413     fd = open(dbFile, O_RDONLY, 0);
414     if (fd < 0) {
415         afs_com_err(whoami, errno, "opening database file %s", dbFile);
416         exit(6);
417     }
418     code = fstat(fd, &info);
419     if (code) {
420         afs_com_err(whoami, errno, "stat'ing file %s", dbFile);
421         exit(6);
422     }
423     if ((info.st_size - UBIK_HEADERSIZE) % UBIK_BUFFERSIZE)
424         fprintf(stderr,
425                 "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
426                 (int) info.st_size, UBIK_BUFFERSIZE, UBIK_HEADERSIZE);
427
428     readUbikHeader();
429
430     readDB(0, &header, sizeof(header));
431     code = CheckHeader(&header);
432     if (listkheader)
433         PrintHeader(&header);
434
435     nentries =
436         (info.st_size -
437          (UBIK_HEADERSIZE + header.headerSize)) / sizeof(struct kaentry);
438     entrys = (int *)malloc(nentries * sizeof(int));
439     memset(entrys, 0, 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, "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 }