mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-02 12:01:32 +01:00
agent: Do not overwrite a key file by a shadow key file.
* agent/findkey.c (agent_write_private_key): Partly rewrite to align with 2.2 code and to make sure that we don't overwrite a real key. (is_shadowed_key): New. -- This change is now also needed in 2.4 due to the the former change "Create and use Token entries to track the display s/n". GnuPG-bug-id: 6386
This commit is contained in:
parent
05f29b5c7c
commit
a1015bf2fc
154
agent/findkey.c
154
agent/findkey.c
@ -39,6 +39,12 @@
|
|||||||
#define O_BINARY 0
|
#define O_BINARY 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static gpg_error_t read_key_file (const unsigned char *grip,
|
||||||
|
gcry_sexp_t *result, nvc_t *r_keymeta);
|
||||||
|
static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey);
|
||||||
|
|
||||||
|
|
||||||
/* Helper to pass data to the check callback of the unprotect function. */
|
/* Helper to pass data to the check callback of the unprotect function. */
|
||||||
struct try_unprotect_arg_s
|
struct try_unprotect_arg_s
|
||||||
{
|
{
|
||||||
@ -113,7 +119,7 @@ agent_write_private_key (const unsigned char *grip,
|
|||||||
char *fname = NULL;
|
char *fname = NULL;
|
||||||
char *tmpfname = NULL;
|
char *tmpfname = NULL;
|
||||||
estream_t fp;
|
estream_t fp;
|
||||||
int update, newkey;
|
int newkey;
|
||||||
nvc_t pk = NULL;
|
nvc_t pk = NULL;
|
||||||
gcry_sexp_t key = NULL;
|
gcry_sexp_t key = NULL;
|
||||||
int removetmp = 0;
|
int removetmp = 0;
|
||||||
@ -121,103 +127,29 @@ agent_write_private_key (const unsigned char *grip,
|
|||||||
char *token = NULL;
|
char *token = NULL;
|
||||||
char *dispserialno_buffer = NULL;
|
char *dispserialno_buffer = NULL;
|
||||||
char **tokenfields = NULL;
|
char **tokenfields = NULL;
|
||||||
|
int is_regular;
|
||||||
int blocksigs = 0;
|
int blocksigs = 0;
|
||||||
|
|
||||||
fname = fname_from_keygrip (grip, 0);
|
fname = fname_from_keygrip (grip, 0);
|
||||||
if (!fname)
|
if (!fname)
|
||||||
return gpg_error_from_syserror ();
|
return gpg_error_from_syserror ();
|
||||||
|
|
||||||
if (!force && !gnupg_access (fname, F_OK))
|
err = read_key_file (grip, &key, &pk);
|
||||||
|
if (err)
|
||||||
{
|
{
|
||||||
log_error ("secret key file '%s' already exists\n", fname);
|
if (gpg_err_code (err) == GPG_ERR_ENOENT)
|
||||||
xfree (fname);
|
newkey = 1;
|
||||||
return gpg_error (GPG_ERR_EEXIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw");
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
gpg_error_t tmperr = gpg_error_from_syserror ();
|
|
||||||
|
|
||||||
if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT)
|
|
||||||
{
|
|
||||||
fp = es_fopen (fname, "wbx,mode=-rw");
|
|
||||||
if (!fp)
|
|
||||||
tmperr = gpg_error_from_syserror ();
|
|
||||||
}
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr));
|
|
||||||
xfree (fname);
|
|
||||||
return tmperr;
|
|
||||||
}
|
|
||||||
update = 0;
|
|
||||||
newkey = 1;
|
|
||||||
}
|
|
||||||
else if (force)
|
|
||||||
{
|
|
||||||
gpg_error_t rc;
|
|
||||||
char first;
|
|
||||||
|
|
||||||
/* See if an existing key is in extended format. */
|
|
||||||
if (es_fread (&first, 1, 1, fp) != 1)
|
|
||||||
{
|
|
||||||
rc = gpg_error_from_syserror ();
|
|
||||||
log_error ("error reading first byte from '%s': %s\n",
|
|
||||||
fname, strerror (errno));
|
|
||||||
xfree (fname);
|
|
||||||
es_fclose (fp);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = es_fseek (fp, 0, SEEK_SET);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
log_error ("error seeking in '%s': %s\n", fname, strerror (errno));
|
|
||||||
xfree (fname);
|
|
||||||
es_fclose (fp);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first == '(')
|
|
||||||
{
|
|
||||||
/* Key is still in the old format - force it into extended
|
|
||||||
* format. We do not request an update here because an
|
|
||||||
* existing key is not yet in extended key format and no
|
|
||||||
* extended infos are yet available. */
|
|
||||||
update = 0;
|
|
||||||
newkey = 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Key is already in the extended format. */
|
log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
|
||||||
update = 1;
|
|
||||||
newkey = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The key file did not exist: we assume this is a new key and
|
|
||||||
* write the Created: entry. */
|
|
||||||
update = 0;
|
|
||||||
newkey = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (update)
|
|
||||||
{
|
|
||||||
int line;
|
|
||||||
|
|
||||||
err = nvc_parse_private_key (&pk, &line, fp);
|
|
||||||
if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
|
|
||||||
{
|
|
||||||
log_error ("error parsing '%s' line %d: %s\n",
|
|
||||||
fname, line, gpg_strerror (err));
|
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!pk)
|
||||||
{
|
{
|
||||||
|
/* Key is still in the old format or does not exist - create a
|
||||||
|
* new container. */
|
||||||
pk = nvc_new_private_key ();
|
pk = nvc_new_private_key ();
|
||||||
if (!pk)
|
if (!pk)
|
||||||
{
|
{
|
||||||
@ -225,11 +157,13 @@ agent_write_private_key (const unsigned char *grip,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
es_fclose (fp);
|
|
||||||
fp = NULL;
|
/* Check whether we already have a regular key. */
|
||||||
|
is_regular = (key && gpg_err_code (is_shadowed_key (key)) != GPG_ERR_TRUE);
|
||||||
|
|
||||||
/* Turn (BUFFER,LENGTH) into a gcrypt s-expression and set it into
|
/* Turn (BUFFER,LENGTH) into a gcrypt s-expression and set it into
|
||||||
* our name value container. */
|
* our name value container. */
|
||||||
|
gcry_sexp_release (key);
|
||||||
err = gcry_sexp_sscan (&key, NULL, buffer, length);
|
err = gcry_sexp_sscan (&key, NULL, buffer, length);
|
||||||
if (err)
|
if (err)
|
||||||
goto leave;
|
goto leave;
|
||||||
@ -237,6 +171,23 @@ agent_write_private_key (const unsigned char *grip,
|
|||||||
if (err)
|
if (err)
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
|
/* Check that we do not update a regular key with a shadow key. */
|
||||||
|
if (is_regular && gpg_err_code (is_shadowed_key (key)) == GPG_ERR_TRUE)
|
||||||
|
{
|
||||||
|
log_info ("updating regular key file '%s'"
|
||||||
|
" by a shadow key inhibited\n", fname);
|
||||||
|
err = 0; /* Simply ignore the error. */
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we update a regular key only in force mode. */
|
||||||
|
if (is_regular && !force)
|
||||||
|
{
|
||||||
|
log_error ("secret key file '%s' already exists\n", fname);
|
||||||
|
err = gpg_error (GPG_ERR_EEXIST);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
/* If requested write a Token line. */
|
/* If requested write a Token line. */
|
||||||
if (serialno && keyref)
|
if (serialno && keyref)
|
||||||
{
|
{
|
||||||
@ -357,8 +308,8 @@ agent_write_private_key (const unsigned char *grip,
|
|||||||
if (blocksigs)
|
if (blocksigs)
|
||||||
gnupg_unblock_all_signals ();
|
gnupg_unblock_all_signals ();
|
||||||
es_fclose (fp);
|
es_fclose (fp);
|
||||||
if (removetmp && fname)
|
if (removetmp && tmpfname)
|
||||||
gnupg_remove (fname);
|
gnupg_remove (tmpfname);
|
||||||
xfree (fname);
|
xfree (fname);
|
||||||
xfree (tmpfname);
|
xfree (tmpfname);
|
||||||
xfree (token);
|
xfree (token);
|
||||||
@ -903,7 +854,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
|
|||||||
|
|
||||||
/* Read the key identified by GRIP from the private key directory and
|
/* Read the key identified by GRIP from the private key directory and
|
||||||
* return it as an gcrypt S-expression object in RESULT. If R_KEYMETA
|
* return it as an gcrypt S-expression object in RESULT. If R_KEYMETA
|
||||||
* is not NULl and the extended key format is used, the meta data
|
* is not NULL and the extended key format is used, the meta data
|
||||||
* items are stored there. However the "Key:" item is removed from
|
* items are stored there. However the "Key:" item is removed from
|
||||||
* it. On failure returns an error code and stores NULL at RESULT and
|
* it. On failure returns an error code and stores NULL at RESULT and
|
||||||
* R_KEYMETA. */
|
* R_KEYMETA. */
|
||||||
@ -1447,6 +1398,29 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This function returns GPG_ERR_TRUE if S_SKEY represents a shadowed
|
||||||
|
* key. 0 is return for other key types. Any other error may occur
|
||||||
|
* if S_SKEY is not valid. */
|
||||||
|
static gpg_error_t
|
||||||
|
is_shadowed_key (gcry_sexp_t s_skey)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
unsigned char *buf;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
err = make_canon_sexp (s_skey, &buf, &buflen);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (agent_private_key_type (buf) == PRIVATE_KEY_SHADOWED)
|
||||||
|
err = gpg_error (GPG_ERR_TRUE);
|
||||||
|
|
||||||
|
wipememory (buf, buflen);
|
||||||
|
xfree (buf);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return the key for the keygrip GRIP. The result is stored at
|
/* Return the key for the keygrip GRIP. The result is stored at
|
||||||
RESULT. This function extracts the key from the private key
|
RESULT. This function extracts the key from the private key
|
||||||
database and returns it as an S-expression object as it is. On
|
database and returns it as an S-expression object as it is. On
|
||||||
|
Loading…
x
Reference in New Issue
Block a user