1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-03-28 22:49:59 +01:00

gpgsm: Extend --learn-card by an optional s/n argument.

* agent/command.c (cmd_learn): Allow for s/n argument.
* agent/learncard.c (agent_handle_learn): Ditto.
* agent/call-scd.c (agent_card_learn): Ditto.  Pass it on to scd.

* scd/command.c (cmd_switchcard): Factor most code out to ...
(switchcard_core): new.
(cmd_learn): Add option --demand to specify a s/n.

* sm/gpgsm.c (main): Allow a s/n argument for --learn-card.
--

This help Kleopatra to get a stable certificate listing.
GnuPG-bug-id: 7379
This commit is contained in:
Werner Koch 2025-03-17 17:37:08 +01:00
parent 5420c4ebde
commit f463586a96
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
9 changed files with 99 additions and 39 deletions

View File

@ -731,7 +731,7 @@ int agent_tpm2d_pkdecrypt (ctrl_t ctrl, const unsigned char *cipher,
char **r_buf, size_t *r_len); char **r_buf, size_t *r_len);
/*-- call-scd.c --*/ /*-- call-scd.c --*/
int agent_card_learn (ctrl_t ctrl, int agent_card_learn (ctrl_t ctrl, const char *demand_sn,
void (*kpinfo_cb)(void*, const char *), void (*kpinfo_cb)(void*, const char *),
void *kpinfo_cb_arg, void *kpinfo_cb_arg,
void (*certinfo_cb)(void*, const char *), void (*certinfo_cb)(void*, const char *),
@ -780,7 +780,8 @@ gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip,
int cap, struct card_key_info_s **result); int cap, struct card_key_info_s **result);
/*-- learncard.c --*/ /*-- 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, const char *demand_sn);
/*-- cvt-openpgp.c --*/ /*-- cvt-openpgp.c --*/

View File

