kbx: Implement keybox_lock for use by gpg.
[gnupg.git] / kbx / keybox-update.c
1 /* keybox-update.c - keybox update operations
2  * Copyright (C) 2001, 2003, 2004, 2012 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 #include <time.h>
26 #include <unistd.h>
27 #include <assert.h>
28
29 #include "keybox-defs.h"
30 #include "../common/sysutils.h"
31 #include "../common/host2net.h"
32
33 #define EXTSEP_S "."
34
35 #define FILECOPY_INSERT 1
36 #define FILECOPY_DELETE 2
37 #define FILECOPY_UPDATE 3
38
39
40 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
41
42 #ifdef HAVE_LIMITS_H
43 # include <limits.h>
44 #endif
45 #ifndef LONG_MAX
46 # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
47 #endif
48 #ifndef LONG_MIN
49 # define LONG_MIN (-1 - LONG_MAX)
50 #endif
51
52 /****************
53  * A substitute for fseeko, for hosts that don't have it.
54  */
55 static int
56 fseeko (FILE * stream, off_t newpos, int whence)
57 {
58   while (newpos != (long) newpos)
59     {
60       long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
61       if (fseek (stream, pos, whence) != 0)
62         return -1;
63       newpos -= pos;
64       whence = SEEK_CUR;
65     }
66   return fseek (stream, (long) newpos, whence);
67 }
68 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
69
70
71
72 static int
73 create_tmp_file (const char *template,
74                  char **r_bakfname, char **r_tmpfname, FILE **r_fp)
75 {
76   char *bakfname, *tmpfname;
77
78   *r_bakfname = NULL;
79   *r_tmpfname = NULL;
80
81 # ifdef USE_ONLY_8DOT3
82   /* Here is another Windoze bug?:
83    * you can't rename("pubring.kbx.tmp", "pubring.kbx");
84    * but        rename("pubring.kbx.tmp", "pubring.aaa");
85    * works.  So we replace ".kbx" by ".kb_" or ".k__".  Note that we
86    * can't use ".bak" and ".tmp", because these suffixes are used by
87    * gpg and would lead to a sharing violation or data corruption.
88    */
89   if (strlen (template) > 4
90       && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
91     {
92       bakfname = xtrymalloc (strlen (template) + 1);
93       if (!bakfname)
94         return gpg_error_from_syserror ();
95       strcpy (bakfname, template);
96       strcpy (bakfname+strlen(template)-4, EXTSEP_S "kb_");
97
98       tmpfname = xtrymalloc (strlen (template) + 1);
99       if (!tmpfname)
100         {
101           gpg_error_t tmperr = gpg_error_from_syserror ();
102           xfree (bakfname);
103           return tmperr;
104         }
105       strcpy (tmpfname,template);
106       strcpy (tmpfname + strlen (template)-4, EXTSEP_S "k__");
107     }
108   else
109     { /* File does not end with kbx, thus we hope we are working on a
110          modern file system and appending a suffix works. */
111       bakfname = xtrymalloc ( strlen (template) + 5);
112       if (!bakfname)
113         return gpg_error_from_syserror ();
114       strcpy (stpcpy (bakfname, template), EXTSEP_S "kb_");
115
116       tmpfname = xtrymalloc ( strlen (template) + 5);
117       if (!tmpfname)
118         {
119           gpg_error_t tmperr = gpg_error_from_syserror ();
120           xfree (bakfname);
121           return tmperr;
122         }
123       strcpy (stpcpy (tmpfname, template), EXTSEP_S "k__");
124     }
125 # else /* Posix file names */
126   bakfname = xtrymalloc (strlen (template) + 2);
127   if (!bakfname)
128     return gpg_error_from_syserror ();
129   strcpy (stpcpy (bakfname,template),"~");
130
131   tmpfname = xtrymalloc ( strlen (template) + 5);
132   if (!tmpfname)
133     {
134       gpg_error_t tmperr = gpg_error_from_syserror ();
135       xfree (bakfname);
136       return tmperr;
137     }
138   strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
139 # endif /* Posix filename */
140
141   *r_fp = fopen (tmpfname, "wb");
142   if (!*r_fp)
143     {
144       gpg_error_t tmperr = gpg_error_from_syserror ();
145       xfree (tmpfname);
146       xfree (bakfname);
147       return tmperr;
148     }
149
150   *r_bakfname = bakfname;
151   *r_tmpfname = tmpfname;
152   return 0;
153 }
154
155
156 static int
157 rename_tmp_file (const char *bakfname, const char *tmpfname,
158                  const char *fname, int secret )
159 {
160   int rc=0;
161
162   /* restrict the permissions for secret keyboxs */
163 #ifndef HAVE_DOSISH_SYSTEM
164 /*    if (secret && !opt.preserve_permissions) */
165 /*      { */
166 /*        if (chmod (tmpfname, S_IRUSR | S_IWUSR) )  */
167 /*          { */
168 /*            log_debug ("chmod of '%s' failed: %s\n", */
169 /*                       tmpfname, strerror(errno) ); */
170 /*            return KEYBOX_Write_File; */
171 /*      } */
172 /*      } */
173 #endif
174
175   /* fixme: invalidate close caches (not used with stdio)*/
176 /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname ); */
177 /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); */
178 /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); */
179
180   /* First make a backup file except for secret keyboxes. */
181   if (!secret)
182     {
183 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
184       gnupg_remove (bakfname);
185 #endif
186       if (rename (fname, bakfname) )
187         {
188           return gpg_error_from_syserror ();
189         }
190     }
191
192   /* Then rename the file. */
193 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
194   gnupg_remove (fname);
195 #endif
196   if (rename (tmpfname, fname) )
197     {
198       rc = gpg_error_from_syserror ();
199       if (secret)
200         {
201 /*            log_info ("WARNING: 2 files with confidential" */
202 /*                       " information exists.\n"); */
203 /*            log_info ("%s is the unchanged one\n", fname ); */
204 /*            log_info ("%s is the new one\n", tmpfname ); */
205 /*            log_info ("Please fix this possible security flaw\n"); */
206         }
207       return rc;
208     }
209
210   return 0;
211 }
212
213
214
215 /* Perform insert/delete/update operation.  MODE is one of
216    FILECOPY_INSERT, FILECOPY_DELETE, FILECOPY_UPDATE.  FOR_OPENPGP
217    indicates that this is called due to an OpenPGP keyblock change.  */
218 static int
219 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
220                int secret, int for_openpgp, off_t start_offset)
221 {
222   FILE *fp, *newfp;
223   int rc=0;
224   char *bakfname = NULL;
225   char *tmpfname = NULL;
226   char buffer[4096];  /* (Must be at least 32 bytes) */
227   int nread, nbytes;
228
229   /* Open the source file. Because we do a rename, we have to check the
230      permissions of the file */
231   if (access (fname, W_OK))
232     return gpg_error_from_syserror ();
233
234   fp = fopen (fname, "rb");
235   if (mode == FILECOPY_INSERT && !fp && errno == ENOENT)
236     {
237       /* Insert mode but file does not exist:
238          Create a new keybox file. */
239       newfp = fopen (fname, "wb");
240       if (!newfp )
241         return gpg_error_from_syserror ();
242
243       rc = _keybox_write_header_blob (newfp, for_openpgp);
244       if (rc)
245         {
246           fclose (newfp);
247           return rc;
248         }
249
250       rc = _keybox_write_blob (blob, newfp);
251       if (rc)
252         {
253           fclose (newfp);
254           return rc;
255         }
256
257       if ( fclose (newfp) )
258         return gpg_error_from_syserror ();
259
260 /*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
261 /*          { */
262 /*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
263 /*            return KEYBOX_File_Error; */
264 /*          } */
265       return 0; /* Ready. */
266     }
267
268   if (!fp)
269     {
270       rc = gpg_error_from_syserror ();
271       goto leave;
272     }
273
274   /* Create the new file.  On success NEWFP is initialized.  */
275   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
276   if (rc)
277     {
278       fclose (fp);
279       goto leave;
280     }
281
282   /* prepare for insert */
283   if (mode == FILECOPY_INSERT)
284     {
285       int first_record = 1;
286
287       /* Copy everything to the new file.  If this is for OpenPGP, we
288          make sure that the openpgp flag is set in the header.  (We
289          failsafe the blob type.) */
290       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
291         {
292           if (first_record && for_openpgp
293               && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
294             {
295               first_record = 0;
296               buffer[7] |= 0x02; /* OpenPGP data may be available.  */
297             }
298
299           if (fwrite (buffer, nread, 1, newfp) != 1)
300             {
301               rc = gpg_error_from_syserror ();
302               fclose (fp);
303               fclose (newfp);
304               goto leave;
305             }
306         }
307       if (ferror (fp))
308         {
309           rc = gpg_error_from_syserror ();
310           fclose (fp);
311           fclose (newfp);
312           goto leave;
313         }
314     }
315
316   /* Prepare for delete or update. */
317   if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
318     {
319       off_t current = 0;
320
321       /* Copy first part to the new file. */
322       while ( current < start_offset )
323         {
324           nbytes = DIM(buffer);
325           if (current + nbytes > start_offset)
326               nbytes = start_offset - current;
327           nread = fread (buffer, 1, nbytes, fp);
328           if (!nread)
329             break;
330           current += nread;
331
332           if (fwrite (buffer, nread, 1, newfp) != 1)
333             {
334               rc = gpg_error_from_syserror ();
335               fclose (fp);
336               fclose (newfp);
337               goto leave;
338             }
339         }
340       if (ferror (fp))
341         {
342           rc = gpg_error_from_syserror ();
343           fclose (fp);
344           fclose (newfp);
345           goto leave;
346         }
347
348       /* Skip this blob. */
349       rc = _keybox_read_blob (NULL, fp);
350       if (rc)
351         {
352           fclose (fp);
353           fclose (newfp);
354           return rc;
355         }
356     }
357
358   /* Do an insert or update. */
359   if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
360     {
361       rc = _keybox_write_blob (blob, newfp);
362       if (rc)
363         {
364           fclose (fp);
365           fclose (newfp);
366           return rc;
367         }
368     }
369
370   /* Copy the rest of the packet for an delete or update. */
371   if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
372     {
373       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
374         {
375           if (fwrite (buffer, nread, 1, newfp) != 1)
376             {
377               rc = gpg_error_from_syserror ();
378               fclose (fp);
379               fclose (newfp);
380               goto leave;
381             }
382         }
383       if (ferror (fp))
384         {
385           rc = gpg_error_from_syserror ();
386           fclose (fp);
387           fclose (newfp);
388           goto leave;
389         }
390     }
391
392   /* Close both files. */
393   if (fclose(fp))
394     {
395       rc = gpg_error_from_syserror ();
396       fclose (newfp);
397       goto leave;
398     }
399   if (fclose(newfp))
400     {
401       rc = gpg_error_from_syserror ();
402       goto leave;
403     }
404
405   rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
406
407  leave:
408   xfree(bakfname);
409   xfree(tmpfname);
410   return rc;
411 }
412
413
414 /* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD.  SIGSTATUS is
415    a vector describing the status of the signatures; its first element
416    gives the number of following elements.  */
417 gpg_error_t
418 keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
419                         u32 *sigstatus)
420 {
421   gpg_error_t err;
422   const char *fname;
423   KEYBOXBLOB blob;
424   size_t nparsed;
425   struct _keybox_openpgp_info info;
426
427   if (!hd)
428     return gpg_error (GPG_ERR_INV_HANDLE);
429   if (!hd->kb)
430     return gpg_error (GPG_ERR_INV_HANDLE);
431   fname = hd->kb->fname;
432   if (!fname)
433     return gpg_error (GPG_ERR_INV_HANDLE);
434
435
436   /* Close this one otherwise we will mess up the position for a next
437      search.  Fixme: it would be better to adjust the position after
438      the write operation.  */
439   _keybox_close_file (hd);
440
441   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
442   if (err)
443     return err;
444   assert (nparsed <= imagelen);
445   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
446                                      sigstatus, hd->ephemeral);
447   _keybox_destroy_openpgp_info (&info);
448   if (!err)
449     {
450       err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0);
451       _keybox_release_blob (blob);
452       /*    if (!rc && !hd->secret && kb_offtbl) */
453       /*      { */
454       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
455       /*      } */
456     }
457   return err;
458 }
459
460
461 /* Update the current key at HD with the given OpenPGP keyblock in
462    {IMAGE,IMAGELEN}.  */
463 gpg_error_t
464 keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
465 {
466   gpg_error_t err;
467   const char *fname;
468   off_t off;
469   KEYBOXBLOB blob;
470   size_t nparsed;
471   struct _keybox_openpgp_info info;
472
473   if (!hd || !image || !imagelen)
474     return gpg_error (GPG_ERR_INV_VALUE);
475   if (!hd->found.blob)
476     return gpg_error (GPG_ERR_NOTHING_FOUND);
477   if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
478     return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
479   fname = hd->kb->fname;
480   if (!fname)
481     return gpg_error (GPG_ERR_INV_HANDLE);
482
483   off = _keybox_get_blob_fileoffset (hd->found.blob);
484   if (off == (off_t)-1)
485     return gpg_error (GPG_ERR_GENERAL);
486
487   /* Close this the file so that we do no mess up the position for a
488      next search.  */
489   _keybox_close_file (hd);
490
491   /* Build a new blob.  */
492   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
493   if (err)
494     return err;
495   assert (nparsed <= imagelen);
496   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
497                                      NULL, hd->ephemeral);
498   _keybox_destroy_openpgp_info (&info);
499
500   /* Update the keyblock.  */
501   if (!err)
502     {
503       err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off);
504       _keybox_release_blob (blob);
505     }
506   return err;
507 }
508
509
510
511 #ifdef KEYBOX_WITH_X509
512 int
513 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
514                     unsigned char *sha1_digest)
515 {
516   int rc;
517   const char *fname;
518   KEYBOXBLOB blob;
519
520   if (!hd)
521     return gpg_error (GPG_ERR_INV_HANDLE);
522   if (!hd->kb)
523     return gpg_error (GPG_ERR_INV_HANDLE);
524   fname = hd->kb->fname;
525   if (!fname)
526     return gpg_error (GPG_ERR_INV_HANDLE);
527
528   /* Close this one otherwise we will mess up the position for a next
529      search.  Fixme: it would be better to adjust the position after
530      the write operation.  */
531   _keybox_close_file (hd);
532
533   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
534   if (!rc)
535     {
536       rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
537       _keybox_release_blob (blob);
538       /*    if (!rc && !hd->secret && kb_offtbl) */
539       /*      { */
540       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
541       /*      } */
542     }
543   return rc;
544 }
545
546 int
547 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
548                     unsigned char *sha1_digest)
549 {
550   (void)hd;
551   (void)cert;
552   (void)sha1_digest;
553   return -1;
554 }
555
556
557 #endif /*KEYBOX_WITH_X509*/
558
559 /* Note: We assume that the keybox has been locked before the current
560    search was executed.  This is needed so that we can depend on the
561    offset information of the flags. */
562 int
563 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
564 {
565   off_t off;
566   const char *fname;
567   FILE *fp;
568   gpg_err_code_t ec;
569   size_t flag_pos, flag_size;
570   const unsigned char *buffer;
571   size_t length;
572
573   (void)idx;  /* Not yet used.  */
574
575   if (!hd)
576     return gpg_error (GPG_ERR_INV_VALUE);
577   if (!hd->found.blob)
578     return gpg_error (GPG_ERR_NOTHING_FOUND);
579   if (!hd->kb)
580     return gpg_error (GPG_ERR_INV_HANDLE);
581   if (!hd->found.blob)
582     return gpg_error (GPG_ERR_NOTHING_FOUND);
583   fname = hd->kb->fname;
584   if (!fname)
585     return gpg_error (GPG_ERR_INV_HANDLE);
586
587   off = _keybox_get_blob_fileoffset (hd->found.blob);
588   if (off == (off_t)-1)
589     return gpg_error (GPG_ERR_GENERAL);
590
591   buffer = _keybox_get_blob_image (hd->found.blob, &length);
592   ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
593   if (ec)
594     return gpg_error (ec);
595
596   off += flag_pos;
597
598   _keybox_close_file (hd);
599   fp = fopen (hd->kb->fname, "r+b");
600   if (!fp)
601     return gpg_error_from_syserror ();
602
603   ec = 0;
604   if (fseeko (fp, off, SEEK_SET))
605     ec = gpg_err_code_from_syserror ();
606   else
607     {
608       unsigned char tmp[4];
609
610       tmp[0] = value >> 24;
611       tmp[1] = value >> 16;
612       tmp[2] = value >>  8;
613       tmp[3] = value;
614
615       switch (flag_size)
616         {
617         case 1:
618         case 2:
619         case 4:
620           if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
621             ec = gpg_err_code_from_syserror ();
622           break;
623         default:
624           ec = GPG_ERR_BUG;
625           break;
626         }
627     }
628
629   if (fclose (fp))
630     {
631       if (!ec)
632         ec = gpg_err_code_from_syserror ();
633     }
634
635   return gpg_error (ec);
636 }
637
638
639
640 int
641 keybox_delete (KEYBOX_HANDLE hd)
642 {
643   off_t off;
644   const char *fname;
645   FILE *fp;
646   int rc;
647
648   if (!hd)
649     return gpg_error (GPG_ERR_INV_VALUE);
650   if (!hd->found.blob)
651     return gpg_error (GPG_ERR_NOTHING_FOUND);
652   if (!hd->kb)
653     return gpg_error (GPG_ERR_INV_HANDLE);
654   fname = hd->kb->fname;
655   if (!fname)
656     return gpg_error (GPG_ERR_INV_HANDLE);
657
658   off = _keybox_get_blob_fileoffset (hd->found.blob);
659   if (off == (off_t)-1)
660     return gpg_error (GPG_ERR_GENERAL);
661   off += 4;
662
663   _keybox_close_file (hd);
664   fp = fopen (hd->kb->fname, "r+b");
665   if (!fp)
666     return gpg_error_from_syserror ();
667
668   if (fseeko (fp, off, SEEK_SET))
669     rc = gpg_error_from_syserror ();
670   else if (putc (0, fp) == EOF)
671     rc = gpg_error_from_syserror ();
672   else
673     rc = 0;
674
675   if (fclose (fp))
676     {
677       if (!rc)
678         rc = gpg_error_from_syserror ();
679     }
680
681   return rc;
682 }
683
684
685 /* Compress the keybox file.  This should be run with the file
686    locked. */
687 int
688 keybox_compress (KEYBOX_HANDLE hd)
689 {
690   int read_rc, rc;
691   const char *fname;
692   FILE *fp, *newfp;
693   char *bakfname = NULL;
694   char *tmpfname = NULL;
695   int first_blob;
696   KEYBOXBLOB blob = NULL;
697   u32 cut_time;
698   int any_changes = 0;
699   int skipped_deleted;
700
701   if (!hd)
702     return gpg_error (GPG_ERR_INV_HANDLE);
703   if (!hd->kb)
704     return gpg_error (GPG_ERR_INV_HANDLE);
705   if (hd->secret)
706     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
707   fname = hd->kb->fname;
708   if (!fname)
709     return gpg_error (GPG_ERR_INV_HANDLE);
710
711   _keybox_close_file (hd);
712
713   /* Open the source file. Because we do a rename, we have to check the
714      permissions of the file */
715   if (access (fname, W_OK))
716     return gpg_error_from_syserror ();
717
718   fp = fopen (fname, "rb");
719   if (!fp && errno == ENOENT)
720     return 0; /* Ready. File has been deleted right after the access above. */
721   if (!fp)
722     {
723       rc = gpg_error_from_syserror ();
724       return rc;
725     }
726
727   /* A quick test to see if we need to compress the file at all.  We
728      schedule a compress run after 3 hours. */
729   if ( !_keybox_read_blob (&blob, fp) )
730     {
731       const unsigned char *buffer;
732       size_t length;
733
734       buffer = _keybox_get_blob_image (blob, &length);
735       if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
736         {
737           u32 last_maint = buf32_to_u32 (buffer+20);
738
739           if ( (last_maint + 3*3600) > time (NULL) )
740             {
741               fclose (fp);
742               _keybox_release_blob (blob);
743               return 0; /* Compress run not yet needed. */
744             }
745         }
746       _keybox_release_blob (blob);
747       fseek (fp, 0, SEEK_SET);
748       clearerr (fp);
749     }
750
751   /* Create the new file. */
752   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
753   if (rc)
754     {
755       fclose (fp);
756       return rc;;
757     }
758
759
760   /* Processing loop.  By reading using _keybox_read_blob we
761      automagically skip any blobs flagged as deleted.  Thus what we
762      only have to do is to check all ephemeral flagged blocks whether
763      their time has come and write out all other blobs. */
764   cut_time = time(NULL) - 86400;
765   first_blob = 1;
766   skipped_deleted = 0;
767   for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
768        _keybox_release_blob (blob), blob = NULL )
769     {
770       unsigned int blobflags;
771       const unsigned char *buffer;
772       size_t length, pos, size;
773       u32 created_at;
774
775       if (skipped_deleted)
776         any_changes = 1;
777       buffer = _keybox_get_blob_image (blob, &length);
778       if (first_blob)
779         {
780           first_blob = 0;
781           if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
782             {
783               /* Write out the blob with an updated maintenance time
784                  stamp and if needed (ie. used by gpg) set the openpgp
785                  flag.  */
786               _keybox_update_header_blob (blob, hd->for_openpgp);
787               rc = _keybox_write_blob (blob, newfp);
788               if (rc)
789                 break;
790               continue;
791             }
792
793           /* The header blob is missing.  Insert it.  */
794           rc = _keybox_write_header_blob (newfp, hd->for_openpgp);
795           if (rc)
796             break;
797           any_changes = 1;
798         }
799       else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
800         {
801           /* Oops: There is another header record - remove it. */
802           any_changes = 1;
803           continue;
804         }
805
806       if (_keybox_get_flag_location (buffer, length,
807                                      KEYBOX_FLAG_BLOB, &pos, &size)
808           || size != 2)
809         {
810           rc = gpg_error (GPG_ERR_BUG);
811           break;
812         }
813       blobflags = buf16_to_uint (buffer+pos);
814       if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
815         {
816           /* This is an ephemeral blob. */
817           if (_keybox_get_flag_location (buffer, length,
818                                          KEYBOX_FLAG_CREATED_AT, &pos, &size)
819               || size != 4)
820             created_at = 0; /* oops. */
821           else
822             created_at = buf32_to_u32 (buffer+pos);
823
824           if (created_at && created_at < cut_time)
825             {
826               any_changes = 1;
827               continue; /* Skip this blob. */
828             }
829         }
830
831       rc = _keybox_write_blob (blob, newfp);
832       if (rc)
833         break;
834     }
835   if (skipped_deleted)
836     any_changes = 1;
837   _keybox_release_blob (blob); blob = NULL;
838   if (!rc && read_rc == -1)
839     rc = 0;
840   else if (!rc)
841     rc = read_rc;
842
843   /* Close both files. */
844   if (fclose(fp) && !rc)
845     rc = gpg_error_from_syserror ();
846   if (fclose(newfp) && !rc)
847     rc = gpg_error_from_syserror ();
848
849   /* Rename or remove the temporary file. */
850   if (rc || !any_changes)
851     gnupg_remove (tmpfname);
852   else
853     rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
854
855   xfree(bakfname);
856   xfree(tmpfname);
857   return rc;
858 }