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