mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
agent,ssh: Make not-inserted OpenPGP.3 keys available for SSH.
* agent/agent.h (agent_ssh_key_from_file): New. * agent/command-ssh.c (get_ssh_keyinfo_on_cards): New. (ssh_send_available_keys): Loop on the GNUPG_PRIVATE_KEYS_DIR. Support keys by agent_ssh_key_from_file. (ssh_handler_request_identities): Move card key handling to ssh_send_available_keys. * agent/findkey.c (public_key_from_file): New. Adding handling for SSH. (agent_public_key_from_file): Use public_key_from_file. (agent_ssh_key_from_file): New. -- GnuPG-bug-id: 5996 Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
parent
c07c79a1d7
commit
193fcc2f7a
@ -468,6 +468,9 @@ gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
|
||||
gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result);
|
||||
gpg_error_t agent_ssh_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result);
|
||||
int agent_pk_get_algo (gcry_sexp_t s_key);
|
||||
int agent_is_tpm2_key(gcry_sexp_t s_key);
|
||||
int agent_key_available (const unsigned char *grip);
|
||||
|
@ -2449,48 +2449,194 @@ card_key_available (ctrl_t ctrl, const struct card_key_info_s *keyinfo,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct card_key_info_s *
|
||||
get_ssh_keyinfo_on_cards (ctrl_t ctrl)
|
||||
{
|
||||
struct card_key_info_s *keyinfo_on_cards = NULL;
|
||||
gpg_error_t err;
|
||||
char *serialno;
|
||||
|
||||
if (opt.disable_daemon[DAEMON_SCD])
|
||||
return NULL;
|
||||
|
||||
/* Scan for new device(s). */
|
||||
err = agent_card_serialno (ctrl, &serialno, NULL);
|
||||
if (err)
|
||||
{
|
||||
if (opt.verbose)
|
||||
log_info (_("error getting list of cards: %s\n"),
|
||||
gpg_strerror (err));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xfree (serialno);
|
||||
|
||||
err = agent_card_keyinfo (ctrl, NULL, GCRY_PK_USAGE_AUTH, &keyinfo_on_cards);
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
return keyinfo_on_cards;
|
||||
}
|
||||
|
||||
static gpg_error_t
|
||||
ssh_send_available_keys (ctrl_t ctrl, estream_t key_blobs, u32 *key_counter_p)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *dirname;
|
||||
gnupg_dir_t dir = NULL;
|
||||
gnupg_dirent_t dir_entry;
|
||||
char hexgrip[41];
|
||||
ssh_control_file_t cf = NULL;
|
||||
struct card_key_info_s *keyinfo_on_cards, *l;
|
||||
char *cardsn;
|
||||
gcry_sexp_t key_public = NULL;
|
||||
|
||||
err = open_control_file (&cf, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
while (!read_control_file_item (cf))
|
||||
/* First, get information keys available on card(s). */
|
||||
keyinfo_on_cards = get_ssh_keyinfo_on_cards (ctrl);
|
||||
|
||||
/* Then, look at all keys with "OPENPGP.3" idstring. */
|
||||
/* Look at all the registered and non-disabled keys, in sshcontrol. */
|
||||
dirname = make_filename_try (gnupg_homedir (),
|
||||
GNUPG_PRIVATE_KEYS_DIR, NULL);
|
||||
if (!dirname)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
agent_card_free_keyinfo (keyinfo_on_cards);
|
||||
return err;
|
||||
}
|
||||
dir = gnupg_opendir (dirname);
|
||||
if (!dir)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
xfree (dirname);
|
||||
agent_card_free_keyinfo (keyinfo_on_cards);
|
||||
return err;
|
||||
}
|
||||
xfree (dirname);
|
||||
|
||||
while ( (dir_entry = gnupg_readdir (dir)) )
|
||||
{
|
||||
struct card_key_info_s *l_prev = NULL;
|
||||
int disabled, is_ssh;
|
||||
unsigned char grip[20];
|
||||
gcry_sexp_t key_public = NULL;
|
||||
|
||||
if (!cf->item.valid)
|
||||
continue; /* Should not happen. */
|
||||
if (cf->item.disabled)
|
||||
cardsn = NULL;
|
||||
if (strlen (dir_entry->d_name) != 44
|
||||
|| strcmp (dir_entry->d_name + 40, ".key"))
|
||||
continue;
|
||||
log_assert (strlen (cf->item.hexgrip) == 40);
|
||||
hex2bin (cf->item.hexgrip, grip, sizeof (grip));
|
||||
strncpy (hexgrip, dir_entry->d_name, 40);
|
||||
hexgrip[40] = 0;
|
||||
|
||||
err = agent_public_key_from_file (ctrl, grip, &key_public);
|
||||
if ( hex2bin (hexgrip, grip, 20) < 0 )
|
||||
continue; /* Bad hex string. */
|
||||
|
||||
/* Check if it's a key on card. */
|
||||
for (l = keyinfo_on_cards; l; l = l->next)
|
||||
if (!memcmp (l->keygrip, hexgrip, 40))
|
||||
break;
|
||||
else
|
||||
l_prev = l;
|
||||
|
||||
/* Check if it's listed in "ssh_control" file. */
|
||||
disabled = is_ssh = 0;
|
||||
err = search_control_file (cf, hexgrip, &disabled, NULL, NULL);
|
||||
if (!err)
|
||||
{
|
||||
if (!disabled)
|
||||
is_ssh = 1;
|
||||
}
|
||||
else if (gpg_err_code (err) != GPG_ERR_EOF)
|
||||
break;
|
||||
|
||||
if (l)
|
||||
{
|
||||
err = card_key_available (ctrl, l, &key_public, &cardsn);
|
||||
/* Remove the entry from the list of KEYINFO_ON_CARD */
|
||||
if (l_prev)
|
||||
l_prev->next = l->next;
|
||||
else
|
||||
keyinfo_on_cards = l->next;
|
||||
xfree (l->serialno);
|
||||
xfree (l->idstr);
|
||||
xfree (l->usage);
|
||||
xfree (l);
|
||||
l = NULL;
|
||||
}
|
||||
else if (is_ssh)
|
||||
err = agent_public_key_from_file (ctrl, grip, &key_public);
|
||||
else
|
||||
/* Examine the file if it's suitable for SSH. */
|
||||
err = agent_ssh_key_from_file (ctrl, grip, &key_public);
|
||||
if (err)
|
||||
{
|
||||
log_error ("%s:%d: key '%s' skipped: %s\n",
|
||||
cf->fname, cf->lnr, cf->item.hexgrip,
|
||||
gpg_strerror (err));
|
||||
/* Clear ERR, skiping the key in question. */
|
||||
err = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = ssh_send_key_public (key_blobs, key_public, NULL);
|
||||
err = ssh_send_key_public (key_blobs, key_public, cardsn);
|
||||
xfree (cardsn);
|
||||
if (err)
|
||||
break;
|
||||
{
|
||||
if (opt.verbose)
|
||||
gcry_log_debugsxp ("pubkey", key_public);
|
||||
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_CURVE
|
||||
|| gpg_err_code (err) == GPG_ERR_INV_CURVE)
|
||||
{
|
||||
/* For example a Brainpool curve or a curve we don't
|
||||
* support at all but a smartcard lists that curve.
|
||||
* We ignore them. */
|
||||
}
|
||||
else
|
||||
{
|
||||
gcry_sexp_release (key_public);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gcry_sexp_release (key_public);
|
||||
(*key_counter_p)++;
|
||||
}
|
||||
|
||||
close_control_file (cf);
|
||||
gnupg_closedir (dir);
|
||||
ssh_close_control_file (cf);
|
||||
|
||||
/* Lastly, handle remaining keys which don't have the stub files. */
|
||||
for (l = keyinfo_on_cards; l; l = l->next)
|
||||
{
|
||||
cardsn = NULL;
|
||||
if (card_key_available (ctrl, l, &key_public, &cardsn))
|
||||
continue;
|
||||
|
||||
err = ssh_send_key_public (key_blobs, key_public, cardsn);
|
||||
xfree (cardsn);
|
||||
if (err)
|
||||
{
|
||||
if (opt.verbose)
|
||||
gcry_log_debugsxp ("pubkey", key_public);
|
||||
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_CURVE
|
||||
|| gpg_err_code (err) == GPG_ERR_INV_CURVE)
|
||||
{
|
||||
/* For example a Brainpool curve or a curve we don't
|
||||
* support at all but a smartcard lists that curve.
|
||||
* We ignore them. */
|
||||
}
|
||||
else
|
||||
{
|
||||
gcry_sexp_release (key_public);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
(*key_counter_p)++;
|
||||
}
|
||||
|
||||
agent_card_free_keyinfo (keyinfo_on_cards);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -2528,72 +2674,6 @@ ssh_handler_request_identities (ctrl_t ctrl,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* First check whether a key is currently available in the card
|
||||
reader - this should be allowed even without being listed in
|
||||
sshcontrol. */
|
||||
|
||||
if (!opt.disable_daemon[DAEMON_SCD])
|
||||
{
|
||||
char *serialno;
|
||||
struct card_key_info_s *keyinfo_list;
|
||||
struct card_key_info_s *keyinfo;
|
||||
|
||||
/* Scan device(s), and get list of KEYGRIP. */
|
||||
err = agent_card_serialno (ctrl, &serialno, NULL);
|
||||
if (!err)
|
||||
{
|
||||
xfree (serialno);
|
||||
err = agent_card_keyinfo (ctrl, NULL, GCRY_PK_USAGE_AUTH,
|
||||
&keyinfo_list);
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
if (opt.verbose)
|
||||
log_info (_("error getting list of cards: %s\n"),
|
||||
gpg_strerror (err));
|
||||
goto scd_out;
|
||||
}
|
||||
|
||||
for (keyinfo = keyinfo_list; keyinfo; keyinfo = keyinfo->next)
|
||||
{
|
||||
char *cardsn;
|
||||
gcry_sexp_t key_public = NULL;
|
||||
|
||||
if (card_key_available (ctrl, keyinfo, &key_public, &cardsn))
|
||||
continue;
|
||||
|
||||
err = ssh_send_key_public (key_blobs, key_public, cardsn);
|
||||
xfree (cardsn);
|
||||
if (err)
|
||||
{
|
||||
if (opt.verbose)
|
||||
gcry_log_debugsxp ("pubkey", key_public);
|
||||
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_CURVE
|
||||
|| gpg_err_code (err) == GPG_ERR_INV_CURVE)
|
||||
{
|
||||
/* For example a Brainpool curve or a curve we don't
|
||||
* support at all but a smartcard lists that curve.
|
||||
* We ignore them. */
|
||||
}
|
||||
else
|
||||
{
|
||||
agent_card_free_keyinfo (keyinfo_list);
|
||||
gcry_sexp_release (key_public);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
key_counter++;
|
||||
|
||||
gcry_sexp_release (key_public);
|
||||
}
|
||||
|
||||
agent_card_free_keyinfo (keyinfo_list);
|
||||
}
|
||||
|
||||
scd_out:
|
||||
/* Then look at all the registered and non-disabled keys. */
|
||||
err = ssh_send_available_keys (ctrl, key_blobs, &key_counter);
|
||||
if (!err)
|
||||
{
|
||||
|
@ -1351,14 +1351,14 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
|
||||
at RESULT. This function extracts the public key from the private
|
||||
key database. On failure an error code is returned and NULL stored
|
||||
at RESULT. */
|
||||
gpg_error_t
|
||||
agent_public_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result)
|
||||
static gpg_error_t
|
||||
public_key_from_file (ctrl_t ctrl, const unsigned char *grip,
|
||||
gcry_sexp_t *result, int for_ssh)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int i, idx;
|
||||
gcry_sexp_t s_skey;
|
||||
nvc_t keymeta = NULL;
|
||||
const char *algoname, *elems;
|
||||
int npkey;
|
||||
gcry_mpi_t array[10];
|
||||
@ -1380,10 +1380,32 @@ agent_public_key_from_file (ctrl_t ctrl,
|
||||
|
||||
*result = NULL;
|
||||
|
||||
err = read_key_file (grip, &s_skey, NULL);
|
||||
err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (keymeta)
|
||||
{
|
||||
/* Token: <SERIALNO> <IDSTR> */
|
||||
const char *p = nvc_get_string (keymeta, "Token:");
|
||||
|
||||
if (!p)
|
||||
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
|
||||
|
||||
while (*p && !spacep (p))
|
||||
p++;
|
||||
|
||||
if (!*p)
|
||||
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
|
||||
|
||||
p++;
|
||||
if (strcmp (p, "OPENPGP.3"))
|
||||
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
|
||||
|
||||
nvc_release (keymeta);
|
||||
keymeta = NULL;
|
||||
}
|
||||
|
||||
for (i=0; i < DIM (array); i++)
|
||||
array[i] = NULL;
|
||||
|
||||
@ -1472,6 +1494,22 @@ agent_public_key_from_file (ctrl_t ctrl,
|
||||
return err;
|
||||
}
|
||||
|
||||
gpg_error_t
|
||||
agent_public_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result)
|
||||
{
|
||||
return public_key_from_file (ctrl, grip, result, 0);
|
||||
}
|
||||
|
||||
gpg_error_t
|
||||
agent_ssh_key_from_file (ctrl_t ctrl,
|
||||
const unsigned char *grip,
|
||||
gcry_sexp_t *result)
|
||||
{
|
||||
return public_key_from_file (ctrl, grip, result, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the secret key identified by GRIP is available.
|
||||
Returns 0 is the key is available. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user