You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
12 KiB
293 lines
12 KiB
https://bugs.gentoo.org/923248
|
|
https://dev.gnupg.org/T6944
|
|
https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=commit;h=3b69d8bf7146b8d10737d0cfea9c97affc60ad73
|
|
|
|
From 3b69d8bf7146b8d10737d0cfea9c97affc60ad73 Mon Sep 17 00:00:00 2001
|
|
From: Werner Koch <wk@gnupg.org>
|
|
Date: Wed, 24 Jan 2024 11:29:24 +0100
|
|
Subject: [PATCH] gpg: Fix leftover unprotected card backup key.
|
|
|
|
* agent/command.c (cmd_learn): Add option --reallyforce.
|
|
* agent/findkey.c (agent_write_private_key): Implement reallyforce.
|
|
Also add arg reallyforce and pass it along the call chain.
|
|
|
|
* g10/call-agent.c (agent_scd_learn): Pass --reallyforce with a
|
|
special force value.
|
|
* g10/keygen.c (card_store_key_with_backup): Use that force value.
|
|
--
|
|
|
|
This was a regression in 2.2.42. We took the easy path to fix it by
|
|
getting the behaviour back to what we did prior to 2.2.42. With GnuPG
|
|
2.4.4 we use an entire different and safer approach by introducing an
|
|
ephemeral private key store.
|
|
|
|
GnuPG-bug-id: 6944
|
|
--- a/agent/agent.h
|
|
+++ b/agent/agent.h
|
|
@@ -422,7 +422,8 @@ void start_command_handler_ssh (ctrl_t, gnupg_fd_t);
|
|
gpg_error_t agent_modify_description (const char *in, const char *comment,
|
|
const gcry_sexp_t key, char **result);
|
|
int agent_write_private_key (const unsigned char *grip,
|
|
- const void *buffer, size_t length, int force,
|
|
+ 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 agent_key_from_file (ctrl_t ctrl,
|
|
@@ -548,6 +549,7 @@ gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo,
|
|
gpg_error_t agent_write_shadow_key (const unsigned char *grip,
|
|
const char *serialno, const char *keyid,
|
|
const unsigned char *pkbuf, int force,
|
|
+ int reallyforce,
|
|
const char *dispserialno);
|
|
|
|
|
|
@@ -628,7 +630,8 @@ void agent_card_killscd (void);
|
|
|
|
|
|
/*-- learncard.c --*/
|
|
-int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force);
|
|
+int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context,
|
|
+ int force, int reallyforce);
|
|
|
|
|
|
/*-- cvt-openpgp.c --*/
|
|
--- a/agent/command-ssh.c
|
|
+++ b/agent/command-ssh.c
|
|
@@ -2499,7 +2499,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
|
|
|
|
/* (Shadow)-key is not available in our key storage. */
|
|
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno);
|
|
- err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0,
|
|
+ err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0, 0,
|
|
dispserialno);
|
|
xfree (dispserialno);
|
|
if (err)
|
|
@@ -3159,7 +3159,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
|
|
|
|
/* Store this key to our key storage. We do not store a creation
|
|
* timestamp because we simply do not know. */
|
|
- err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0,
|
|
+ err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, 0,
|
|
NULL, NULL, NULL, 0);
|
|
if (err)
|
|
goto out;
|
|
--- a/agent/command.c
|
|
+++ b/agent/command.c
|
|
@@ -1042,7 +1042,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
|
|
/* Shadow-key is or is not available in our key storage. In
|
|
* any case we need to check whether we need to update with
|
|
* a new display-s/n or whatever. */
|
|
- rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0,
|
|
+ rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0, 0,
|
|
dispserialno);
|
|
if (rc)
|
|
goto leave;
|
|
@@ -1855,16 +1855,18 @@ cmd_learn (assuan_context_t ctx, char *line)
|
|
{
|
|
ctrl_t ctrl = assuan_get_pointer (ctx);
|
|
gpg_error_t err;
|
|
- int send, sendinfo, force;
|
|
+ int send, sendinfo, force, reallyforce;
|
|
|
|
send = has_option (line, "--send");
|
|
sendinfo = send? 1 : has_option (line, "--sendinfo");
|
|
force = has_option (line, "--force");
|
|
+ reallyforce = has_option (line, "--reallyforce");
|
|
|
|
if (ctrl->restricted)
|
|
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
|
|
|
|
- err = agent_handle_learn (ctrl, send, sendinfo? ctx : NULL, force);
|
|
+ err = agent_handle_learn (ctrl, send, sendinfo? ctx : NULL,
|
|
+ force, reallyforce);
|
|
return leave_cmd (ctx, err);
|
|
}
|
|
|
|
@@ -2427,11 +2429,11 @@ cmd_import_key (assuan_context_t ctx, char *line)
|
|
err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
|
|
ctrl->s2k_count);
|
|
if (!err)
|
|
- err = agent_write_private_key (grip, finalkey, finalkeylen, force,
|
|
+ err = agent_write_private_key (grip, finalkey, finalkeylen, force, 0,
|
|
NULL, NULL, NULL, opt_timestamp);
|
|
}
|
|
else
|
|
- err = agent_write_private_key (grip, key, realkeylen, force,
|
|
+ err = agent_write_private_key (grip, key, realkeylen, force, 0,
|
|
NULL, NULL, NULL, opt_timestamp);
|
|
|
|
leave:
|
|
--- a/agent/cvt-openpgp.c
|
|
+++ b/agent/cvt-openpgp.c
|
|
@@ -1070,7 +1070,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
|
|
&protectedkey, &protectedkeylen,
|
|
ctrl->s2k_count))
|
|
agent_write_private_key (grip, protectedkey, protectedkeylen,
|
|
- 1/*force*/, NULL, NULL, NULL, 0);
|
|
+ 1/*force*/, 0, NULL, NULL, NULL, 0);
|
|
xfree (protectedkey);
|
|
}
|
|
else
|
|
@@ -1079,7 +1079,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
|
|
agent_write_private_key (grip,
|
|
*r_key,
|
|
gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
|
|
- 1/*force*/, NULL, NULL, NULL, 0);
|
|
+ 1/*force*/, 0, NULL, NULL, NULL, 0);
|
|
}
|
|
}
|
|
|
|
--- a/agent/findkey.c
|
|
+++ b/agent/findkey.c
|
|
@@ -82,7 +82,8 @@ fname_from_keygrip (const unsigned char *grip, int for_new)
|
|
* recorded as creation date. */
|
|
int
|
|
agent_write_private_key (const unsigned char *grip,
|
|
- const void *buffer, size_t length, int force,
|
|
+ const void *buffer, size_t length,
|
|
+ int force, int reallyforce,
|
|
const char *serialno, const char *keyref,
|
|
const char *dispserialno,
|
|
time_t timestamp)
|
|
@@ -165,10 +166,13 @@ agent_write_private_key (const unsigned char *grip,
|
|
/* 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", oldfname);
|
|
- err = 0; /* Simply ignore the error. */
|
|
- goto leave;
|
|
+ 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)
|
|
@@ -1704,12 +1708,13 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
|
|
* 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. */
|
|
+ * 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 (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;
|
|
@@ -1737,7 +1742,7 @@ agent_write_shadow_key (const unsigned char *grip,
|
|
}
|
|
|
|
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
|
|
- err = agent_write_private_key (grip, shdkey, len, force,
|
|
+ err = agent_write_private_key (grip, shdkey, len, force, reallyforce,
|
|
serialno, keyid, dispserialno, 0);
|
|
xfree (shdkey);
|
|
if (err)
|
|
--- a/agent/genkey.c
|
|
+++ b/agent/genkey.c
|
|
@@ -69,7 +69,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
|
|
buf = p;
|
|
}
|
|
|
|
- rc = agent_write_private_key (grip, buf, len, force,
|
|
+ rc = agent_write_private_key (grip, buf, len, force, 0,
|
|
NULL, NULL, NULL, timestamp);
|
|
xfree (buf);
|
|
return rc;
|
|
--- a/agent/learncard.c
|
|
+++ b/agent/learncard.c
|
|
@@ -297,9 +297,12 @@ send_cert_back (ctrl_t ctrl, const char *id, void *assuan_context)
|
|
}
|
|
|
|
/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL and
|
|
- SEND is true all new certificates are send back via Assuan. */
|
|
+ SEND is true all new certificates are send back via Assuan. If
|
|
+ REALLYFORCE is true a private key will be overwritten by a stub
|
|
+ key. */
|
|
int
|
|
-agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
|
|
+agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context,
|
|
+ int force, int reallyforce)
|
|
{
|
|
int rc;
|
|
struct kpinfo_cb_parm_s parm;
|
|
@@ -414,7 +417,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
|
|
|
|
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno);
|
|
rc = agent_write_shadow_key (grip, serialno, item->id, pubkey,
|
|
- force, dispserialno);
|
|
+ force, reallyforce, dispserialno);
|
|
xfree (dispserialno);
|
|
}
|
|
xfree (pubkey);
|
|
--- a/agent/protect-tool.c
|
|
+++ b/agent/protect-tool.c
|
|
@@ -807,13 +807,15 @@ agent_askpin (ctrl_t ctrl,
|
|
* to stdout. */
|
|
int
|
|
agent_write_private_key (const unsigned char *grip,
|
|
- const void *buffer, size_t length, int force,
|
|
+ const void *buffer, size_t length,
|
|
+ int force, int reallyforce,
|
|
const char *serialno, const char *keyref,
|
|
const char *dispserialno, time_t timestamp)
|
|
{
|
|
char hexgrip[40+4+1];
|
|
char *p;
|
|
|
|
+ (void)reallyforce;
|
|
(void)force;
|
|
(void)timestamp;
|
|
(void)serialno;
|
|
--- a/g10/call-agent.c
|
|
+++ b/g10/call-agent.c
|
|
@@ -745,6 +745,11 @@ learn_status_cb (void *opaque, const char *line)
|
|
* card-util.c
|
|
* keyedit_menu
|
|
* card_store_key_with_backup (Woth force to remove secret key data)
|
|
+ *
|
|
+ * If force has the value 2 the --reallyforce option is also used.
|
|
+ * This is to make sure the sshadow key overwrites the private key.
|
|
+ * Note that this option is gnupg 2.2 specific because since 2.4.4 an
|
|
+ * ephemeral private key store is used instead.
|
|
*/
|
|
int
|
|
agent_scd_learn (struct agent_card_info_s *info, int force)
|
|
@@ -764,6 +769,7 @@ agent_scd_learn (struct agent_card_info_s *info, int force)
|
|
|
|
parm.ctx = agent_ctx;
|
|
rc = assuan_transact (agent_ctx,
|
|
+ force == 2? "LEARN --sendinfo --force --reallyforce" :
|
|
force ? "LEARN --sendinfo --force" : "LEARN --sendinfo",
|
|
dummy_data_cb, NULL, default_inq_cb, &parm,
|
|
learn_status_cb, info);
|
|
--- a/g10/keygen.c
|
|
+++ b/g10/keygen.c
|
|
@@ -5201,8 +5201,11 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
|
|
if (err)
|
|
log_error ("writing card key to backup file: %s\n", gpg_strerror (err));
|
|
else
|
|
- /* Remove secret key data in agent side. */
|
|
- agent_scd_learn (NULL, 1);
|
|
+ {
|
|
+ /* Remove secret key data in agent side. We use force 2 here to
|
|
+ * allow overwriting of the temporary private key. */
|
|
+ agent_scd_learn (NULL, 2);
|
|
+ }
|
|
|
|
leave:
|
|
xfree (ecdh_param_str);
|
|
--
|
|
2.30.2
|