@ -260,10 +260,14 @@ learn_status_cb (void *opaque, const char *line)
return err; return err;
} }
/* Perform the LEARN command and return a list of all private keys /* Perform the LEARN command and return a list of all private keys
stored on the card. */ * stored on the card. If DEMAND_SN is given the info is returned for
* the card with that S/N instead of the current card. This may then
* switch the current card. */
int int
agent_card_learn (ctrl_t ctrl, agent_card_learn (ctrl_t ctrl,
const char *demand_sn,
void (*kpinfo_cb)(void*, const char *), void (*kpinfo_cb)(void*, const char *),
void *kpinfo_cb_arg, void *kpinfo_cb_arg,
void (*certinfo_cb)(void*, const char *), void (*certinfo_cb)(void*, const char *),
@ -273,6 +277,7 @@ agent_card_learn (ctrl_t ctrl,
{ {
int rc; int rc;
struct learn_parm_s parm; struct learn_parm_s parm;
char line[ASSUAN_LINELENGTH];
rc = start_scd (ctrl); rc = start_scd (ctrl);
if (rc) if (rc)
@ -285,7 +290,13 @@ agent_card_learn (ctrl_t ctrl,
parm.certinfo_cb_arg = certinfo_cb_arg; parm.certinfo_cb_arg = certinfo_cb_arg;
parm.sinfo_cb = sinfo_cb; parm.sinfo_cb = sinfo_cb;
parm.sinfo_cb_arg = sinfo_cb_arg; parm.sinfo_cb_arg = sinfo_cb_arg;
rc = assuan_transact (daemon_ctx (ctrl), "LEARN --force",
if (demand_sn && *demand_sn)
snprintf (line, sizeof line, "LEARN --demand=%s --force", demand_sn);
else
snprintf (line, sizeof line, "LEARN --force");
rc = assuan_transact (daemon_ctx (ctrl), line,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
learn_status_cb, &parm); learn_status_cb, &parm);
if (rc) if (rc)

View File

@ -2376,27 +2376,31 @@ cmd_get_confirmation (assuan_context_t ctx, char *line)
static const char hlp_learn[] = static const char hlp_learn[] =
"LEARN [--send] [--sendinfo] [--force]\n" "LEARN [--send] [--sendinfo] [--force] [SERIALNO]\n"
"\n" "\n"
"Learn something about the currently inserted smartcard. With\n" "Learn something about the currently inserted smartcard. With\n"
"--sendinfo information about the card is returned; with --send\n" "--sendinfo information about the card is returned; with --send\n"
"the available certificates are returned as D lines; with --force\n" "the available certificates are returned as D lines; with --force\n"
"private key storage will be updated by the result."; "private key storage will be updated by the result. With SERIALNO\n"
"given the current card is first switched to the specified one.";
static gpg_error_t static gpg_error_t
cmd_learn (assuan_context_t ctx, char *line) cmd_learn (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err; gpg_error_t err;
int send, sendinfo, force; int send, sendinfo, force;
const char *demand_sn;
send = has_option (line, "--send"); send = has_option (line, "--send");
sendinfo = send? 1 : has_option (line, "--sendinfo"); sendinfo = send? 1 : has_option (line, "--sendinfo");
force = has_option (line, "--force"); force = has_option (line, "--force");
line = skip_options (line);
demand_sn = *line? line : NULL;
if (ctrl->restricted) if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); 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, demand_sn);
return leave_cmd (ctx, err); return leave_cmd (ctx, err);
} }

View File

@ -295,10 +295,14 @@ send_cert_back (ctrl_t ctrl, const char *id, void *assuan_context)
return 0; return 0;
} }
/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL and /* 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
* DEMAND_SN is not NULL it has a string with the serial number of the
* card requested. */
int 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,
const char *demand_sn)
{ {
int rc; int rc;
struct kpinfo_cb_parm_s parm; struct kpinfo_cb_parm_s parm;
@ -328,7 +332,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
cparm.ctrl = ctrl; cparm.ctrl = ctrl;
/* Now gather all the available info. */ /* Now gather all the available info. */
rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm, rc = agent_card_learn (ctrl, demand_sn, kpinfo_cb, &parm, certinfo_cb, &cparm,
sinfo_cb, &sparm); sinfo_cb, &sparm);
if (!rc && (parm.error || cparm.error || sparm.error)) if (!rc && (parm.error || cparm.error || sparm.error))
rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error; rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;

View File

@ -291,11 +291,12 @@ Import the certificates from the PEM or binary encoded files as well as
from signed-only messages. This command may also be used to import a from signed-only messages. This command may also be used to import a
secret key from a PKCS#12 file. secret key from a PKCS#12 file.
@item --learn-card @item --learn-card [@var{serialno}]
@opindex learn-card @opindex learn-card
Read information about the private keys from the smartcard and import Read information about the private keys from the current smartcard and import
the certificates from there. This command utilizes the @command{gpg-agent} the certificates from there. This command utilizes @command{gpg-agent}
and in turn the @command{scdaemon}. and in turn @command{scdaemon}. If @var{serialno} is provided the
system first makes that card the current one.
@item --change-passphrase @var{user_id} @item --change-passphrase @var{user_id}
@opindex change-passphrase @opindex change-passphrase

View File

@ -384,28 +384,14 @@ cmd_serialno (assuan_context_t ctx, char *line)
static const char hlp_switchcard[] = /* Helper for cmd_swicthcard and cmd_learn. */
"SWITCHCARD [<serialno>]\n"
"\n"
"Make the card with SERIALNO the current card.\n"
"The command \"getinfo card_list\" can be used to list\n"
"the serial numbers of inserted and known cards. Note\n"
"that the command \"SERIALNO\" can be used to refresh\n"
"the list of known cards. A simple SERIALNO status\n"
"is printed on success.";
static gpg_error_t static gpg_error_t
cmd_switchcard (assuan_context_t ctx, char *line) switchcard_core (ctrl_t ctrl, const char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0; gpg_error_t err = 0;
unsigned char *sn_bin = NULL; unsigned char *sn_bin = NULL;
size_t sn_bin_len = 0; size_t sn_bin_len = 0;
if ((err = open_card (ctrl)))
return err;
line = skip_options (line);
if (*line) if (*line)
{ {
sn_bin = hex_to_buffer (line, &sn_bin_len); sn_bin = hex_to_buffer (line, &sn_bin_len);
@ -425,6 +411,30 @@ cmd_switchcard (assuan_context_t ctx, char *line)
} }
static const char hlp_switchcard[] =
"SWITCHCARD [<serialno>]\n"
"\n"
"Make the card with SERIALNO the current card.\n"
"The command \"getinfo card_list\" can be used to list\n"
"the serial numbers of inserted and known cards. Note\n"
"that the command \"SERIALNO\" can be used to refresh\n"
"the list of known cards. A simple SERIALNO status\n"
"is printed on success.";
static gpg_error_t
cmd_switchcard (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
if ((err = open_card (ctrl)))
return err;
line = skip_options (line);
return switchcard_core (ctrl, line);
}
static const char hlp_switchapp[] = static const char hlp_switchapp[] =
"SWITCHAPP [<appname>]\n" "SWITCHAPP [<appname>]\n"
"\n" "\n"
@ -458,7 +468,8 @@ cmd_switchapp (assuan_context_t ctx, char *line)
static const char hlp_learn[] = static const char hlp_learn[] =
"LEARN [--force] [--keypairinfo] [--reread] [--multi]\n" "LEARN [--force] [--keypairinfo] [--reread] [--multi] KEYGRIP\n"
"LEARN [--demand=<serialno>] [--force] [--keypairinfo] [--reread] [--multi]\n"
"\n" "\n"
"Learn all useful information of the currently inserted card. When\n" "Learn all useful information of the currently inserted card. When\n"
"used without the force options, the command might do an INQUIRE\n" "used without the force options, the command might do an INQUIRE\n"
@ -529,6 +540,8 @@ static const char hlp_learn[] =
"\n" "\n"
"The URL to be used for locating the entire public key.\n" "The URL to be used for locating the entire public key.\n"
" \n" " \n"
"If KEYGRIP is given the card holding a key with that keygrip is used.\n"
"If --demand is used the card with the specified S/N is used.\n"
"Note, that this function may even be used on a locked card."; "Note, that this function may even be used on a locked card.";
static gpg_error_t static gpg_error_t
cmd_learn (assuan_context_t ctx, char *line) cmd_learn (assuan_context_t ctx, char *line)
@ -539,17 +552,37 @@ cmd_learn (assuan_context_t ctx, char *line)
int opt_multi = has_option (line, "--multi"); int opt_multi = has_option (line, "--multi");
int opt_reread = has_option (line, "--reread"); int opt_reread = has_option (line, "--reread");
int opt_force = has_option (line, "--force"); int opt_force = has_option (line, "--force");
const char *opt_demand;
unsigned int flags; unsigned int flags;
card_t card; card_t card;
const char *keygrip = NULL; const char *keygrip = NULL;
if ((rc = open_card (ctrl))) opt_demand = has_option_name (line, "--demand");
return rc; if (opt_demand)
{
if (*opt_demand != '=')
return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option");
line = (char *)++opt_demand;
while (*line && !spacep (line))
line++;
if (*line)
*line++ = 0;
}
line = skip_options (line); line = skip_options (line);
if (strlen (line) == 40) if (strlen (line) == 40)
keygrip = line; keygrip = line;
if ((rc = open_card (ctrl)))
return rc;
if (opt_demand)
{
rc = switchcard_core (ctrl, opt_demand);
if (rc)
return rc;
}
card = card_get (ctrl, keygrip); card = card_get (ctrl, keygrip);
if (!card) if (!card)
return gpg_error (GPG_ERR_CARD_NOT_PRESENT); return gpg_error (GPG_ERR_CARD_NOT_PRESENT);

View File

@ -1275,14 +1275,17 @@ learn_cb (void *opaque, const void *buffer, size_t length)
return 0; return 0;
} }
/* Call the agent to learn about a smartcard */
/* Call the agent to learn about a smartcard. If SERIALNO is not NULL
* switch to the card with that s/n first. */
int int
gpgsm_agent_learn (ctrl_t ctrl) gpgsm_agent_learn (ctrl_t ctrl, const char *serialno)
{ {
int rc; int rc;
struct learn_parm_s learn_parm; struct learn_parm_s learn_parm;
membuf_t data; membuf_t data;
size_t len; size_t len;
char line[ASSUAN_LINELENGTH];
rc = start_agent (ctrl); rc = start_agent (ctrl);
if (rc) if (rc)
@ -1297,7 +1300,10 @@ gpgsm_agent_learn (ctrl_t ctrl)
learn_parm.ctrl = ctrl; learn_parm.ctrl = ctrl;
learn_parm.ctx = agent_ctx; learn_parm.ctx = agent_ctx;
learn_parm.data = &data; learn_parm.data = &data;
rc = assuan_transact (agent_ctx, "LEARN --send", snprintf (line, sizeof line, "LEARN --send%s%s",
serialno? " -- ":"",
serialno? serialno:"");
rc = assuan_transact (agent_ctx, line,
learn_cb, &learn_parm, learn_cb, &learn_parm,
NULL, NULL, NULL, NULL,
learn_status_cb, &learn_parm); learn_status_cb, &learn_parm);

View File

@ -2269,11 +2269,11 @@ main ( int argc, char **argv)
case aLearnCard: case aLearnCard:
if (argc) if (argc > 1)
wrong_args ("--learn-card"); wrong_args ("--learn-card");
else else
{ {
int rc = gpgsm_agent_learn (&ctrl); int rc = gpgsm_agent_learn (&ctrl, argc? *argv : NULL);
if (rc) if (rc)
log_error ("error learning card: %s\n", gpg_strerror (rc)); log_error ("error learning card: %s\n", gpg_strerror (rc));
} }

View File

@ -539,7 +539,7 @@ int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
struct rootca_flags_s *rootca_flags); struct rootca_flags_s *rootca_flags);
int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip); int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert); int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_learn (ctrl_t ctrl, const char *serialno);
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);