/* keybox-update.c - keybox update operations * Copyright (C) 2001, 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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 */ #include #include #include #include #include #include #include "keybox-defs.h" #define EXTSEP_S "." static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, FILE **r_fp) { char *bakfname, *tmpfname; *r_bakfname = NULL; *r_tmpfname = NULL; # ifdef USE_ONLY_8DOT3 /* Here is another Windoze bug?: * you cant rename("pubring.kbx.tmp", "pubring.kbx"); * but rename("pubring.kbx.tmp", "pubring.aaa"); * works. So we replace .kbx by .bak or .tmp */ if (strlen (template) > 4 && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") ) { bakfname = xtrymalloc (strlen (template) + 1); if (!bakfname) return gpg_error (gpg_err_code_from_errno (errno)); strcpy (bakfname, template); strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak"); tmpfname = xtrymalloc (strlen (template) + 1); if (!tmpfname) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (bakfname); return tmperr; } strcpy (tmpfname,template); strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp"); } else { /* file does not end with kbx; hmmm */ bakfname = xtrymalloc ( strlen (template) + 5); if (!bakfname) return gpg_error (gpg_err_code_from_errno (errno)); strcpy (stpcpy (bakfname, template), EXTSEP_S "bak"); tmpfname = xtrymalloc ( strlen (template) + 5); if (!tmpfname) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (bakfname); return tmperr; } strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp"); } # else /* Posix file names */ bakfname = xtrymalloc (strlen (template) + 2); if (!bakfname) return gpg_error (gpg_err_code_from_errno (errno)); strcpy (stpcpy (bakfname,template),"~"); tmpfname = xtrymalloc ( strlen (template) + 5); if (!tmpfname) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (bakfname); return tmperr; } strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp"); # endif /* Posix filename */ *r_fp = fopen (tmpfname, "wb"); if (!*r_fp) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (tmpfname); xfree (bakfname); return tmperr; } *r_bakfname = bakfname; *r_tmpfname = tmpfname; return 0; } static int rename_tmp_file (const char *bakfname, const char *tmpfname, const char *fname, int secret ) { int rc=0; /* restrict the permissions for secret keyboxs */ #ifndef HAVE_DOSISH_SYSTEM /* if (secret && !opt.preserve_permissions) */ /* { */ /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */ /* { */ /* log_debug ("chmod of `%s' failed: %s\n", */ /* tmpfname, strerror(errno) ); */ /* return KEYBOX_Write_File; */ /* } */ /* } */ #endif /* fixme: invalidate close caches (not used with stdio)*/ /* iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */ /* iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */ /* iobuf_ioctl (NULL, 2, 0, (char*)fname ); */ /* first make a backup file except for secret keyboxs */ if (!secret) { #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove (bakfname); #endif if (rename (fname, bakfname) ) { return gpg_error (gpg_err_code_from_errno (errno)); } } /* then rename the file */ #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) remove (fname); #endif if (rename (tmpfname, fname) ) { rc = gpg_error (gpg_err_code_from_errno (errno)); if (secret) { /* log_info ("WARNING: 2 files with confidential" */ /* " information exists.\n"); */ /* log_info ("%s is the unchanged one\n", fname ); */ /* log_info ("%s is the new one\n", tmpfname ); */ /* log_info ("Please fix this possible security flaw\n"); */ } return rc; } return 0; } /* Perform insert/delete/update operation. mode 1 = insert 2 = delete 3 = update */ static int blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, int secret, off_t start_offset, unsigned int n_packets ) { FILE *fp, *newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; char buffer[4096]; int nread, nbytes; /* 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 (mode == 1 && !fp && errno == ENOENT) { /* 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)); } rc = _keybox_write_blob (blob, newfp); if (rc) { return rc; } if ( fclose (newfp) ) { 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 */ } if (!fp) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } /* create the new file */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { fclose(fp); goto leave; } /* prepare for insert */ if (mode == 1) { /* copy everything to the new file */ while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) { if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } } if (ferror (fp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } } /* prepare for delete or update */ if ( mode == 2 || mode == 3 ) { off_t current = 0; /* 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) break; current += nread; if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } } if (ferror (fp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } /* skip this blob */ rc = _keybox_read_blob (NULL, fp); if (rc) return rc; } /* Do an insert or update */ if ( mode == 1 || mode == 3 ) { rc = _keybox_write_blob (blob, newfp); if (rc) return rc; } /* 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 ) { if (fwrite (buffer, nread, 1, newfp) != 1) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } } if (ferror (fp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } } /* close both files */ if (fclose(fp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); fclose (newfp); goto leave; } if (fclose(newfp)) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } rc = rename_tmp_file (bakfname, tmpfname, fname, secret); leave: xfree(bakfname); xfree(tmpfname); return rc; } #ifdef KEYBOX_WITH_X509 int keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, unsigned char *sha1_digest) { int rc; const char *fname; KEYBOXBLOB blob; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (!hd->kb) return gpg_error (GPG_ERR_INV_HANDLE); fname = hd->kb->fname; if (!fname) return gpg_error (GPG_ERR_INV_HANDLE); /* 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) { fclose (hd->fp); hd->fp = NULL; } rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral); if (!rc) { rc = blob_filecopy (1, fname, blob, hd->secret, 0, 0 ); _keybox_release_blob (blob); /* if (!rc && !hd->secret && kb_offtbl) */ /* { */ /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */ /* } */ } return rc; } int keybox_update_cert (KEYBOX_HANDLE hd, KsbaCert cert, unsigned char *sha1_digest) { return -1; } #endif /*KEYBOX_WITH_X509*/ int keybox_delete (KEYBOX_HANDLE hd) { off_t off; const char *fname; FILE *fp; int rc; 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); 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); off += 4; 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)); if (fseeko (fp, off, SEEK_SET)) rc = gpg_error (gpg_err_code_from_errno (errno)); else if (putc (0, fp) == EOF) rc = gpg_error (gpg_err_code_from_errno (errno)); else rc = 0; if (fclose (fp)) { if (!rc) rc = gpg_error (gpg_err_code_from_errno (errno)); } return rc; }