warning-fixes-20001213
[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 ((char *)&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), (char *)&entryp->key, 
233                    sizeof(entryp->key));
234   fprintf(out, " -initial_password foo\n", key);
235
236   strcpy(flags,"");
237   if (entryp->flags & KAFADMIN)  strcat(flags, "+ADMIN");
238   if (entryp->flags & KAFNOTGS)  strcat(flags, "+NOTGS");
239   if (entryp->flags & KAFNOSEAL) strcat(flags, "+NOSEAL");
240   if (entryp->flags & KAFNOCPW)  strcat(flags, "+NOCPW");
241
242   fprintf(out, "setfields -name %s", principal);
243   if (strcmp(flags,"") != 0)
244     fprintf(out, " -flags %s", &flags[1]);
245   if (entryp->user_expiration != 0xffffffff) {
246      strftime(Time, 50, "%m/%d/%Y %H:%M",
247               localtime((time_t *)&entryp->user_expiration));
248      fprintf(out, " -expiration '%s'", Time);
249   }
250   fprintf(out, " -lifetime %u", entryp->max_ticket_lifetime);
251   if (entryp->misc_auth_bytes[EXPIRES])
252      fprintf(out, " -pwexpires %u", entryp->misc_auth_bytes[EXPIRES]);
253   if (entryp->pwsums[0] || entryp->pwsums[1])
254      fprintf(out, " -reuse no");
255   if (entryp->misc_auth_bytes[ATTEMPTS])
256     fprintf(out, " -attempts %u", entryp->misc_auth_bytes[ATTEMPTS]);
257   if (entryp->misc_auth_bytes[LOCKTIME])
258      fprintf(out, " -locktime %d", (int)(entryp->misc_auth_bytes[LOCKTIME]*8.5));
259   fprintf(out, "\n");
260
261   fprintf(out, "setkey    -name %s -new_key %s -kvno %d\n",
262            principal, key, ntohl(entryp->key_version));
263 }
264
265 CheckHeader(header)
266   struct kaheader *header;
267 {
268     afs_int32 i, code = 0;
269
270     header->version             = ntohl(header->version);
271     header->headerSize          = ntohl(header->headerSize);
272     header->freePtr             = ntohl(header->freePtr);
273     header->eofPtr              = ntohl(header->eofPtr);
274     header->kvnoPtr             = ntohl(header->kvnoPtr);
275     header->stats.minor_version = ntohl(header->stats.minor_version);
276     header->stats.allocs        = ntohl(header->stats.allocs);
277     header->stats.frees         = ntohl(header->stats.frees);
278     header->stats.cpws          = ntohl(header->stats.cpws);
279     header->admin_accounts      = ntohl(header->admin_accounts);
280     header->specialKeysVersion  = ntohl(header->specialKeysVersion);
281     header->hashsize            = ntohl(header->hashsize);
282     for (i=0; i<HASHSIZE; i++) {
283        header->nameHash[i]      = ntohl(header->nameHash[i]);
284     }
285     header->checkVersion        = ntohl(header->checkVersion);
286
287     if (header->version != header->checkVersion) {
288        code++;
289        fprintf(stderr, "HEADER VERSION MISMATCH: initial %d, final %d\n",
290                header->version, header->checkVersion);
291     }
292     if (header->headerSize != sizeof(struct kaheader)) {
293        code++;
294        fprintf(stderr, "HEADER SIZE WRONG: file indicates %d, should be %d\n",
295                header->headerSize, sizeof(struct kaheader));
296     }
297     if (header->hashsize != HASHSIZE) {
298        code++;
299        fprintf(stderr, "HASH SIZE WRONG: file indicates %d, should be %d\n",
300                header->hashsize, HASHSIZE);
301     }
302     if ((header->kvnoPtr && ((header->kvnoPtr < header->headerSize) ||
303                              (header->eofPtr  < header->freePtr)))  ||
304         (header->freePtr && ((header->freePtr < header->headerSize) ||
305                              (header->eofPtr  < header->kvnoPtr)))) {
306        code++;
307        fprintf(stderr, 
308                "DATABASE POINTERS BAD: header size = %d, freePtr = %d, kvnoPtr = %d, eofPtr = %d\n",
309                header->headerSize, header->freePtr, header->kvnoPtr,
310                header->eofPtr);
311     }
312
313 /*
314  *  fprintf(stderr, "DB Version %d, %d possible entries\n", header->version,
315  *          (header->eofPtr-header->headerSize) / sizeof(struct kaentry));
316  */
317     return code;
318 }
319
320 afs_int32 NameHash(entryp)
321   struct kaentry *entryp;
322 {   
323   unsigned int hash;
324   int          i;
325   char *aname     = entryp->userID.name;
326   char *ainstance = entryp->userID.instance;
327
328   /* stolen directly from the HashString function in the vol package */
329   hash = 0;
330   for (i=strlen(aname), aname += i-1; i--; aname--)
331      hash = (hash*31) + (*((unsigned char *)aname) - 31);
332   for (i=strlen(ainstance), ainstance += i-1; i--; ainstance--)
333      hash = (hash*31) + (*((unsigned char *)ainstance) - 31);
334   return(hash % HASHSIZE);
335 }
336
337 readDB(offset, buffer, size)
338   afs_int32 offset;
339   char  *buffer;
340   afs_int32 size;
341 {
342   afs_int32 code;
343
344   offset += UBIK_HEADERSIZE;
345   code = lseek(fd, offset, SEEK_SET);
346   if (code != offset) {
347      com_err (whoami, errno, "skipping Ubik header");
348      exit(2);
349   }
350   code = read(fd, buffer, size);
351   if (code != size) {
352      com_err (whoami, errno, "reading db got %d bytes", code);
353      exit(3);
354   }
355 }
356
357 #include "AFS_component_version_number.c"
358
359 WorkerBee (as, arock)
360   struct cmd_syndesc *as;
361   char *arock;
362 {
363     afs_int32 code;
364     int a;
365     char *dbFile;
366     char *outFile;
367     afs_int32 index;
368     struct stat info;
369     struct kaheader header;
370     int nentries, i, j, count;
371     int *entrys;
372     struct kaentry entry;
373
374     dbFile      = as->parms[0].items->data;         /* -database */
375     listuheader = (as->parms[1].items ? 1 : 0);     /* -uheader  */
376     listkheader = (as->parms[2].items ? 1 : 0);     /* -kheader  */
377     listentries = (as->parms[3].items ? 1 : 0);     /* -entries  */
378     verbose     = (as->parms[4].items ? 1 : 0);     /* -verbose  */
379     outFile     = (as->parms[5].items ? as->parms[5].items->data : 
380                                         (char *)0); /* -rebuild  */
381
382     if (outFile) {
383         out = fopen (outFile, "w");
384         if (!out) {
385             com_err (whoami, errno, "opening output file %s", outFile);
386             exit (7);
387         }
388     } else out = 0;
389
390     fd = open (dbFile, O_RDONLY, 0);
391     if (fd < 0) {
392         com_err (whoami, errno, "opening database file %s", dbFile);
393         exit (6);
394     }
395     code = fstat (fd, &info);
396     if (code) {
397         com_err (whoami, errno, "stat'ing file %s", dbFile);
398         exit (6);
399     }
400     if ((info.st_size - UBIK_HEADERSIZE) % UBIK_BUFFERSIZE)
401        fprintf(stderr, 
402                "DATABASE SIZE INCONSISTENT: was %d, should be (n*%d + %d), for integral n\n",
403                 info.st_size, UBIK_BUFFERSIZE, UBIK_HEADERSIZE);
404
405     readUbikHeader();
406
407     readDB(0, &header, sizeof(header));
408     code = CheckHeader(&header);
409     if (listkheader) PrintHeader(&header);
410
411     nentries = (info.st_size-(UBIK_HEADERSIZE + header.headerSize)) / sizeof(struct kaentry);
412     entrys = (int *)malloc(nentries * sizeof(int));
413     bzero(entrys, nentries * sizeof(int));
414
415     for (i=0, index=sizeof(header); i<nentries; i++, index+=sizeof(struct kaentry)) {
416         readDB (index, &entry, sizeof(entry));
417
418         if (index >= header.eofPtr) {
419            entrys[i] |= 0x8;
420         }
421         else if (listentries) {
422            PrintEntry(index, &entry);
423         }
424
425         if (entry.flags & KAFNORMAL) {
426            entrys[i] |= 0x1;                  /* user entry */
427
428            if (strlen(entry.userID.name) == 0) {
429               if (verbose) printf("Entry %d has zero length name\n", i);
430               continue;
431            }
432            if (!des_check_key_parity (&entry.key) || des_is_weak_key (&entry.key)) {
433               fprintf(stderr, "Entry %d, %s, has bad key\n", i, EntryName(&entry));
434               continue;
435            }
436
437            if (out) {
438               RebuildEntry(&entry);
439            }
440
441         } else if (entry.flags & KAFFREE) {
442            entrys[i] |= 0x2;                  /* free entry */
443
444         } else if (entry.flags & KAFOLDKEYS) {
445            entrys[i] |= 0x4;                  /* old keys block */
446            /* Should check the structure of the oldkeys block? */
447
448         } else {
449            if (index < header.eofPtr) {
450               fprintf(stderr, "Entry %d is unrecognizable\n", i);
451            }
452         }
453     }
454
455     /* Follow the hash chains */
456     for (j=0; j<HASHSIZE; j++) {
457        for (index = header.nameHash[j]; index; index=entry.next) {
458           readDB (index, &entry, sizeof(entry));
459
460           /* check to see if the name is hashed correctly */
461           i = NameHash(&entry);
462           if (i != j) {
463              fprintf(stderr, 
464                      "Entry %d, %s, found in hash chain %d (should be %d)\n", 
465                      ((index - sizeof(struct kaheader))/sizeof(struct kaentry)),
466                      EntryName(&entry), j, i);
467           }
468
469           /* Is it on another hash chain or circular hash chain */
470           i = (index - header.headerSize)/sizeof(entry);
471           if (entrys[i] & 0x10) {
472              fprintf(stderr, 
473                      "Entry %d, %s, hash index %d, was found on another hash chain\n", 
474                      i, EntryName(&entry), j);
475              if (entry.next)
476                 fprintf(stderr, "Skipping rest of hash chain %d\n", j);
477              else 
478                 fprintf(stderr, "No next entry in hash chain %d\n", j);
479              code++;
480              break;
481           }
482           entrys[i] |= 0x10;                      /* On hash chain */
483        }
484     }
485
486     /* Follow the free pointers */
487     count = 0;
488     for (index = header.freePtr; index; index=entry.next) {
489        readDB (index, &entry, sizeof(entry));
490
491        /* Is it on another chain or circular free chain */
492        i = (index - header.headerSize)/sizeof(entry);
493        if (entrys[i] & 0x20) {
494           fprintf(stderr, "Entry %d, %s, already found on free chain\n", 
495                   i, EntryName(&entry));
496           fprintf(stderr, "Skipping rest of free chain\n");
497           code++;
498           break;
499        }
500        entrys[i] |= 0x20;                      /* On free chain */
501        
502        count++;
503     }
504     if (verbose) printf("Found %d free entries\n", count);
505
506     /* Follow the oldkey blocks */
507     count = 0;
508     for (index = header.kvnoPtr; index; index=entry.next) {
509        readDB (index, &entry, sizeof(entry));
510
511        /* Is it on another chain or circular free chain */
512        i = (index - header.headerSize)/sizeof(entry);
513        if (entrys[i] & 0x40) {
514           fprintf(stderr, "Entry %d, %s, already found on olkeys chain\n", 
515                   i, EntryName(&entry));
516           fprintf(stderr, "Skipping rest of oldkeys chain\n");
517           code++;
518           break;
519        }
520        entrys[i] |= 0x40;                      /* On free chain */
521
522        count++;
523     }
524     if (verbose) printf("Found %d oldkey blocks\n", count);
525
526     /* Now recheck all the blocks and see if they are allocated correctly
527      * 0x1 --> User Entry           0x10 --> On hash chain
528      * 0x2 --> Free Entry           0x20 --> On Free chain
529      * 0x4 --> OldKeys Entry        0x40 --> On Oldkeys chain
530      * 0x8 --> Past EOF
531      */
532     for (i=0; i<nentries; i++) {
533        j = entrys[i];
534        if (j & 0x1) {                        /* user entry */
535           if (!(j & 0x10))   badEntry(j, i);   /* on hash chain? */
536           else if (j & 0xee) badEntry(j, i);   /* anything else? */
537        }
538        else if (j & 0x2) {                   /* free entry */
539           if (!(j & 0x20))   badEntry(j, i);   /* on free chain? */
540           else if (j & 0xdd) badEntry(j, i);   /* anything else? */
541        }
542        else if (j & 0x4) {                   /* oldkeys entry */
543           if (!(j & 0x40))   badEntry(j, i);   /* on oldkeys chain? */
544           else if (j & 0xbb) badEntry(j, i);   /* anything else? */
545        }
546        else if (j & 0x8) {                   /* past eof */
547           if (j & 0xf7) badEntry(j, i);        /* anything else? */
548        }
549        else badEntry(j, i);                  /* anything else? */
550     }
551
552     exit (code != 0);
553 }
554
555 badEntry(e, i)
556   afs_int32 e, i;
557 {
558   int offset;
559   struct kaentry entry;
560
561   offset = i * sizeof(struct kaentry) + sizeof(struct kaheader);
562   readDB (offset, &entry, sizeof(entry));
563
564   fprintf(stderr, "Entry %d, %s, hash index %d, is bad: [", 
565           i, EntryName(&entry), NameHash(&entry));
566   if ( e & 0x1) fprintf(stderr, " UserEntry");
567   if ( e & 0x2) fprintf(stderr, " FreeEntry");
568   if ( e & 0x4) fprintf(stderr, " OldkeysEntry");
569   if ( e & 0x8) fprintf(stderr, " PastEOF");
570   if (!(e & 0xf)) fprintf(stderr, " <NULL>");
571   fprintf(stderr, " ] [");
572   if ( e & 0x10) fprintf(stderr, " UserChain");
573   if ( e & 0x20) fprintf(stderr, " FreeChain");
574   if ( e & 0x40) fprintf(stderr, " OldkeysChain");
575   if (!(e & 0xf0)) fprintf(stderr, " <NULL>");
576   fprintf(stderr, " ]\n");
577 }
578
579 main (argc, argv)
580   int   argc;
581   char *argv[];
582 {
583   struct cmd_syndesc *ts;
584   struct cmd_item    *ti;
585
586   setlinebuf(stdout);
587
588   ts=cmd_CreateSyntax((char *)0, WorkerBee, (char *) 0, "KADB check");
589   cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "kadb_file");
590   cmd_AddParm(ts, "-uheader",  CMD_FLAG,   CMD_OPTIONAL, "Display UBIK header");
591   cmd_AddParm(ts, "-kheader",  CMD_FLAG,   CMD_OPTIONAL, "Display KADB header");
592   cmd_AddParm(ts, "-entries",  CMD_FLAG,   CMD_OPTIONAL, "Display entries");
593   cmd_AddParm(ts, "-verbose",  CMD_FLAG,   CMD_OPTIONAL, "verbose");
594   cmd_AddParm(ts, "-rebuild",  CMD_SINGLE, CMD_OPTIONAL, "out_file");
595
596   return cmd_Dispatch(argc, argv);
597 }