agent: Backport changes from 2.1 to support an external password manager.
[gnupg.git] / kbx / keybox-dump.c
1 /* keybox-dump.c - Debug helpers
2  *      Copyright (C) 2001, 2003 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include "keybox-defs.h"
27 #include <gcrypt.h>
28 #include "../include/host2net.h"
29
30 /* Argg, we can't include ../common/util.h */
31 char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
32
33
34 static ulong
35 get32 (const byte *buffer)
36 {
37   return buf32_to_ulong (buffer);
38 }
39
40 static ulong
41 get16 (const byte *buffer)
42 {
43   return buf16_to_ulong (buffer);
44 }
45
46 void
47 print_string (FILE *fp, const byte *p, size_t n, int delim)
48 {
49   for ( ; n; n--, p++ )
50     {
51       if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim)
52         {
53           putc('\\', fp);
54           if( *p == '\n' )
55             putc('n', fp);
56           else if( *p == '\r' )
57             putc('r', fp);
58           else if( *p == '\f' )
59             putc('f', fp);
60           else if( *p == '\v' )
61             putc('v', fp);
62           else if( *p == '\b' )
63             putc('b', fp);
64           else if( !*p )
65             putc('0', fp);
66           else
67             fprintf(fp, "x%02x", *p );
68         }
69       else
70         putc(*p, fp);
71     }
72 }
73
74
75 static int
76 dump_header_blob (const byte *buffer, size_t length, FILE *fp)
77 {
78   unsigned long n;
79
80   if (length < 32)
81     {
82       fprintf (fp, "[blob too short]\n");
83       return -1;
84     }
85   fprintf (fp, "Version: %d\n", buffer[5]);
86   if ( memcmp (buffer+8, "KBXf", 4))
87     fprintf (fp, "[Error: invalid magic number]\n");
88
89   n = get32 (buffer+16);
90   fprintf( fp, "created-at: %lu\n", n );
91   n = get32 (buffer+20);
92   fprintf( fp, "last-maint: %lu\n", n );
93
94   return 0;
95 }
96
97 \f
98 /* Dump one block to FP */
99 int
100 _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp)
101 {
102   const byte *buffer;
103   size_t length;
104   int type;
105   ulong n, nkeys, keyinfolen;
106   ulong nuids, uidinfolen;
107   ulong nsigs, siginfolen;
108   ulong rawdata_off, rawdata_len;
109   ulong nserial;
110   const byte *p;
111
112   buffer = _keybox_get_blob_image (blob, &length);
113
114   if (length < 32)
115     {
116       fprintf (fp, "[blob too short]\n");
117       return -1;
118     }
119
120   n = get32( buffer );
121   if (n > length)
122     fprintf (fp, "[blob larger than length - output truncated]\n");
123   else
124     length = n;  /* ignore the rest */
125
126   fprintf (fp, "Length: %lu\n", n );
127   type = buffer[4];
128   switch (type)
129     {
130     case BLOBTYPE_EMPTY:
131       fprintf (fp, "Type:   Empty\n");
132       return 0;
133
134     case BLOBTYPE_HEADER:
135       fprintf (fp, "Type:   Header\n");
136       return dump_header_blob (buffer, length, fp);
137     case BLOBTYPE_PGP:
138       fprintf (fp, "Type:   OpenPGP\n");
139       break;
140     case BLOBTYPE_X509:
141       fprintf (fp, "Type:   X.509\n");
142       break;
143     default:
144       fprintf (fp, "Type:   %d\n", type);
145       fprintf (fp, "[can't dump this blob type]\n");
146       return 0;
147     }
148   fprintf (fp, "Version: %d\n", buffer[5]);
149
150   if (length < 40)
151     {
152       fprintf (fp, "[blob too short]\n");
153       return -1;
154     }
155
156   n = get16 (buffer + 6);
157   fprintf( fp, "Blob-Flags: %04lX", n);
158   if (n)
159     {
160       int any = 0;
161
162       fputs (" (", fp);
163       if ((n & 1))
164         {
165           fputs ("secret", fp);
166           any++;
167         }
168       if ((n & 2))
169         {
170           if (any)
171             putc (',', fp);
172           fputs ("ephemeral", fp);
173           any++;
174         }
175       putc (')', fp);
176     }
177   putc ('\n', fp);
178
179   rawdata_off = get32 (buffer + 8);
180   rawdata_len = get32 (buffer + 12);
181
182   fprintf( fp, "Data-Offset: %lu\n", rawdata_off );
183   fprintf( fp, "Data-Length: %lu\n", rawdata_len );
184   if (rawdata_off > length || rawdata_len > length
185       || rawdata_off+rawdata_off > length)
186     fprintf (fp, "[Error: raw data larger than blob]\n");
187
188   nkeys = get16 (buffer + 16);
189   fprintf (fp, "Key-Count: %lu\n", nkeys );
190   if (!nkeys)
191     fprintf (fp, "[Error: no keys]\n");
192   if (nkeys > 1 && type == BLOBTYPE_X509)
193     fprintf (fp, "[Error: only one key allowed for X509]\n");
194
195   keyinfolen = get16 (buffer + 18 );
196   fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen);
197   /* fixme: check bounds */
198   p = buffer + 20;
199   for (n=0; n < nkeys; n++, p += keyinfolen)
200     {
201       int i;
202       ulong kidoff, kflags;
203
204       fprintf (fp, "Key-Fpr[%lu]: ", n );
205       for (i=0; i < 20; i++ )
206         fprintf (fp, "%02X", p[i]);
207       kidoff = get32 (p + 20);
208       fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff );
209       fprintf (fp, "Key-Kid[%lu]: ", n );
210       /* fixme: check bounds */
211       for (i=0; i < 8; i++ )
212         fprintf (fp, "%02X", buffer[kidoff+i] );
213       kflags = get16 (p + 24 );
214       fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags);
215     }
216
217   /* serial number */
218   fputs ("Serial-No: ", fp);
219   nserial = get16 (p);
220   p += 2;
221   if (!nserial)
222     fputs ("none", fp);
223   else
224     {
225       for (; nserial; nserial--, p++)
226         fprintf (fp, "%02X", *p);
227     }
228   putc ('\n', fp);
229
230   /* user IDs */
231   nuids = get16 (p);
232   fprintf (fp, "Uid-Count: %lu\n", nuids );
233   uidinfolen = get16  (p + 2);
234   fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen);
235   /* fixme: check bounds */
236   p += 4;
237   for (n=0; n < nuids; n++, p += uidinfolen)
238     {
239       ulong uidoff, uidlen, uflags;
240
241       uidoff = get32( p );
242       uidlen = get32( p+4 );
243       if (type == BLOBTYPE_X509 && !n)
244         {
245           fprintf (fp, "Issuer-Off: %lu\n", uidoff );
246           fprintf (fp, "Issuer-Len: %lu\n", uidlen );
247           fprintf (fp, "Issuer: \"");
248         }
249       else if (type == BLOBTYPE_X509 && n == 1)
250         {
251           fprintf (fp, "Subject-Off: %lu\n", uidoff );
252           fprintf (fp, "Subject-Len: %lu\n", uidlen );
253           fprintf (fp, "Subject: \"");
254         }
255       else
256         {
257           fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff );
258           fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen );
259           fprintf (fp, "Uid[%lu]: \"", n );
260         }
261       print_string (fp, buffer+uidoff, uidlen, '\"');
262       fputs ("\"\n", fp);
263       uflags = get16 (p + 8);
264       if (type == BLOBTYPE_X509 && !n)
265         {
266           fprintf (fp, "Issuer-Flags: %04lX\n", uflags );
267           fprintf (fp, "Issuer-Validity: %d\n", p[10] );
268         }
269       else if (type == BLOBTYPE_X509 && n == 1)
270         {
271           fprintf (fp, "Subject-Flags: %04lX\n", uflags );
272           fprintf (fp, "Subject-Validity: %d\n", p[10] );
273         }
274       else
275         {
276           fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags );
277           fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] );
278         }
279     }
280
281   nsigs = get16 (p);
282   fprintf (fp, "Sig-Count: %lu\n", nsigs );
283   siginfolen = get16 (p + 2);
284   fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen );
285   /* fixme: check bounds  */
286   p += 4;
287   for (n=0; n < nsigs; n++, p += siginfolen)
288     {
289       ulong sflags;
290
291       sflags = get32 (p);
292       fprintf (fp, "Sig-Expire[%lu]: ", n );
293       if (!sflags)
294         fputs ("[not checked]", fp);
295       else if (sflags == 1 )
296         fputs ("[missing key]", fp);
297       else if (sflags == 2 )
298         fputs ("[bad signature]", fp);
299       else if (sflags < 0x10000000)
300         fprintf (fp, "[bad flag %0lx]", sflags);
301       else if (sflags == 0xffffffff)
302         fputs ("0", fp );
303       else
304         fputs ("a time"/*strtimestamp( sflags )*/, fp );
305       putc ('\n', fp );
306     }
307
308   fprintf (fp, "Ownertrust: %d\n", p[0] );
309   fprintf (fp, "All-Validity: %d\n", p[1] );
310   p += 4;
311   n = get32 (p); p += 4;
312   fprintf (fp, "Recheck-After: %lu\n", n );
313   n = get32 (p ); p += 4;
314   fprintf( fp, "Latest-Timestamp: %lu\n", n );
315   n = get32 (p ); p += 4;
316   fprintf (fp, "Created-At: %lu\n", n );
317   n = get32 (p ); p += 4;
318   fprintf (fp, "Reserved-Space: %lu\n", n );
319
320   /* check that the keyblock is at the correct offset and other bounds */
321   /*fprintf (fp, "Blob-Checksum: [MD5-hash]\n");*/
322   return 0;
323 }
324
325
326 /* Compute the SHA_1 checksum of teh rawdata in BLOB and aput it into
327    DIGEST. */
328 static int
329 hash_blob_rawdata (KEYBOXBLOB blob, unsigned char *digest)
330 {
331   const unsigned char *buffer;
332   size_t n, length;
333   int type;
334   ulong rawdata_off, rawdata_len;
335
336   buffer = _keybox_get_blob_image (blob, &length);
337
338   if (length < 32)
339     return -1;
340   n = get32 (buffer);
341   if (n < length)
342     length = n;  /* Blob larger than length in header - ignore the rest. */
343
344   type = buffer[4];
345   switch (type)
346     {
347     case BLOBTYPE_PGP:
348     case BLOBTYPE_X509:
349       break;
350
351     case BLOBTYPE_EMPTY:
352     case BLOBTYPE_HEADER:
353     default:
354       memset (digest, 0, 20);
355       return 0;
356     }
357
358   if (length < 40)
359     return -1;
360
361   rawdata_off = get32 (buffer + 8);
362   rawdata_len = get32 (buffer + 12);
363
364   if (rawdata_off > length || rawdata_len > length
365       || rawdata_off+rawdata_off > length)
366     return -1; /* Out of bounds.  */
367
368   gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer+rawdata_off, rawdata_len);
369   return 0;
370 }
371
372
373 struct file_stats_s
374 {
375   unsigned long too_short_blobs;
376   unsigned long too_large_blobs;
377   unsigned long total_blob_count;
378   unsigned long empty_blob_count;
379   unsigned long header_blob_count;
380   unsigned long pgp_blob_count;
381   unsigned long x509_blob_count;
382   unsigned long unknown_blob_count;
383   unsigned long non_flagged;
384   unsigned long secret_flagged;
385   unsigned long ephemeral_flagged;
386 };
387
388 static int
389 update_stats (KEYBOXBLOB blob, struct file_stats_s *s)
390 {
391   const unsigned char *buffer;
392   size_t length;
393   int type;
394   unsigned long n;
395
396   buffer = _keybox_get_blob_image (blob, &length);
397   if (length < 32)
398     {
399       s->too_short_blobs++;
400       return -1;
401     }
402
403   n = get32( buffer );
404   if (n > length)
405     s->too_large_blobs++;
406   else
407     length = n;  /* ignore the rest */
408
409   s->total_blob_count++;
410   type = buffer[4];
411   switch (type)
412     {
413     case BLOBTYPE_EMPTY:
414       s->empty_blob_count++;
415       return 0;
416     case BLOBTYPE_HEADER:
417       s->header_blob_count++;
418       return 0;
419     case BLOBTYPE_PGP:
420       s->pgp_blob_count++;
421       break;
422     case BLOBTYPE_X509:
423       s->x509_blob_count++;
424       break;
425     default:
426       s->unknown_blob_count++;
427       return 0;
428     }
429
430   if (length < 40)
431     {
432       s->too_short_blobs++;
433       return -1;
434     }
435
436   n = get16 (buffer + 6);
437   if (n)
438     {
439       if ((n & 1))
440         s->secret_flagged++;
441       if ((n & 2))
442         s->ephemeral_flagged++;
443     }
444   else
445     s->non_flagged++;
446
447   return 0;
448 }
449
450
451 \f
452 static FILE *
453 open_file (const char **filename, FILE *outfp)
454 {
455   FILE *fp;
456
457   if (!*filename)
458     {
459       *filename = "-";
460       fp = stdin;
461     }
462   else
463     fp = fopen (*filename, "rb");
464   if (!fp)
465     {
466       int save_errno = errno;
467       fprintf (outfp, "can't open `%s': %s\n", *filename, strerror(errno));
468       errno = save_errno;
469     }
470   return fp;
471 }
472
473
474
475 int
476 _keybox_dump_file (const char *filename, int stats_only, FILE *outfp)
477 {
478   FILE *fp;
479   KEYBOXBLOB blob;
480   int rc;
481   unsigned long count = 0;
482   struct file_stats_s stats;
483
484   memset (&stats, 0, sizeof stats);
485
486   if (!(fp = open_file (&filename, outfp)))
487     return gpg_error_from_syserror ();
488
489   while ( !(rc = _keybox_read_blob (&blob, fp)) )
490     {
491       if (stats_only)
492         {
493           update_stats (blob, &stats);
494         }
495       else
496         {
497           fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
498           _keybox_dump_blob (blob, outfp);
499           fprintf (outfp, "END-RECORD\n");
500         }
501       _keybox_release_blob (blob);
502       count++;
503     }
504   if (rc == -1)
505     rc = 0;
506   if (rc)
507     fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc));
508
509   if (fp != stdin)
510     fclose (fp);
511
512   if (stats_only)
513     {
514       fprintf (outfp,
515                "Total number of blobs: %8lu\n"
516                "               header: %8lu\n"
517                "                empty: %8lu\n"
518                "              openpgp: %8lu\n"
519                "                 x509: %8lu\n"
520                "          non flagged: %8lu\n"
521                "       secret flagged: %8lu\n"
522                "    ephemeral flagged: %8lu\n",
523                stats.total_blob_count,
524                stats.header_blob_count,
525                stats.empty_blob_count,
526                stats.pgp_blob_count,
527                stats.x509_blob_count,
528                stats.non_flagged,
529                stats.secret_flagged,
530                stats.ephemeral_flagged);
531         if (stats.unknown_blob_count)
532           fprintf (outfp, "   unknown blob types: %8lu\n",
533                    stats.unknown_blob_count);
534         if (stats.too_short_blobs)
535           fprintf (outfp, "      too short blobs: %8lu\n",
536                    stats.too_short_blobs);
537         if (stats.too_large_blobs)
538           fprintf (outfp, "      too large blobs: %8lu\n",
539                    stats.too_large_blobs);
540     }
541
542   return rc;
543 }
544
545
546 \f
547 struct dupitem_s
548 {
549   unsigned long recno;
550   unsigned char digest[20];
551 };
552
553
554 static int
555 cmp_dupitems (const void *arg_a, const void *arg_b)
556 {
557   struct dupitem_s *a = (struct dupitem_s *)arg_a;
558   struct dupitem_s *b = (struct dupitem_s *)arg_b;
559
560   return memcmp (a->digest, b->digest, 20);
561 }
562
563
564 int
565 _keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp)
566 {
567   FILE *fp;
568   KEYBOXBLOB blob;
569   int rc;
570   unsigned long recno = 0;
571   unsigned char zerodigest[20];
572   struct dupitem_s *dupitems;
573   size_t dupitems_size, dupitems_count, lastn, n;
574   char fprbuf[3*20+1];
575
576   (void)print_them;
577
578   memset (zerodigest, 0, sizeof zerodigest);
579
580   if (!(fp = open_file (&filename, outfp)))
581     return gpg_error_from_syserror ();
582
583   dupitems_size = 1000;
584   dupitems = malloc (dupitems_size * sizeof *dupitems);
585   if (!dupitems)
586     {
587       gpg_error_t tmperr = gpg_error_from_syserror ();
588       fprintf (outfp, "error allocating array for `%s': %s\n",
589                filename, strerror(errno));
590       return tmperr;
591     }
592   dupitems_count = 0;
593
594   while ( !(rc = _keybox_read_blob (&blob, fp)) )
595     {
596       unsigned char digest[20];
597
598       if (hash_blob_rawdata (blob, digest))
599         fprintf (outfp, "error in blob %ld of `%s'\n", recno, filename);
600       else if (memcmp (digest, zerodigest, 20))
601         {
602           if (dupitems_count >= dupitems_size)
603             {
604               struct dupitem_s *tmp;
605
606               dupitems_size += 1000;
607               tmp = realloc (dupitems, dupitems_size * sizeof *dupitems);
608               if (!tmp)
609                 {
610                   gpg_error_t tmperr = gpg_error_from_syserror ();
611                   fprintf (outfp, "error reallocating array for `%s': %s\n",
612                            filename, strerror(errno));
613                   free (dupitems);
614                   return tmperr;
615                 }
616               dupitems = tmp;
617             }
618           dupitems[dupitems_count].recno = recno;
619           memcpy (dupitems[dupitems_count].digest, digest, 20);
620           dupitems_count++;
621         }
622       _keybox_release_blob (blob);
623       recno++;
624     }
625   if (rc == -1)
626     rc = 0;
627   if (rc)
628     fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc));
629   if (fp != stdin)
630     fclose (fp);
631
632   qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems);
633
634   for (lastn=0, n=1; n < dupitems_count; lastn=n, n++)
635     {
636       if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20))
637         {
638           bin2hexcolon (dupitems[lastn].digest, 20, fprbuf);
639           fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno);
640           do
641             fprintf (outfp, " %lu", dupitems[n].recno);
642           while (++n < dupitems_count
643                  && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20));
644           putc ('\n', outfp);
645           n--;
646         }
647     }
648
649   free (dupitems);
650
651   return rc;
652 }
653
654
655 /* Print records with record numbers FROM to TO to OUTFP.  */
656 int
657 _keybox_dump_cut_records (const char *filename, unsigned long from,
658                           unsigned long to, FILE *outfp)
659 {
660   FILE *fp;
661   KEYBOXBLOB blob;
662   int rc;
663   unsigned long recno = 0;
664
665   if (!(fp = open_file (&filename, stderr)))
666     return gpg_error_from_syserror ();
667
668   while ( !(rc = _keybox_read_blob (&blob, fp)) )
669     {
670       if (recno > to)
671         break; /* Ready.  */
672       if (recno >= from)
673         {
674           if ((rc = _keybox_write_blob (blob, outfp)))
675             {
676               fprintf (stderr, "error writing output: %s\n",
677                        gpg_strerror (rc));
678               goto leave;
679             }
680         }
681       _keybox_release_blob (blob);
682       recno++;
683     }
684   if (rc == -1)
685     rc = 0;
686   if (rc)
687     fprintf (stderr, "error reading `%s': %s\n", filename, gpg_strerror (rc));
688  leave:
689   if (fp != stdin)
690     fclose (fp);
691   return rc;
692 }