[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