gnupg/kbx/keybox-update.c

438 lines
11 KiB
C

/* 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 <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#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;
}