mirror of
git://git.gnupg.org/gnupg.git
synced 2025-04-12 22:11:29 +02: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,
|
gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
|
||||||
const unsigned char *grip,
|
const unsigned char *grip,
|
||||||
gcry_sexp_t *result);
|
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_pk_get_algo (gcry_sexp_t s_key);
|
||||||
int agent_is_tpm2_key(gcry_sexp_t s_key);
|
int agent_is_tpm2_key(gcry_sexp_t s_key);
|
||||||
int agent_key_available (const unsigned char *grip);
|
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;
|
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
|
static gpg_error_t
|
||||||
ssh_send_available_keys (ctrl_t ctrl, estream_t key_blobs, u32 *key_counter_p)
|
ssh_send_available_keys (ctrl_t ctrl, estream_t key_blobs, u32 *key_counter_p)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
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;
|
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);
|
err = open_control_file (&cf, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return 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];
|
unsigned char grip[20];
|
||||||
gcry_sexp_t key_public = NULL;
|
|
||||||
|
|
||||||
if (!cf->item.valid)
|
cardsn = NULL;
|
||||||
continue; /* Should not happen. */
|
if (strlen (dir_entry->d_name) != 44
|
||||||
if (cf->item.disabled)
|
|| strcmp (dir_entry->d_name + 40, ".key"))
|
||||||
continue;
|
continue;
|
||||||
log_assert (strlen (cf->item.hexgrip) == 40);
|
strncpy (hexgrip, dir_entry->d_name, 40);
|
||||||
hex2bin (cf->item.hexgrip, grip, sizeof (grip));
|
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)
|
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. */
|
/* Clear ERR, skiping the key in question. */
|
||||||
err = 0;
|
err = 0;
|
||||||
continue;
|
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)
|
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);
|
gcry_sexp_release (key_public);
|
||||||
(*key_counter_p)++;
|
(*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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2528,72 +2674,6 @@ ssh_handler_request_identities (ctrl_t ctrl,
|
|||||||
goto out;
|
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);
|
err = ssh_send_available_keys (ctrl, key_blobs, &key_counter);
|
||||||
if (!err)
|
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
|
at RESULT. This function extracts the public key from the private
|
||||||
key database. On failure an error code is returned and NULL stored
|
key database. On failure an error code is returned and NULL stored
|
||||||
at RESULT. */
|
at RESULT. */
|
||||||
gpg_error_t
|
static gpg_error_t
|
||||||
agent_public_key_from_file (ctrl_t ctrl,
|
public_key_from_file (ctrl_t ctrl, const unsigned char *grip,
|
||||||
const unsigned char *grip,
|
gcry_sexp_t *result, int for_ssh)
|
||||||
gcry_sexp_t *result)
|
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
int i, idx;
|
int i, idx;
|
||||||
gcry_sexp_t s_skey;
|
gcry_sexp_t s_skey;
|
||||||
|
nvc_t keymeta = NULL;
|
||||||
const char *algoname, *elems;
|
const char *algoname, *elems;
|
||||||
int npkey;
|
int npkey;
|
||||||
gcry_mpi_t array[10];
|
gcry_mpi_t array[10];
|
||||||
@ -1380,10 +1380,32 @@ agent_public_key_from_file (ctrl_t ctrl,
|
|||||||
|
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
|
|
||||||
err = read_key_file (grip, &s_skey, NULL);
|
err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL);
|
||||||
if (err)
|
if (err)
|
||||||
return 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++)
|
for (i=0; i < DIM (array); i++)
|
||||||
array[i] = NULL;
|
array[i] = NULL;
|
||||||
|
|
||||||
@ -1472,6 +1494,22 @@ agent_public_key_from_file (ctrl_t ctrl,
|
|||||||
return err;
|
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.
|
/* Check whether the secret key identified by GRIP is available.
|
||||||
Returns 0 is the key is available. */
|
Returns 0 is the key is available. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user