Changed to GPLv3.
[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
28 static ulong
29 get32 (const byte *buffer)
30 {
31   ulong a;
32   a =  *buffer << 24;
33   a |= buffer[1] << 16;
34   a |= buffer[2] << 8;
35   a |= buffer[3];
36   return a;
37 }
38
39 static ulong
40 get16 (const byte *buffer)
41 {
42   ulong a;
43   a =  *buffer << 8;
44   a |= buffer[1];
45   return a;
46 }
47
48 void
49 print_string (FILE *fp, const byte *p, size_t n, int delim)
50 {
51   for ( ; n; n--, p++ )
52     {
53       if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim)
54         {
55           putc('\\', fp);
56           if( *p == '\n' )
57             putc('n', fp);
58           else if( *p == '\r' )
59             putc('r', fp);
60           else if( *p == '\f' )
61             putc('f', fp);
62           else if( *p == '\v' )
63             putc('v', fp);
64           else if( *p == '\b' )
65             putc('b', fp);
66           else if( !*p )
67             putc('0', fp);
68           else
69             fprintf(fp, "x%02x", *p );
70         }
71       else
72         putc(*p, fp);
73     }
74 }
75
76
77 static int
78 dump_header_blob (const byte *buffer, size_t length, FILE *fp)
79 {
80   unsigned long n;
81
82   if (length < 32)
83     {
84       fprintf (fp, "[blob too short]\n");
85       return -1;
86     }
87   fprintf (fp, "Version: %d\n", buffer[5]);
88   if ( memcmp (buffer+8, "KBXf", 4))
89     fprintf (fp, "[Error: invalid magic number]\n");
90
91   n = get32 (buffer+16); 
92   fprintf( fp, "created-at: %lu\n", n );
93   n = get32 (buffer+20); 
94   fprintf( fp, "last-maint: %lu\n", n );
95
96   return 0;
97 }
98
99 \f
100 /* Dump one block to FP */
101 int
102 _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp)
103 {
104   const byte *buffer;
105   size_t length;
106   int type;
107   ulong n, nkeys, keyinfolen;
108   ulong nuids, uidinfolen;
109   ulong nsigs, siginfolen;
110   ulong rawdata_off, rawdata_len;
111   ulong nserial;
112   const byte *p;
113
114   buffer = _keybox_get_blob_image (blob, &length);
115   
116   if (length < 32)
117     {
118       fprintf (fp, "[blob too short]\n");
119       return -1;
120     }
121
122   n = get32( buffer );
123   if (n > length) 
124     fprintf (fp, "[blob larger than length - output truncated]\n");
125   else
126     length = n;  /* ignore the rest */
127
128   fprintf (fp, "Length: %lu\n", n );
129   type = buffer[4];
130   switch (type)
131     {
132     case BLOBTYPE_EMPTY:
133       fprintf (fp, "Type:   Empty\n");
134       return 0;
135
136     case BLOBTYPE_HEADER:
137       fprintf (fp, "Type:   Header\n");
138       return dump_header_blob (buffer, length, fp);
139     case BLOBTYPE_PGP:
140       fprintf (fp, "Type:   OpenPGP\n");
141       break;
142     case BLOBTYPE_X509:
143       fprintf (fp, "Type:   X.509\n");
144       break;
145     default:
146       fprintf (fp, "Type:   %d\n", type);
147       fprintf (fp, "[can't dump this blob type]\n");
148       return 0;
149     }
150   fprintf (fp, "Version: %d\n", buffer[5]);
151
152   if (length < 40)
153     {
154       fprintf (fp, "[blob too short]\n");
155       return -1;
156     }
157   
158   n = get16 (buffer + 6);
159   fprintf( fp, "Blob-Flags: %04lX", n);
160   if (n)
161     {
162       int any = 0;
163
164       fputs (" (", fp);
165       if ((n & 1))
166         {
167           fputs ("secret", fp);
168           any++;
169         }
170       if ((n & 2))
171         {
172           if (any)
173             putc (',', fp);
174           fputs ("ephemeral", fp);
175           any++;
176         }
177       putc (')', fp);
178     }
179   putc ('\n', fp);
180
181   rawdata_off = get32 (buffer + 8);
182   rawdata_len = get32 (buffer + 12);
183
184   fprintf( fp, "Data-Offset: %lu\n", rawdata_off );
185   fprintf( fp, "Data-Length: %lu\n", rawdata_len );
186
187   nkeys = get16 (buffer + 16);
188   fprintf (fp, "Key-Count: %lu\n", nkeys );
189   if (!nkeys)
190     fprintf (fp, "[Error: no keys]\n");
191   if (nkeys > 1 && type == BLOBTYPE_X509)
192     fprintf (fp, "[Error: only one key allowed for X509]\n");
193
194   keyinfolen = get16 (buffer + 18 );
195   fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen);
196   /* fixme: check bounds */
197   p = buffer + 20;
198   for (n=0; n < nkeys; n++, p += keyinfolen)
199     {
200       int i;
201       ulong kidoff, kflags;
202     
203       fprintf (fp, "Key-Fpr[%lu]: ", n );
204       for (i=0; i < 20; i++ )
205         fprintf (fp, "%02X", p[i]);
206       kidoff = get32 (p + 20);
207       fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff );
208       fprintf (fp, "Key-Kid[%lu]: ", n );
209       /* fixme: check bounds */
210       for (i=0; i < 8; i++ )
211         fprintf (fp, "%02X", buffer[kidoff+i] );
212       kflags = get16 (p + 24 );
213       fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags);
214     }
215   
216   /* serial number */
217   fputs ("Serial-No: ", fp);
218   nserial = get16 (p);
219   p += 2;
220   if (!nserial)
221     fputs ("none", fp);
222   else
223     {
224       for (; nserial; nserial--, p++)
225         fprintf (fp, "%02X", *p);
226     }
227   putc ('\n', fp);
228
229   /* user IDs */
230   nuids = get16 (p);
231   fprintf (fp, "Uid-Count: %lu\n", nuids );
232   uidinfolen = get16  (p + 2);
233   fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen);
234   /* fixme: check bounds */
235   p += 4;
236   for (n=0; n < nuids; n++, p += uidinfolen)
237     {
238       ulong uidoff, uidlen, uflags;
239       
240       uidoff = get32( p );
241       uidlen = get32( p+4 );
242       if (type == BLOBTYPE_X509 && !n)
243         {
244           fprintf (fp, "Issuer-Off: %lu\n", uidoff );
245           fprintf (fp, "Issuer-Len: %lu\n", uidlen );
246           fprintf (fp, "Issuer: \"");
247         }
248       else if (type == BLOBTYPE_X509 && n == 1)
249         {
250           fprintf (fp, "Subject-Off: %lu\n", uidoff );
251           fprintf (fp, "Subject-Len: %lu\n", uidlen );
252           fprintf (fp, "Subject: \"");
253         }
254       else
255         {
256           fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff );
257           fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen );
258           fprintf (fp, "Uid[%lu]: \"", n );
259         }
260       print_string (fp, buffer+uidoff, uidlen, '\"');
261       fputs ("\"\n", fp);
262       uflags = get16 (p + 8);
263       if (type == BLOBTYPE_X509 && !n)
264         {
265           fprintf (fp, "Issuer-Flags: %04lX\n", uflags );
266           fprintf (fp, "Issuer-Validity: %d\n", p[10] );
267         }
268       else if (type == BLOBTYPE_X509 && n == 1)
269         {
270           fprintf (fp, "Subject-Flags: %04lX\n", uflags );
271           fprintf (fp, "Subject-Validity: %d\n", p[10] );
272         }
273       else
274         {
275           fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags );
276           fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] );
277         }
278     }
279   
280   nsigs = get16 (p);
281   fprintf (fp, "Sig-Count: %lu\n", nsigs );
282   siginfolen = get16 (p + 2);
283   fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen );
284   /* fixme: check bounds  */
285   p += 4;
286   for (n=0; n < nsigs; n++, p += siginfolen)
287     {
288       ulong sflags;
289     
290       sflags = get32 (p);
291       fprintf (fp, "Sig-Expire[%lu]: ", n );
292       if (!sflags)
293         fputs ("[not checked]", fp);
294       else if (sflags == 1 )
295         fputs ("[missing key]", fp);
296       else if (sflags == 2 )
297         fputs ("[bad signature]", fp);
298       else if (sflags < 0x10000000)
299         fprintf (fp, "[bad flag %0lx]", sflags);
300       else if (sflags == 0xffffffff)
301         fputs ("0", fp );
302       else
303         fputs ("a time"/*strtimestamp( sflags )*/, fp );
304       putc ('\n', fp );
305     }
306
307   fprintf (fp, "Ownertrust: %d\n", p[0] );
308   fprintf (fp, "All-Validity: %d\n", p[1] );
309   p += 4;
310   n = get32 (p); p += 4;
311   fprintf (fp, "Recheck-After: %lu\n", n );
312   n = get32 (p ); p += 4;
313   fprintf( fp, "Latest-Timestamp: %lu\n", n );
314   n = get32 (p ); p += 4;
315   fprintf (fp, "Created-At: %lu\n", n );
316   n = get32 (p ); p += 4;
317   fprintf (fp, "Reserved-Space: %lu\n", n );
318
319   /* check that the keyblock is at the correct offset and other bounds */
320   /*fprintf (fp, "Blob-Checksum: [MD5-hash]\n");*/
321   return 0;
322 }
323
324
325 struct file_stats_s
326 {
327   unsigned long too_short_blobs;
328   unsigned long too_large_blobs;
329   unsigned long total_blob_count;
330   unsigned long empty_blob_count;
331   unsigned long header_blob_count;
332   unsigned long pgp_blob_count;
333   unsigned long x509_blob_count;
334   unsigned long unknown_blob_count;
335   unsigned long non_flagged;
336   unsigned long secret_flagged;
337   unsigned long ephemeral_flagged;
338 };
339
340 static int
341 update_stats (KEYBOXBLOB blob, struct file_stats_s *s)
342 {
343   const unsigned char *buffer;
344   size_t length;
345   int type;
346   unsigned long n;
347
348   buffer = _keybox_get_blob_image (blob, &length);
349   if (length < 32)
350     {
351       s->too_short_blobs++;
352       return -1;
353     }
354
355   n = get32( buffer );
356   if (n > length) 
357     s->too_large_blobs++;
358   else
359     length = n;  /* ignore the rest */
360
361   s->total_blob_count++;
362   type = buffer[4];
363   switch (type)
364     {
365     case BLOBTYPE_EMPTY:
366       s->empty_blob_count++;
367       return 0;
368     case BLOBTYPE_HEADER:
369       s->header_blob_count++;
370       return 0;
371     case BLOBTYPE_PGP:
372       s->pgp_blob_count++;
373       break;
374     case BLOBTYPE_X509:
375       s->x509_blob_count++;
376       break;
377     default:
378       s->unknown_blob_count++;
379       return 0;
380     }
381
382   if (length < 40)
383     {
384       s->too_short_blobs++;
385       return -1;
386     }
387   
388   n = get16 (buffer + 6);
389   if (n)
390     {
391       if ((n & 1))
392         s->secret_flagged++;
393       if ((n & 2))
394         s->ephemeral_flagged++;
395     }
396   else
397     s->non_flagged++;
398
399   return 0;
400 }
401
402
403 \f
404 int
405 _keybox_dump_file (const char *filename, int stats_only, FILE *outfp)
406 {
407   FILE *fp;
408   KEYBOXBLOB blob;
409   int rc;
410   unsigned long count = 0;
411   struct file_stats_s stats;
412
413   memset (&stats, 0, sizeof stats);
414
415   if (!filename)
416     {
417       filename = "-";
418       fp = stdin;
419     }
420   else
421     fp = fopen (filename, "rb");
422   if (!fp)
423     {
424       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
425       fprintf (outfp, "can't open `%s': %s\n", filename, strerror(errno));
426       return tmperr;
427     }
428
429   while ( !(rc = _keybox_read_blob (&blob, fp)) )
430     {
431       if (stats_only)
432         {
433           update_stats (blob, &stats);
434         }
435       else
436         {
437           fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
438           _keybox_dump_blob (blob, outfp);
439           fprintf (outfp, "END-RECORD\n");
440         }
441       _keybox_release_blob (blob);
442       count++;
443     }
444   if (rc == -1)
445     rc = 0;
446   if (rc)
447     fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc));
448   
449   if (fp != stdin)
450     fclose (fp);
451
452   if (stats_only)
453     {
454       fprintf (outfp, 
455                "Total number of blobs: %8lu\n"
456                "               header: %8lu\n"
457                "                empty: %8lu\n"
458                "              openpgp: %8lu\n"
459                "                 x509: %8lu\n"
460                "          non flagged: %8lu\n"
461                "       secret flagged: %8lu\n"
462                "    ephemeral flagged: %8lu\n",
463                stats.total_blob_count,
464                stats.header_blob_count,
465                stats.empty_blob_count,
466                stats.pgp_blob_count,
467                stats.x509_blob_count,
468                stats.non_flagged,
469                stats.secret_flagged,
470                stats.ephemeral_flagged);
471         if (stats.unknown_blob_count)
472           fprintf (outfp, "   unknown blob types: %8lu\n",
473                    stats.unknown_blob_count);
474         if (stats.too_short_blobs)
475           fprintf (outfp, "      too short blobs: %8lu\n",
476                    stats.too_short_blobs);
477         if (stats.too_large_blobs)
478           fprintf (outfp, "      too large blobs: %8lu\n",
479                    stats.too_large_blobs);
480     }
481
482   return rc;
483 }