1 /* keybox-update.c - keybox update operations
2 * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
28 #include "keybox-defs.h"
33 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
39 # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
42 # define LONG_MIN (-1 - LONG_MAX)
46 * A substitute for fseeko, for hosts that don't have it.
49 fseeko (FILE * stream
, off_t newpos
, int whence
)
51 while (newpos
!= (long) newpos
)
53 long pos
= newpos
< 0 ? LONG_MIN
: LONG_MAX
;
54 if (fseek (stream
, pos
, whence
) != 0)
59 return fseek (stream
, (long) newpos
, whence
);
61 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
66 create_tmp_file (const char *template,
67 char **r_bakfname
, char **r_tmpfname
, FILE **r_fp
)
69 char *bakfname
, *tmpfname
;
74 # ifdef USE_ONLY_8DOT3
75 /* Here is another Windoze bug?:
76 * you cant rename("pubring.kbx.tmp", "pubring.kbx");
77 * but rename("pubring.kbx.tmp", "pubring.aaa");
78 * works. So we replace ".kbx" by ".kb_" or ".k__". Note that we
79 * can't use ".bak" and ".tmp", because these suffixes are used by
80 * gpg and would lead to a sharing violation or data corruption.
82 if (strlen (template) > 4
83 && !strcmp (template+strlen(template)-4, EXTSEP_S
"kbx") )
85 bakfname
= xtrymalloc (strlen (template) + 1);
87 return gpg_error_from_syserror ();
88 strcpy (bakfname
, template);
89 strcpy (bakfname
+strlen(template)-4, EXTSEP_S
"kb_");
91 tmpfname
= xtrymalloc (strlen (template) + 1);
94 gpg_error_t tmperr
= gpg_error_from_syserror ();
98 strcpy (tmpfname
,template);
99 strcpy (tmpfname
+ strlen (template)-4, EXTSEP_S
"k__");
102 { /* File does not end with kbx, thus we hope we are working on a
103 modern file system and appending a suffix works. */
104 bakfname
= xtrymalloc ( strlen (template) + 5);
106 return gpg_error_from_syserror ();
107 strcpy (stpcpy (bakfname
, template), EXTSEP_S
"kb_");
109 tmpfname
= xtrymalloc ( strlen (template) + 5);
112 gpg_error_t tmperr
= gpg_error_from_syserror ();
116 strcpy (stpcpy (tmpfname
, template), EXTSEP_S
"k__");
118 # else /* Posix file names */
119 bakfname
= xtrymalloc (strlen (template) + 2);
121 return gpg_error_from_syserror ();
122 strcpy (stpcpy (bakfname
,template),"~");
124 tmpfname
= xtrymalloc ( strlen (template) + 5);
127 gpg_error_t tmperr
= gpg_error_from_syserror ();
131 strcpy (stpcpy (tmpfname
,template), EXTSEP_S
"tmp");
132 # endif /* Posix filename */
134 *r_fp
= fopen (tmpfname
, "wb");
137 gpg_error_t tmperr
= gpg_error_from_syserror ();
143 *r_bakfname
= bakfname
;
144 *r_tmpfname
= tmpfname
;
150 rename_tmp_file (const char *bakfname
, const char *tmpfname
,
151 const char *fname
, int secret
)
155 /* restrict the permissions for secret keyboxs */
156 #ifndef HAVE_DOSISH_SYSTEM
157 /* if (secret && !opt.preserve_permissions) */
159 /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */
161 /* log_debug ("chmod of `%s' failed: %s\n", */
162 /* tmpfname, strerror(errno) ); */
163 /* return KEYBOX_Write_File; */
168 /* fixme: invalidate close caches (not used with stdio)*/
169 /* iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */
170 /* iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */
171 /* iobuf_ioctl (NULL, 2, 0, (char*)fname ); */
173 /* First make a backup file except for secret keyboxes. */
176 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
179 if (rename (fname
, bakfname
) )
181 return gpg_error_from_syserror ();
185 /* Then rename the file. */
186 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
189 if (rename (tmpfname
, fname
) )
191 rc
= gpg_error_from_syserror ();
194 /* log_info ("WARNING: 2 files with confidential" */
195 /* " information exists.\n"); */
196 /* log_info ("%s is the unchanged one\n", fname ); */
197 /* log_info ("%s is the new one\n", tmpfname ); */
198 /* log_info ("Please fix this possible security flaw\n"); */
208 /* Perform insert/delete/update operation.
214 blob_filecopy (int mode
, const char *fname
, KEYBOXBLOB blob
,
215 int secret
, off_t start_offset
)
219 char *bakfname
= NULL
;
220 char *tmpfname
= NULL
;
224 /* Open the source file. Because we do a rename, we have to check the
225 permissions of the file */
226 if (access (fname
, W_OK
))
227 return gpg_error_from_syserror ();
229 fp
= fopen (fname
, "rb");
230 if (mode
== 1 && !fp
&& errno
== ENOENT
)
232 /* Insert mode but file does not exist:
233 Create a new keybox file. */
234 newfp
= fopen (fname
, "wb");
236 return gpg_error_from_syserror ();
238 rc
= _keybox_write_header_blob (newfp
);
242 rc
= _keybox_write_blob (blob
, newfp
);
246 if ( fclose (newfp
) )
247 return gpg_error_from_syserror ();
249 /* if (chmod( fname, S_IRUSR | S_IWUSR )) */
251 /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
252 /* return KEYBOX_File_Error; */
254 return 0; /* Ready. */
259 rc
= gpg_error_from_syserror ();
263 /* Create the new file. */
264 rc
= create_tmp_file (fname
, &bakfname
, &tmpfname
, &newfp
);
271 /* prepare for insert */
274 /* Copy everything to the new file. */
275 while ( (nread
= fread (buffer
, 1, DIM(buffer
), fp
)) > 0 )
277 if (fwrite (buffer
, nread
, 1, newfp
) != 1)
279 rc
= gpg_error_from_syserror ();
285 rc
= gpg_error_from_syserror ();
290 /* Prepare for delete or update. */
291 if ( mode
== 2 || mode
== 3 )
295 /* Copy first part to the new file. */
296 while ( current
< start_offset
)
298 nbytes
= DIM(buffer
);
299 if (current
+ nbytes
> start_offset
)
300 nbytes
= start_offset
- current
;
301 nread
= fread (buffer
, 1, nbytes
, fp
);
306 if (fwrite (buffer
, nread
, 1, newfp
) != 1)
308 rc
= gpg_error_from_syserror ();
314 rc
= gpg_error_from_syserror ();
318 /* Skip this blob. */
319 rc
= _keybox_read_blob (NULL
, fp
);
324 /* Do an insert or update. */
325 if ( mode
== 1 || mode
== 3 )
327 rc
= _keybox_write_blob (blob
, newfp
);
332 /* Copy the rest of the packet for an delete or update. */
333 if (mode
== 2 || mode
== 3)
335 while ( (nread
= fread (buffer
, 1, DIM(buffer
), fp
)) > 0 )
337 if (fwrite (buffer
, nread
, 1, newfp
) != 1)
339 rc
= gpg_error_from_syserror ();
345 rc
= gpg_error_from_syserror ();
350 /* Close both files. */
353 rc
= gpg_error_from_syserror ();
359 rc
= gpg_error_from_syserror ();
363 rc
= rename_tmp_file (bakfname
, tmpfname
, fname
, secret
);
373 #ifdef KEYBOX_WITH_X509
375 keybox_insert_cert (KEYBOX_HANDLE hd
, ksba_cert_t cert
,
376 unsigned char *sha1_digest
)
383 return gpg_error (GPG_ERR_INV_HANDLE
);
385 return gpg_error (GPG_ERR_INV_HANDLE
);
386 fname
= hd
->kb
->fname
;
388 return gpg_error (GPG_ERR_INV_HANDLE
);
390 /* Close this one otherwise we will mess up the position for a next
391 search. Fixme: it would be better to adjust the position after
392 the write operation. */
393 _keybox_close_file (hd
);
395 rc
= _keybox_create_x509_blob (&blob
, cert
, sha1_digest
, hd
->ephemeral
);
398 rc
= blob_filecopy (1, fname
, blob
, hd
->secret
, 0);
399 _keybox_release_blob (blob
);
400 /* if (!rc && !hd->secret && kb_offtbl) */
402 /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
409 keybox_update_cert (KEYBOX_HANDLE hd
, ksba_cert_t cert
,
410 unsigned char *sha1_digest
)
419 #endif /*KEYBOX_WITH_X509*/
421 /* Note: We assume that the keybox has been locked before the current
422 search was executed. This is needed so that we can depend on the
423 offset information of the flags. */
425 keybox_set_flags (KEYBOX_HANDLE hd
, int what
, int idx
, unsigned int value
)
431 size_t flag_pos
, flag_size
;
432 const unsigned char *buffer
;
435 (void)idx
; /* Not yet used. */
438 return gpg_error (GPG_ERR_INV_VALUE
);
440 return gpg_error (GPG_ERR_NOTHING_FOUND
);
442 return gpg_error (GPG_ERR_INV_HANDLE
);
444 return gpg_error (GPG_ERR_NOTHING_FOUND
);
445 fname
= hd
->kb
->fname
;
447 return gpg_error (GPG_ERR_INV_HANDLE
);
449 off
= _keybox_get_blob_fileoffset (hd
->found
.blob
);
450 if (off
== (off_t
)-1)
451 return gpg_error (GPG_ERR_GENERAL
);
453 buffer
= _keybox_get_blob_image (hd
->found
.blob
, &length
);
454 ec
= _keybox_get_flag_location (buffer
, length
, what
, &flag_pos
, &flag_size
);
456 return gpg_error (ec
);
460 _keybox_close_file (hd
);
461 fp
= fopen (hd
->kb
->fname
, "r+b");
463 return gpg_error_from_syserror ();
466 if (fseeko (fp
, off
, SEEK_SET
))
467 ec
= gpg_error_from_syserror ();
470 unsigned char tmp
[4];
472 tmp
[0] = value
>> 24;
473 tmp
[1] = value
>> 16;
482 if (fwrite (tmp
+4-flag_size
, flag_size
, 1, fp
) != 1)
483 ec
= gpg_err_code_from_syserror ();
494 ec
= gpg_err_code_from_syserror ();
497 return gpg_error (ec
);
503 keybox_delete (KEYBOX_HANDLE hd
)
511 return gpg_error (GPG_ERR_INV_VALUE
);
513 return gpg_error (GPG_ERR_NOTHING_FOUND
);
515 return gpg_error (GPG_ERR_INV_HANDLE
);
516 fname
= hd
->kb
->fname
;
518 return gpg_error (GPG_ERR_INV_HANDLE
);
520 off
= _keybox_get_blob_fileoffset (hd
->found
.blob
);
521 if (off
== (off_t
)-1)
522 return gpg_error (GPG_ERR_GENERAL
);
525 _keybox_close_file (hd
);
526 fp
= fopen (hd
->kb
->fname
, "r+b");
528 return gpg_error_from_syserror ();
530 if (fseeko (fp
, off
, SEEK_SET
))
531 rc
= gpg_error_from_syserror ();
532 else if (putc (0, fp
) == EOF
)
533 rc
= gpg_error_from_syserror ();
540 rc
= gpg_error_from_syserror ();
547 /* Compress the keybox file. This should be run with the file
550 keybox_compress (KEYBOX_HANDLE hd
)
555 char *bakfname
= NULL
;
556 char *tmpfname
= NULL
;
558 KEYBOXBLOB blob
= NULL
;
564 return gpg_error (GPG_ERR_INV_HANDLE
);
566 return gpg_error (GPG_ERR_INV_HANDLE
);
568 return gpg_error (GPG_ERR_NOT_IMPLEMENTED
);
569 fname
= hd
->kb
->fname
;
571 return gpg_error (GPG_ERR_INV_HANDLE
);
573 _keybox_close_file (hd
);
575 /* Open the source file. Because we do a rename, we have to check the
576 permissions of the file */
577 if (access (fname
, W_OK
))
578 return gpg_error_from_syserror ();
580 fp
= fopen (fname
, "rb");
581 if (!fp
&& errno
== ENOENT
)
582 return 0; /* Ready. File has been deleted right after the access above. */
585 rc
= gpg_error_from_syserror ();
589 /* A quick test to see if we need to compress the file at all. We
590 schedule a compress run after 3 hours. */
591 if ( !_keybox_read_blob (&blob
, fp
) )
593 const unsigned char *buffer
;
596 buffer
= _keybox_get_blob_image (blob
, &length
);
597 if (length
> 4 && buffer
[4] == BLOBTYPE_HEADER
)
599 u32 last_maint
= ((buffer
[20] << 24) | (buffer
[20+1] << 16)
600 | (buffer
[20+2] << 8) | (buffer
[20+3]));
602 if ( (last_maint
+ 3*3600) > time (NULL
) )
605 _keybox_release_blob (blob
);
606 return 0; /* Compress run not yet needed. */
609 _keybox_release_blob (blob
);
613 /* Create the new file. */
614 rc
= create_tmp_file (fname
, &bakfname
, &tmpfname
, &newfp
);
622 /* Processing loop. By reading using _keybox_read_blob we
623 automagically skip any blobs flagged as deleted. Thus what we
624 only have to do is to check all ephemeral flagged blocks whether
625 their time has come and write out all other blobs. */
626 cut_time
= time(NULL
) - 86400;
629 for (rc
=0; !(read_rc
= _keybox_read_blob2 (&blob
, fp
, &skipped_deleted
));
630 _keybox_release_blob (blob
), blob
= NULL
)
632 unsigned int blobflags
;
633 const unsigned char *buffer
;
634 size_t length
, pos
, size
;
639 buffer
= _keybox_get_blob_image (blob
, &length
);
643 if (length
> 4 && buffer
[4] == BLOBTYPE_HEADER
)
645 /* Write out the blob with an updated maintenance time stamp. */
646 _keybox_update_header_blob (blob
);
647 rc
= _keybox_write_blob (blob
, newfp
);
653 /* The header blob is missing. Insert it. */
654 rc
= _keybox_write_header_blob (newfp
);
659 else if (length
> 4 && buffer
[4] == BLOBTYPE_HEADER
)
661 /* Oops: There is another header record - remove it. */
666 if (_keybox_get_flag_location (buffer
, length
,
667 KEYBOX_FLAG_BLOB
, &pos
, &size
)
670 rc
= gpg_error (GPG_ERR_BUG
);
673 blobflags
= ((buffer
[pos
] << 8) | (buffer
[pos
+1]));
674 if ((blobflags
& KEYBOX_FLAG_BLOB_EPHEMERAL
))
676 /* This is an ephemeral blob. */
677 if (_keybox_get_flag_location (buffer
, length
,
678 KEYBOX_FLAG_CREATED_AT
, &pos
, &size
)
680 created_at
= 0; /* oops. */
682 created_at
= ((buffer
[pos
] << 24) | (buffer
[pos
+1] << 16)
683 | (buffer
[pos
+2] << 8) | (buffer
[pos
+3]));
685 if (created_at
&& created_at
< cut_time
)
688 continue; /* Skip this blob. */
692 rc
= _keybox_write_blob (blob
, newfp
);
698 _keybox_release_blob (blob
); blob
= NULL
;
699 if (!rc
&& read_rc
== -1)
704 /* Close both files. */
705 if (fclose(fp
) && !rc
)
706 rc
= gpg_error_from_syserror ();
707 if (fclose(newfp
) && !rc
)
708 rc
= gpg_error_from_syserror ();
710 /* Rename or remove the temporary file. */
711 if (rc
|| !any_changes
)
714 rc
= rename_tmp_file (bakfname
, tmpfname
, fname
, hd
->secret
);