Fix typos found using codespell.
[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. */
275   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
276   if (rc)
277     {
278       fclose (fp);
279       fclose (newfp);
280       goto leave;
281     }
282
283   /* prepare for insert */
284   if (mode == FILECOPY_INSERT)
285     {
286       int first_record = 1;
287
288       /* Copy everything to the new file.  If this is for OpenPGP, we
289          make sure that the openpgp flag is set in the header.  (We
290          failsafe the blob type.) */
291       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
292         {
293           if (first_record && for_openpgp
294               && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
295             {
296               first_record = 0;
297               buffer[7] |= 0x02; /* OpenPGP data may be available.  */
298             }
299
300           if (fwrite (buffer, nread, 1, newfp) != 1)
301             {
302               rc = gpg_error_from_syserror ();
303               fclose (fp);
304               fclose (newfp);
305               goto leave;
306             }
307         }
308       if (ferror (fp))
309         {
310           rc = gpg_error_from_syserror ();
311           fclose (fp);
312           fclose (newfp);
313           goto leave;
314         }
315     }
316
317   /* Prepare for delete or update. */
318   if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
319     {
320       off_t current = 0;
321
322       /* Copy first part to the new file. */
323       while ( current < start_offset )
324         {
325           nbytes = DIM(buffer);
326           if (current + nbytes > start_offset)
327               nbytes = start_offset - current;
328           nread = fread (buffer, 1, nbytes, fp);
329           if (!nread)
330             break;
331           current += nread;
332
333           if (fwrite (buffer, nread, 1, newfp) != 1)
334             {
335               rc = gpg_error_from_syserror ();
336               fclose (fp);
337               fclose (newfp);
338               goto leave;
339             }
340         }
341       if (ferror (fp))
342         {
343           rc = gpg_error_from_syserror ();
344           fclose (fp);
345           fclose (newfp);
346           goto leave;
347         }
348
349       /* Skip this blob. */
350       rc = _keybox_read_blob (NULL, fp);
351       if (rc)
352         {
353           fclose (fp);
354           fclose (newfp);
355           return rc;
356         }
357     }
358
359   /* Do an insert or update. */
360   if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
361     {
362       rc = _keybox_write_blob (blob, newfp);
363       if (rc)
364         {
365           fclose (fp);
366           fclose (newfp);
367           return rc;
368         }
369     }
370
371   /* Copy the rest of the packet for an delete or update. */
372   if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
373     {
374       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
375         {
376           if (fwrite (buffer, nread, 1, newfp) != 1)
377             {
378               rc = gpg_error_from_syserror ();
379               fclose (fp);
380               fclose (newfp);
381               goto leave;
382             }
383         }
384       if (ferror (fp))
385         {
386           rc = gpg_error_from_syserror ();
387           fclose (fp);
388           fclose (newfp);
389           goto leave;
390         }
391     }
392
393   /* Close both files. */
394   if (fclose(fp))
395     {
396       rc = gpg_error_from_syserror ();
397       fclose (newfp);
398       goto leave;
399     }
400   if (fclose(newfp))
401     {
402       rc = gpg_error_from_syserror ();
403       goto leave;
404     }
405
406   rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
407
408  leave:
409   xfree(bakfname);
410   xfree(tmpfname);
411   return rc;
412 }
413
414
415 /* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD.  SIGSTATUS is
416    a vector describing the status of the signatures; its first element
417    gives the number of following elements.  */
418 gpg_error_t
419 keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
420                         u32 *sigstatus)
421 {
422   gpg_error_t err;
423   const char *fname;
424   KEYBOXBLOB blob;
425   size_t nparsed;
426   struct _keybox_openpgp_info info;
427
428   if (!hd)
429     return gpg_error (GPG_ERR_INV_HANDLE);
430   if (!hd->kb)
431     return gpg_error (GPG_ERR_INV_HANDLE);
432   fname = hd->kb->fname;
433   if (!fname)
434     return gpg_error (GPG_ERR_INV_HANDLE);
435
436
437   /* Close this one otherwise we will mess up the position for a next
438      search.  Fixme: it would be better to adjust the position after
439      the write operation.  */
440   _keybox_close_file (hd);
441
442   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
443   if (err)
444     return err;
445   assert (nparsed <= imagelen);
446   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
447                                      sigstatus, hd->ephemeral);
448   _keybox_destroy_openpgp_info (&info);
449   if (!err)
450     {
451       err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0);
452       _keybox_release_blob (blob);
453       /*    if (!rc && !hd->secret && kb_offtbl) */
454       /*      { */
455       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
456       /*      } */
457     }
458   return err;
459 }
460
461
462 /* Update the current key at HD with the given OpenPGP keyblock in
463    {IMAGE,IMAGELEN}.  */
464 gpg_error_t
465 keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
466 {
467   gpg_error_t err;
468   const char *fname;
469   off_t off;
470   KEYBOXBLOB blob;
471   size_t nparsed;
472   struct _keybox_openpgp_info info;
473
474   if (!hd || !image || !imagelen)
475     return gpg_error (GPG_ERR_INV_VALUE);
476   if (!hd->found.blob)
477     return gpg_error (GPG_ERR_NOTHING_FOUND);
478   if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
479     return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
480   fname = hd->kb->fname;
481   if (!fname)
482     return gpg_error (GPG_ERR_INV_HANDLE);
483
484   off = _keybox_get_blob_fileoffset (hd->found.blob);
485   if (off == (off_t)-1)
486     return gpg_error (GPG_ERR_GENERAL);
487
488   /* Close this the file so that we do no mess up the position for a
489      next search.  */
490   _keybox_close_file (hd);
491
492   /* Build a new blob.  */
493   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
494   if (err)
495     return err;
496   assert (nparsed <= imagelen);
497   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
498                                      NULL, hd->ephemeral);
499   _keybox_destroy_openpgp_info (&info);
500
501   /* Update the keyblock.  */
502   if (!err)
503     {
504       err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off);
505       _keybox_release_blob (blob);
506     }
507   return err;
508 }
509
510
511
512 #ifdef KEYBOX_WITH_X509
513 int
514 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
515                     unsigned char *sha1_digest)
516 {
517   int rc;
518   const char *fname;
519   KEYBOXBLOB blob;
520
521   if (!hd)
522     return gpg_error (GPG_ERR_INV_HANDLE);
523   if (!hd->kb)
524     return gpg_error (GPG_ERR_INV_HANDLE);
525   fname = hd->kb->fname;
526   if (!fname)
527     return gpg_error (GPG_ERR_INV_HANDLE);
528
529   /* Close this one otherwise we will mess up the position for a next
530      search.  Fixme: it would be better to adjust the position after
531      the write operation.  */
532   _keybox_close_file (hd);
533
534   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
535   if (!rc)
536     {
537       rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
538       _keybox_release_blob (blob);
539       /*    if (!rc && !hd->secret && kb_offtbl) */
540       /*      { */
541       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
542       /*      } */
543     }
544   return rc;
545 }
546
547 int
548 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
549                     unsigned char *sha1_digest)
550 {
551   (void)hd;
552   (void)cert;
553   (void)sha1_digest;
554   return -1;
555 }
556
557
558 #endif /*KEYBOX_WITH_X509*/
559
560 /* Note: We assume that the keybox has been locked before the current
561    search was executed.  This is needed so that we can depend on the
562    offset information of the flags. */
563 int
564 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
565 {
566   off_t off;
567   const char *fname;
568   FILE *fp;
569   gpg_err_code_t ec;
570   size_t flag_pos, flag_size;
571   const unsigned char *buffer;
572   size_t length;
573
574   (void)idx;  /* Not yet used.  */
575
576   if (!hd)
577     return gpg_error (GPG_ERR_INV_VALUE);
578   if (!hd->found.blob)
579     return gpg_error (GPG_ERR_NOTHING_FOUND);
580   if (!hd->kb)
581     return gpg_error (GPG_ERR_INV_HANDLE);
582   if (!hd->found.blob)
583     return gpg_error (GPG_ERR_NOTHING_FOUND);
584   fname = hd->kb->fname;
585   if (!fname)
586     return gpg_error (GPG_ERR_INV_HANDLE);
587
588   off = _keybox_get_blob_fileoffset (hd->found.blob);
589   if (off == (off_t)-1)
590     return gpg_error (GPG_ERR_GENERAL);
591
592   buffer = _keybox_get_blob_image (hd->found.blob, &length);
593   ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
594   if (ec)
595     return gpg_error (ec);
596
597   off += flag_pos;
598
599   _keybox_close_file (hd);
600   fp = fopen (hd->kb->fname, "r+b");
601   if (!fp)
602     return gpg_error_from_syserror ();
603
604   ec = 0;
605   if (fseeko (fp, off, SEEK_SET))
606     ec = gpg_err_code_from_syserror ();
607   else
608     {
609       unsigned char tmp[4];
610
611       tmp[0] = value >> 24;
612       tmp[1] = value >> 16;
613       tmp[2] = value >>  8;
614       tmp[3] = value;
615
616       switch (flag_size)
617         {
618         case 1:
619         case 2:
620         case 4:
621           if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
622             ec = gpg_err_code_from_syserror ();
623           break;
624         default:
625           ec = GPG_ERR_BUG;
626           break;
627         }
628     }
629
630   if (fclose (fp))
631     {
632       if (!ec)
633         ec = gpg_err_code_from_syserror ();
634     }
635
636   return gpg_error (ec);
637 }
638
639
640
641 int
642 keybox_delete (KEYBOX_HANDLE hd)
643 {
644   off_t off;
645   const char *fname;
646   FILE *fp;
647   int rc;
648
649   if (!hd)
650     return gpg_error (GPG_ERR_INV_VALUE);
651   if (!hd->found.blob)
652     return gpg_error (GPG_ERR_NOTHING_FOUND);
653   if (!hd->kb)
654     return gpg_error (GPG_ERR_INV_HANDLE);
655   fname = hd->kb->fname;
656   if (!fname)
657     return gpg_error (GPG_ERR_INV_HANDLE);
658
659   off = _keybox_get_blob_fileoffset (hd->found.blob);
660   if (off == (off_t)-1)
661     return gpg_error (GPG_ERR_GENERAL);
662   off += 4;
663
664   _keybox_close_file (hd);
665   fp = fopen (hd->kb->fname, "r+b");
666   if (!fp)
667     return gpg_error_from_syserror ();
668
669   if (fseeko (fp, off, SEEK_SET))
670     rc = gpg_error_from_syserror ();
671   else if (putc (0, fp) == EOF)
672     rc = gpg_error_from_syserror ();
673   else
674     rc = 0;
675
676   if (fclose (fp))
677     {
678       if (!rc)
679         rc = gpg_error_from_syserror ();
680     }
681
682   return rc;
683 }
684
685
686 /* Compress the keybox file.  This should be run with the file
687    locked. */
688 int
689 keybox_compress (KEYBOX_HANDLE hd)
690 {
691   int read_rc, rc;
692   const char *fname;
693   FILE *fp, *newfp;
694   char *bakfname = NULL;
695   char *tmpfname = NULL;
696   int first_blob;
697   KEYBOXBLOB blob = NULL;
698   u32 cut_time;
699   int any_changes = 0;
700   int skipped_deleted;
701
702   if (!hd)
703     return gpg_error (GPG_ERR_INV_HANDLE);
704   if (!hd->kb)
705     return gpg_error (GPG_ERR_INV_HANDLE);
706   if (hd->secret)
707     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
708   fname = hd->kb->fname;
709   if (!fname)
710     return gpg_error (GPG_ERR_INV_HANDLE);
711
712   _keybox_close_file (hd);
713
714   /* Open the source file. Because we do a rename, we have to check the
715      permissions of the file */
716   if (access (fname, W_OK))
717     return gpg_error_from_syserror ();
718
719   fp = fopen (fname, "rb");
720   if (!fp && errno == ENOENT)
721     return 0; /* Ready. File has been deleted right after the access above. */
722   if (!fp)
723     {
724       rc = gpg_error_from_syserror ();
725       return rc;
726     }
727
728   /* A quick test to see if we need to compress the file at all.  We
729      schedule a compress run after 3 hours. */
730   if ( !_keybox_read_blob (&blob, fp) )
731     {
732       const unsigned char *buffer;
733       size_t length;
734
735       buffer = _keybox_get_blob_image (blob, &length);
736       if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
737         {
738           u32 last_maint = buf32_to_u32 (buffer+20);
739
740           if ( (last_maint + 3*3600) > time (NULL) )
741             {
742               fclose (fp);
743               _keybox_release_blob (blob);
744               return 0; /* Compress run not yet needed. */
745             }
746         }
747       _keybox_release_blob (blob);
748       fseek (fp, 0, SEEK_SET);
749       clearerr (fp);
750     }
751
752   /* Create the new file. */
753   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
754   if (rc)
755     {
756       fclose (fp);
757       return rc;;
758     }
759
760
761   /* Processing loop.  By reading using _keybox_read_blob we
762      automagically skip any blobs flagged as deleted.  Thus what we
763      only have to do is to check all ephemeral flagged blocks whether
764      their time has come and write out all other blobs. */
765   cut_time = time(NULL) - 86400;
766   first_blob = 1;
767   skipped_deleted = 0;
768   for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
769        _keybox_release_blob (blob), blob = NULL )
770     {
771       unsigned int blobflags;
772       const unsigned char *buffer;
773       size_t length, pos, size;
774       u32 created_at;
775
776       if (skipped_deleted)
777         any_changes = 1;
778       buffer = _keybox_get_blob_image (blob, &length);
779       if (first_blob)
780         {
781           first_blob = 0;
782           if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
783             {
784               /* Write out the blob with an updated maintenance time
785                  stamp and if needed (ie. used by gpg) set the openpgp
786                  flag.  */
787               _keybox_update_header_blob (blob, hd->for_openpgp);
788               rc = _keybox_write_blob (blob, newfp);
789               if (rc)
790                 break;
791               continue;
792             }
793
794           /* The header blob is missing.  Insert it.  */
795           rc = _keybox_write_header_blob (newfp, hd->for_openpgp);
796           if (rc)
797             break;
798           any_changes = 1;
799         }
800       else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
801         {
802           /* Oops: There is another header record - remove it. */
803           any_changes = 1;
804           continue;
805         }
806
807       if (_keybox_get_flag_location (buffer, length,
808                                      KEYBOX_FLAG_BLOB, &pos, &size)
809           || size != 2)
810         {
811           rc = gpg_error (GPG_ERR_BUG);
812           break;
813         }
814       blobflags = buf16_to_uint (buffer+pos);
815       if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
816         {
817           /* This is an ephemeral blob. */
818           if (_keybox_get_flag_location (buffer, length,
819                                          KEYBOX_FLAG_CREATED_AT, &pos, &size)
820               || size != 4)
821             created_at = 0; /* oops. */
822           else
823             created_at = buf32_to_u32 (buffer+pos);
824
825           if (created_at && created_at < cut_time)
826             {
827               any_changes = 1;
828               continue; /* Skip this blob. */
829             }
830         }
831
832       rc = _keybox_write_blob (blob, newfp);
833       if (rc)
834         break;
835     }
836   if (skipped_deleted)
837     any_changes = 1;
838   _keybox_release_blob (blob); blob = NULL;
839   if (!rc && read_rc == -1)
840     rc = 0;
841   else if (!rc)
842     rc = read_rc;
843
844   /* Close both files. */
845   if (fclose(fp) && !rc)
846     rc = gpg_error_from_syserror ();
847   if (fclose(newfp) && !rc)
848     rc = gpg_error_from_syserror ();
849
850   /* Rename or remove the temporary file. */
851   if (rc || !any_changes)
852     gnupg_remove (tmpfname);
853   else
854     rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
855
856   xfree(bakfname);
857   xfree(tmpfname);
858   return rc;
859 }