aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/command.c44
-rw-r--r--tools/card-call-scd.c92
-rw-r--r--tools/gpg-card.c138
-rw-r--r--tools/gpg-card.h2
4 files changed, 265 insertions, 11 deletions
diff --git a/agent/command.c b/agent/command.c
index b7a71cbe5..da91053d8 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -634,10 +634,12 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
static const char hlp_havekey[] =
"HAVEKEY <hexstrings_with_keygrips>\n"
"HAVEKEY --list[=<limit>]\n"
+ "HAVEKEY --info <hexkeygrip>\n"
"\n"
"Return success if at least one of the secret keys with the given\n"
"keygrips is available. With --list return all available keygrips\n"
- "as binary data; with <limit> bail out at this number of keygrips";
+ "as binary data; with <limit> bail out at this number of keygrips.\n"
+ "In --info mode check just one keygrip.";
static gpg_error_t
cmd_havekey (assuan_context_t ctx, char *line)
{
@@ -645,7 +647,8 @@ cmd_havekey (assuan_context_t ctx, char *line)
gpg_error_t err;
unsigned char grip[20];
char *p;
- int list_mode; /* Less than 0 for no limit. */
+ int list_mode = 0; /* Less than 0 for no limit. */
+ int info_mode = 0;
int counter;
char *dirname;
gnupg_dir_t dir;
@@ -653,15 +656,46 @@ cmd_havekey (assuan_context_t ctx, char *line)
char hexgrip[41];
struct card_key_info_s *keyinfo_on_cards, *l;
- if (has_option_name (line, "--list"))
+ if (has_option (line, "--info"))
+ info_mode = 1;
+ else if (has_option_name (line, "--list"))
{
if ((p = option_value (line, "--list")))
list_mode = atoi (p);
else
list_mode = -1;
}
- else
- list_mode = 0;
+
+ line = skip_options (line);
+
+
+ if (info_mode)
+ {
+ int keytype;
+ const char *infostring;
+
+ ctrl = assuan_get_pointer (ctx);
+
+ err = parse_keygrip (ctx, line, grip);
+ if (err)
+ goto leave;
+
+ err = agent_key_info_from_file (ctrl, grip, &keytype, NULL, NULL);
+ if (err)
+ goto leave;
+
+ switch (keytype)
+ {
+ case PRIVATE_KEY_CLEAR:
+ case PRIVATE_KEY_OPENPGP_NONE: infostring = "clear"; break;
+ case PRIVATE_KEY_PROTECTED: infostring = "protected"; break;
+ case PRIVATE_KEY_SHADOWED: infostring = "shadowed"; break;
+ default: infostring = "unknown"; break;
+ }
+
+ err = agent_write_status (ctrl, "KEYFILEINFO", infostring, NULL);
+ goto leave;
+ }
if (!list_mode)
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 34b03e694..f6ce565c3 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -1529,14 +1529,16 @@ scd_readkey (const char *keyrefstr, int create_shadow, gcry_sexp_t *r_result)
unsigned char *buf;
size_t len, buflen;
- *r_result = NULL;
+ if (r_result)
+ *r_result = NULL;
err = start_agent (0);
if (err)
return err;
init_membuf (&data, 1024);
if (create_shadow)
- snprintf (line, DIM(line), "READKEY --card -- %s", keyrefstr);
+ snprintf (line, DIM(line), "READKEY %s--card -- %s",
+ r_result? "" : "--no-data ", keyrefstr);
else
snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr);
err = assuan_transact (agent_ctx, line,
@@ -1552,7 +1554,7 @@ scd_readkey (const char *keyrefstr, int create_shadow, gcry_sexp_t *r_result)
if (!buf)
return gpg_error_from_syserror ();
- err = gcry_sexp_new (r_result, buf, buflen, 0);
+ err = r_result ? gcry_sexp_new (r_result, buf, buflen, 0) : 0;
xfree (buf);
return err;
@@ -1769,6 +1771,90 @@ agent_get_s2k_count (void)
}
+
+struct havekey_status_parm_s
+{
+ char *string;
+};
+
+static gpg_error_t
+havekey_status_cb (void *opaque, const char *line)
+{
+ struct havekey_status_parm_s *parm = opaque;
+ const char *s;
+ char *p;
+
+ if ((s = has_leading_keyword (line, "KEYFILEINFO")))
+ {
+ xfree (parm->string);
+ parm->string = xtrystrdup (s);
+ if (!parm->string)
+ return gpg_error_from_syserror ();
+ p = strchr (parm->string, ' ');
+ if (p)
+ *p = 0;
+ }
+
+ return 0;
+}
+
+
+/* Run the HAVEKEY --info command and stores the retrieved string at
+ * R_RESULT. Caller must free that string. If an error is returned
+ * R_RESULT is set to NULL. */
+gpg_error_t
+scd_havekey_info (const unsigned char *grip, char **r_result)
+{
+ gpg_error_t err;
+ char line[ASSUAN_LINELENGTH];
+ struct havekey_status_parm_s parm = {NULL};
+
+ *r_result = NULL;
+
+ err = start_agent (0);
+ if (err)
+ return err;
+
+ snprintf (line, sizeof line, "HAVEKEY --info ");
+ log_assert (ASSUAN_LINELENGTH > strlen(line) + 2*KEYGRIP_LEN + 10);
+ bin2hex (grip, KEYGRIP_LEN, line+strlen(line));
+
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL,
+ havekey_status_cb, &parm);
+ if (err)
+ xfree (parm.string);
+ else
+ *r_result = parm.string;
+ return err;
+}
+
+
+/* Run the DELETE_KEY command. If FORCE is given the user will not be
+ * asked for confirmation. */
+gpg_error_t
+scd_delete_key (const unsigned char *grip, int force)
+{
+ gpg_error_t err;
+ char line[ASSUAN_LINELENGTH];
+ struct default_inq_parm_s dfltparm = {NULL};
+
+ err = start_agent (0);
+ if (err)
+ return err;
+ dfltparm.ctx = agent_ctx;
+
+ snprintf (line, sizeof line, "DELETE_KEY%s ", force?" --force":"");
+ log_assert (ASSUAN_LINELENGTH > strlen(line) + 2*KEYGRIP_LEN + 10);
+ bin2hex (grip, KEYGRIP_LEN, line+strlen(line));
+
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL);
+ return err;
+}
+
+
+
/* Return a malloced string describing the statusword SW. On error
* NULL is returned. */
char *
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index 4b0457ab9..442acdc84 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -582,13 +582,14 @@ print_shax_fpr (estream_t fp, const unsigned char *fpr, unsigned int fprlen)
/* Print the keygrip GRP. */
static void
-print_keygrip (estream_t fp, const unsigned char *grp)
+print_keygrip (estream_t fp, const unsigned char *grp, int with_lf)
{
int i;
for (i=0; i < 20 ; i++, grp++)
tty_fprintf (fp, "%02X", *grp);
- tty_fprintf (fp, "\n");
+ if (with_lf)
+ tty_fprintf (fp, "\n");
}
@@ -700,7 +701,7 @@ list_one_kinfo (card_info_t info, key_info_t kinfo,
goto leave;
}
- print_keygrip (fp, kinfo->grip);
+ print_keygrip (fp, kinfo->grip, 1);
tty_fprintf (fp, " keyref .....: %s", kinfo->keyref);
if (kinfo->usage)
{
@@ -1376,6 +1377,133 @@ cmd_list (card_info_t info, char *argstr)
}
+
+/* The CHECKKEYS command. */
+static gpg_error_t
+cmd_checkkeys (card_info_t callerinfo, char *argstr)
+{
+ gpg_error_t err;
+ estream_t fp = opt.interactive? NULL : es_stdout;
+ strlist_t cards = NULL;
+ strlist_t sl;
+ int opt_ondisk;
+ int opt_delete_clear;
+ int opt_delete_protected;
+ int delete_count = 0;
+ struct card_info_s info_buffer = { 0 };
+ card_info_t info = &info_buffer;
+ key_info_t kinfo;
+
+
+ if (!callerinfo)
+ return print_help
+ ("CHECKKEYS [--ondisk] [--delete-clear-copy]\n\n"
+ "Print a list of keys on all inserted cards. With --ondisk only\n"
+ "keys are listed which also have a copy on disk. Missing shadow\n"
+ "keys are created. With --delete-clear, copies of keys also stored\n"
+ "on disk without any protection will be deleted.\n"
+ , 0);
+
+
+ opt_ondisk = has_leading_option (argstr, "--ondisk");
+ opt_delete_clear = has_leading_option (argstr, "--delete-clear-copy");
+ opt_delete_protected = has_leading_option (argstr, "--delete-protected-copy");
+ argstr = skip_options (argstr);
+
+ if (*argstr)
+ {
+ /* No args expected */
+ err = gpg_error (GPG_ERR_INV_ARG);
+ goto leave;
+ }
+
+ if (!callerinfo->serialno)
+ {
+ /* This is probably the first call We need to send a SERIALNO
+ * command to scdaemon so that our session knows all cards. */
+ err = scd_serialno (NULL, NULL);
+ if (err)
+ goto leave;
+ }
+
+ /* Get the list of all cards. */
+ err = scd_cardlist (&cards);
+ if (err)
+ goto leave;
+
+ /* Loop over all cards. We use our own info buffer here. */
+ for (sl = cards; sl; sl = sl->next)
+ {
+ err = scd_switchcard (sl->d);
+ if (err)
+ {
+ log_error ("Error switching to card %s: %s\n",
+ sl->d, gpg_strerror (err));
+ continue;
+ }
+ release_card_info (info);
+ err = scd_learn (info, 0);
+ if (err)
+ {
+ log_error ("Error getting infos from card %s: %s\n",
+ sl->d, gpg_strerror (err));
+ continue;
+ }
+
+ for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next)
+ {
+ char *infostr;
+
+ err = scd_havekey_info (kinfo->grip, &infostr);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ {
+ /* Create a shadow key and try again. */
+ scd_readkey (kinfo->keyref, 1, NULL);
+ err = scd_havekey_info (kinfo->grip, &infostr);
+ }
+ if (err)
+ log_error ("Error getting infos for a key: %s\n",
+ gpg_strerror (err));
+
+ if (opt_ondisk && infostr && !strcmp (infostr, "shadowed"))
+ ; /* Don't print this one. */
+ else
+ {
+ tty_fprintf (fp, "%s %s ",
+ nullnone (info->serialno),
+ app_type_string (info->apptype));
+ print_keygrip (fp, kinfo->grip, 0);
+ tty_fprintf (fp, " %s %s\n",
+ kinfo->keyref, infostr? infostr: "error");
+ }
+ if (infostr
+ && ((opt_delete_clear && !strcmp (infostr, "clear"))
+ || (opt_delete_protected && !strcmp (infostr, "protected"))))
+ {
+ err = scd_delete_key (kinfo->grip, 0);
+ if (err)
+ log_error ("Error deleting a key copy: %s\n",
+ gpg_strerror (err));
+ else
+ delete_count++;
+ }
+ xfree (infostr);
+ }
+ }
+ if (delete_count)
+ log_info ("Number of deleted key copies: %d\n", delete_count);
+
+ err = 0;
+
+ leave:
+ release_card_info (info);
+ free_strlist (cards);
+ /* Better reset to the original card. */
+ scd_learn (callerinfo, 0);
+ return err;
+}
+
+
/* The VERIFY command. */
static gpg_error_t
@@ -3726,6 +3854,7 @@ enum cmdids
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU, cmdGPG, cmdGPGSM, cmdHISTORY,
+ cmdCHECKKEYS,
cmdINVCMD
};
@@ -3765,6 +3894,7 @@ static struct
{ "readcert", cmdREADCERT, N_("read a certificate from a data object")},
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
+ { "checkkeys", cmdCHECKKEYS, N_("run various checks on the keys")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ "gpg", cmdGPG, NULL},
{ "gpgsm", cmdGPGSM, NULL},
@@ -3901,6 +4031,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdGPG: err = cmd_gpg (info, argstr, 0); break;
case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break;
case cmdHISTORY: err = 0; break; /* Only used in interactive mode. */
+ case cmdCHECKKEYS: err = cmd_checkkeys (info, argstr); break;
case cmdINVCMD:
default:
@@ -4160,6 +4291,7 @@ interactive_loop (void)
case cmdGPG: err = cmd_gpg (info, argstr, 0); break;
case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break;
case cmdHISTORY: err = cmd_history (info, argstr); break;
+ case cmdCHECKKEYS: err = cmd_checkkeys (info, argstr); break;
case cmdINVCMD:
default:
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 86b63cda0..5b49ef31e 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -246,6 +246,8 @@ 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, int nullpin);
gpg_error_t scd_checkpin (const char *serialno);
+gpg_error_t scd_havekey_info (const unsigned char *grip, char **r_result);
+gpg_error_t scd_delete_key (const unsigned char *grip, int force);
unsigned long agent_get_s2k_count (void);