summaryrefslogtreecommitdiffstats
path: root/agent/command-ssh.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2016-01-12 19:12:02 +0100
committerWerner Koch <wk@gnupg.org>2016-01-12 19:12:02 +0100
commite2f984b4afffaa89bdeba2f5d447b5681237177e (patch)
treee6c12f002a78cafdf9f0c0c916aa0a1db370af21 /agent/command-ssh.c
parentgpg: Re-indent check_key_signature2. (diff)
downloadgnupg2-e2f984b4afffaa89bdeba2f5d447b5681237177e.tar.xz
gnupg2-e2f984b4afffaa89bdeba2f5d447b5681237177e.zip
ssh: Accept OpenSSH *cert-v01 key variants.
* agent/command-ssh.c (SPEC_FLAG_WITH_CERT): New. (ssh_key_types): Add OpenSSH cert types. (stream_read_string): Allow a dummy read. (ssh_receive_mpint_list): Pass SPEC by reference. (ssh_receive_mpint_list): New arg CERT and use it. (ssh_receive_key): Read certificate into an estream object and modify parser to make use of that object. -- This is a first step to support certificate via the agent. The only effect of this change is the removal of an error message parsing the certificate. Note that ssh-add sends the private key anyway first and only then follows with the certificate+private key. What we need to implement next is a way to store the certificate in the agent and return it on request. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to '')
-rw-r--r--agent/command-ssh.c174
1 files changed, 136 insertions, 38 deletions
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 5f159f1ca..6e809f63d 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -75,6 +75,7 @@
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
#define SPEC_FLAG_IS_ECDSA (1 << 1)
#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
+#define SPEC_FLAG_WITH_CERT (1 << 7)
/* The name of the control file. */
#define SSH_CONTROL_FILE_NAME "sshcontrol"
@@ -302,6 +303,42 @@ static ssh_key_type_spec_t ssh_key_types[] =
"ecdsa-sha2-nistp521", "ECDSA", "ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
+ },
+ {
+ "ssh-ed25519-cert-v01@openssh.com", "Ed25519",
+ "ecc", "qd", "q", "rs", "qd",
+ NULL, ssh_signature_encoder_eddsa,
+ "Ed25519", 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT
+ },
+ {
+ "ssh-rsa-cert-v01@openssh.com", "RSA",
+ "rsa", "nedupq", "en", "s", "nedpqu",
+ ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
+ NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT
+ },
+ {
+ "ssh-dss-cert-v01@openssh.com", "DSA",
+ "dsa", "pqgyx", "pqgy", "rs", "pqgyx",
+ NULL, ssh_signature_encoder_dsa,
+ NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT
+ },
+ {
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA",
+ "ecdsa", "qd", "q", "rs", "qd",
+ NULL, ssh_signature_encoder_ecdsa,
+ "nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
+ },
+ {
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA",
+ "ecdsa", "qd", "q", "rs", "qd",
+ NULL, ssh_signature_encoder_ecdsa,
+ "nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
+ },
+ {
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA",
+ "ecdsa", "qd", "q", "rs", "qd",
+ NULL, ssh_signature_encoder_ecdsa,
+ "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
}
};
@@ -531,7 +568,7 @@ stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
/* Read a binary string from STREAM into STRING, store size of string
in STRING_SIZE. Append a hidden nul so that the result may
directly be used as a C string. Depending on SECURE use secure
- memory for STRING. */
+ memory for STRING. If STRING is NULL do only a dummy read. */
static gpg_error_t
stream_read_string (estream_t stream, unsigned int secure,
unsigned char **string, u32 *string_size)
@@ -548,25 +585,35 @@ stream_read_string (estream_t stream, unsigned int secure,
if (err)
goto out;
- /* Allocate space. */
- if (secure)
- buffer = xtrymalloc_secure (length + 1);
- else
- buffer = xtrymalloc (length + 1);
- if (! buffer)
+ if (string)
{
- err = gpg_error_from_syserror ();
- goto out;
- }
+ /* Allocate space. */
+ if (secure)
+ buffer = xtrymalloc_secure (length + 1);
+ else
+ buffer = xtrymalloc (length + 1);
+ if (! buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
- /* Read data. */
- err = stream_read_data (stream, buffer, length);
- if (err)
- goto out;
+ /* Read data. */
+ err = stream_read_data (stream, buffer, length);
+ if (err)
+ goto out;
+
+ /* Finalize string object. */
+ buffer[length] = 0;
+ *string = buffer;
+ }
+ else /* Dummy read requested. */
+ {
+ err = stream_read_skip (stream, length);
+ if (err)
+ goto out;
+ }
- /* Finalize string object. */
- buffer[length] = 0;
- *string = buffer;
if (string_size)
*string_size = length;
@@ -1258,31 +1305,38 @@ mpint_list_free (gcry_mpi_t *mpi_list)
}
/* Receive key material MPIs from STREAM according to KEY_SPEC;
- depending on SECRET expect a public key or secret key. The newly
+ depending on SECRET expect a public key or secret key. CERT is the
+ certificate blob used if KEY_SPEC indicates the certificate format;
+ it needs to be positioned to the end of the nonce. The newly
allocated list of MPIs is stored in MPI_LIST. Returns usual error
code. */
static gpg_error_t
ssh_receive_mpint_list (estream_t stream, int secret,
- ssh_key_type_spec_t key_spec, gcry_mpi_t **mpi_list)
+ ssh_key_type_spec_t *spec, estream_t cert,
+ gcry_mpi_t **mpi_list)
{
const char *elems_public;
unsigned int elems_n;
const char *elems;
int elem_is_secret;
- gcry_mpi_t *mpis;
- gpg_error_t err;
+ gcry_mpi_t *mpis = NULL;
+ gpg_error_t err = 0;
unsigned int i;
- mpis = NULL;
- err = 0;
-
if (secret)
- elems = key_spec.elems_key_secret;
+ elems = spec->elems_key_secret;
else
- elems = key_spec.elems_key_public;
+ elems = spec->elems_key_public;
elems_n = strlen (elems);
+ elems_public = spec->elems_key_public;
- elems_public = key_spec.elems_key_public;
+ /* Check that either noth, CERT and the WITH_CERT flag, are given or
+ none of them. */
+ if (!(!!(spec->flags & SPEC_FLAG_WITH_CERT) ^ !cert))
+ {
+ err = gpg_error (GPG_ERR_INV_CERT_OBJ);
+ goto out;
+ }
mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
if (!mpis)
@@ -1295,18 +1349,20 @@ ssh_receive_mpint_list (estream_t stream, int secret,
for (i = 0; i < elems_n; i++)
{
if (secret)
- elem_is_secret = ! strchr (elems_public, elems[i]);
- err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
+ elem_is_secret = !strchr (elems_public, elems[i]);
+
+ if (cert && !elem_is_secret)
+ err = stream_read_mpi (cert, elem_is_secret, &mpis[i]);
+ else
+ err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
if (err)
- break;
+ goto out;
}
- if (err)
- goto out;
*mpi_list = mpis;
+ mpis = NULL;
out:
-
if (err)
mpint_list_free (mpis);
@@ -2112,6 +2168,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
gpg_error_t err;
char *key_type = NULL;
char *comment = NULL;
+ estream_t cert = NULL;
gcry_sexp_t key = NULL;
ssh_key_type_spec_t spec;
gcry_mpi_t *mpi_list = NULL;
@@ -2127,6 +2184,44 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
if (err)
goto out;
+ if ((spec.flags & SPEC_FLAG_WITH_CERT))
+ {
+ /* This is an OpenSSH certificate+private key. The certificate
+ is an SSH string and which we store in an estream object. */
+ unsigned char *buffer;
+ u32 buflen;
+ char *cert_key_type;
+
+ err = stream_read_string (stream, 0, &buffer, &buflen);
+ if (err)
+ goto out;
+ cert = es_fopenmem_init (0, "rb", buffer, buflen);
+ xfree (buffer);
+ if (!cert)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ /* Check that the key type matches. */
+ err = stream_read_cstring (cert, &cert_key_type);
+ if (err)
+ goto out;
+ if (strcmp (cert_key_type, key_type) )
+ {
+ xfree (cert_key_type);
+ log_error ("key types in received ssh certificate do not match\n");
+ err = gpg_error (GPG_ERR_INV_CERT_OBJ);
+ goto out;
+ }
+ xfree (cert_key_type);
+
+ /* Skip the nonce. */
+ err = stream_read_string (cert, 0, NULL, NULL);
+ if (err)
+ goto out;
+ }
+
if ((spec.flags & SPEC_FLAG_IS_EdDSA))
{
/* The format of an EdDSA key is:
@@ -2146,7 +2241,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
goto out;
}
- err = stream_read_blob (stream, 0, &mpi_list[0]);
+ err = stream_read_blob (cert? cert : stream, 0, &mpi_list[0]);
if (err)
goto out;
if (secret)
@@ -2196,12 +2291,14 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
* mpint ecdsa_private
*
* Note that we use the mpint reader instead of the string
- * reader for ecsa_public_key.
+ * reader for ecsa_public_key. For the certificate variante
+ * ecdsa_curve_name+ecdsa_public_key are replaced by the
+ * certificate.
*/
unsigned char *buffer;
const char *mapped;
- err = stream_read_string (stream, 0, &buffer, NULL);
+ err = stream_read_string (cert? cert : stream, 0, &buffer, NULL);
if (err)
goto out;
curve_name = buffer;
@@ -2228,13 +2325,13 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
}
}
- err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
+ err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
if (err)
goto out;
}
else
{
- err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
+ err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
if (err)
goto out;
}
@@ -2292,6 +2389,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
*key_new = key;
out:
+ es_fclose (cert);
mpint_list_free (mpi_list);
xfree (curve_name);
xfree (key_type);