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