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