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"
29 #include "../common/sysutils.h"
34 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
40 # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
43 # define LONG_MIN (-1 - LONG_MAX)
47 * A substitute for fseeko, for hosts that don't have it.
50 fseeko (FILE * stream, off_t newpos, int whence)
52 while (newpos != (long) newpos)
54 long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
55 if (fseek (stream, pos, whence) != 0)
60 return fseek (stream, (long) newpos, whence);
62 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
67 create_tmp_file (const char *template,
68 char **r_bakfname, char **r_tmpfname, FILE **r_fp)
70 char *bakfname, *tmpfname;
75 # ifdef USE_ONLY_8DOT3
76 /* Here is another Windoze bug?:
77 * you cant rename("pubring.kbx.tmp", "pubring.kbx");
78 * but rename("pubring.kbx.tmp", "pubring.aaa");
79 * works. So we replace ".kbx" by ".kb_" or ".k__". Note that we
80 * can't use ".bak" and ".tmp", because these suffixes are used by
81 * gpg and would lead to a sharing violation or data corruption.
83 if (strlen (template) > 4
84 && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
86 bakfname = xtrymalloc (strlen (template) + 1);
88 return gpg_error_from_syserror ();
89 strcpy (bakfname, template);
90 strcpy (bakfname+strlen(template)-4, EXTSEP_S "kb_");
92 tmpfname = xtrymalloc (strlen (template) + 1);
95 gpg_error_t tmperr = gpg_error_from_syserror ();
99 strcpy (tmpfname,template);
100 strcpy (tmpfname + strlen (template)-4, EXTSEP_S "k__");
103 { /* File does not end with kbx, thus we hope we are working on a
104 modern file system and appending a suffix works. */
105 bakfname = xtrymalloc ( strlen (template) + 5);
107 return gpg_error_from_syserror ();
108 strcpy (stpcpy (bakfname, template), EXTSEP_S "kb_");
110 tmpfname = xtrymalloc ( strlen (template) + 5);
113 gpg_error_t tmperr = gpg_error_from_syserror ();
117 strcpy (stpcpy (tmpfname, template), EXTSEP_S "k__");
119 # else /* Posix file names */
120 bakfname = xtrymalloc (strlen (template) + 2);
122 return gpg_error_from_syserror ();
123 strcpy (stpcpy (bakfname,template),"~");
125 tmpfname = xtrymalloc ( strlen (template) + 5);
128 gpg_error_t tmperr = gpg_error_from_syserror ();
132 strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
133 # endif /* Posix filename */
135 *r_fp = fopen (tmpfname, "wb");
138 gpg_error_t tmperr = gpg_error_from_syserror ();
144 *r_bakfname = bakfname;
145 *r_tmpfname = tmpfname;
151 rename_tmp_file (const char *bakfname, const char *tmpfname,
152 const char *fname, int secret )
156 /* restrict the permissions for secret keyboxs */
157 #ifndef HAVE_DOSISH_SYSTEM
158 /* if (secret && !opt.preserve_permissions) */
160 /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */
162 /* log_debug ("chmod of `%s' failed: %s\n", */
163 /* tmpfname, strerror(errno) ); */
164 /* return KEYBOX_Write_File; */
169 /* fixme: invalidate close caches (not used with stdio)*/
170 /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname ); */
171 /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); */
172 /* iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); */
174 /* First make a backup file except for secret keyboxes. */
177 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
178 gnupg_remove (bakfname);
180 if (rename (fname, bakfname) )
182 return gpg_error_from_syserror ();
186 /* Then rename the file. */
187 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
188 gnupg_remove (fname);
190 if (rename (tmpfname, fname) )
192 rc = gpg_error_from_syserror ();
195 /* log_info ("WARNING: 2 files with confidential" */
196 /* " information exists.\n"); */
197 /* log_info ("%s is the unchanged one\n", fname ); */
198 /* log_info ("%s is the new one\n", tmpfname ); */
199 /* log_info ("Please fix this possible security flaw\n"); */
209 /* Perform insert/delete/update operation.
215 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
216 int secret, off_t start_offset)
220 char *bakfname = NULL;
221 char *tmpfname = NULL;
225 /* Open the source file. Because we do a rename, we have to check the
226 permissions of the file */
227 if (access (fname, W_OK))
228 return gpg_error_from_syserror ();
230 fp = fopen (fname, "rb");
231 if (mode == 1 && !fp && errno == ENOENT)
233 /* Insert mode but file does not exist:
234 Create a new keybox file. */
235 newfp = fopen (fname, "wb");
237 return gpg_error_from_syserror ();
239 rc = _keybox_write_header_blob (newfp);
243 rc = _keybox_write_blob (blob, newfp);
247 if ( fclose (newfp) )
248 return gpg_error_from_syserror ();
250 /* if (chmod( fname, S_IRUSR | S_IWUSR )) */
252 /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
253 /* return KEYBOX_File_Error; */
255 return 0; /* Ready. */
260 rc = gpg_error_from_syserror ();
264 /* Create the new file. */
265 rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
272 /* prepare for insert */
275 /* Copy everything to the new file. */
276 while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
278 if (fwrite (buffer, nread, 1, newfp) != 1)
280 rc = gpg_error_from_syserror ();
286 rc = gpg_error_from_syserror ();
291 /* Prepare for delete or update. */
292 if ( mode == 2 || mode == 3 )
296 /* Copy first part to the new file. */
297 while ( current < start_offset )
299 nbytes = DIM(buffer);
300 if (current + nbytes > start_offset)
301 nbytes = start_offset - current;
302 nread = fread (buffer, 1, nbytes, fp);
307 if (fwrite (buffer, nread, 1, newfp) != 1)
309 rc = gpg_error_from_syserror ();
315 rc = gpg_error_from_syserror ();
319 /* Skip this blob. */
320 rc = _keybox_read_blob (NULL, fp);
325 /* Do an insert or update. */
326 if ( mode == 1 || mode == 3 )
328 rc = _keybox_write_blob (blob, newfp);
333 /* Copy the rest of the packet for an delete or update. */
334 if (mode == 2 || mode == 3)
336 while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
338 if (fwrite (buffer, nread, 1, newfp) != 1)
340 rc = gpg_error_from_syserror ();
346 rc = gpg_error_from_syserror ();
351 /* Close both files. */
354 rc = gpg_error_from_syserror ();
360 rc = gpg_error_from_syserror ();
364 rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
374 #ifdef KEYBOX_WITH_X509
376 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
377 unsigned char *sha1_digest)
384 return gpg_error (GPG_ERR_INV_HANDLE);
386 return gpg_error (GPG_ERR_INV_HANDLE);
387 fname = hd->kb->fname;
389 return gpg_error (GPG_ERR_INV_HANDLE);
391 /* Close this one otherwise we will mess up the position for a next
392 search. Fixme: it would be better to adjust the position after
393 the write operation. */
394 _keybox_close_file (hd);
396 rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
399 rc = blob_filecopy (1, fname, blob, hd->secret, 0);
400 _keybox_release_blob (blob);
401 /* if (!rc && !hd->secret && kb_offtbl) */
403 /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
410 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
411 unsigned char *sha1_digest)
420 #endif /*KEYBOX_WITH_X509*/
422 /* Note: We assume that the keybox has been locked before the current
423 search was executed. This is needed so that we can depend on the
424 offset information of the flags. */
426 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
432 size_t flag_pos, flag_size;
433 const unsigned char *buffer;
436 (void)idx; /* Not yet used. */
439 return gpg_error (GPG_ERR_INV_VALUE);
441 return gpg_error (GPG_ERR_NOTHING_FOUND);
443 return gpg_error (GPG_ERR_INV_HANDLE);
445 return gpg_error (GPG_ERR_NOTHING_FOUND);
446 fname = hd->kb->fname;
448 return gpg_error (GPG_ERR_INV_HANDLE);
450 off = _keybox_get_blob_fileoffset (hd->found.blob);
451 if (off == (off_t)-1)
452 return gpg_error (GPG_ERR_GENERAL);
454 buffer = _keybox_get_blob_image (hd->found.blob, &length);
455 ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
457 return gpg_error (ec);
461 _keybox_close_file (hd);
462 fp = fopen (hd->kb->fname, "r+b");
464 return gpg_error_from_syserror ();
467 if (fseeko (fp, off, SEEK_SET))
468 ec = gpg_error_from_syserror ();
471 unsigned char tmp[4];
473 tmp[0] = value >> 24;
474 tmp[1] = value >> 16;
483 if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
484 ec = gpg_err_code_from_syserror ();
495 ec = gpg_err_code_from_syserror ();
498 return gpg_error (ec);
504 keybox_delete (KEYBOX_HANDLE hd)
512 return gpg_error (GPG_ERR_INV_VALUE);
514 return gpg_error (GPG_ERR_NOTHING_FOUND);
516 return gpg_error (GPG_ERR_INV_HANDLE);
517 fname = hd->kb->fname;
519 return gpg_error (GPG_ERR_INV_HANDLE);
521 off = _keybox_get_blob_fileoffset (hd->found.blob);
522 if (off == (off_t)-1)
523 return gpg_error (GPG_ERR_GENERAL);
526 _keybox_close_file (hd);
527 fp = fopen (hd->kb->fname, "r+b");
529 return gpg_error_from_syserror ();
531 if (fseeko (fp, off, SEEK_SET))
532 rc = gpg_error_from_syserror ();
533 else if (putc (0, fp) == EOF)
534 rc = gpg_error_from_syserror ();
541 rc = gpg_error_from_syserror ();
548 /* Compress the keybox file. This should be run with the file
551 keybox_compress (KEYBOX_HANDLE hd)
556 char *bakfname = NULL;
557 char *tmpfname = NULL;
559 KEYBOXBLOB blob = NULL;
565 return gpg_error (GPG_ERR_INV_HANDLE);
567 return gpg_error (GPG_ERR_INV_HANDLE);
569 return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
570 fname = hd->kb->fname;
572 return gpg_error (GPG_ERR_INV_HANDLE);
574 _keybox_close_file (hd);
576 /* Open the source file. Because we do a rename, we have to check the
577 permissions of the file */
578 if (access (fname, W_OK))
579 return gpg_error_from_syserror ();
581 fp = fopen (fname, "rb");
582 if (!fp && errno == ENOENT)
583 return 0; /* Ready. File has been deleted right after the access above. */
586 rc = gpg_error_from_syserror ();
590 /* A quick test to see if we need to compress the file at all. We
591 schedule a compress run after 3 hours. */
592 if ( !_keybox_read_blob (&blob, fp) )
594 const unsigned char *buffer;
597 buffer = _keybox_get_blob_image (blob, &length);
598 if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
600 u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16)
601 | (buffer[20+2] << 8) | (buffer[20+3]));
603 if ( (last_maint + 3*3600) > time (NULL) )
606 _keybox_release_blob (blob);
607 return 0; /* Compress run not yet needed. */
610 _keybox_release_blob (blob);
611 fseek (fp, 0, SEEK_SET);
615 /* Create the new file. */
616 rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
624 /* Processing loop. By reading using _keybox_read_blob we
625 automagically skip any blobs flagged as deleted. Thus what we
626 only have to do is to check all ephemeral flagged blocks whether
627 their time has come and write out all other blobs. */
628 cut_time = time(NULL) - 86400;
631 for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
632 _keybox_release_blob (blob), blob = NULL )
634 unsigned int blobflags;
635 const unsigned char *buffer;
636 size_t length, pos, size;
641 buffer = _keybox_get_blob_image (blob, &length);
645 if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
647 /* Write out the blob with an updated maintenance time stamp. */
648 _keybox_update_header_blob (blob);
649 rc = _keybox_write_blob (blob, newfp);
655 /* The header blob is missing. Insert it. */
656 rc = _keybox_write_header_blob (newfp);
661 else if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
663 /* Oops: There is another header record - remove it. */
668 if (_keybox_get_flag_location (buffer, length,
669 KEYBOX_FLAG_BLOB, &pos, &size)
672 rc = gpg_error (GPG_ERR_BUG);
675 blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
676 if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
678 /* This is an ephemeral blob. */
679 if (_keybox_get_flag_location (buffer, length,
680 KEYBOX_FLAG_CREATED_AT, &pos, &size)
682 created_at = 0; /* oops. */
684 created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16)
685 | (buffer[pos+2] << 8) | (buffer[pos+3]));
687 if (created_at && created_at < cut_time)
690 continue; /* Skip this blob. */
694 rc = _keybox_write_blob (blob, newfp);
700 _keybox_release_blob (blob); blob = NULL;
701 if (!rc && read_rc == -1)
706 /* Close both files. */
707 if (fclose(fp) && !rc)
708 rc = gpg_error_from_syserror ();
709 if (fclose(newfp) && !rc)
710 rc = gpg_error_from_syserror ();
712 /* Rename or remove the temporary file. */
713 if (rc || !any_changes)
714 gnupg_remove (tmpfname);
716 rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);