diff options
Diffstat (limited to 'kbx/keybox-update.c')
-rw-r--r-- | kbx/keybox-update.c | 348 |
1 files changed, 321 insertions, 27 deletions
diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c index 52ad258b0..bb43d287b 100644 --- a/kbx/keybox-update.c +++ b/kbx/keybox-update.c @@ -1,5 +1,5 @@ /* keybox-update.c - keybox update operations - * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -23,6 +24,7 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#include <time.h> #include <unistd.h> #include "keybox-defs.h" @@ -30,6 +32,38 @@ #define EXTSEP_S "." +#if !defined(HAVE_FSEEKO) && !defined(fseeko) + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif +#ifndef LONG_MAX +# define LONG_MAX ((long) ((unsigned long) -1 >> 1)) +#endif +#ifndef LONG_MIN +# define LONG_MIN (-1 - LONG_MAX) +#endif + +/**************** + * A substitute for fseeko, for hosts that don't have it. + */ +static int +fseeko (FILE * stream, off_t newpos, int whence) +{ + while (newpos != (long) newpos) + { + long pos = newpos < 0 ? LONG_MIN : LONG_MAX; + if (fseek (stream, pos, whence) != 0) + return -1; + newpos -= pos; + whence = SEEK_CUR; + } + return fseek (stream, (long) newpos, whence); +} +#endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */ + + + static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, FILE **r_fp) @@ -65,7 +99,7 @@ create_tmp_file (const char *template, strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp"); } else - { /* file does not end with kbx; hmmm */ + { /* File does not end with kbx; hmmm. */ bakfname = xtrymalloc ( strlen (template) + 5); if (!bakfname) return gpg_error (gpg_err_code_from_errno (errno)); @@ -193,29 +227,30 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, fp = fopen (fname, "rb"); if (mode == 1 && !fp && errno == ENOENT) - { /* insert mode but file does not exist: create a new keybox file */ + { + /* Insert mode but file does not exist: + Create a new keybox file. */ newfp = fopen (fname, "wb"); if (!newfp ) - { - return gpg_error (gpg_err_code_from_errno (errno)); - } + return gpg_error (gpg_err_code_from_errno (errno)); + + rc = _keybox_write_header_blob (newfp); + if (rc) + return rc; rc = _keybox_write_blob (blob, newfp); if (rc) - { - return rc; - } + return rc; + if ( fclose (newfp) ) - { - return gpg_error (gpg_err_code_from_errno (errno)); - } + return gpg_error (gpg_err_code_from_errno (errno)); /* if (chmod( fname, S_IRUSR | S_IWUSR )) */ /* { */ /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */ /* return KEYBOX_File_Error; */ /* } */ - return 0; /* ready */ + return 0; /* Ready. */ } if (!fp) @@ -224,7 +259,7 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, goto leave; } - /* create the new file */ + /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { @@ -235,7 +270,7 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, /* prepare for insert */ if (mode == 1) { - /* copy everything to the new file */ + /* Copy everything to the new file. */ while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) { if (fwrite (buffer, nread, 1, newfp) != 1) @@ -251,19 +286,19 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, } } - /* prepare for delete or update */ + /* Prepare for delete or update. */ if ( mode == 2 || mode == 3 ) { off_t current = 0; - /* copy first part to the new file */ + /* Copy first part to the new file. */ while ( current < start_offset ) { nbytes = DIM(buffer); if (current + nbytes > start_offset) nbytes = start_offset - current; nread = fread (buffer, 1, nbytes, fp); - if (!fread) + if (!nread) break; current += nread; @@ -279,13 +314,13 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, goto leave; } - /* skip this blob */ + /* Skip this blob. */ rc = _keybox_read_blob (NULL, fp); if (rc) return rc; } - /* Do an insert or update */ + /* Do an insert or update. */ if ( mode == 1 || mode == 3 ) { rc = _keybox_write_blob (blob, newfp); @@ -293,7 +328,7 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, return rc; } - /* copy the rest of the packet for an delete or update */ + /* Copy the rest of the packet for an delete or update. */ if (mode == 2 || mode == 3) { while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) @@ -311,7 +346,7 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, } } - /* close both files */ + /* Close both files. */ if (fclose(fp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); @@ -334,10 +369,9 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, - #ifdef KEYBOX_WITH_X509 int -keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, +keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert, unsigned char *sha1_digest) { int rc; @@ -352,7 +386,7 @@ keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); - /* close this one otherwise we will mess up the position for a next + /* Close this one otherwise we will mess up the position for a next search. Fixme: it would be better to adjust the position after the write opertions. */ if (hd->fp) @@ -375,7 +409,7 @@ keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, } int -keybox_update_cert (KEYBOX_HANDLE hd, KsbaCert cert, +keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert, unsigned char *sha1_digest) { return -1; @@ -384,6 +418,88 @@ keybox_update_cert (KEYBOX_HANDLE hd, KsbaCert cert, #endif /*KEYBOX_WITH_X509*/ +/* Note: We assume that the keybox has been locked before the current + search was executed. This is needed so that we can depend on the + offset information of the flags. */ +int +keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value) +{ + off_t off; + const char *fname; + FILE *fp; + gpg_err_code_t ec; + size_t flag_pos, flag_size; + const unsigned char *buffer; + size_t length; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + if (!hd->found.blob) + return gpg_error (GPG_ERR_NOTHING_FOUND); + if (!hd->kb) + return gpg_error (GPG_ERR_INV_HANDLE); + if (!hd->found.blob) + return gpg_error (GPG_ERR_NOTHING_FOUND); + fname = hd->kb->fname; + if (!fname) + return gpg_error (GPG_ERR_INV_HANDLE); + + off = _keybox_get_blob_fileoffset (hd->found.blob); + if (off == (off_t)-1) + return gpg_error (GPG_ERR_GENERAL); + + buffer = _keybox_get_blob_image (hd->found.blob, &length); + ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size); + if (ec) + return gpg_error (ec); + + off += flag_pos; + + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; + } + fp = fopen (hd->kb->fname, "r+b"); + if (!fp) + return gpg_error (gpg_err_code_from_errno (errno)); + + ec = 0; + if (fseeko (fp, off, SEEK_SET)) + ec = gpg_error (gpg_err_code_from_errno (errno)); + else + { + unsigned char tmp[4]; + + tmp[0] = value >> 24; + tmp[1] = value >> 16; + tmp[2] = value >> 8; + tmp[3] = value; + + switch (flag_size) + { + case 1: + case 2: + case 4: + if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1) + ec = gpg_err_code_from_errno (errno); + break; + default: + ec = GPG_ERR_BUG; + break; + } + } + + if (fclose (fp)) + { + if (!ec) + ec = gpg_err_code_from_errno (errno); + } + + return gpg_error (ec); +} + + int keybox_delete (KEYBOX_HANDLE hd) @@ -435,3 +551,181 @@ keybox_delete (KEYBOX_HANDLE hd) } +/* Compress the keybox file. This should be run with the file + locked. */ +int +keybox_compress (KEYBOX_HANDLE hd) +{ + int read_rc, rc; + const char *fname; + FILE *fp, *newfp; + char *bakfname = NULL; + char *tmpfname = NULL; + int first_blob; + KEYBOXBLOB blob = NULL; + u32 cut_time; + int any_changes = 0; + int skipped_deleted; + + if (!hd) + return gpg_error (GPG_ERR_INV_HANDLE); + if (!hd->kb) + return gpg_error (GPG_ERR_INV_HANDLE); + if (hd->secret) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + fname = hd->kb->fname; + if (!fname) + return gpg_error (GPG_ERR_INV_HANDLE); + + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; + } + + /* Open the source file. Because we do a rename, we have to check the + permissions of the file */ + if (access (fname, W_OK)) + return gpg_error (gpg_err_code_from_errno (errno)); + + fp = fopen (fname, "rb"); + if (!fp && errno == ENOENT) + return 0; /* Ready. File has been deleted right after the access above. */ + if (!fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + return rc; + } + + /* A quick test to see if we need to compress the file at all. We + schedule a compress run after 3 hours. */ + if ( !_keybox_read_blob (&blob, fp) ) + { + const unsigned char *buffer; + size_t length; + + buffer = _keybox_get_blob_image (blob, &length); + if (length > 4 && buffer[4] == BLOBTYPE_HEADER) + { + u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16) + | (buffer[20+2] << 8) | (buffer[20+3])); + + if ( (last_maint + 3*3600) > time (NULL) ) + { + fclose (fp); + _keybox_release_blob (blob); + return 0; /* Compress run not yet needed. */ + } + } + _keybox_release_blob (blob); + rewind (fp); + } + + /* Create the new file. */ + rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); + if (rc) + { + fclose(fp); + return rc;; + } + + + /* Processing loop. By reading using _keybox_read_blob we + automagically skip and blobs flagged as deleted. Thus what we + only have to do is to check all ephemeral flagged blocks whether + their time has come and write out all other blobs. */ + cut_time = time(NULL) - 86400; + first_blob = 1; + skipped_deleted = 0; + for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted)); + _keybox_release_blob (blob), blob = NULL ) + { + unsigned int blobflags; + const unsigned char *buffer; + size_t length, pos, size; + u32 created_at; + + if (skipped_deleted) + any_changes = 1; + buffer = _keybox_get_blob_image (blob, &length); + if (first_blob) + { + first_blob = 0; + if (length > 4 && buffer[4] == BLOBTYPE_HEADER) + { + /* Write out the blob with an updated maintenance time stamp. */ + _keybox_update_header_blob (blob); + rc = _keybox_write_blob (blob, newfp); + if (rc) + break; + continue; + } + + /* The header blob is missing. Insert it. */ + rc = _keybox_write_header_blob (newfp); + if (rc) + break; + any_changes = 1; + } + else if (length > 4 && buffer[4] == BLOBTYPE_HEADER) + { + /* Oops: There is another header record - remove it. */ + any_changes = 1; + continue; + } + + if (_keybox_get_flag_location (buffer, length, + KEYBOX_FLAG_BLOB, &pos, &size) + || size != 2) + { + rc = gpg_error (GPG_ERR_BUG); + break; + } + blobflags = ((buffer[pos] << 8) | (buffer[pos+1])); + if ((blobflags & 2)) + { + /* This is an ephemeral blob. */ + if (_keybox_get_flag_location (buffer, length, + KEYBOX_FLAG_CREATED_AT, &pos, &size) + || size != 4) + created_at = 0; /* oops. */ + else + created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16) + | (buffer[pos+2] << 8) | (buffer[pos+3])); + + if (created_at && created_at < cut_time) + { + any_changes = 1; + continue; /* Skip this blob. */ + } + } + + rc = _keybox_write_blob (blob, newfp); + if (rc) + break; + } + if (skipped_deleted) + any_changes = 1; + _keybox_release_blob (blob); blob = NULL; + if (!rc && read_rc == -1) + rc = 0; + else if (!rc) + rc = read_rc; + + /* Close both files. */ + if (fclose(fp) && !rc) + rc = gpg_error (gpg_err_code_from_errno (errno)); + if (fclose(newfp) && !rc) + rc = gpg_error (gpg_err_code_from_errno (errno)); + + /* Rename or remove the temporary file. */ + if (rc || !any_changes) + remove (tmpfname); + else + rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret); + + xfree(bakfname); + xfree(tmpfname); + return rc; +} + |