From 50293ec2ebf2a997dbad9a47166d694efcc0709a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 23 Apr 2021 08:47:06 +0200 Subject: [PATCH] gpg: Allow decryption w/o public key but with correct card inserted. * agent/command.c (cmd_readkey): Add option --no-data and special handling for $SIGNKEYID and $AUTHKEYID. * g10/call-agent.c (agent_scd_getattr): Create shadow keys for KEY-FPR output. * g10/skclist.c (enum_secret_keys): Automagically get a missing public key for the current card. Signed-off-by: Werner Koch --- agent/command.c | 18 +++++++++++++----- g10/call-agent.c | 10 ++++++++++ g10/skclist.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/agent/command.c b/agent/command.c index 88580a754..21f4289b1 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1073,8 +1073,8 @@ cmd_genkey (assuan_context_t ctx, char *line) static const char hlp_readkey[] = - "READKEY \n" - " --card \n" + "READKEY [--no-data] \n" + " --card \n" "\n" "Return the public key for the given keygrip or keyid.\n" "With --card, private key file with card information will be created."; @@ -1087,12 +1087,14 @@ cmd_readkey (assuan_context_t ctx, char *line) gcry_sexp_t s_pkey = NULL; unsigned char *pkbuf = NULL; char *serialno = NULL; + char *keyidbuf = NULL; size_t pkbuflen; - int opt_card; + int opt_card, opt_no_data; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + opt_no_data = has_option (line, "--no-data"); opt_card = has_option (line, "--card"); line = skip_options (line); @@ -1108,6 +1110,11 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } + /* Hack to create the shadow key for the OpenPGP standard keys. */ + if ((!strcmp (keyid, "$SIGNKEYID") || !strcmp (keyid, "$ENCRKEYID")) + && !agent_card_getattr (ctrl, keyid, &keyidbuf, NULL)) + keyid = keyidbuf; + rc = agent_card_readkey (ctrl, keyid, &pkbuf, NULL); if (rc) goto leave; @@ -1133,7 +1140,7 @@ cmd_readkey (assuan_context_t ctx, char *line) goto leave; } - rc = assuan_send_data (ctx, pkbuf, pkbuflen); + rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen); } else { @@ -1153,12 +1160,13 @@ cmd_readkey (assuan_context_t ctx, char *line) { pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen); - rc = assuan_send_data (ctx, pkbuf, pkbuflen); + rc = opt_no_data? 0 : assuan_send_data (ctx, pkbuf, pkbuflen); } } } leave: + xfree (keyidbuf); xfree (serialno); xfree (pkbuf); gcry_sexp_release (s_pkey); diff --git a/g10/call-agent.c b/g10/call-agent.c index fb80489b2..83355454a 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1232,6 +1232,16 @@ agent_scd_getattr (const char *name, struct agent_card_info_s *info) parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, learn_status_cb, info); + if (!rc && !strcmp (name, "KEY-FPR")) + { + /* Let the agent create the shadow keys if not yet done. */ + if (info->fpr1len) + assuan_transact (agent_ctx, "READKEY --card --no-data -- $SIGNKEYID", + NULL, NULL, NULL, NULL, NULL, NULL); + if (info->fpr2len) + assuan_transact (agent_ctx, "READKEY --card --no-data -- $ENCRKEYID", + NULL, NULL, NULL, NULL, NULL, NULL); + } return rc; } diff --git a/g10/skclist.c b/g10/skclist.c index c9f7d126a..f9647cc1d 100644 --- a/g10/skclist.c +++ b/g10/skclist.c @@ -31,6 +31,7 @@ #include "keydb.h" #include "../common/util.h" #include "../common/i18n.h" +#include "keyserver-internal.h" #include "call-agent.h" @@ -322,11 +323,13 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) { gpg_error_t err = 0; const char *name; + int cardkey; kbnode_t keyblock; struct { int eof; int state; + int cardkey_done; strlist_t sl; keypair_info_t card_keyinfo; keypair_info_t card_keyinfo_list; @@ -381,6 +384,7 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) do { name = NULL; + cardkey = 0; keyblock = NULL; switch (c->state) { @@ -431,6 +435,7 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) c->fpr2[i] = *s; c->fpr2[i] = 0; name = c->fpr2; + cardkey = 1; c->card_keyinfo = c->card_keyinfo->next; } @@ -482,11 +487,47 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk) err = getkey_byname (ctrl, NULL, NULL, name, 1, &c->keyblock); if (err) { + struct agent_card_info_s cinfo = { 0 }; + /* getkey_byname might return a keyblock even in the error case - I have not checked. Thus better release it. */ release_kbnode (c->keyblock); c->keyblock = NULL; + /* If this was a card key we might not yet have the + * public key for it. Thus check whether the card + * can return the fingerprint of the encryption key + * and we can then find the public key via LDAP. */ + if (cardkey && !c->cardkey_done + && gpg_err_code (err) == GPG_ERR_NO_SECKEY + && !agent_scd_getattr ("KEY-FPR", &cinfo) + && cinfo.fpr2len) + { + /* Note that this code does not handle the case + * for two readers having both openpgp + * encryption keys. only one will be tried. */ + c->cardkey_done = 1; + if (opt.debug) + log_debug ("using LDAP to find public key" + " for current card\n"); + if (!keyserver_import_fprint + (ctrl, cinfo.fpr2, cinfo.fpr2len, opt.keyserver, + KEYSERVER_IMPORT_FLAG_LDAP)) + { + char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; + + bin2hex (cinfo.fpr2, cinfo.fpr2len, fpr_string); + err = getkey_byname (ctrl, NULL, NULL, fpr_string, 1, + &c->keyblock); + if (err) + { + release_kbnode (c->keyblock); + c->keyblock = NULL; + } + else + c->node = c->keyblock; + } + } } else c->node = c->keyblock;