[PATCH 1/4] ssh: update certificate support
Igor Okulist
okigan at gmail.com
Wed Mar 17 10:04:02 CET 2021
---
agent/agent.h | 3 +-
agent/command-ssh.c | 156 ++++++++++++++++++++++++++++++++++++++++----
agent/cvt-openpgp.c | 12 +++-
agent/findkey.c | 58 +++++++++++++---
4 files changed, 204 insertions(+), 25 deletions(-)
diff --git a/agent/agent.h b/agent/agent.h
index fb4641259..207ed45a9 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -619,6 +619,7 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
const char **r_algoname, int *r_npkey, int *r_nskey,
const char **r_format,
gcry_mpi_t *mpi_array, int arraysize,
- gcry_sexp_t *r_curve, gcry_sexp_t *r_flags);
+ gcry_sexp_t *r_curve, gcry_sexp_t *r_flags,
+ gcry_sexp_t *key_type);
#endif /*AGENT_H*/
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index bcc78bd15..d8ccbafb0 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1897,6 +1897,10 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
goto out;
}
+ gcry_sexp_t list = gcry_sexp_find_token (sexp, "key-type", 0);
+ size_t len = 0;
+ const char *key_type = gcry_sexp_nth_data (list, 1, &len);
+
/* Get key value list. */
value_list = gcry_sexp_cadr (sexp);
if (!value_list)
@@ -1933,10 +1937,42 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
}
else
{
- /* Note: This is also used for EdDSA. */
- err = stream_write_cstring (stream, key_spec.ssh_identifier);
- if (err)
- goto out;
+ if (key_type)
+ {
+ // err = stream_write_string (stream, key_type, len);
+ // if (err)
+ // goto out;
+ // err = stream_write_string (stream, "<nonce>", strlen ("<nonce>"));
+ // if (err)
+ // goto out;
+
+ gcry_sexp_t certificate_sexp = gcry_sexp_find_token (sexp, "certificate", 0);
+ size_t certificate_sexp_b64_len = 0;
+ const char *certificate_sexp_b64 = gcry_sexp_nth_data(certificate_sexp, 1, &certificate_sexp_b64_len);
+
+ char *certificate = xtrymalloc (certificate_sexp_b64_len + 1);
+ strncpy(certificate, certificate_sexp_b64, certificate_sexp_b64_len);
+ certificate[certificate_sexp_b64_len] = '\0';
+
+ struct b64state b64s = {};
+ long int len = 0;
+
+ err = b64dec_start (&b64s, NULL);
+ err = b64dec_proc (&b64s, certificate, certificate_sexp_b64_len, &len);
+ err = b64dec_finish (&b64s);
+ err = stream_write_data (stream, certificate, len);
+
+ xfree (certificate);
+
+ goto done;
+ }
+ else
+ {
+ /* Note: This is also used for EdDSA. */
+ err = stream_write_cstring (stream, key_spec.ssh_identifier);
+ if (err)
+ goto out;
+ }
}
/* Write the parameters. */
@@ -1986,6 +2022,7 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
}
}
+done:
if (es_fclose_snatch (stream, &blob, &blob_size))
{
err = gpg_error_from_syserror ();
@@ -2005,7 +2042,6 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
return err;
}
-
/*
@@ -2065,23 +2101,31 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
if (err)
goto out;
+ log_info("key type: %s", key_type);
+
err = ssh_key_type_lookup (key_type, 0, &spec);
if (err)
goto out;
+ log_info("key spec flags: 0x%x", spec.flags);
+
+ unsigned char *cert_buffer = NULL;
+ u32 cert_buffer_len = 0;
+
if ((spec.flags & SPEC_FLAG_WITH_CERT))
{
+ /* in case of certs it reads part of cert to get public mpints
+ and parts of the payload for private mpints -- so -cert.pub files
+ have private mpints? or is it just reading random data to fill out the
+ key struct ? */
/* 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);
+ err = stream_read_string (stream, 0, &cert_buffer, &cert_buffer_len);
if (err)
goto out;
- cert = es_fopenmem_init (0, "rb", buffer, buflen);
- xfree (buffer);
+ cert = es_fopenmem_init (0, "rb", cert_buffer, cert_buffer_len);
if (!cert)
{
err = gpg_error_from_syserror ();
@@ -2092,6 +2136,9 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
err = stream_read_cstring (cert, &cert_key_type);
if (err)
goto out;
+
+ log_info ("certificate type: %s", cert_key_type);
+
if (strcmp (cert_key_type, key_type) )
{
xfree (cert_key_type);
@@ -2211,6 +2258,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
err = stream_read_cstring (stream, &comment);
if (err)
goto out;
+ log_info("key comment: %s", comment);
}
if (secret)
@@ -2246,12 +2294,55 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
comment? comment:"");
}
}
- else
+ else if (0==strcmp(spec.ssh_identifier, "ssh-rsa-cert-v01 at openssh.com"))
{
+ struct b64state b64s;
+ estream_t stream;
+ long int len;
+
+ stream = es_fopenmem(0, "wt");
+ err = b64enc_start_es (&b64s, stream, "");
+ err = b64enc_write (&b64s, cert_buffer, cert_buffer_len);
+ err = b64enc_finish (&b64s);
+ len = es_ftell (stream);
+
+ char *result = xtrymalloc (len + 1);
+ size_t nread;
+
+ es_fseek(stream, 0, SEEK_SET);
+ es_read (stream, result, len, &nread);
+ result[len] = 0;
+
+ es_fclose (stream);
+
+ err = gcry_sexp_build (&key, NULL,
+ "(private-key "
+ " (rsa "
+ " (n %m)"
+ " (e %m)"
+ " (d %m)"
+ " (p %m)"
+ " (q %m)"
+ " (u %m)"
+ " )"
+ " (comment %s)"
+ " (key-type %s)"
+ " (certificate %s)"
+ " )",
+ mpi_list[1], // !swapped 1 and 0
+ mpi_list[0],
+ mpi_list[2],
+ mpi_list[3],
+ mpi_list[4],
+ mpi_list[5],
+ comment!=NULL?comment:"",
+ spec.ssh_identifier,
+ result);
+ }
+ else
+ {
err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
- comment? comment:"");
- if (err)
- goto out;
+ comment? comment:"");
}
if (key_spec)
@@ -2264,6 +2355,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
xfree (key_type);
xfree (comment);
+ xfree (cert_buffer);
+
return err;
}
@@ -2366,6 +2459,28 @@ ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
return err? err : gpg_error (GPG_ERR_INTERNAL);
}
+ gcry_sexp_t list = NULL;
+ size_t len = 0;
+ const char *data = NULL;
+
+ list = gcry_sexp_find_token (key, "key-type", 0);
+ len = 0;
+ data = gcry_sexp_nth_data(list, 1, &len);
+
+ if (data)
+ {
+ gcry_md_hd_t md = NULL;
+ gcry_md_open (&md, GCRY_MD_SHA1, 0);
+
+ gcry_md_write (md, buffer, 20);
+ gcry_md_write (md, data, len);
+
+ memcpy (buffer, gcry_md_read (md, GCRY_MD_SHA1), 20);
+ gcry_md_close (md);
+ }
+
+ gcry_sexp_release(list);
+
return 0;
}
@@ -2652,6 +2767,13 @@ ssh_handler_request_identities (ctrl_t ctrl,
gpg_strerror (err));
continue;
}
+ if (opt.verbose > 1) {
+ log_info ("fname: %s", cf->fname);
+ log_info ("hexgrip: %s", cf->item.hexgrip);
+ char debug_buffer[8192] = "\0";
+ err = gcry_sexp_sprint (key_public, GCRYSEXP_FMT_ADVANCED, debug_buffer, sizeof(debug_buffer));
+ log_info ("key sExpression: %s", debug_buffer);
+ }
err = ssh_send_key_public (key_blobs, key_public, NULL);
if (err)
@@ -3132,6 +3254,12 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
}
}
+ if (opt.verbose > 1) {
+ char debug_buffer[8192] = "\0";
+ err = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, debug_buffer, sizeof(debug_buffer));
+ log_info ("key Sexpression: %s", debug_buffer);
+ }
+
err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
if (err)
goto out;
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index ff153c358..54f38997c 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1194,7 +1194,8 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
const char **r_algoname, int *r_npkey, int *r_nskey,
const char **r_elems,
gcry_mpi_t *array, int arraysize,
- gcry_sexp_t *r_curve, gcry_sexp_t *r_flags)
+ gcry_sexp_t *r_curve, gcry_sexp_t *r_flags,
+ gcry_sexp_t *r_key_type)
{
gpg_error_t err;
gcry_sexp_t list, l2;
@@ -1203,6 +1204,7 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
int npkey, nskey;
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
+ gcry_sexp_t key_type = NULL;
*r_curve = NULL;
*r_flags = NULL;
@@ -1224,6 +1226,8 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
return gpg_error (GPG_ERR_BAD_SECKEY);
}
+ key_type = gcry_sexp_find_token (list, "key_type", 0);
+
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
@@ -1305,6 +1309,9 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
*r_curve = curve;
*r_flags = flags;
+ if (r_key_type)
+ *r_key_type = key_type;
+
return 0;
}
}
@@ -1323,6 +1330,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
gcry_mpi_t array[10];
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
+ gcry_sexp_t key_name = NULL;
char protect_iv[16];
char salt[8];
unsigned long s2k_count;
@@ -1336,7 +1344,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
array[i] = NULL;
err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL,
- array, DIM (array), &curve, &flags);
+ array, DIM (array), &curve, &flags, &key_name);
if (err)
return err;
diff --git a/agent/findkey.c b/agent/findkey.c
index cea21959f..5b54de7cb 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1245,13 +1245,13 @@ agent_public_key_from_file (ctrl_t ctrl,
gcry_mpi_t array[10];
gcry_sexp_t curve = NULL;
gcry_sexp_t flags = NULL;
- gcry_sexp_t uri_sexp, comment_sexp;
- const char *uri, *comment;
- size_t uri_length, comment_length;
- int uri_intlen, comment_intlen;
+ gcry_sexp_t uri_sexp, comment_sexp, key_type_sexp, certificate_sexp;
+ const char *uri, *comment, *key_type, *certificate;
+ size_t uri_length, comment_length, key_type_length, certificate_length;
+ int uri_intlen, comment_intlen, key_type_intlen, certificate_intlen;
char *format, *p;
- void *args[2+7+2+2+1]; /* Size is 2 + max. # of elements + 2 for uri + 2
- for comment + end-of-list. */
+ void *args[2+7+2+2+2+1]; /* Size is 2 + max. # of elements + 2 for uri + 2
+ for comment + key_type + certificate + end-of-list. */
int argidx;
gcry_sexp_t list = NULL;
const char *s;
@@ -1264,11 +1264,21 @@ agent_public_key_from_file (ctrl_t ctrl,
if (err)
return err;
+ if (opt.verbose > 1) {
+ char hexgrip[40+4+1];
+ bin2hex (grip, 20, hexgrip);
+
+ log_info ("hexgrip: %s", hexgrip);
+ char debug_buffer[8192] = "\0";
+ err = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_ADVANCED, debug_buffer, sizeof(debug_buffer));
+ log_info ("loaded key sExpression: %s", debug_buffer);
+ }
+
for (i=0; i < DIM (array); i++)
array[i] = NULL;
err = extract_private_key (s_skey, 0, &algoname, &npkey, NULL, &elems,
- array, DIM (array), &curve, &flags);
+ array, DIM (array), &curve, &flags, &key_type_sexp);
if (err)
{
gcry_sexp_release (s_skey);
@@ -1287,10 +1297,25 @@ agent_public_key_from_file (ctrl_t ctrl,
if (comment_sexp)
comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
+ key_type = NULL;
+ key_type_length = 0;
+ key_type_sexp = gcry_sexp_find_token (s_skey, "key-type", 0);
+ if (key_type_sexp)
+ key_type = gcry_sexp_nth_data (key_type_sexp, 1, &key_type_length);
+
+ certificate = NULL;
+ certificate_length = 0;
+ certificate_sexp = gcry_sexp_find_token (s_skey, "certificate", 0);
+ if (certificate_sexp)
+ certificate = gcry_sexp_nth_data (certificate_sexp, 1, &certificate_length);
+
+
gcry_sexp_release (s_skey);
s_skey = NULL;
+ // TODO: the following FIXME is so true -- following code is
+ // prone to buffer overrun
/* FIXME: The following thing is pretty ugly code; we should
investigate how to make it cleaner. Probably code to handle
canonical S-expressions in a memory buffer is better suited for
@@ -1299,7 +1324,7 @@ agent_public_key_from_file (ctrl_t ctrl,
them. */
assert (sizeof (size_t) <= sizeof (void*));
- format = xtrymalloc (15+4+7*npkey+10+15+1+1);
+ format = xtrymalloc (15+4+7*npkey+10+15+1+1+5+4096);
if (!format)
{
err = gpg_error_from_syserror ();
@@ -1342,6 +1367,23 @@ agent_public_key_from_file (ctrl_t ctrl,
args[argidx++] = (void *)&comment_intlen;
args[argidx++] = (void*)&comment;
}
+ if (key_type)
+ {
+ p = stpcpy (p, "(key-type %b)");
+ log_assert (argidx+1 < DIM (args));
+ key_type_intlen = (int)key_type_length;
+ args[argidx++] = (void *)&key_type_intlen;
+ args[argidx++] = (void*)&key_type;
+ }
+ if (certificate)
+ {
+ p = stpcpy (p, "(certificate %b)");
+ log_assert (argidx+1 < DIM (args));
+ certificate_intlen = (int)certificate_length;
+ args[argidx++] = (void *)&certificate_intlen;
+ args[argidx++] = (void*)&certificate;
+ }
+
*p++ = ')';
*p = 0;
assert (argidx < DIM (args));
--
2.25.1
More information about the Gnupg-devel
mailing list