summaryrefslogtreecommitdiffstats
path: root/agent/findkey.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 21:21:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 21:21:03 +0000
commit69349561bf941cc67f1afcbbc115af8dbd624f94 (patch)
tree49d5db9fac516d5de488244d4cffd2e9d74220e7 /agent/findkey.c
parentAdding debian version 2.2.40-3. (diff)
downloadgnupg2-69349561bf941cc67f1afcbbc115af8dbd624f94.tar.xz
gnupg2-69349561bf941cc67f1afcbbc115af8dbd624f94.zip
Merging upstream version 2.2.43.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'agent/findkey.c')
-rw-r--r--agent/findkey.c595
1 files changed, 260 insertions, 335 deletions
diff --git a/agent/findkey.c b/agent/findkey.c
index dadcc3c..a5055bc 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1,7 +1,8 @@
/* findkey.c - Locate the secret key
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007,
* 2010, 2011 Free Software Foundation, Inc.
- * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 2014, 2019 Werner Koch
+ * Copyright (C) 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -26,10 +27,8 @@
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
-#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <assert.h>
#include <npth.h> /* (we use pth_sleep) */
#include "agent.h"
@@ -41,6 +40,13 @@
#define O_BINARY 0
#endif
+
+static gpg_error_t read_key_file (const unsigned char *grip,
+ gcry_sexp_t *result, nvc_t *r_keymeta,
+ char **r_orig_key_value);
+static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey);
+
+
/* Helper to pass data to the check callback of the unprotect function. */
struct try_unprotect_arg_s
{
@@ -52,96 +58,134 @@ struct try_unprotect_arg_s
};
-/* Return the file name for the 20 byte keygrip GRIP. Return NULL on
- * error. */
+/* Return the file name for the 20 byte keygrip GRIP. With FOR_NEW
+ * create a file name for later renaming to the actual name. Return
+ * NULL on error. */
static char *
-fname_from_keygrip (const unsigned char *grip)
+fname_from_keygrip (const unsigned char *grip, int for_new)
{
- char hexgrip[40+4+1];
+ char hexgrip[40+4+4+1];
bin2hex (grip, 20, hexgrip);
- strcpy (hexgrip+40, ".key");
+ strcpy (hexgrip+40, for_new? ".key.tmp" : ".key");
return make_filename_try (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR,
hexgrip, NULL);
}
-/* Note: Ownership of FNAME and FP are moved to this function.
- * OLD_FORMAT is true if the file exists but is still in the
- * non-extended mode format. If MAYBE_UPDATE is set the function
- * assumes that the file exists but writes it only if it figures that
- * an update is required. */
-static gpg_error_t
-write_extended_private_key (int maybe_update,
- char *fname, estream_t fp,
- int old_format, int newkey,
- const void *buf, size_t len, time_t timestamp,
- const char *serialno, const char *keyref,
- const char *dispserialno)
+/* Write the S-expression formatted key (BUFFER,LENGTH) to our key
+ * storage. With FORCE passed as true an existing key with the given
+ * GRIP will be overwritten. If SERIALNO and KEYREF are given a Token
+ * line is added to the key if the extended format is used. If
+ * TIMESTAMP is not zero and the key doies not yet exists it will be
+ * recorded as creation date. */
+int
+agent_write_private_key (const unsigned char *grip,
+ const void *buffer, size_t length,
+ int force, int reallyforce,
+ const char *serialno, const char *keyref,
+ const char *dispserialno,
+ time_t timestamp)
{
gpg_error_t err;
+ char *oldfname = NULL;
+ char *fname = NULL;
+ estream_t fp;
+ int newkey = 0;
nvc_t pk = NULL;
gcry_sexp_t key = NULL;
+ int is_regular;
int remove = 0;
char *token0 = NULL;
char *token = NULL;
char *dispserialno_buffer = NULL;
char **tokenfields = NULL;
+ int blocksigs = 0;
+ char *orig_key_value = NULL;
+ const char *s;
+ int force_modify = 0;
+
+ oldfname = fname_from_keygrip (grip, 0);
+ if (!oldfname)
+ return out_of_core ();
- if (old_format || newkey)
+ err = read_key_file (grip, &key, &pk, &orig_key_value);
+ if (err)
{
- /* We must create a new NVC if the key is still in the old
- * format and of course if it is a new key. */
- pk = nvc_new_private_key ();
- if (!pk)
+ if (gpg_err_code (err) == GPG_ERR_ENOENT)
+ newkey = 1;
+ else
{
- err = gpg_error_from_syserror ();
+ log_error ("can't open '%s': %s\n", oldfname, gpg_strerror (err));
goto leave;
}
- maybe_update = 0; /* Always write. */
}
- else
- { /* Parse the existing NVC. */
- int lineno = 0;
- err = nvc_parse_private_key (&pk, &lineno, fp);
- if (err)
+ nvc_modified (pk, 1); /* Clear that flag after a read. */
+
+ if (!pk)
+ {
+ /* Key is still in the old format or does not exist - create a
+ * new container. */
+ pk = nvc_new_private_key ();
+ if (!pk)
{
- log_error ("error parsing '%s' line %d: %s\n",
- fname, lineno, gpg_strerror (err));
+ err = gpg_error_from_syserror ();
goto leave;
}
+ force_modify = 1;
}
- es_clearerr (fp);
- err = gcry_sexp_sscan (&key, NULL, buf, len);
+ /* 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
+ * our name value container. */
+ gcry_sexp_release (key);
+ err = gcry_sexp_sscan (&key, NULL, buffer, length);
if (err)
goto leave;
-
err = nvc_set_private_key (pk, key);
if (err)
goto leave;
- /* If a timestamp has been supplied and the key is new write a
- * creation timestamp. Note that we can't add this item if we are
- * still in the old format. We also add an extra check that there
- * is no Created item yet. */
- if (timestamp && newkey && !nvc_lookup (pk, "Created:"))
+ /* Detect whether the key value actually changed and if not clear
+ * the modified flag. This extra check is required because
+ * read_key_file removes the Key entry from the container and we
+ * then create a new Key entry which might be the same, though. */
+ if (!force_modify
+ && orig_key_value && (s = nvc_get_string (pk, "Key:"))
+ && !strcmp (orig_key_value, s))
{
- gnupg_isotime_t timebuf;
+ nvc_modified (pk, 1); /* Clear that flag. */
+ }
+ xfree (orig_key_value);
+ orig_key_value = NULL;
- epoch2isotime (timebuf, timestamp);
- err = nvc_add (pk, "Created:", timebuf);
- if (err)
- 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)
+ {
+ if (!reallyforce)
+ {
+ log_info ("updating regular key file '%s'"
+ " by a shadow key inhibited\n", oldfname);
+ 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", oldfname);
+ err = gpg_error (GPG_ERR_EEXIST);
+ goto leave;
}
/* If requested write a Token line. */
if (serialno && keyref)
{
nve_t item;
- const char *s;
size_t token0len;
if (dispserialno)
@@ -179,50 +223,63 @@ write_extended_private_key (int maybe_update,
err = nvc_add (pk, "Token:", token);
if (err)
goto leave;
- maybe_update = 0; /* Force write. */
}
else
{
/* Token exists: Update the display s/n. It may have
* changed due to changes in a newer software version. */
- if (maybe_update && s && (tokenfields = strtokenize (s, " \t\n"))
+ if (s && (tokenfields = strtokenize (s, " \t\n"))
&& tokenfields[0] && tokenfields[1] && tokenfields[2]
&& tokenfields[3]
&& !strcmp (tokenfields[3], dispserialno))
; /* No need to update Token entry. */
else
{
- err = nve_set (item, token);
+ err = nve_set (pk, item, token);
if (err)
goto leave;
- maybe_update = 0; /* Force write. */
}
}
}
- err = es_fseek (fp, 0, SEEK_SET);
- if (err)
- goto leave;
-
- if (!maybe_update)
+ /* If a timestamp has been supplied and the key is new, write a
+ * creation timestamp. (We douple check that there is no Created
+ * item yet.)*/
+ if (timestamp && newkey && !nvc_lookup (pk, "Created:"))
{
- err = nvc_write (pk, fp);
- if (!err)
- err = es_fflush (fp);
+ gnupg_isotime_t timebuf;
+
+ epoch2isotime (timebuf, timestamp);
+ err = nvc_add (pk, "Created:", timebuf);
if (err)
- {
- log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
- remove = 1;
- goto leave;
- }
+ goto leave;
+ }
- if (ftruncate (es_fileno (fp), es_ftello (fp)))
- {
- err = gpg_error_from_syserror ();
- log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err));
- remove = 1;
- goto leave;
- }
+ /* Check whether we need to write the file at all. */
+ if (!nvc_modified (pk, 0))
+ {
+ err = 0;
+ goto leave;
+ }
+
+ /* Create a temporary file for writing. */
+ fname = fname_from_keygrip (grip, 1);
+ fp = fname ? es_fopen (fname, "wbx,mode=-rw") : NULL;
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't create '%s': %s\n", fname, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = nvc_write (pk, fp);
+ if (!err && es_fflush (fp))
+ err = gpg_error_from_syserror ();
+ if (err)
+ {
+ log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
+ remove = 1;
+ goto leave;
}
if (es_fclose (fp))
@@ -232,17 +289,28 @@ write_extended_private_key (int maybe_update,
remove = 1;
goto leave;
}
- else
- fp = NULL;
+ fp = NULL;
- if (!maybe_update)
- bump_key_eventcounter ();
+ err = gnupg_rename_file (fname, oldfname, &blocksigs);
+ if (err)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error renaming '%s': %s\n", fname, gpg_strerror (err));
+ remove = 1;
+ goto leave;
+ }
+
+ bump_key_eventcounter ();
leave:
+ if (blocksigs)
+ gnupg_unblock_all_signals ();
es_fclose (fp);
- if (remove)
+ if (remove && fname)
gnupg_remove (fname);
+ xfree (orig_key_value);
xfree (fname);
+ xfree (oldfname);
xfree (token);
xfree (token0);
xfree (dispserialno_buffer);
@@ -252,136 +320,6 @@ write_extended_private_key (int maybe_update,
return err;
}
-/* Write an S-expression formatted key to our key storage. With FORCE
- * passed as true an existing key with the given GRIP will get
- * overwritten. If TIMESTAMP is not zero and the key does not yet
- * exists it will be recorded as creation date. If SERIALNO, KEYREF,
- * of DISPSERIALNO are not NULL they will be recorded as well. */
-int
-agent_write_private_key (const unsigned char *grip,
- const void *buffer, size_t length,
- int force, time_t timestamp,
- const char *serialno, const char *keyref,
- const char *dispserialno)
-{
- char *fname;
- estream_t fp;
-
- fname = fname_from_keygrip (grip);
- if (!fname)
- return gpg_error_from_syserror ();
-
- /* FIXME: Write to a temp file first so that write failures during
- key updates won't lead to a key loss. */
-
- if (!force && !gnupg_access (fname, F_OK))
- {
- log_error ("secret key file '%s' already exists\n", fname);
- xfree (fname);
- 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;
- }
- }
- 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 already in the extended format. */
- return write_extended_private_key (0, fname, fp, 0, 0,
- buffer, length,
- timestamp, serialno, keyref,
- dispserialno);
- }
- if (first == '(' && opt.enable_extended_key_format)
- {
- /* Key is in the old format - but we want the extended format. */
- return write_extended_private_key (0, fname, fp, 1, 0,
- buffer, length,
- timestamp, serialno, keyref,
- dispserialno);
- }
- }
-
- if (opt.enable_extended_key_format)
- return write_extended_private_key (0, fname, fp, 0, 1,
- buffer, length,
- timestamp, serialno, keyref,
- dispserialno);
-
- if (es_fwrite (buffer, length, 1, fp) != 1)
- {
- gpg_error_t tmperr = gpg_error_from_syserror ();
- log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr));
- es_fclose (fp);
- gnupg_remove (fname);
- xfree (fname);
- return tmperr;
- }
-
- /* When force is given, the file might have to be truncated. */
- if (force && ftruncate (es_fileno (fp), es_ftello (fp)))
- {
- gpg_error_t tmperr = gpg_error_from_syserror ();
- log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr));
- es_fclose (fp);
- gnupg_remove (fname);
- xfree (fname);
- return tmperr;
- }
-
- if (es_fclose (fp))
- {
- gpg_error_t tmperr = gpg_error_from_syserror ();
- log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr));
- gnupg_remove (fname);
- xfree (fname);
- return tmperr;
- }
- bump_key_eventcounter ();
- xfree (fname);
- return 0;
-}
-
/* Callback function to try the unprotection from the passphrase query
code. */
@@ -395,7 +333,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
gnupg_isotime_t now, protected_at, tmptime;
char *desc = NULL;
- assert (!arg->unprotected_key);
+ log_assert (!arg->unprotected_key);
arg->change_required = 0;
err = agent_unprotect (ctrl, arg->protected_key, pi->pin, protected_at,
@@ -772,7 +710,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
}
else
{
- assert (arg.unprotected_key);
+ log_assert (arg.unprotected_key);
if (arg.change_required)
{
/* The callback told as that the user should change their
@@ -780,7 +718,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
size_t canlen, erroff;
gcry_sexp_t s_skey;
- assert (arg.unprotected_key);
+ log_assert (arg.unprotected_key);
canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
rc = gcry_sexp_sscan (&s_skey, &erroff,
(char*)arg.unprotected_key, canlen);
@@ -824,39 +762,43 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
/* 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
- * is not NULl and the extended key format is used, the meta data
- * items are stored there. However the "Key:" item is removed from
- * it. On failure returns an error code and stores NULL at RESULT. */
+ * is not NULL, the meta data items are stored there. However the
+ * "Key:" item is removed. If R_ORIG_KEY_VALUE is non-NULL and the
+ * Key items was removed, its value is stored at that R_ORIG_KEY_VALUE
+ * and the caller must free it. Returns an error code and stores NULL
+ * at RESULT. */
static gpg_error_t
-read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
+read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta,
+ char **r_orig_key_value)
{
gpg_error_t err;
char *fname;
- estream_t fp;
+ estream_t fp = NULL;
struct stat st;
- unsigned char *buf;
+ unsigned char *buf = NULL;
size_t buflen, erroff;
gcry_sexp_t s_skey;
- char hexgrip[40+4+1];
char first;
*result = NULL;
if (r_keymeta)
*r_keymeta = NULL;
+ if (r_orig_key_value)
+ *r_orig_key_value = NULL;
- bin2hex (grip, 20, hexgrip);
- strcpy (hexgrip+40, ".key");
-
- fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR,
- hexgrip, NULL);
+ fname = fname_from_keygrip (grip, 0);
+ if (!fname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
fp = es_fopen (fname, "rb");
if (!fp)
{
err = gpg_error_from_syserror ();
if (gpg_err_code (err) != GPG_ERR_ENOENT)
log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
- xfree (fname);
- return err;
+ goto leave;
}
if (es_fread (&first, 1, 1, fp) != 1)
@@ -864,18 +806,14 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
err = gpg_error_from_syserror ();
log_error ("error reading first byte from '%s': %s\n",
fname, gpg_strerror (err));
- xfree (fname);
- es_fclose (fp);
- return err;
+ goto leave;
}
if (es_fseek (fp, 0, SEEK_SET))
{
err = gpg_error_from_syserror ();
log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err));
- xfree (fname);
- es_fclose (fp);
- return err;
+ goto leave;
}
if (first != '(')
@@ -885,8 +823,6 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
int line;
err = nvc_parse_private_key (&pk, &line, fp);
- es_fclose (fp);
-
if (err)
log_error ("error parsing '%s' line %d: %s\n",
fname, line, gpg_strerror (err));
@@ -897,24 +833,37 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
log_error ("error getting private key from '%s': %s\n",
fname, gpg_strerror (err));
else
- nvc_delete_named (pk, "Key:");
+ {
+ if (r_orig_key_value)
+ {
+ const char *s = nvc_get_string (pk, "Key:");
+ if (s)
+ {
+ *r_orig_key_value = xtrystrdup (s);
+ if (!*r_orig_key_value)
+ {
+ err = gpg_error_from_syserror ();
+ nvc_release (pk);
+ goto leave;
+ }
+ }
+ }
+ nvc_delete_named (pk, "Key:");
+ }
}
if (!err && r_keymeta)
*r_keymeta = pk;
else
nvc_release (pk);
- xfree (fname);
- return err;
+ goto leave;
}
if (fstat (es_fileno (fp), &st))
{
err = gpg_error_from_syserror ();
log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
- xfree (fname);
- es_fclose (fp);
- return err;
+ goto leave;
}
buflen = st.st_size;
@@ -924,11 +873,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
err = gpg_error_from_syserror ();
log_error ("error allocating %zu bytes for '%s': %s\n",
buflen, fname, gpg_strerror (err));
- xfree (fname);
- es_fclose (fp);
- xfree (buf);
- return err;
-
+ goto leave;
}
if (es_fread (buf, buflen, 1, fp) != 1)
@@ -936,25 +881,24 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta)
err = gpg_error_from_syserror ();
log_error ("error reading %zu bytes from '%s': %s\n",
buflen, fname, gpg_strerror (err));
- xfree (fname);
- es_fclose (fp);
- xfree (buf);
- return err;
+ goto leave;
}
/* Convert the file into a gcrypt S-expression object. */
err = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
- xfree (fname);
- es_fclose (fp);
- xfree (buf);
if (err)
{
log_error ("failed to build S-Exp (off=%u): %s\n",
(unsigned int)erroff, gpg_strerror (err));
- return err;
+ goto leave;
}
*result = s_skey;
- return 0;
+
+ leave:
+ es_fclose (fp);
+ xfree (fname);
+ xfree (buf);
+ return err;
}
@@ -997,20 +941,24 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
- gcry_sexp_t *result, char **r_passphrase)
+ gcry_sexp_t *result, char **r_passphrase,
+ uint64_t *r_timestamp)
{
gpg_error_t err;
unsigned char *buf;
size_t len, buflen, erroff;
gcry_sexp_t s_skey;
+ nvc_t keymeta = NULL;
*result = NULL;
if (shadow_info)
*shadow_info = NULL;
if (r_passphrase)
*r_passphrase = NULL;
+ if (r_timestamp)
+ *r_timestamp = (uint64_t)(-1);
- err = read_key_file (grip, &s_skey, NULL);
+ err = read_key_file (grip, &s_skey, &keymeta, NULL);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_ENOENT)
@@ -1023,7 +971,19 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
now. */
err = make_canon_sexp (s_skey, &buf, &len);
if (err)
- return err;
+ {
+ nvc_release (keymeta);
+ return err;
+ }
+
+ if (r_timestamp && keymeta)
+ {
+ const char *created = nvc_get_string (keymeta, "Created:");
+
+ if (created)
+ *r_timestamp = isotime2epoch_u64 (created);
+ }
+ nvc_release (keymeta);
switch (agent_private_key_type (buf))
{
@@ -1325,6 +1285,29 @@ agent_is_eddsa_key (gcry_sexp_t s_key)
}
+/* 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
RESULT. This function extracts the key from the private key
database and returns it as an S-expression object as it is. On
@@ -1340,7 +1323,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
*result = NULL;
- err = read_key_file (grip, &s_skey, NULL);
+ err = read_key_file (grip, &s_skey, NULL, NULL);
if (!err)
*result = s_skey;
return err;
@@ -1356,7 +1339,7 @@ agent_keymeta_from_file (ctrl_t ctrl, const unsigned char *grip,
(void)ctrl;
- err = read_key_file (grip, &s_skey, r_keymeta);
+ err = read_key_file (grip, &s_skey, r_keymeta, NULL);
gcry_sexp_release (s_skey);
return err;
}
@@ -1394,7 +1377,7 @@ agent_public_key_from_file (ctrl_t ctrl,
*result = NULL;
- err = read_key_file (grip, &s_skey, NULL);
+ err = read_key_file (grip, &s_skey, NULL, NULL);
if (err)
return err;
@@ -1431,7 +1414,7 @@ agent_public_key_from_file (ctrl_t ctrl,
such a task. After all that is what we do in protect.c. Need
to find common patterns and write a straightformward API to use
them. */
- assert (sizeof (size_t) <= sizeof (void*));
+ log_assert (sizeof (size_t) <= sizeof (void*));
format = xtrymalloc (15+4+7*npkey+10+15+1+1);
if (!format)
@@ -1456,14 +1439,14 @@ agent_public_key_from_file (ctrl_t ctrl,
*p++ = '(';
*p++ = *s++;
p = stpcpy (p, " %m)");
- assert (argidx < DIM (args));
+ log_assert (argidx < DIM (args));
args[argidx++] = &array[idx];
}
*p++ = ')';
if (uri)
{
p = stpcpy (p, "(uri %b)");
- assert (argidx+1 < DIM (args));
+ log_assert (argidx+1 < DIM (args));
uri_intlen = (int)uri_length;
args[argidx++] = (void *)&uri_intlen;
args[argidx++] = (void *)&uri;
@@ -1471,14 +1454,14 @@ agent_public_key_from_file (ctrl_t ctrl,
if (comment)
{
p = stpcpy (p, "(comment %b)");
- assert (argidx+1 < DIM (args));
+ log_assert (argidx+1 < DIM (args));
comment_intlen = (int)comment_length;
args[argidx++] = (void *)&comment_intlen;
args[argidx++] = (void*)&comment;
}
*p++ = ')';
*p = 0;
- assert (argidx < DIM (args));
+ log_assert (argidx < DIM (args));
args[argidx] = NULL;
err = gcry_sexp_build_array (&list, NULL, format, args);
@@ -1541,7 +1524,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
{
gcry_sexp_t sexp;
- err = read_key_file (grip, &sexp, NULL);
+ err = read_key_file (grip, &sexp, NULL, NULL);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_ENOENT)
@@ -1575,7 +1558,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
if (!err)
{
n = gcry_sexp_canon_len (s, 0, NULL, NULL);
- assert (n);
+ log_assert (n);
*r_shadow_info = xtrymalloc (n);
if (!*r_shadow_info)
err = gpg_error_from_syserror ();
@@ -1625,7 +1608,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
char *default_desc = NULL;
int key_type;
- err = read_key_file (grip, &s_skey, NULL);
+ err = read_key_file (grip, &s_skey, NULL, NULL);
if (gpg_err_code (err) == GPG_ERR_ENOENT)
err = gpg_error (GPG_ERR_NO_SECKEY);
if (err)
@@ -1722,29 +1705,22 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
/* Write an S-expression formatted shadow key to our key storage.
- Shadow key is created by an S-expression public key in PKBUF and
- card's SERIALNO and the IDSTRING. With FORCE passed as true an
- existing key with the given GRIP will get overwritten. If
- DISPSERIALNO is not NULL the human readable s/n will also be
- recorded in the key file. If MAYBE_UPDATE is set it is assumed that
- the shadow key already exists and we test whether we should update
- it (FORCE is ignored in this case). */
+ * Shadow key is created by an S-expression public key in PKBUF and
+ * card's SERIALNO and the IDSTRING. With FORCE passed as true an
+ * existing key with the given GRIP will get overwritten. If
+ * REALLYFORCE is also true, even a private key will be overwritten by
+ * a shadown key. If DISPSERIALNO is not NULL the human readable s/n
+ * will also be recorded in the key file. */
gpg_error_t
-agent_write_shadow_key (int maybe_update, const unsigned char *grip,
+agent_write_shadow_key (const unsigned char *grip,
const char *serialno, const char *keyid,
- const unsigned char *pkbuf, int force,
+ const unsigned char *pkbuf, int force, int reallyforce,
const char *dispserialno)
{
gpg_error_t err;
unsigned char *shadow_info;
unsigned char *shdkey;
size_t len;
- char *fname = NULL;
- estream_t fp = NULL;
- char first;
-
- if (maybe_update && !opt.enable_extended_key_format)
- return 0; /* Silently ignore. */
/* Just in case some caller did not parse the stuff correctly, skip
* leading spaces. */
@@ -1766,62 +1742,11 @@ agent_write_shadow_key (int maybe_update, const unsigned char *grip,
}
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
-
- if (maybe_update) /* Update mode. */
- {
- fname = fname_from_keygrip (grip);
- if (!fname)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
-
- fp = es_fopen (fname, "rb+,mode=-rw");
- if (!fp)
- {
- err = gpg_error_from_syserror ();
- log_error ("shadow key file '%s' disappeared\n", fname);
- goto leave;
- }
-
- /* See if an existing key is in extended format. */
- if (es_fread (&first, 1, 1, fp) != 1)
- {
- err = gpg_error_from_syserror ();
- log_error ("error reading first byte from '%s': %s\n",
- fname, gpg_strerror (err));
- goto leave;
- }
-
- if (es_fseek (fp, 0, SEEK_SET))
- {
- err = gpg_error_from_syserror ();
- log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err));
- goto leave;
- }
-
- /* "(first == '(')" indicates that the key is in the old format. */
- err = write_extended_private_key (maybe_update,
- fname, fp, (first == '('), 0,
- shdkey, len,
- 0, serialno, keyid,
- dispserialno);
- fname = NULL; /* Ownership was transferred. */
- fp = NULL; /* Ditto. */
- }
- else /* Standard mode */
- {
- err = agent_write_private_key (grip, shdkey, len, force, 0,
- serialno, keyid, dispserialno);
- }
-
- leave:
- xfree (fname);
- es_fclose (fp);
+ err = agent_write_private_key (grip, shdkey, len, force, reallyforce,
+ serialno, keyid, dispserialno, 0);
xfree (shdkey);
if (err)
- log_error ("error %s key: %s\n", maybe_update? "updating":"writing",
- gpg_strerror (err));
+ log_error ("error writing key: %s\n", gpg_strerror (err));
return err;
}