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