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