summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/card-call-scd.c95
-rw-r--r--tools/gpg-card.c116
-rw-r--r--tools/gpg-card.h6
3 files changed, 193 insertions, 24 deletions
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index e4fa6abd3..80058efa9 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -1,5 +1,5 @@
/* card-call-scd.c - IPC calls to scdaemon.
- * Copyright (C) 2019 g10 Code GmbH
+ * Copyright (C) 2019, 2020 g10 Code GmbH
* Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc.
* Copyright (C) 2013-2015 Werner Koch
*
@@ -88,6 +88,7 @@ struct genkey_parm_s
struct card_cardlist_parm_s
{
gpg_error_t error;
+ int with_apps;
strlist_t list;
};
@@ -558,6 +559,45 @@ get_serialno_cb (void *opaque, const char *line)
}
+/* Make the card with SERIALNO the current one. */
+gpg_error_t
+scd_switchcard (const char *serialno)
+{
+ int err;
+ char line[ASSUAN_LINELENGTH];
+
+ err = start_agent (START_AGENT_SUPPRESS_ERRORS);
+ if (err)
+ return err;
+
+ snprintf (line, DIM(line), "SCD SWITCHCARD -- %s", serialno);
+ return assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+}
+
+
+/* Make the app APPNAME the one on the card. */
+gpg_error_t
+scd_switchapp (const char *appname)
+{
+ int err;
+ char line[ASSUAN_LINELENGTH];
+
+ if (appname && !*appname)
+ appname = NULL;
+
+ err = start_agent (START_AGENT_SUPPRESS_ERRORS);
+ if (err)
+ return err;
+
+ snprintf (line, DIM(line), "SCD SWITCHAPP --%s%s",
+ appname? " ":"", appname? appname:"");
+ return assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL);
+}
+
/* For historical reasons OpenPGP cards simply use the numbers 1 to 3
* for the <keyref>. Other cards and future versions of
@@ -1286,7 +1326,7 @@ scd_serialno (char **r_serialno, const char *demand)
if (!demand)
strcpy (line, "SCD SERIALNO --all");
else
- snprintf (line, DIM(line), "SCD SERIALNO --demand=%s", demand);
+ snprintf (line, DIM(line), "SCD SERIALNO --demand=%s --all", demand);
err = assuan_transact (agent_ctx, line,
NULL, NULL, NULL, NULL,
@@ -1407,10 +1447,24 @@ card_cardlist_cb (void *opaque, const char *line)
for (n=0,s=line; hexdigitp (s); s++, n++)
;
- if (!n || (n&1) || *s)
+ if (!n || (n&1))
parm->error = gpg_error (GPG_ERR_ASS_PARAMETER);
+ if (parm->with_apps)
+ {
+ /* Format of the stored string is the S/N, a space, and a
+ * space separated list of appnames. */
+ if (*s != ' ' || spacep (s+1) || !s[1])
+ parm->error = gpg_error (GPG_ERR_ASS_PARAMETER);
+ else /* We assume the rest of the line is well formatted. */
+ add_to_strlist (&parm->list, line);
+ }
else
- add_to_strlist (&parm->list, line);
+ {
+ if (*s)
+ parm->error = gpg_error (GPG_ERR_ASS_PARAMETER);
+ else
+ add_to_strlist (&parm->list, line);
+ }
}
return 0;
@@ -1446,6 +1500,39 @@ scd_cardlist (strlist_t *result)
}
+/* Return the serial numbers and appnames of the current card or, with
+ * ALL given has true, of all cards currently inserted. */
+gpg_error_t
+scd_applist (strlist_t *result, int all)
+{
+ gpg_error_t err;
+ struct card_cardlist_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ *result = NULL;
+
+ err = start_agent (START_AGENT_SUPPRESS_ERRORS);
+ if (err)
+ return err;
+
+ parm.with_apps = 1;
+ err = assuan_transact (agent_ctx,
+ all ? "SCD GETINFO all_active_apps"
+ /**/: "SCD GETINFO active_apps",
+ NULL, NULL, NULL, NULL,
+ card_cardlist_cb, &parm);
+ if (!err && parm.error)
+ err = parm.error;
+
+ if (!err)
+ *result = parm.list;
+ else
+ free_strlist (parm.list);
+
+ return err;
+}
+
+
/* Change the PIN of an OpenPGP card or reset the retry counter.
* CHVNO 1: Change the PIN
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index e9dc8ebfd..52f37591f 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -1,5 +1,5 @@
/* gpg-card.c - An interactive tool to work with cards.
- * Copyright (C) 2019 g10 Code GmbH
+ * Copyright (C) 2019, 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -1000,53 +1000,118 @@ list_card (card_info_t info)
-/* The LIST command. This also updates INFO. */
+/* The LIST command. This also updates INFO if needed. */
static gpg_error_t
cmd_list (card_info_t info, char *argstr)
{
gpg_error_t err;
- int opt_cards;
+ int opt_cards, opt_apps;
strlist_t cards = NULL;
strlist_t sl;
estream_t fp = opt.interactive? NULL : es_stdout;
- int cardno, count;
-
+ int cardno = -1;
+ char *appstr = NULL;
+ int count;
+ int need_learn = 0;
+ int star;
+ size_t snlen;
+ const char *s;
if (!info)
return print_help
- ("LIST [--cards] [N]\n\n"
- "Show the content of the current card or with N given the N-th card.\n"
- "Option --cards lists available cards.",
+ ("LIST [--cards] [--apps] [N] [APP]\n\n"
+ "Show the content of the current card.\n"
+ "With N given select and list the n-th card;\n"
+ "with APP also given select that application.\n"
+ "To select an APP on the current card use '-' for N.\n"
+ "Option --cards lists available cards.\n"
+ "Option --apps lists additional card applications",
0);
opt_cards = has_leading_option (argstr, "--cards");
+ opt_apps = has_leading_option (argstr, "--apps");
argstr = skip_options (argstr);
- if (digitp (argstr))
+ if (digitp (argstr) || (*argstr == '-' && spacep (argstr+1)))
{
- cardno = atoi (argstr);
- while (digitp (argstr))
- argstr++;
+ if (*argstr == '-' && (argstr[1] || spacep (argstr+1)))
+ argstr++; /* Keep current card. */
+ else
+ {
+ cardno = atoi (argstr);
+ while (digitp (argstr))
+ argstr++;
+ }
while (spacep (argstr))
argstr++;
+ if (*argstr)
+ {
+ appstr = argstr;
+ while (*argstr && !spacep (argstr))
+ argstr++;
+ while (spacep (argstr))
+ argstr++;
+ if (*argstr)
+ {
+ /* Extra arguments found. */
+ err = gpg_error (GPG_ERR_INV_ARG);
+ goto leave;
+ }
+ }
+ }
+ else if (*argstr)
+ {
+ /* First argument needs to be a digit. */
+ err = gpg_error (GPG_ERR_INV_ARG);
+ goto leave;
}
- else
- cardno = -1;
- if (opt_cards)
+ if (!info->serialno)
{
- err = scd_cardlist (&cards);
+ /* This is probably the first call. We need to send a SERIALNO
+ * command to scd so that our session knows all cards. */
+ err = scd_serialno (NULL, NULL);
+ if (err)
+ goto leave;
+ need_learn = 1;
+ }
+
+
+ if (opt_cards || opt_apps)
+ {
+ /* Note that with option --apps CARDS is here the list of all
+ * apps. Format is "SERIALNO APPNAME {APPNAME}". We print the
+ * card number in the first column. */
+ if (opt_apps)
+ err = scd_applist (&cards, opt_cards);
+ else
+ err = scd_cardlist (&cards);
if (err)
goto leave;
for (count = 0, sl = cards; sl; sl = sl->next, count++)
- tty_fprintf (fp, "%d %s\n", count, sl->d);
+ {
+ if (info && info->serialno)
+ {
+ s = strchr (sl->d, ' ');
+ if (s)
+ snlen = s - sl->d;
+ else
+ snlen = strlen (sl->d);
+ star = (strlen (info->serialno) == snlen
+ && !memcmp (info->serialno, sl->d, snlen));
+ }
+ else
+ star = 0;
+ tty_fprintf (fp, "%d%c %s\n", count, star? '*':' ', sl->d);
+ }
}
else
{
if (cardno != -1)
{
+ /* Switch to the requested card. */
err = scd_cardlist (&cards);
if (err)
goto leave;
@@ -1058,12 +1123,23 @@ cmd_list (card_info_t info, char *argstr)
err = gpg_error (GPG_ERR_INV_INDEX);
goto leave;
}
- err = scd_serialno (NULL, sl->d);
+ err = scd_switchcard (sl->d);
+ need_learn = 1;
+ }
+
+ if (appstr && *appstr)
+ {
+ /* Switch to the requested app. */
+ err = scd_switchapp (appstr);
if (err)
goto leave;
+ need_learn = 1;
}
- err = scd_learn (info);
+ if (need_learn)
+ err = scd_learn (info);
+ else
+ err = 0;
if (!err)
list_card (info);
}
@@ -3341,7 +3417,7 @@ interactive_loop (void)
if (!info)
{
- /* Copy the pending help arg into our answer. Noe that
+ /* Copy the pending help arg into our answer. Note that
* help_arg points into answer. */
p = xstrdup (help_arg);
help_arg = NULL;
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 35db14d25..c6f73405f 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -204,6 +204,10 @@ const char *app_type_string (app_type_t app_type);
gpg_error_t scd_apdu (const char *hexapdu, unsigned int *r_sw,
unsigned char **r_data, size_t *r_datalen);
+
+gpg_error_t scd_switchcard (const char *serialno);
+gpg_error_t scd_switchapp (const char *appname);
+
gpg_error_t scd_learn (card_info_t info);
gpg_error_t scd_getattr (const char *name, struct card_info_s *info);
gpg_error_t scd_setattr (const char *name,
@@ -214,10 +218,12 @@ gpg_error_t scd_writekey (const char *keyref, int force, const char *keygrip);
gpg_error_t scd_genkey (const char *keyref, int force, const char *algo,
u32 *createtime);
gpg_error_t scd_serialno (char **r_serialno, const char *demand);
+
gpg_error_t scd_readcert (const char *certidstr,
void **r_buf, size_t *r_buflen);
gpg_error_t scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result);
gpg_error_t scd_cardlist (strlist_t *result);
+gpg_error_t scd_applist (strlist_t *result, int all);
gpg_error_t scd_change_pin (const char *pinref, int reset_mode);
gpg_error_t scd_checkpin (const char *serialno);