Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / kauth / rebuild.c
1
2 /* Copyright (C) 1990 Transarc Corporation - All rights reserved */
3
4 #include <afs/param.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #ifdef AFS_NT40_ENV
8 #include <winsock2.h>
9 #include <fcntl.h>
10 #include <io.h>
11 #else
12 #include <sys/file.h>
13 #include <netinet/in.h>
14 #endif
15 #include <stdio.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <ubik.h>
19 #include <afs/cmd.h>
20
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 int listuheader, listkheader, listentries, verbose;
35
36 int readUbikHeader()
37 {
38   int offset, r;
39   struct ubik_hdr uheader;
40
41   offset = lseek(fd, 0, 0);
42   if (offset != 0) {
43      printf("error: lseek to 0 failed: %d %d\n", offset, errno);
44      return(-1);
45   }
46
47   /* now read the info */
48   r = read(fd, &uheader, sizeof(uheader));
49   if (r != sizeof(uheader)) {
50      printf("error: read of %d bytes failed: %d %d\n", sizeof(uheader), r, errno);
51      return(-1);
52   }
53
54   uheader.magic = ntohl(uheader.magic);
55   uheader.size  = ntohl(uheader.size);
56   uheader.version.epoch   = ntohl(uheader.version.epoch);
57   uheader.version.counter = ntohl(uheader.version.counter);
58
59   if (listuheader) {
60      printf("Ubik Header\n");
61      printf("   Magic           = 0x%x\n", uheader.magic);
62      printf("   Size            = %u\n",   uheader.size);
63      printf("   Version.epoch   = %u\n",   uheader.version.epoch);
64      printf("   Version.counter = %u\n",   uheader.version.counter);
65   }
66
67   if (uheader.size != UBIK_HEADERSIZE)
68      printf("Ubik header size is %u (should be %u)\n", uheader.size, UBIK_HEADERSIZE);
69   if (uheader.magic != UBIK_MAGIC)
70      printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic, UBIK_MAGIC);
71
72   return(0);
73 }
74
75 PrintHeader(header)
76   struct kaheader *header;
77 {
78   printf("Version          = %d\n", header->version);
79   printf("HeaderSize       = %d\n", header->headerSize);
80   printf("Free Ptr         = %u\n", header->freePtr);
81   printf("EOF  Ptr         = %u\n", header->eofPtr);
82   printf("Kvno Ptr         = %u\n", header->kvnoPtr);
83   printf("SpecialKeysVersion changed = %d\n", header->specialKeysVersion);
84   printf("# admin accounts = %d\n", header->admin_accounts);
85   printf("HashSize         = %d\n", header->hashsize);
86   printf("Check Version    = %d\n", header->checkVersion);
87   printf("stats.minorVersion     = %d\n", header->stats.minor_version);
88   printf("stats.AllocBlock calls = %d\n", header->stats.allocs);
89   printf("stats.FreeBlock  calls = %d\n", header->stats.frees);
90   printf("stats.cpw commands     = %d\n", header->stats.cpws);
91 }
92
93 PrintEntry(index, entry)
94   afs_int32 index;
95   struct kaentry *entry;
96 {
97   int i;
98   char Time[100];
99   struct tm *tm_p;
100
101   printf("\n");
102
103   i = (index - sizeof(struct kaheader))/sizeof(struct kaentry);
104
105   printf("Entry %5d (%u):\n", i, index);
106
107   if (entry->flags & KAFNORMAL) {
108      printf ("   Name = %s", entry->userID.name);
109      if (strlen(entry->userID.instance) > 0) {
110         printf (".%s", entry->userID.instance);
111      }
112      printf("\n");
113   }
114
115   printf("   flags = ");
116   if (entry->flags & KAFNORMAL)      printf("NORMAL ");
117   if (entry->flags & KAFADMIN)         printf("ADMIN ");
118   if (entry->flags & KAFNOTGS)         printf("NOTGS ");
119   if (entry->flags & KAFNOSEAL)        printf("NOSEAL ");
120   if (entry->flags & KAFNOCPW)         printf("NOCPW ");
121
122   if (entry->flags & KAFNEWASSOC)      printf("CR-ASSOC ");
123   if (entry->flags & KAFFREE)        printf("FREE ");
124   if (entry->flags & KAFOLDKEYS)     printf("OLDKEYS ");
125   if (entry->flags & KAFSPECIAL)       printf("SPECIAL ");
126   if (entry->flags & KAFASSOCROOT)     printf("ROOT-ASSOC ");
127   if (entry->flags & KAFASSOC)         printf("AN-ASSOC ");
128   printf("\n");
129
130   printf("   Next = %u\n", entry->next);
131
132   if (entry->flags & KAFFREE)
133      return;
134   if (entry->flags & KAFOLDKEYS)
135      return;
136
137   tm_p =  localtime((time_t *)&entry->user_expiration);
138   if (tm_p)
139     strftime(Time, 100, "%m/%d/%Y %H:%M", tm_p);
140
141   printf("   User Expiration = %s\n",
142          (entry->user_expiration == 0xffffffff) ? "never" : Time);
143
144   printf("   Password Expiration = %u days %s\n", 
145          entry->misc_auth_bytes[EXPIRES],
146          (entry->misc_auth_bytes[EXPIRES]?"":"(never)"));
147
148   printf("   Password Attempts before lock = ");
149   if (!entry->misc_auth_bytes[ATTEMPTS]) printf("unlimited\n");
150   else printf("%d\n", entry->misc_auth_bytes[ATTEMPTS]);
151
152   printf("   Password lockout time = ");
153   if (!entry->misc_auth_bytes[LOCKTIME]) printf("unlimited\n");
154   else printf("%.1f min\n", (entry->misc_auth_bytes[LOCKTIME] * 8.5));
155
156   printf("   Is entry locked = %s\n", 
157          (entry->misc_auth_bytes[REUSEFLAGS] == KA_ISLOCKED) ? "yes" : "no");
158
159   printf("   Permit password reuse = %s\n", 
160          (!entry->pwsums[0] && !entry->pwsums[1]) ? "yes" : "no");
161   
162   printf("   Mod Time = %u: %s", 
163          entry->modification_time, ctime((time_t *)&entry->modification_time));
164   printf("   Mod ID = %u\n", entry->modification_id);
165   printf("   Change Password Time = %u: %s", 
166          entry->change_password_time,
167          ctime((time_t *)&entry->change_password_time));
168   printf("   Ticket lifetime = %u: %s",
169          entry->max_ticket_lifetime, ctime((time_t *)&entry->max_ticket_lifetime));
170   printf("   Key Version = %d\n", entry->key_version);
171
172   printf("   Key = ");
173   ka_PrintBytes (&entry->key, sizeof(entry->key));
174   printf("\n");
175
176   /* What about asServer structs and such and misc_ath_bytes */
177 }
178
179 /* ntohEntry - convert back to host-order */
180 ntohEntry(struct kaentry *entryp)
181 {
182   entryp->flags = ntohl(entryp->flags);
183   entryp->next = ntohl(entryp->next);
184   entryp->user_expiration = ntohl(entryp->user_expiration);
185   entryp->modification_time = ntohl(entryp->modification_time);
186   entryp->modification_id = ntohl(entryp->modification_id);
187   entryp->change_password_time = ntohl(entryp->change_password_time);
188   entryp->max_ticket_lifetime = ntohl(entryp->max_ticket_lifetime);
189   entryp->key_version = ntohl(entryp->key_version);
190   entryp->misc.asServer.nOldKeys = ntohl(entryp->misc.asServer.nOldKeys);
191   entryp->misc.asServer.oldKeys = ntohl(entryp->misc.asServer.oldKeys);
192 }
193
194 char principal[64];
195 char *EntryName(entryp)
196   struct kaentry *entryp;
197 {
198   char name[32], inst[32];
199
200   ka_ConvertBytes(name, sizeof(name), 
201                   entryp->userID.name, strlen(entryp->userID.name));
202   ka_ConvertBytes(inst, sizeof(inst), 
203                   entryp->userID.instance, strlen(entryp->userID.instance));
204
205   if (strlen(entryp->userID.instance)) {
206      sprintf (principal, "%s.%s", name, inst);
207   } else {
208      strcpy (principal, name);
209   }
210
211   return(principal);
212 }
213
214 RebuildEntry(entryp)
215   struct kaentry *entryp;
216 {
217   char key[33];
218   char flags[128];
219   char Time[50];
220
221   /* Special entries are not rebuilt */
222   if (entryp->flags & KAFSPECIAL) return;
223
224   fprintf(out, "create    -name %s", EntryName(entryp));
225
226   ka_ConvertBytes (key, sizeof(key), &entryp->key, sizeof(entryp->key));
227   fprintf(out, " -initial_password foo\n", key);
228
229   strcpy(flags,"");
230   if (entryp->flags & KAFADMIN)  strcat(flags, "+ADMIN");
231   if (entryp->flags & KAFNOTGS)  strcat(flags, "+NOTGS");
232   if (entryp->flags & KAFNOSEAL) strcat(flags, "+NOSEAL");
233   if (entryp->flags & KAFNOCPW)  strcat(flags, "+NOCPW");
234
235   fprintf(out, "setfields -name %s", principal);
236   if (strcmp(flags,"") != 0)
237     fprintf(out, " -flags %s", &flags[1]);
238   if (entryp->user_expiration != 0xffffffff) {
239      strftime(Time, 50, "%m/%d/%Y %H:%M",
240               localtime((time_t *)&entryp->user_expiration));
241      fprintf(out, " -expiration '%s'", Time);
242   }
243   fprintf(out, " -lifetime %u", entryp->max_ticket_lifetime);
244   if (entryp->misc_auth_bytes[EXPIRES])
245      fprintf(out, " -pwexpires %u", entryp->misc_auth_bytes[EXPIRES]);
246   if (entryp->pwsums[0] || entryp->pwsums[1])
247      fprintf(out, " -reuse no");
248   if (entryp->misc_auth_bytes[ATTEMPTS])
249     fprintf(out, " -attempts %u", entryp->misc_auth_bytes[ATTEMPTS]);
250   if (entryp->misc_auth_bytes[LOCKTIME])
251      fprintf(out, " -locktime %d", (int)(entryp->misc_auth_bytes[LOCKTIME]*8.5));
252   fprintf(out, "\n");
253
254   fprintf(out, "setkey    -name %s -new_key %s -kvno %d\n",
255            principal, key, ntohl(entryp->key_version));
256 }
257
258 CheckHeader(header)
259   struct kaheader *header;
260 {
261     afs_int32 i, code = 0;
262
263     header->version             = ntohl(header->version);
264     header->headerSize          = ntohl(header->headerSize);
265     header->freePtr             = ntohl(header->freePtr);
266     header->eofPtr              = ntohl(header->eofPtr);
267     header->kvnoPtr             = ntohl(header->kvnoPtr);
268     header->stats.minor_version = ntohl(header->stats.minor_version);
269     header->stats.allocs        = ntohl(header->stats.allocs);
270     header->stats.frees         = ntohl(header->stats.frees);
271     header->stats.cpws          = ntohl(header->stats.cpws);
272     header->admin_accounts      = ntohl(header->admin_accounts);
273     header->specialKeysVersion  = ntohl(header->specialKeysVersion);
274     header->hashsize            = ntohl(header->hashsize);
275     for (i=0; i<HASHSIZE; i++) {
276        header->nameHash[i]      = ntohl(header->nameHash[i]);
277     }
278     header->checkVersion        = ntohl(header->checkVersion);
279
280     if (header->version != header->checkVersion) {
281        code++;
282        fprintf(stderr, "HEADER VERSION MISMATCH: initial %d, final %d\n",
283                header->version, header->checkVersion);
284     }
285     if (header->headerSize != sizeof(struct kaheader)) {
286        code++;
287        fprintf(stderr, "HEADER SIZE WRONG: file indicates %d, should be %d\n",
288                header->headerSize, sizeof(struct kaheader));
289     }
290     if (header->hashsize != HASHSIZE) {
291        code++;
292        fprintf(stderr, "HASH SIZE WRONG: file indicates %d, should be %d\n",
293                header->hashsize, HASHSIZE);
294     }
295     if ((header->kvnoPtr && ((header->kvnoPtr < header->headerSize) ||
296                              (header->eofPtr  < header->freePtr)))  ||
297         (header->freePtr && ((header->freePtr < header->headerSize) ||
298                              (header->eofPtr  < header->kvnoPtr)))) {
299        code++;
300        fprintf(stderr, 
301                "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
302                header->headerSize, header->freePtr, header->kvnoPtr,
303                header->eofPtr);
304     }
305
306 /*
307  *  fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
308  *          (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
309  */
310     return code;
311 }
312
313 afs_int32 NameHash(entryp)
314   struct kaentry *entryp;
315 {   
316   unsigned int hash;
317   int          i;
318   char *aname     = entryp->userID.name;
319   char *ainstance = entryp->userID.instance;
320
321   /* stolen directly from the HashString function in the vol package */
322   hash = 0;
323   for (i=strlen(aname), aname += i-1; i--; aname--)
324      hash = (hash*31) + (*((unsigned char *)aname) - 31);
325   for (i=strlen(ainstance), ainstance += i-1; i--; ainstance--)
326      hash = (hash*31) + (*((unsigned char *)ainstance) - 31);
327   return(hash % HASHSIZE);
328 }
329
330 readDB(offset, buffer, size)
331   afs_int32 offset;
332   char  *buffer;
333   afs_int32 size;
334 {
335   afs_int32 code;
336
337   offset += UBIK_HEADERSIZE;
338   code = lseek(fd, offset, SEEK_SET);
339   if (code != offset) {
340      com_err (whoami, errno, "skipping Ubik header");
341      exit(2);
342   }
343   code = read(fd, buffer, size);
344   if (code != size) {
345      com_err (whoami, errno, "reading db got %d bytes", code);
346      exit(3);
347   }
348 }
349
350 #include "AFS_component_version_number.c"
351
352 WorkerBee (as, arock)
353   struct cmd_syndesc *as;
354   char *arock;
355 {
356     afs_int32 code;
357     int a;
358     char *dbFile;
359     char *outFile;
360     afs_int32 index;
361     struct stat info;
362     struct kaheader header;
363     int nentries, i, j, count;
364     int *entrys;
365     struct kaentry entry;
366
367     dbFile      = as->parms[0].items->data;         /* -database */
368     listuheader = (as->parms[1].items ? 1 : 0);     /* -uheader  */
369     listkheader = (as->parms[2].items ? 1 : 0);     /* -kheader  */
370     listentries = (as->parms[3].items ? 1 : 0);     /* -entries  */
371     verbose     = (as->parms[4].items ? 1 : 0);     /* -verbose  */
372     outFile     = (as->parms[5].items ? as->parms[5].items->data : 
373                                         (char *)0); /* -rebuild  */
374
375     if (outFile) {
376         out = fopen (outFile, "w");
377         if (!out) {
378             com_err (whoami, errno, "opening output file %s", outFile);
379             exit (7);
380         }
381     } else out = 0;
382
383     fd = open (dbFile, O_RDONLY, 0);
384     if (fd < 0) {
385         com_err (whoami, errno, "opening database file %s", dbFile);
386         exit (6);
387     }
388     code = fstat (fd, &info);
389     if (code) {
390         com_err (whoami, errno, "stat'ing file %s", dbFile);
391         exit (6);
392     }
393     if ((info.st_size - UBIK_HEADERSIZE) % UBIK_BUFFERSIZE)
394        fprintf(stderr, 
395                "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
396                 info.st_size, UBIK_BUFFERSIZE, UBIK_HEADERSIZE);
397
398     readUbikHeader();
399
400     readDB(0, &header, sizeof(header));
401     code = CheckHeader(&header);
402     if (listkheader) PrintHeader(&header);
403
404     nentries = (info.st_size-(UBIK_HEADERSIZE + header.headerSize)) / sizeof(struct kaentry);
405     entrys = (int *)malloc(nentries * sizeof(int));
406     bzero(entrys, nentries * sizeof(int));
407
408     for (i=0, index=sizeof(header); i<nentries; i++, index+=sizeof(struct kaentry)) {
409         readDB (index, &entry, sizeof(entry));
410
411         if (index >= header.eofPtr) {
412            entrys[i] |= 0x8;
413         }
414         else if (listentries) {
415            PrintEntry(index, &entry);
416         }
417
418         if (entry.flags & KAFNORMAL) {
419            entrys[i] |= 0x1;                  /* user entry */
420
421            if (strlen(entry.userID.name) == 0) {
422               if (verbose) printf("Entry %d has zero length name\n", i);
423               continue;
424            }
425            if (!des_check_key_parity (&entry.key) || des_is_weak_key (&entry.key)) {
426               fprintf(stderr, "Entry %d, %s, has bad key\n", i, EntryName(&entry));
427               continue;
428            }
429
430            if (out) {
431               RebuildEntry(&entry);
432            }
433
434         } else if (entry.flags & KAFFREE) {
435            entrys[i] |= 0x2;                  /* free entry */
436
437         } else if (entry.flags & KAFOLDKEYS) {
438            entrys[i] |= 0x4;                  /* old keys block */
439            /* Should check the structure of the oldkeys block? */
440
441         } else {
442            if (index < header.eofPtr) {
443               fprintf(stderr, "Entry %d is unrecognizable\n", i);
444            }
445         }
446     }
447
448     /* Follow the hash chains */
449     for (j=0; j<HASHSIZE; j++) {
450        for (index = header.nameHash[j]; index; index=entry.next) {
451           readDB (index, &entry, sizeof(entry));
452
453           /* check to see if the name is hashed correctly */
454           i = NameHash(&entry);
455           if (i != j) {
456              fprintf(stderr, 
457                      "Entry %d, %s, found in hash chain %d (should be %d)\n", 
458                      ((index - sizeof(struct kaheader))/sizeof(struct kaentry)),
459                      EntryName(&entry), j, i);
460           }
461
462           /* Is it on another hash chain or circular hash chain */
463           i = (index - header.headerSize)/sizeof(entry);
464           if (entrys[i] & 0x10) {
465              fprintf(stderr, 
466                      "Entry %d, %s, hash index %d, was found on another hash chain\n", 
467                      i, EntryName(&entry), j);
468              if (entry.next)
469                 fprintf(stderr, "Skipping rest of hash chain %d\n", j);
470              else 
471                 fprintf(stderr, "No next entry in hash chain %d\n", j);
472              code++;
473              break;
474           }
475           entrys[i] |= 0x10;                      /* On hash chain */
476        }
477     }
478
479     /* Follow the free pointers */
480     count = 0;
481     for (index = header.freePtr; index; index=entry.next) {
482        readDB (index, &entry, sizeof(entry));
483
484        /* Is it on another chain or circular free chain */
485        i = (index - header.headerSize)/sizeof(entry);
486        if (entrys[i] & 0x20) {
487           fprintf(stderr, "Entry %d, %s, already found on free chain\n", 
488                   i, EntryName(&entry));
489           fprintf(stderr, "Skipping rest of free chain\n");
490           code++;
491           break;
492        }
493        entrys[i] |= 0x20;                      /* On free chain */
494        
495        count++;
496     }
497     if (verbose) printf("Found %d free entries\n", count);
498
499     /* Follow the oldkey blocks */
500     count = 0;
501     for (index = header.kvnoPtr; index; index=entry.next) {
502        readDB (index, &entry, sizeof(entry));
503
504        /* Is it on another chain or circular free chain */
505        i = (index - header.headerSize)/sizeof(entry);
506        if (entrys[i] & 0x40) {
507           fprintf(stderr, "Entry %d, %s, already found on olkeys chain\n", 
508                   i, EntryName(&entry));
509           fprintf(stderr, "Skipping rest of oldkeys chain\n");
510           code++;
511           break;
512        }
513        entrys[i] |= 0x40;                      /* On free chain */
514
515        count++;
516     }
517     if (verbose) printf("Found %d oldkey blocks\n", count);
518
519     /* Now recheck all the blocks and see if they are allocated correctly
520      * 0x1 --> User Entry           0x10 --> On hash chain
521      * 0x2 --> Free Entry           0x20 --> On Free chain
522      * 0x4 --> OldKeys Entry        0x40 --> On Oldkeys chain
523      * 0x8 --> Past EOF
524      */
525     for (i=0; i<nentries; i++) {
526        j = entrys[i];
527        if (j & 0x1) {                        /* user entry */
528           if (!(j & 0x10))   badEntry(j, i);   /* on hash chain? */
529           else if (j & 0xee) badEntry(j, i);   /* anything else? */
530        }
531        else if (j & 0x2) {                   /* free entry */
532           if (!(j & 0x20))   badEntry(j, i);   /* on free chain? */
533           else if (j & 0xdd) badEntry(j, i);   /* anything else? */
534        }
535        else if (j & 0x4) {                   /* oldkeys entry */
536           if (!(j & 0x40))   badEntry(j, i);   /* on oldkeys chain? */
537           else if (j & 0xbb) badEntry(j, i);   /* anything else? */
538        }
539        else if (j & 0x8) {                   /* past eof */
540           if (j & 0xf7) badEntry(j, i);        /* anything else? */
541        }
542        else badEntry(j, i);                  /* anything else? */
543     }
544
545     exit (code != 0);
546 }
547
548 badEntry(e, i)
549   afs_int32 e, i;
550 {
551   int offset;
552   struct kaentry entry;
553
554   offset = i * sizeof(struct kaentry) + sizeof(struct kaheader);
555   readDB (offset, &entry, sizeof(entry));
556
557   fprintf(stderr, "Entry %d, %s, hash index %d, is bad: [", 
558           i, EntryName(&entry), NameHash(&entry));
559   if ( e & 0x1) fprintf(stderr, " UserEntry");
560   if ( e & 0x2) fprintf(stderr, " FreeEntry");
561   if ( e & 0x4) fprintf(stderr, " OldkeysEntry");
562   if ( e & 0x8) fprintf(stderr, " PastEOF");
563   if (!(e & 0xf)) fprintf(stderr, " <NULL>");
564   fprintf(stderr, " ] [");
565   if ( e & 0x10) fprintf(stderr, " UserChain");
566   if ( e & 0x20) fprintf(stderr, " FreeChain");
567   if ( e & 0x40) fprintf(stderr, " OldkeysChain");
568   if (!(e & 0xf0)) fprintf(stderr, " <NULL>");
569   fprintf(stderr, " ]\n");
570 }
571
572 main (argc, argv)
573   int   argc;
574   char *argv[];
575 {
576   struct cmd_syndesc *ts;
577   struct cmd_item    *ti;
578
579   setlinebuf(stdout);
580
581   ts=cmd_CreateSyntax((char *)0, WorkerBee, (char *) 0, "KADB check");
582   cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "kadb_file");
583   cmd_AddParm(ts, "-uheader",  CMD_FLAG,   CMD_OPTIONAL, "Display UBIK header");
584   cmd_AddParm(ts, "-kheader",  CMD_FLAG,   CMD_OPTIONAL, "Display KADB header");
585   cmd_AddParm(ts, "-entries",  CMD_FLAG,   CMD_OPTIONAL, "Display entries");
586   cmd_AddParm(ts, "-verbose",  CMD_FLAG,   CMD_OPTIONAL, "verbose");
587   cmd_AddParm(ts, "-rebuild",  CMD_SINGLE, CMD_OPTIONAL, "out_file");
588
589   return cmd_Dispatch(argc, argv);
590 }