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