rxkad: Move conversion functions to own file
[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 = (int *)malloc(nentries * sizeof(int));
440     memset(entrys, 0, nentries * sizeof(int));
441
442     for (i = 0, index = sizeof(header); i < nentries;
443          i++, index += sizeof(struct kaentry)) {
444         readDB(index, &entry, sizeof(entry));
445
446         if (index >= header.eofPtr) {
447             entrys[i] |= 0x8;
448         } else if (listentries) {
449             PrintEntry(index, &entry);
450         }
451
452         if (entry.flags & KAFNORMAL) {
453             entrys[i] |= 0x1;   /* user entry */
454
455             if (strlen(entry.userID.name) == 0) {
456                 if (verbose)
457                     printf("Entry %d has zero length name\n", i);
458                 continue;
459             }
460             if (!DES_check_key_parity(ktc_to_cblock(&entry.key))
461                 || DES_is_weak_key(ktc_to_cblock(&entry.key))) {
462                 fprintf(stderr, "Entry %d, %s, has bad key\n", i,
463                         EntryName(&entry));
464                 continue;
465             }
466
467             if (out) {
468                 RebuildEntry(&entry);
469             }
470
471         } else if (entry.flags & KAFFREE) {
472             entrys[i] |= 0x2;   /* free entry */
473
474         } else if (entry.flags & KAFOLDKEYS) {
475             entrys[i] |= 0x4;   /* old keys block */
476             /* Should check the structure of the oldkeys block? */
477
478         } else {
479             if (index < header.eofPtr) {
480                 fprintf(stderr, "Entry %d is unrecognizable\n", i);
481             }
482         }
483     }
484
485     /* Follow the hash chains */
486     for (j = 0; j < HASHSIZE; j++) {
487         for (index = header.nameHash[j]; index; index = entry.next) {
488             readDB(index, &entry, sizeof(entry));
489
490             /* check to see if the name is hashed correctly */
491             i = NameHash(&entry);
492             if (i != j) {
493                 fprintf(stderr,
494                         "Entry %" AFS_SIZET_FMT ", %s, found in hash chain %d (should be %d)\n",
495                         ((index -
496                           sizeof(struct kaheader)) / sizeof(struct kaentry)),
497                         EntryName(&entry), j, i);
498             }
499
500             /* Is it on another hash chain or circular hash chain */
501             i = (index - header.headerSize) / sizeof(entry);
502             if (entrys[i] & 0x10) {
503                 fprintf(stderr,
504                         "Entry %d, %s, hash index %d, was found on another hash chain\n",
505                         i, EntryName(&entry), j);
506                 if (entry.next)
507                     fprintf(stderr, "Skipping rest of hash chain %d\n", j);
508                 else
509                     fprintf(stderr, "No next entry in hash chain %d\n", j);
510                 code++;
511                 break;
512             }
513             entrys[i] |= 0x10;  /* On hash chain */
514         }
515     }
516
517     /* Follow the free pointers */
518     count = 0;
519     for (index = header.freePtr; index; index = entry.next) {
520         readDB(index, &entry, sizeof(entry));
521
522         /* Is it on another chain or circular free chain */
523         i = (index - header.headerSize) / sizeof(entry);
524         if (entrys[i] & 0x20) {
525             fprintf(stderr, "Entry %d, %s, already found on free chain\n", i,
526                     EntryName(&entry));
527             fprintf(stderr, "Skipping rest of free chain\n");
528             code++;
529             break;
530         }
531         entrys[i] |= 0x20;      /* On free chain */
532
533         count++;
534     }
535     if (verbose)
536         printf("Found %d free entries\n", count);
537
538     /* Follow the oldkey blocks */
539     count = 0;
540     for (index = header.kvnoPtr; index; index = entry.next) {
541         readDB(index, &entry, sizeof(entry));
542
543         /* Is it on another chain or circular free chain */
544         i = (index - header.headerSize) / sizeof(entry);
545         if (entrys[i] & 0x40) {
546             fprintf(stderr, "Entry %d, %s, already found on olkeys chain\n",
547                     i, EntryName(&entry));
548             fprintf(stderr, "Skipping rest of oldkeys chain\n");
549             code++;
550             break;
551         }
552         entrys[i] |= 0x40;      /* On free chain */
553
554         count++;
555     }
556     if (verbose)
557         printf("Found %d oldkey blocks\n", count);
558
559     /* Now recheck all the blocks and see if they are allocated correctly
560      * 0x1 --> User Entry           0x10 --> On hash chain
561      * 0x2 --> Free Entry           0x20 --> On Free chain
562      * 0x4 --> OldKeys Entry        0x40 --> On Oldkeys chain
563      * 0x8 --> Past EOF
564      */
565     for (i = 0; i < nentries; i++) {
566         j = entrys[i];
567         if (j & 0x1) {          /* user entry */
568             if (!(j & 0x10))
569                 badEntry(j, i); /* on hash chain? */
570             else if (j & 0xee)
571                 badEntry(j, i); /* anything else? */
572         } else if (j & 0x2) {   /* free entry */
573             if (!(j & 0x20))
574                 badEntry(j, i); /* on free chain? */
575             else if (j & 0xdd)
576                 badEntry(j, i); /* anything else? */
577         } else if (j & 0x4) {   /* oldkeys entry */
578             if (!(j & 0x40))
579                 badEntry(j, i); /* on oldkeys chain? */
580             else if (j & 0xbb)
581                 badEntry(j, i); /* anything else? */
582         } else if (j & 0x8) {   /* past eof */
583             if (j & 0xf7)
584                 badEntry(j, i); /* anything else? */
585         } else
586             badEntry(j, i);     /* anything else? */
587     }
588
589     exit(code != 0);
590 }
591
592 void
593 badEntry(afs_int32 e, afs_int32 i)
594 {
595     int offset;
596     struct kaentry entry;
597
598     offset = i * sizeof(struct kaentry) + sizeof(struct kaheader);
599     readDB(offset, &entry, sizeof(entry));
600
601     fprintf(stderr, "Entry %d, %s, hash index %d, is bad: [", i,
602             EntryName(&entry), NameHash(&entry));
603     if (e & 0x1)
604         fprintf(stderr, " UserEntry");
605     if (e & 0x2)
606         fprintf(stderr, " FreeEntry");
607     if (e & 0x4)
608         fprintf(stderr, " OldkeysEntry");
609     if (e & 0x8)
610         fprintf(stderr, " PastEOF");
611     if (!(e & 0xf))
612         fprintf(stderr, " <NULL>");
613     fprintf(stderr, " ] [");
614     if (e & 0x10)
615         fprintf(stderr, " UserChain");
616     if (e & 0x20)
617         fprintf(stderr, " FreeChain");
618     if (e & 0x40)
619         fprintf(stderr, " OldkeysChain");
620     if (!(e & 0xf0))
621         fprintf(stderr, " <NULL>");
622     fprintf(stderr, " ]\n");
623 }
624
625 int
626 main(int argc, char **argv)
627 {
628     struct cmd_syndesc *ts;
629
630     setlinebuf(stdout);
631
632     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "KADB check");
633     cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "kadb_file");
634     cmd_AddParm(ts, "-uheader", CMD_FLAG, CMD_OPTIONAL,
635                 "Display UBIK header");
636     cmd_AddParm(ts, "-kheader", CMD_FLAG, CMD_OPTIONAL,
637                 "Display KADB header");
638     cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
639     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
640     cmd_AddParm(ts, "-rebuild", CMD_SINGLE, CMD_OPTIONAL, "out_file");
641
642     return cmd_Dispatch(argc, argv);
643 }