gpg: More fix of get_best_pubkey_byname.
[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 <https://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 "../common/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 #define get32(a) buf32_to_ulong ((a))
34 #define get16(a) buf16_to_ulong ((a))
35
36
37 void
38 print_string (FILE *fp, const byte *p, size_t n, int delim)
39 {
40   for ( ; n; n--, p++ )
41     {
42       if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim)
43         {
44           putc('\\', fp);
45           if( *p == '\n' )
46             putc('n', fp);
47           else if( *p == '\r' )
48             putc('r', fp);
49           else if( *p == '\f' )
50             putc('f', fp);
51           else if( *p == '\v' )
52             putc('v', fp);
53           else if( *p == '\b' )
54             putc('b', fp);
55           else if( !*p )
56             putc('0', fp);
57           else
58             fprintf(fp, "x%02x", *p );
59         }
60       else
61         putc(*p, fp);
62     }
63 }
64
65
66 static void
67 print_ubib (const byte *buffer, size_t length, FILE *fp)
68 {
69   const byte *p;
70   int i;
71   size_t image_off, image_len;
72   unsigned char digest[20];
73
74   fprintf (fp, "UBIB: ");
75   if (length < 40)
76     {
77       fputs ("[blob too short for a stored UBIB]\n", fp);
78       return;
79     }
80
81   p = buffer + length - 40;
82   for (i=0; i < 20; p++, i++)
83     fprintf (fp, "%02X", *p);
84
85   image_off = get32 (buffer+8);
86   image_len = get32 (buffer+12);
87   if ((uint64_t)image_off+(uint64_t)image_len > (uint64_t)length)
88     {
89       fputs (" [image claims to be longer than the blob]\n", fp);
90       return;
91     }
92
93   gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer+image_off,image_len);
94   if (memcmp (digest, buffer + length - 40, 20))
95     fputs (" [does not match the image]\n", fp);
96   else
97     fputc ('\n', fp);
98 }
99
100
101 static int
102 print_checksum (const byte *buffer, size_t length, size_t unhashed, FILE *fp)
103 {
104   const byte *p;
105   int i;
106   int hashlen;
107   unsigned char digest[20];
108
109   fprintf (fp, "Checksum: ");
110   if (unhashed && unhashed < 20)
111     {
112       fputs ("[specified unhashed sized too short]\n", fp);
113       return 0;
114     }
115   if (!unhashed)
116     {
117       unhashed = 16;
118       hashlen = 16;
119     }
120   else
121     hashlen = 20;
122   if (length < 5+unhashed)
123     {
124       fputs ("[blob too short for a checksum]\n", fp);
125       return 0;
126     }
127
128   p = buffer + length - hashlen;
129   for (i=0; i < hashlen; p++, i++)
130     fprintf (fp, "%02x", *p);
131
132   if (hashlen == 16) /* Compatibility method.  */
133     {
134       gcry_md_hash_buffer (GCRY_MD_MD5, digest, buffer, length - 16);
135       if (!memcmp (buffer + length - 16, digest, 16))
136         fputs (" [valid]\n", fp);
137       else
138         fputs (" [bad]\n", fp);
139     }
140   else
141     {
142       gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer, length - unhashed);
143       if (!memcmp (buffer + length - hashlen, digest, hashlen))
144         fputs (" [valid]\n", fp);
145       else
146         fputs (" [bad]\n", fp);
147     }
148   return 0;
149 }
150
151
152 static int
153 dump_header_blob (const byte *buffer, size_t length, FILE *fp)
154 {
155   unsigned long n;
156
157   if (length < 32)
158     {
159       fprintf (fp, "[blob too short]\n");
160       return -1;
161     }
162   fprintf (fp, "Version: %d\n", buffer[5]);
163
164   n = get16 (buffer + 6);
165   fprintf( fp, "Flags:   %04lX", n);
166   if (n)
167     {
168       int any = 0;
169
170       fputs (" (", fp);
171       if ((n & 2))
172         {
173           if (any)
174             putc (',', fp);
175           fputs ("openpgp", fp);
176           any++;
177         }
178       putc (')', fp);
179     }
180   putc ('\n', fp);
181
182   if ( memcmp (buffer+8, "KBXf", 4))
183     fprintf (fp, "[Error: invalid magic number]\n");
184
185   n = get32 (buffer+16);
186   fprintf( fp, "created-at: %lu\n", n );
187   n = get32 (buffer+20);
188   fprintf( fp, "last-maint: %lu\n", n );
189
190   return 0;
191 }
192
193 \f
194 /* Dump one block to FP */
195 int
196 _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp)
197 {
198   const byte *buffer;
199   size_t length;
200   int type, i;
201   ulong n, nkeys, keyinfolen;
202   ulong nuids, uidinfolen;
203   ulong nsigs, siginfolen;
204   ulong rawdata_off, rawdata_len;
205   ulong nserial;
206   ulong unhashed;
207   const byte *p;
208   int is_fpr32;  /* blob ersion 2 */
209   int have_ubib = 0;
210
211   buffer = _keybox_get_blob_image (blob, &length);
212
213   if (length < 32)
214     {
215       fprintf (fp, "[blob too short]\n");
216       return -1;
217     }
218
219   n = get32( buffer );
220   if (n > length)
221     fprintf (fp, "[blob larger than length - output truncated]\n");
222   else
223     length = n;  /* ignore the rest */
224
225   fprintf (fp, "Length: %lu\n", n );
226   type = buffer[4];
227   switch (type)
228     {
229     case KEYBOX_BLOBTYPE_EMPTY:
230       fprintf (fp, "Type:   Empty\n");
231       return 0;
232
233     case KEYBOX_BLOBTYPE_HEADER:
234       fprintf (fp, "Type:   Header\n");
235       return dump_header_blob (buffer, length, fp);
236     case KEYBOX_BLOBTYPE_PGP:
237       fprintf (fp, "Type:   OpenPGP\n");
238       break;
239     case KEYBOX_BLOBTYPE_X509:
240       fprintf (fp, "Type:   X.509\n");
241       break;
242     default:
243       fprintf (fp, "Type:   %d\n", type);
244       fprintf (fp, "[can't dump this blob type]\n");
245       return 0;
246     }
247   /* Here we have either BLOGTYPE_X509 or BLOBTYPE_OPENPGP */
248   fprintf (fp, "Version: %d\n", buffer[5]);
249   is_fpr32 = buffer[5] == 2;
250
251   if (length < 40)
252     {
253       fprintf (fp, "[blob too short]\n");
254       return -1;
255     }
256
257   n = get16 (buffer + 6);
258   fprintf( fp, "Blob-Flags: %04lX", n);
259   if (n)
260     {
261       int any = 0;
262
263       fputs (" (", fp);
264       if ((n & 1))
265         {
266           fputs ("secret", fp);
267           any++;
268         }
269       if ((n & 2))
270         {
271           if (any)
272             putc (',', fp);
273           fputs ("ephemeral", fp);
274           any++;
275         }
276       if ((n & 4))
277         {
278           if (any)
279             putc (',', fp);
280           fputs ("ubid", fp);
281           any++;
282           have_ubib = 1;
283         }
284       putc (')', fp);
285     }
286   putc ('\n', fp);
287
288   rawdata_off = get32 (buffer + 8);
289   rawdata_len = get32 (buffer + 12);
290
291   fprintf( fp, "Data-Offset: %lu\n", rawdata_off );
292   fprintf( fp, "Data-Length: %lu\n", rawdata_len );
293   if (rawdata_off > length || rawdata_len > length
294       || rawdata_off+rawdata_len > length
295       || rawdata_len + 4 > length
296       || rawdata_off+rawdata_len + 4 > length)
297     fprintf (fp, "[Error: raw data larger than blob]\n");
298   unhashed = length - rawdata_off - rawdata_len;
299   fprintf (fp, "Unhashed: %lu\n", unhashed);
300
301   nkeys = get16 (buffer + 16);
302   fprintf (fp, "Key-Count: %lu\n", nkeys );
303   if (!nkeys)
304     fprintf (fp, "[Error: no keys]\n");
305   if (nkeys > 1 && type == KEYBOX_BLOBTYPE_X509)
306     fprintf (fp, "[Error: only one key allowed for X509]\n");
307
308   keyinfolen = get16 (buffer + 18 );
309   fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen);
310   /* fixme: check bounds */
311   p = buffer + 20;
312   for (n=0; n < nkeys; n++, p += keyinfolen)
313     {
314       ulong kidoff, kflags;
315
316       fprintf (fp, "Key-Fpr[%lu]: ", n );
317       if (is_fpr32)
318         {
319           kflags = get16 (p + 32 );
320           for (i=0; i < ((kflags & 0x80)?32:20); i++ )
321             fprintf (fp, "%02X", p[i]);
322         }
323       else
324         {
325           for (i=0; i < 20; i++ )
326             fprintf (fp, "%02X", p[i]);
327           kidoff = get32 (p + 20);
328           fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff );
329           fprintf (fp, "Key-Kid[%lu]: ", n );
330           /* fixme: check bounds */
331           for (i=0; i < 8; i++ )
332             fprintf (fp, "%02X", buffer[kidoff+i] );
333           kflags = get16 (p + 24 );
334         }
335       fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags);
336     }
337
338   /* serial number */
339   fputs ("Serial-No: ", fp);
340   nserial = get16 (p);
341   p += 2;
342   if (!nserial)
343     fputs ("none", fp);
344   else
345     {
346       for (; nserial; nserial--, p++)
347         fprintf (fp, "%02X", *p);
348     }
349   putc ('\n', fp);
350
351   /* user IDs */
352   nuids = get16 (p);
353   fprintf (fp, "Uid-Count: %lu\n", nuids );
354   uidinfolen = get16  (p + 2);
355   fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen);
356   /* fixme: check bounds */
357   p += 4;
358   for (n=0; n < nuids; n++, p += uidinfolen)
359     {
360       ulong uidoff, uidlen, uflags;
361
362       uidoff = get32( p );
363       uidlen = get32( p+4 );
364       if (type == KEYBOX_BLOBTYPE_X509 && !n)
365         {
366           fprintf (fp, "Issuer-Off: %lu\n", uidoff );
367           fprintf (fp, "Issuer-Len: %lu\n", uidlen );
368           fprintf (fp, "Issuer: \"");
369         }
370       else if (type == KEYBOX_BLOBTYPE_X509 && n == 1)
371         {
372           fprintf (fp, "Subject-Off: %lu\n", uidoff );
373           fprintf (fp, "Subject-Len: %lu\n", uidlen );
374           fprintf (fp, "Subject: \"");
375         }
376       else
377         {
378           fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff );
379           fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen );
380           fprintf (fp, "Uid[%lu]: \"", n );
381         }
382       print_string (fp, buffer+uidoff, uidlen, '\"');
383       fputs ("\"\n", fp);
384       uflags = get16 (p + 8);
385       if (type == KEYBOX_BLOBTYPE_X509 && !n)
386         {
387           fprintf (fp, "Issuer-Flags: %04lX\n", uflags );
388           fprintf (fp, "Issuer-Validity: %d\n", p[10] );
389         }
390       else if (type == KEYBOX_BLOBTYPE_X509 && n == 1)
391         {
392           fprintf (fp, "Subject-Flags: %04lX\n", uflags );
393           fprintf (fp, "Subject-Validity: %d\n", p[10] );
394         }
395       else
396         {
397           fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags );
398           fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] );
399         }
400     }
401
402   nsigs = get16 (p);
403   fprintf (fp, "Sig-Count: %lu\n", nsigs );
404   siginfolen = get16 (p + 2);
405   fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen );
406   /* fixme: check bounds  */
407   p += 4;
408   {
409     int in_range = 0;
410     ulong first = 0;
411
412     for (n=0; n < nsigs; n++, p += siginfolen)
413       {
414         ulong sflags;
415
416         sflags = get32 (p);
417         if (!in_range && !sflags)
418           {
419             in_range = 1;
420             first = n;
421             continue;
422           }
423         if (in_range && !sflags)
424           continue;
425         if (in_range)
426           {
427             fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1);
428             in_range = 0;
429           }
430
431         fprintf (fp, "Sig-Expire[%lu]: ", n );
432         if (!sflags)
433           fputs ("[not checked]", fp);
434         else if (sflags == 1 )
435           fputs ("[missing key]", fp);
436         else if (sflags == 2 )
437           fputs ("[bad signature]", fp);
438         else if (sflags < 0x10000000)
439           fprintf (fp, "[bad flag %0lx]", sflags);
440         else if (sflags == (ulong)(-1))
441           fputs ("[good - does not expire]", fp );
442         else
443           fprintf (fp, "[good - expires at %lu]", sflags);
444         putc ('\n', fp );
445       }
446     if (in_range)
447       fprintf (fp, "Sig-Expire[%lu-%lu]: [not checked]\n", first, n-1);
448   }
449   fprintf (fp, "Ownertrust: %d\n", p[0] );
450   fprintf (fp, "All-Validity: %d\n", p[1] );
451   p += 4;
452   n = get32 (p);
453   p += 4;
454   fprintf (fp, "Recheck-After: %lu\n", n );
455   n = get32 (p );
456   p += 4;
457   fprintf( fp, "Latest-Timestamp: %lu\n", n );
458   n = get32 (p );
459   p += 4;
460   fprintf (fp, "Created-At: %lu\n", n );
461   n = get32 (p );
462   fprintf (fp, "Reserved-Space: %lu\n", n );
463
464   if (n >= 4 && unhashed >= 24)
465     {
466       n = get32 ( buffer + length - unhashed);
467       fprintf (fp, "Storage-Flags: %08lx\n", n );
468     }
469   if (have_ubib)
470     print_ubib (buffer, length, fp);
471   print_checksum (buffer, length, unhashed, fp);
472   return 0;
473 }
474
475
476 /* Compute the SHA-1 checksum of the rawdata in BLOB and put it into
477    DIGEST. */
478 static int
479 hash_blob_rawdata (KEYBOXBLOB blob, unsigned char *digest)
480 {
481   const unsigned char *buffer;
482   size_t n, length;
483   int type;
484   ulong rawdata_off, rawdata_len;
485
486   buffer = _keybox_get_blob_image (blob, &length);
487
488   if (length < 32)
489     return -1;
490   n = get32 (buffer);
491   if (n < length)
492     length = n;  /* Blob larger than length in header - ignore the rest. */
493
494   type = buffer[4];
495   switch (type)
496     {
497     case KEYBOX_BLOBTYPE_PGP:
498     case KEYBOX_BLOBTYPE_X509:
499       break;
500
501     case KEYBOX_BLOBTYPE_EMPTY:
502     case KEYBOX_BLOBTYPE_HEADER:
503     default:
504       memset (digest, 0, 20);
505       return 0;
506     }
507
508   if (length < 40)
509     return -1;
510
511   rawdata_off = get32 (buffer + 8);
512   rawdata_len = get32 (buffer + 12);
513
514   if (rawdata_off > length || rawdata_len > length
515       || rawdata_off+rawdata_off > length)
516     return -1; /* Out of bounds.  */
517
518   gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer+rawdata_off, rawdata_len);
519   return 0;
520 }
521
522
523 struct file_stats_s
524 {
525   unsigned long too_short_blobs;
526   unsigned long too_large_blobs;
527   unsigned long total_blob_count;
528   unsigned long empty_blob_count;
529   unsigned long header_blob_count;
530   unsigned long pgp_blob_count;
531   unsigned long x509_blob_count;
532   unsigned long unknown_blob_count;
533   unsigned long non_flagged;
534   unsigned long secret_flagged;
535   unsigned long ephemeral_flagged;
536   unsigned long skipped_long_blobs;
537 };
538
539 static int
540 update_stats (KEYBOXBLOB blob, struct file_stats_s *s)
541 {
542   const unsigned char *buffer;
543   size_t length;
544   int type;
545   unsigned long n;
546
547   buffer = _keybox_get_blob_image (blob, &length);
548   if (length < 32)
549     {
550       s->too_short_blobs++;
551       return -1;
552     }
553
554   n = get32( buffer );
555   if (n > length)
556     s->too_large_blobs++;
557   else
558     length = n;  /* ignore the rest */
559
560   s->total_blob_count++;
561   type = buffer[4];
562   switch (type)
563     {
564     case KEYBOX_BLOBTYPE_EMPTY:
565       s->empty_blob_count++;
566       return 0;
567     case KEYBOX_BLOBTYPE_HEADER:
568       s->header_blob_count++;
569       return 0;
570     case KEYBOX_BLOBTYPE_PGP:
571       s->pgp_blob_count++;
572       break;
573     case KEYBOX_BLOBTYPE_X509:
574       s->x509_blob_count++;
575       break;
576     default:
577       s->unknown_blob_count++;
578       return 0;
579     }
580
581   if (length < 40)
582     {
583       s->too_short_blobs++;
584       return -1;
585     }
586
587   n = get16 (buffer + 6);
588   if (n)
589     {
590       if ((n & 1))
591         s->secret_flagged++;
592       if ((n & 2))
593         s->ephemeral_flagged++;
594     }
595   else
596     s->non_flagged++;
597
598   return 0;
599 }
600
601
602 \f
603 static FILE *
604 open_file (const char **filename, FILE *outfp)
605 {
606   FILE *fp;
607
608   if (!*filename)
609     {
610       *filename = "-";
611       fp = stdin;
612     }
613   else
614     fp = fopen (*filename, "rb");
615   if (!fp)
616     {
617       int save_errno = errno;
618       fprintf (outfp, "can't open '%s': %s\n", *filename, strerror(errno));
619       gpg_err_set_errno (save_errno);
620     }
621   return fp;
622 }
623
624
625
626 int
627 _keybox_dump_file (const char *filename, int stats_only, FILE *outfp)
628 {
629   FILE *fp;
630   KEYBOXBLOB blob;
631   int rc;
632   unsigned long count = 0;
633   struct file_stats_s stats;
634   int skipped_deleted;
635
636   memset (&stats, 0, sizeof stats);
637
638   if (!(fp = open_file (&filename, outfp)))
639     return gpg_error_from_syserror ();
640
641   for (;;)
642     {
643       rc = _keybox_read_blob (&blob, fp, &skipped_deleted);
644       if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE
645           && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX)
646         {
647           if (stats_only)
648             stats.skipped_long_blobs++;
649           else
650             {
651               fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
652               fprintf (outfp, "# Record too large\nEND-RECORD\n");
653             }
654           count++;
655           continue;
656         }
657       if (rc)
658         break;
659
660       count += skipped_deleted;
661
662       if (stats_only)
663         {
664           stats.total_blob_count += skipped_deleted;
665           stats.empty_blob_count += skipped_deleted;
666           update_stats (blob, &stats);
667         }
668       else
669         {
670           fprintf (outfp, "BEGIN-RECORD: %lu\n", count );
671           _keybox_dump_blob (blob, outfp);
672           fprintf (outfp, "END-RECORD\n");
673         }
674       _keybox_release_blob (blob);
675       count++;
676     }
677   if (rc == -1)
678     rc = 0;
679   if (rc)
680     fprintf (outfp, "# error reading '%s': %s\n", filename, gpg_strerror (rc));
681
682   if (fp != stdin)
683     fclose (fp);
684
685   if (stats_only)
686     {
687       fprintf (outfp,
688                "Total number of blobs: %8lu\n"
689                "               header: %8lu\n"
690                "                empty: %8lu\n"
691                "              openpgp: %8lu\n"
692                "                 x509: %8lu\n"
693                "          non flagged: %8lu\n"
694                "       secret flagged: %8lu\n"
695                "    ephemeral flagged: %8lu\n",
696                stats.total_blob_count,
697                stats.header_blob_count,
698                stats.empty_blob_count,
699                stats.pgp_blob_count,
700                stats.x509_blob_count,
701                stats.non_flagged,
702                stats.secret_flagged,
703                stats.ephemeral_flagged);
704         if (stats.skipped_long_blobs)
705           fprintf (outfp, "   skipped long blobs: %8lu\n",
706                    stats.skipped_long_blobs);
707         if (stats.unknown_blob_count)
708           fprintf (outfp, "   unknown blob types: %8lu\n",
709                    stats.unknown_blob_count);
710         if (stats.too_short_blobs)
711           fprintf (outfp, "      too short blobs: %8lu (error)\n",
712                    stats.too_short_blobs);
713         if (stats.too_large_blobs)
714           fprintf (outfp, "      too large blobs: %8lu (error)\n",
715                    stats.too_large_blobs);
716     }
717
718   return rc;
719 }
720
721
722 \f
723 struct dupitem_s
724 {
725   unsigned long recno;
726   unsigned char digest[20];
727 };
728
729
730 static int
731 cmp_dupitems (const void *arg_a, const void *arg_b)
732 {
733   struct dupitem_s *a = (struct dupitem_s *)arg_a;
734   struct dupitem_s *b = (struct dupitem_s *)arg_b;
735
736   return memcmp (a->digest, b->digest, 20);
737 }
738
739
740 int
741 _keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp)
742 {
743   FILE *fp;
744   KEYBOXBLOB blob;
745   int rc;
746   unsigned long recno = 0;
747   unsigned char zerodigest[20];
748   struct dupitem_s *dupitems;
749   size_t dupitems_size, dupitems_count, lastn, n;
750   char fprbuf[3*20+1];
751
752   (void)print_them;
753
754   memset (zerodigest, 0, sizeof zerodigest);
755
756   if (!(fp = open_file (&filename, outfp)))
757     return gpg_error_from_syserror ();
758
759   dupitems_size = 1000;
760   dupitems = malloc (dupitems_size * sizeof *dupitems);
761   if (!dupitems)
762     {
763       gpg_error_t tmperr = gpg_error_from_syserror ();
764       fprintf (outfp, "error allocating array for '%s': %s\n",
765                filename, strerror(errno));
766       return tmperr;
767     }
768   dupitems_count = 0;
769
770   while ( !(rc = _keybox_read_blob (&blob, fp, NULL)) )
771     {
772       unsigned char digest[20];
773
774       if (hash_blob_rawdata (blob, digest))
775         fprintf (outfp, "error in blob %ld of '%s'\n", recno, filename);
776       else if (memcmp (digest, zerodigest, 20))
777         {
778           if (dupitems_count >= dupitems_size)
779             {
780               struct dupitem_s *tmp;
781
782               dupitems_size += 1000;
783               tmp = realloc (dupitems, dupitems_size * sizeof *dupitems);
784               if (!tmp)
785                 {
786                   gpg_error_t tmperr = gpg_error_from_syserror ();
787                   fprintf (outfp, "error reallocating array for '%s': %s\n",
788                            filename, strerror(errno));
789                   free (dupitems);
790                   return tmperr;
791                 }
792               dupitems = tmp;
793             }
794           dupitems[dupitems_count].recno = recno;
795           memcpy (dupitems[dupitems_count].digest, digest, 20);
796           dupitems_count++;
797         }
798       _keybox_release_blob (blob);
799       recno++;
800     }
801   if (rc == -1)
802     rc = 0;
803   if (rc)
804     fprintf (outfp, "error reading '%s': %s\n", filename, gpg_strerror (rc));
805   if (fp != stdin)
806     fclose (fp);
807
808   qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems);
809
810   for (lastn=0, n=1; n < dupitems_count; lastn=n, n++)
811     {
812       if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20))
813         {
814           bin2hexcolon (dupitems[lastn].digest, 20, fprbuf);
815           fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno);
816           do
817             fprintf (outfp, " %lu", dupitems[n].recno);
818           while (++n < dupitems_count
819                  && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20));
820           putc ('\n', outfp);
821           n--;
822         }
823     }
824
825   free (dupitems);
826
827   return rc;
828 }
829
830
831 /* Print records with record numbers FROM to TO to OUTFP.  */
832 int
833 _keybox_dump_cut_records (const char *filename, unsigned long from,
834                           unsigned long to, FILE *outfp)
835 {
836   FILE *fp;
837   KEYBOXBLOB blob;
838   int rc;
839   unsigned long recno = 0;
840
841   if (!(fp = open_file (&filename, stderr)))
842     return gpg_error_from_syserror ();
843
844   while ( !(rc = _keybox_read_blob (&blob, fp, NULL)) )
845     {
846       if (recno > to)
847         break; /* Ready.  */
848       if (recno >= from)
849         {
850           if ((rc = _keybox_write_blob (blob, outfp)))
851             {
852               fprintf (stderr, "error writing output: %s\n",
853                        gpg_strerror (rc));
854               goto leave;
855             }
856         }
857       _keybox_release_blob (blob);
858       recno++;
859     }
860   if (rc == -1)
861     rc = 0;
862   if (rc)
863     fprintf (stderr, "error reading '%s': %s\n", filename, gpg_strerror (rc));
864  leave:
865   if (fp != stdin)
866     fclose (fp);
867   return rc;
868 }