1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-16 00:29:50 +02:00

card: New subcommand "checkkeys".

* agent/command.c (cmd_havekey): Add new option --info.
* tools/card-call-scd.c (scd_readkey): Allow using without result arg.
(struct havekey_status_parm_s): New.
(havekey_status_cb): New.
(scd_havekey_info): New.
(scd_delete_key): New.
* tools/gpg-card.c (print_keygrip): Add arg with_lf.
(cmd_checkkeys): New.
(cmdCHECKKEYS): New.
(cmds): Add command "checkkeys".
(dispatch_command, interactive_loop): Call cmd_checkkeys.
--

GnuPG-bug-id: 6943
This commit is contained in:
Werner Koch 2024-01-16 18:05:46 +01:00
parent c8060a8f23
commit adeb17e375
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 265 additions and 11 deletions

View File

@ -634,10 +634,12 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
static const char hlp_havekey[] = static const char hlp_havekey[] =
"HAVEKEY <hexstrings_with_keygrips>\n" "HAVEKEY <hexstrings_with_keygrips>\n"
"HAVEKEY --list[=<limit>]\n" "HAVEKEY --list[=<limit>]\n"
"HAVEKEY --info <hexkeygrip>\n"
"\n" "\n"
"Return success if at least one of the secret keys with the given\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" "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 static gpg_error_t
cmd_havekey (assuan_context_t ctx, char *line) cmd_havekey (assuan_context_t ctx, char *line)
{ {
@ -645,7 +647,8 @@ cmd_havekey (assuan_context_t ctx, char *line)
gpg_error_t err; gpg_error_t err;
unsigned char grip[20]; unsigned char grip[20];
char *p; 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; int counter;
char *dirname; char *dirname;
gnupg_dir_t dir; gnupg_dir_t dir;
@ -653,15 +656,46 @@ cmd_havekey (assuan_context_t ctx, char *line)
char hexgrip[41]; char hexgrip[41];
struct card_key_info_s *keyinfo_on_cards, *l; 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"))) if ((p = option_value (line, "--list")))
list_mode = atoi (p); list_mode = atoi (p);
else else
list_mode = -1; 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) if (!list_mode)

View File

@ -1529,14 +1529,16 @@ scd_readkey (const char *keyrefstr, int create_shadow, gcry_sexp_t *r_result)
unsigned char *buf; unsigned char *buf;
size_t len, buflen; size_t len, buflen;
*r_result = NULL; if (r_result)
*r_result = NULL;
err = start_agent (0); err = start_agent (0);
if (err) if (err)
return err; return err;
init_membuf (&data, 1024); init_membuf (&data, 1024);
if (create_shadow) 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 else
snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr); snprintf (line, DIM(line), "SCD READKEY %s", keyrefstr);
err = assuan_transact (agent_ctx, line, 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) if (!buf)
return gpg_error_from_syserror (); 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); xfree (buf);
return err; 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 /* Return a malloced string describing the statusword SW. On error
* NULL is returned. */ * NULL is returned. */
char * char *

View File

@ -582,13 +582,14 @@ print_shax_fpr (estream_t fp, const unsigned char *fpr, unsigned int fprlen)
/* Print the keygrip GRP. */ /* Print the keygrip GRP. */
static void 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; int i;
for (i=0; i < 20 ; i++, grp++) for (i=0; i < 20 ; i++, grp++)
tty_fprintf (fp, "%02X", *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; goto leave;
} }
print_keygrip (fp, kinfo->grip); print_keygrip (fp, kinfo->grip, 1);
tty_fprintf (fp, " keyref .....: %s", kinfo->keyref); tty_fprintf (fp, " keyref .....: %s", kinfo->keyref);
if (kinfo->usage) 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. */ /* The VERIFY command. */
static gpg_error_t static gpg_error_t
@ -3726,6 +3854,7 @@ enum cmdids
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP, cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU, cmdGPG, cmdGPGSM, cmdHISTORY, cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU, cmdGPG, cmdGPGSM, cmdHISTORY,
cmdCHECKKEYS,
cmdINVCMD cmdINVCMD
}; };
@ -3765,6 +3894,7 @@ static struct
{ "readcert", cmdREADCERT, N_("read a certificate from a data object")}, { "readcert", cmdREADCERT, N_("read a certificate from a data object")},
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")}, { "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key 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")}, { "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ "gpg", cmdGPG, NULL}, { "gpg", cmdGPG, NULL},
{ "gpgsm", cmdGPGSM, 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 cmdGPG: err = cmd_gpg (info, argstr, 0); break;
case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break; case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break;
case cmdHISTORY: err = 0; break; /* Only used in interactive mode. */ case cmdHISTORY: err = 0; break; /* Only used in interactive mode. */
case cmdCHECKKEYS: err = cmd_checkkeys (info, argstr); break;
case cmdINVCMD: case cmdINVCMD:
default: default:
@ -4160,6 +4291,7 @@ interactive_loop (void)
case cmdGPG: err = cmd_gpg (info, argstr, 0); break; case cmdGPG: err = cmd_gpg (info, argstr, 0); break;
case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break; case cmdGPGSM: err = cmd_gpg (info, argstr, 1); break;
case cmdHISTORY: err = cmd_history (info, argstr); break; case cmdHISTORY: err = cmd_history (info, argstr); break;
case cmdCHECKKEYS: err = cmd_checkkeys (info, argstr); break;
case cmdINVCMD: case cmdINVCMD:
default: default:

View File

@ -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_applist (strlist_t *result, int all);
gpg_error_t scd_change_pin (const char *pinref, int reset_mode, int nullpin); 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_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); unsigned long agent_get_s2k_count (void);