1fdf4351cb4fb142611cfd38d415afeafcda016b
[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   (void)hd;
429   (void)image;
430   (void)imagelen;
431   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
432 }
433
434
435
436 #ifdef KEYBOX_WITH_X509
437 int
438 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
439                     unsigned char *sha1_digest)
440 {
441   int rc;
442   const char *fname;
443   KEYBOXBLOB blob;
444
445   if (!hd)
446     return gpg_error (GPG_ERR_INV_HANDLE);
447   if (!hd->kb)
448     return gpg_error (GPG_ERR_INV_HANDLE);
449   fname = hd->kb->fname;
450   if (!fname)
451     return gpg_error (GPG_ERR_INV_HANDLE);
452
453   /* Close this one otherwise we will mess up the position for a next
454      search.  Fixme: it would be better to adjust the position after
455      the write operation.  */
456   _keybox_close_file (hd);
457
458   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
459   if (!rc)
460     {
461       rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0);
462       _keybox_release_blob (blob);
463       /*    if (!rc && !hd->secret && kb_offtbl) */
464       /*      { */
465       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
466       /*      } */
467     }
468   return rc;
469 }
470
471 int
472 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
473                     unsigned char *sha1_digest)
474 {
475   (void)hd;
476   (void)cert;
477   (void)sha1_digest;
478   return -1;
479 }
480
481
482 #endif /*KEYBOX_WITH_X509*/
483
484 /* Note: We assume that the keybox has been locked before the current
485    search was executed.  This is needed so that we can depend on the
486    offset information of the flags. */
487 int
488 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
489 {
490   off_t off;
491   const char *fname;
492   FILE *fp;
493   gpg_err_code_t ec;
494   size_t flag_pos, flag_size;
495   const unsigned char *buffer;
496   size_t length;
497
498   (void)idx;  /* Not yet used.  */
499
500   if (!hd)
501     return gpg_error (GPG_ERR_INV_VALUE);
502   if (!hd->found.blob)
503     return gpg_error (GPG_ERR_NOTHING_FOUND);
504   if (!hd->kb)
505     return gpg_error (GPG_ERR_INV_HANDLE);
506   if (!hd->found.blob)
507     return gpg_error (GPG_ERR_NOTHING_FOUND);
508   fname = hd->kb->fname;
509   if (!fname)
510     return gpg_error (GPG_ERR_INV_HANDLE);
511
512   off = _keybox_get_blob_fileoffset (hd->found.blob);
513   if (off == (off_t)-1)
514     return gpg_error (GPG_ERR_GENERAL);
515
516   buffer = _keybox_get_blob_image (hd->found.blob, &length);
517   ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
518   if (ec)
519     return gpg_error (ec);
520
521   off += flag_pos;
522
523   _keybox_close_file (hd);
524   fp = fopen (hd->kb->fname, "r+b");
525   if (!fp)
526     return gpg_error_from_syserror ();
527
528   ec = 0;
529   if (fseeko (fp, off, SEEK_SET))
530     ec = gpg_error_from_syserror ();
531   else
532     {
533       unsigned char tmp[4];
534
535       tmp[0] = value >> 24;
536       tmp[1] = value >> 16;
537       tmp[2] = value >>  8;
538       tmp[3] = value;
539
540       switch (flag_size)
541         {
542         case 1:
543         case 2:
544         case 4:
545           if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
546             ec = gpg_err_code_from_syserror ();
547           break;
548         default:
549           ec = GPG_ERR_BUG;
550           break;
551         }
552     }
553
554   if (fclose (fp))
555     {
556       if (!ec)
557         ec = gpg_err_code_from_syserror ();
558     }
559
560   return gpg_error (ec);
561 }
562
563
564
565 int
566 keybox_delete (KEYBOX_HANDLE hd)
567 {
568   off_t off;
569   const char *fname;
570   FILE *fp;
571   int rc;
572
573   if (!hd)
574     return gpg_error (GPG_ERR_INV_VALUE);
575   if (!hd->found.blob)
576     return gpg_error (GPG_ERR_NOTHING_FOUND);
577   if (!hd->kb)
578     return gpg_error (GPG_ERR_INV_HANDLE);
579   fname = hd->kb->fname;
580   if (!fname)
581     return gpg_error (GPG_ERR_INV_HANDLE);
582
583   off = _keybox_get_blob_fileoffset (hd->found.blob);
584   if (off == (off_t)-1)
585     return gpg_error (GPG_ERR_GENERAL);
586   off += 4;
587
588   _keybox_close_file (hd);
589   fp = fopen (hd->kb->fname, "r+b");
590   if (!fp)
591     return gpg_error_from_syserror ();
592
593   if (fseeko (fp, off, SEEK_SET))
594     rc = gpg_error_from_syserror ();
595   else if (putc (0, fp) == EOF)
596     rc = gpg_error_from_syserror ();
597   else
598     rc = 0;
599
600   if (fclose (fp))
601     {
602       if (!rc)
603         rc = gpg_error_from_syserror ();
604     }
605
606   return rc;
607 }
608
609
610 /* Compress the keybox file.  This should be run with the file
611    locked. */
612 int
613 keybox_compress (KEYBOX_HANDLE hd)
614 {
615   int read_rc, rc;
616   const char *fname;
617   FILE *fp, *newfp;
618   char *bakfname = NULL;
619   char *tmpfname = NULL;
620   int first_blob;
621   KEYBOXBLOB blob = NULL;
622   u32 cut_time;
623   int any_changes = 0;
624   int skipped_deleted;
625
626   if (!hd)
627     return gpg_error (GPG_ERR_INV_HANDLE);
628   if (!hd->kb)
629     return gpg_error (GPG_ERR_INV_HANDLE);
630   if (hd->secret)
631     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
632   fname = hd->kb->fname;
633   if (!fname)
634     return gpg_error (GPG_ERR_INV_HANDLE);
635
636   _keybox_close_file (hd);
637
638   /* Open the source file. Because we do a rename, we have to check the
639      permissions of the file */
640   if (access (fname, W_OK))
641     return gpg_error_from_syserror ();
642
643   fp = fopen (fname, "rb");
644   if (!fp && errno == ENOENT)
645     return 0; /* Ready. File has been deleted right after the access above. */
646   if (!fp)
647     {
648       rc = gpg_error_from_syserror ();
649       return rc;
650     }
651
652   /* A quick test to see if we need to compress the file at all.  We
653      schedule a compress run after 3 hours. */
654   if ( !_keybox_read_blob (&blob, fp) )
655     {
656       const unsigned char *buffer;
657       size_t length;
658
659       buffer = _keybox_get_blob_image (blob, &length);
660       if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
661         {
662           u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16)
663                             | (buffer[20+2] << 8) | (buffer[20+3]));
664
665           if ( (last_maint + 3*3600) > time (NULL) )
666             {
667               fclose (fp);
668               _keybox_release_blob (blob);
669               return 0; /* Compress run not yet needed. */
670             }
671         }
672       _keybox_release_blob (blob);
673       fseek (fp, 0, SEEK_SET);
674       clearerr (fp);
675     }
676
677   /* Create the new file. */
678   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
679   if (rc)
680     {
681       fclose(fp);
682       return rc;;
683     }
684
685
686   /* Processing loop.  By reading using _keybox_read_blob we
687      automagically skip any blobs flagged as deleted.  Thus what we
688      only have to do is to check all ephemeral flagged blocks whether
689      their time has come and write out all other blobs. */
690   cut_time = time(NULL) - 86400;
691   first_blob = 1;
692   skipped_deleted = 0;
693   for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
694        _keybox_release_blob (blob), blob = NULL )
695     {
696       unsigned int blobflags;
697       const unsigned char *buffer;
698       size_t length, pos, size;
699       u32 created_at;
700
701       if (skipped_deleted)
702         any_changes = 1;
703       buffer = _keybox_get_blob_image (blob, &length);
704       if (first_blob)
705         {
706           first_blob = 0;
707           if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
708             {
709               /* Write out the blob with an updated maintenance time stamp. */
710               _keybox_update_header_blob (blob);
711               rc = _keybox_write_blob (blob, newfp);
712               if (rc)
713                 break;
714               continue;
715             }
716
717           /* The header blob is missing.  Insert it.  */
718           rc = _keybox_write_header_blob (newfp);
719           if (rc)
720             break;
721           any_changes = 1;
722         }
723       else if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
724         {
725           /* Oops: There is another header record - remove it. */
726           any_changes = 1;
727           continue;
728         }
729
730       if (_keybox_get_flag_location (buffer, length,
731                                      KEYBOX_FLAG_BLOB, &pos, &size)
732           || size != 2)
733         {
734           rc = gpg_error (GPG_ERR_BUG);
735           break;
736         }
737       blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
738       if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
739         {
740           /* This is an ephemeral blob. */
741           if (_keybox_get_flag_location (buffer, length,
742                                          KEYBOX_FLAG_CREATED_AT, &pos, &size)
743               || size != 4)
744             created_at = 0; /* oops. */
745           else
746             created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16)
747                           | (buffer[pos+2] << 8) | (buffer[pos+3]));
748
749           if (created_at && created_at < cut_time)
750             {
751               any_changes = 1;
752               continue; /* Skip this blob. */
753             }
754         }
755
756       rc = _keybox_write_blob (blob, newfp);
757       if (rc)
758         break;
759     }
760   if (skipped_deleted)
761     any_changes = 1;
762   _keybox_release_blob (blob); blob = NULL;
763   if (!rc && read_rc == -1)
764     rc = 0;
765   else if (!rc)
766     rc = read_rc;
767
768   /* Close both files. */
769   if (fclose(fp) && !rc)
770     rc = gpg_error_from_syserror ();
771   if (fclose(newfp) && !rc)
772     rc = gpg_error_from_syserror ();
773
774   /* Rename or remove the temporary file. */
775   if (rc || !any_changes)
776     gnupg_remove (tmpfname);
777   else
778     rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
779
780   xfree(bakfname);
781   xfree(tmpfname);
782   return rc;
783 }