mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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>
This commit is contained in:
parent
4619ea8e51
commit
e2f984b4af
@ -75,6 +75,7 @@
|
|||||||
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
|
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
|
||||||
#define SPEC_FLAG_IS_ECDSA (1 << 1)
|
#define SPEC_FLAG_IS_ECDSA (1 << 1)
|
||||||
#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
|
#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
|
||||||
|
#define SPEC_FLAG_WITH_CERT (1 << 7)
|
||||||
|
|
||||||
/* The name of the control file. */
|
/* The name of the control file. */
|
||||||
#define SSH_CONTROL_FILE_NAME "sshcontrol"
|
#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",
|
"ecdsa-sha2-nistp521", "ECDSA", "ecdsa", "qd", "q", "rs", "qd",
|
||||||
NULL, ssh_signature_encoder_ecdsa,
|
NULL, ssh_signature_encoder_ecdsa,
|
||||||
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_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
|
/* Read a binary string from STREAM into STRING, store size of string
|
||||||
in STRING_SIZE. Append a hidden nul so that the result may
|
in STRING_SIZE. Append a hidden nul so that the result may
|
||||||
directly be used as a C string. Depending on SECURE use secure
|
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
|
static gpg_error_t
|
||||||
stream_read_string (estream_t stream, unsigned int secure,
|
stream_read_string (estream_t stream, unsigned int secure,
|
||||||
unsigned char **string, u32 *string_size)
|
unsigned char **string, u32 *string_size)
|
||||||
@ -548,25 +585,35 @@ stream_read_string (estream_t stream, unsigned int secure,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Allocate space. */
|
if (string)
|
||||||
if (secure)
|
|
||||||
buffer = xtrymalloc_secure (length + 1);
|
|
||||||
else
|
|
||||||
buffer = xtrymalloc (length + 1);
|
|
||||||
if (! buffer)
|
|
||||||
{
|
{
|
||||||
err = gpg_error_from_syserror ();
|
/* Allocate space. */
|
||||||
goto out;
|
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;
|
||||||
|
|
||||||
|
/* Finalize string object. */
|
||||||
|
buffer[length] = 0;
|
||||||
|
*string = buffer;
|
||||||
|
}
|
||||||
|
else /* Dummy read requested. */
|
||||||
|
{
|
||||||
|
err = stream_read_skip (stream, 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;
|
|
||||||
if (string_size)
|
if (string_size)
|
||||||
*string_size = length;
|
*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;
|
/* 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
|
allocated list of MPIs is stored in MPI_LIST. Returns usual error
|
||||||
code. */
|
code. */
|
||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
ssh_receive_mpint_list (estream_t stream, int secret,
|
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;
|
const char *elems_public;
|
||||||
unsigned int elems_n;
|
unsigned int elems_n;
|
||||||
const char *elems;
|
const char *elems;
|
||||||
int elem_is_secret;
|
int elem_is_secret;
|
||||||
gcry_mpi_t *mpis;
|
gcry_mpi_t *mpis = NULL;
|
||||||
gpg_error_t err;
|
gpg_error_t err = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
mpis = NULL;
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
if (secret)
|
if (secret)
|
||||||
elems = key_spec.elems_key_secret;
|
elems = spec->elems_key_secret;
|
||||||
else
|
else
|
||||||
elems = key_spec.elems_key_public;
|
elems = spec->elems_key_public;
|
||||||
elems_n = strlen (elems);
|
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 );
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
|
||||||
if (!mpis)
|
if (!mpis)
|
||||||
@ -1295,18 +1349,20 @@ ssh_receive_mpint_list (estream_t stream, int secret,
|
|||||||
for (i = 0; i < elems_n; i++)
|
for (i = 0; i < elems_n; i++)
|
||||||
{
|
{
|
||||||
if (secret)
|
if (secret)
|
||||||
elem_is_secret = ! strchr (elems_public, elems[i]);
|
elem_is_secret = !strchr (elems_public, elems[i]);
|
||||||
err = stream_read_mpi (stream, elem_is_secret, &mpis[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)
|
if (err)
|
||||||
break;
|
goto out;
|
||||||
}
|
}
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
*mpi_list = mpis;
|
*mpi_list = mpis;
|
||||||
|
mpis = NULL;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
mpint_list_free (mpis);
|
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;
|
gpg_error_t err;
|
||||||
char *key_type = NULL;
|
char *key_type = NULL;
|
||||||
char *comment = NULL;
|
char *comment = NULL;
|
||||||
|
estream_t cert = NULL;
|
||||||
gcry_sexp_t key = NULL;
|
gcry_sexp_t key = NULL;
|
||||||
ssh_key_type_spec_t spec;
|
ssh_key_type_spec_t spec;
|
||||||
gcry_mpi_t *mpi_list = NULL;
|
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)
|
if (err)
|
||||||
goto out;
|
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))
|
if ((spec.flags & SPEC_FLAG_IS_EdDSA))
|
||||||
{
|
{
|
||||||
/* The format of an EdDSA key is:
|
/* 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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = stream_read_blob (stream, 0, &mpi_list[0]);
|
err = stream_read_blob (cert? cert : stream, 0, &mpi_list[0]);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
if (secret)
|
if (secret)
|
||||||
@ -2196,12 +2291,14 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
|
|||||||
* mpint ecdsa_private
|
* mpint ecdsa_private
|
||||||
*
|
*
|
||||||
* Note that we use the mpint reader instead of the string
|
* 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;
|
unsigned char *buffer;
|
||||||
const char *mapped;
|
const char *mapped;
|
||||||
|
|
||||||
err = stream_read_string (stream, 0, &buffer, NULL);
|
err = stream_read_string (cert? cert : stream, 0, &buffer, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
curve_name = buffer;
|
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)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
|
err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2292,6 +2389,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
|
|||||||
*key_new = key;
|
*key_new = key;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
es_fclose (cert);
|
||||||
mpint_list_free (mpi_list);
|
mpint_list_free (mpi_list);
|
||||||
xfree (curve_name);
|
xfree (curve_name);
|
||||||
xfree (key_type);
|
xfree (key_type);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user