[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] basic client certificate support for libssh


Hi Andreas,


On Fri, Aug 21, 2015 at 3:31 PM, Andreas Schneider <asn@xxxxxxxxxxxxxx>
wrote:

> The first patch(es) should introduce functions to pki*.c. Then the tests
> for
> the new pki functions. Afterward start using them in the libssh code ...
>

I splitted this into 6 logical chunks roughly as suggested.


> Some comments:
>
> Do not mix tabs and spaces (see changes to agent.c) and always braces on
> if-
> clause see README.CodingStyle.
>
> Yes, we do not have this all over the place for historic reasons, but the
> code
> should look like pki.c ...
>

Sorry for that, bad editor. Fixed both.


> In ssh_userauth_publickey():
>
> Why is the privkey->type_c incorrect? it should already hold the correct
> type
> ...
>

Public and private keys have the same type, but not certs. I could change
the privkey's cert type when I copy the cert data, but it seems wrong (you
can derive the pubkey from the private key, but not the cert, so it makes
sense imo that they're not the same type). It would also require more
invasive changes in other parts.


> ssh_pki_copy_cert_to_privkey() uses 2 spaces instead of 4 :)
>

Fixed. Also checked everywhere else.


>
> pki_publickey_to_blob():
>
> You should use buffer_add_ssh_string()
>
>
> pki_import_cert_buffer():
>
> Use buffer_get_ssh_string()
>

Changed to use a ssh_buffer to store the cert now, should be cleaner. It'd
be nice if there was a ssh_buffer_reset() function - right now we're doing
a buffer_get_ssh_string to read the key type, but this moves the internal
data pointer. I need to add the key type back as a ssh_string, which is a
bit awkward. I'm happy to send you another patch with such a function.

Thanks,

-- Axel
From ede7fa6e62a2de8c28a16a5b9daebd02bb501afc Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 17:26:11 +0100
Subject: [PATCH 1/6] Client cert auth: add rsa, dss key type definitions.

- Add rsa/dsa (ssh-{rsa,dss}-cert-v01@xxxxxxxxxxx) as key types.
- Add a cert_type member in the ssh_key struct.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 include/libssh/libssh.h     |  4 +++-
 include/libssh/pki.h        |  1 +
 src/pki.c                   | 18 ++++++++++++++++++
 src/pki_container_openssh.c |  2 ++
 src/pki_crypto.c            |  4 ++++
 5 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 232d7c2..9a24347 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -254,7 +254,9 @@ enum ssh_keytypes_e{
   SSH_KEYTYPE_RSA,
   SSH_KEYTYPE_RSA1,
   SSH_KEYTYPE_ECDSA,
-  SSH_KEYTYPE_ED25519
+  SSH_KEYTYPE_ED25519,
+  SSH_KEYTYPE_DSS_CERT01,
+  SSH_KEYTYPE_RSA_CERT01
 };
 
 enum ssh_keycmp_e {
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 9f9ddf4..b146d98 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -60,6 +60,7 @@ struct ssh_key_struct {
     ed25519_pubkey *ed25519_pubkey;
     ed25519_privkey *ed25519_privkey;
     void *cert;
+    enum ssh_keytypes_e cert_type;
 };
 
 struct ssh_signature_struct {
diff --git a/src/pki.c b/src/pki.c
index 7cc3d6d..5aea849 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -162,6 +162,10 @@ void ssh_key_clean (ssh_key key){
         SAFE_FREE(key->ed25519_privkey);
     }
     SAFE_FREE(key->ed25519_pubkey);
+    if (key->cert != NULL) {
+        ssh_buffer_free(key->cert);
+    }
+    key->cert_type = SSH_KEYTYPE_UNKNOWN;
     key->flags=SSH_KEY_FLAG_EMPTY;
     key->type=SSH_KEYTYPE_UNKNOWN;
     key->ecdsa_nid = 0;
@@ -214,6 +218,10 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
       return "ssh-ecdsa";
     case SSH_KEYTYPE_ED25519:
       return "ssh-ed25519";
+    case SSH_KEYTYPE_DSS_CERT01:
+      return "ssh-dss-cert-v01@xxxxxxxxxxx";
+    case SSH_KEYTYPE_RSA_CERT01:
+      return "ssh-rsa-cert-v01@xxxxxxxxxxx";
     case SSH_KEYTYPE_UNKNOWN:
       return NULL;
   }
@@ -254,6 +262,10 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
         return SSH_KEYTYPE_ECDSA;
     } else if (strcmp(name, "ssh-ed25519") == 0){
         return SSH_KEYTYPE_ED25519;
+    } else if (strcmp(name, "ssh-dss-cert-v01@xxxxxxxxxxx") == 0) {
+        return SSH_KEYTYPE_DSS_CERT01;
+    } else if (strcmp(name, "ssh-rsa-cert-v01@xxxxxxxxxxx") == 0) {
+        return SSH_KEYTYPE_RSA_CERT01;
     }
 
     return SSH_KEYTYPE_UNKNOWN;
@@ -370,6 +382,8 @@ void ssh_signature_free(ssh_signature sig)
         case SSH_KEYTYPE_ED25519:
             SAFE_FREE(sig->ed25519_sig);
             break;
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             break;
     }
@@ -807,6 +821,8 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
             ssh_string_free(pubkey);
         }
         break;
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
         default:
             ssh_pki_log("Unknown public key protocol %d", type);
@@ -1075,6 +1091,8 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
                 goto error;
             }
             break;
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             goto error;
     }
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index bfc6f8d..2ebca0a 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -113,8 +113,10 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
         SAFE_FREE(privkey);
         SAFE_FREE(pubkey);
         break;
+    case SSH_KEYTYPE_DSS_CERT01:
     case SSH_KEYTYPE_DSS:
         /* p,q,g,pub_key,priv_key */
+    case SSH_KEYTYPE_RSA_CERT01:
     case SSH_KEYTYPE_RSA:
         /* n,e,d,iqmp,p,q */
     case SSH_KEYTYPE_RSA1:
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index b53bba2..dcce3b5 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -651,6 +651,8 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
             BIO_free(mem);
             ssh_pki_log("PEM output not supported for key type ssh-ed25519");
             return NULL;
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             BIO_free(mem);
             ssh_pki_log("Unkown or invalid private key type %d", key->type);
@@ -777,6 +779,8 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
 #endif
         case SSH_KEYTYPE_ED25519:
             /* Cannot open ed25519 keys with libcrypto */
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             BIO_free(mem);
             ssh_pki_log("Unkown or invalid private key type %d", type);
-- 
1.9.1

From 33270b1e330498596c75c8dbb4f1f6c7f9847a29 Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 17:38:21 +0100
Subject: [PATCH 2/6] Client cert auth: add new pki_import_cert_buffer
 function.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 src/pki.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/src/pki.c b/src/pki.c
index 5aea849..b40345b 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -837,6 +837,57 @@ fail:
     return SSH_ERROR;
 }
 
+static int pki_import_cert_buffer(ssh_buffer buffer,
+                                  enum ssh_keytypes_e type,
+                                  ssh_key *pkey) {
+    ssh_buffer cert;
+    ssh_string type_s;
+    ssh_key key;
+    int rc;
+
+    key = ssh_key_new();
+    if (key == NULL) {
+        return SSH_ERROR;
+    }
+    cert = ssh_buffer_new();
+    if (cert == NULL) {
+        ssh_key_free(key);
+        return SSH_ERROR;
+    }
+
+    key->type = type;
+    key->type_c = ssh_key_type_to_char(type);
+    key->flags = SSH_KEY_FLAG_PUBLIC;
+
+    /*
+     * The cert blob starts with the key type as an ssh_string, but this
+     * string has been read out of the buffer to identify the key type.
+     * Simply add it again as first element before copying the rest.
+     */
+    type_s = ssh_string_from_char(key->type_c);
+    if (type_s == NULL) {
+        goto fail;
+    }
+    rc = buffer_add_ssh_string(cert, type_s);
+    if (rc != 0) {
+        goto fail;
+    }
+
+    rc = buffer_add_buffer(cert, buffer);
+    if (rc != 0) {
+        goto fail;
+    }
+    key->cert = (void*) cert;
+
+    *pkey = key;
+    return SSH_OK;
+
+fail:
+    ssh_key_free(key);
+    ssh_buffer_free(cert);
+    return SSH_ERROR;
+}
+
 /**
  * @brief Import a base64 formated public key from a memory c-string.
  *
-- 
1.9.1

From b2b51d69218009fff366e70b957723e93b93b131 Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 17:42:21 +0100
Subject: [PATCH 3/6] Client cert auth: add cert loading functions.

- ssh_pki_import_cert_base64()
- ssh_pki_import_cert_file()
- ssh_pki_import_cert_blob()
Those functions are currently simple wrappers around their pubkey counterpart.

- ssh_pki_copy_cert_to_privkey()
This function copies the cert-specific data to a private key.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 include/libssh/libssh.h |   9 ++++
 include/libssh/pki.h    |   4 ++
 src/pki.c               | 112 +++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 9a24347..aff0190 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -551,12 +551,21 @@ LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey,
                                            void *auth_data,
                                            const char *filename);
 
+LIBSSH_API int ssh_pki_copy_cert_to_privkey(const ssh_key cert_key,
+                                            ssh_key privkey);
+
 LIBSSH_API int ssh_pki_import_pubkey_base64(const char *b64_key,
                                             enum ssh_keytypes_e type,
                                             ssh_key *pkey);
 LIBSSH_API int ssh_pki_import_pubkey_file(const char *filename,
                                           ssh_key *pkey);
 
+LIBSSH_API int ssh_pki_import_cert_base64(const char *b64_cert,
+                                          enum ssh_keytypes_e type,
+                                          ssh_key *pkey);
+LIBSSH_API int ssh_pki_import_cert_file(const char *filename,
+                                        ssh_key *pkey);
+
 LIBSSH_API int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
                                                 ssh_key *pkey);
 LIBSSH_API int ssh_pki_export_pubkey_base64(const ssh_key key,
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index b146d98..905956b 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -113,6 +113,10 @@ int ssh_pki_export_pubkey_rsa1(const ssh_key key,
                                char *rsa1,
                                size_t rsa1_len);
 
+int ssh_pki_import_cert_blob(const ssh_string cert_blob,
+                             ssh_key *pkey);
+
+
 /* SSH Signing Functions */
 ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf,
     const ssh_key privatekey);
diff --git a/src/pki.c b/src/pki.c
index b40345b..9d613fc 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -925,7 +925,12 @@ int ssh_pki_import_pubkey_base64(const char *b64_key,
     }
     ssh_string_free(type_s);
 
-    rc = pki_import_pubkey_buffer(buffer, type, pkey);
+    if (type == SSH_KEYTYPE_RSA_CERT01 ||
+        type == SSH_KEYTYPE_DSS_CERT01) {
+        rc = pki_import_cert_buffer(buffer, type, pkey);
+    } else {
+        rc = pki_import_pubkey_buffer(buffer, type, pkey);
+    }
     ssh_buffer_free(buffer);
 
     return rc;
@@ -983,7 +988,12 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
     }
     ssh_string_free(type_s);
 
-    rc = pki_import_pubkey_buffer(buffer, type, pkey);
+    if (type == SSH_KEYTYPE_RSA_CERT01 ||
+        type == SSH_KEYTYPE_DSS_CERT01) {
+        rc = pki_import_cert_buffer(buffer, type, pkey);
+    } else {
+        rc = pki_import_pubkey_buffer(buffer, type, pkey);
+    }
 
     ssh_buffer_free(buffer);
 
@@ -1085,6 +1095,64 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
 }
 
 /**
+ * @brief Import a base64 formated certificate from a memory c-string.
+ *
+ * @param[in]  b64_cert  The base64 cert to format.
+ *
+ * @param[in]  type     The type of the cert to format.
+ *
+ * @param[out] pkey     A pointer where the allocated key can be stored. You
+ *                      need to free the memory.
+ *
+ * @return              SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_key_free()
+ */
+int ssh_pki_import_cert_base64(const char *b64_cert,
+                               enum ssh_keytypes_e type,
+                               ssh_key *pkey) {
+    return ssh_pki_import_pubkey_base64(b64_cert, type, pkey);
+}
+
+/**
+ * @internal
+ *
+ * @brief Import a certificate from a ssh string.
+ *
+ * @param[in]  cert_blob The cert blob to import as specified in RFC 4253 section
+ *                      6.6 "Public Key Algorithms".
+ *
+ * @param[out] pkey     A pointer where the allocated key can be stored. You
+ *                      need to free the memory.
+ *
+ * @return              SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_key_free()
+ */
+int ssh_pki_import_cert_blob(const ssh_string cert_blob,
+                             ssh_key *pkey) {
+    return ssh_pki_import_pubkey_blob(cert_blob, pkey);
+}
+
+/**
+ * @brief Import a certificate from the given filename.
+ *
+ * @param[in]  filename The path to the certificate.
+ *
+ * @param[out] pkey     A pointer to store the allocated certificate. You need to
+ *                      free the memory.
+ *
+ * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
+ *          denied, SSH_ERROR otherwise.
+ *
+ * @see ssh_key_free()
+ */
+int ssh_pki_import_cert_file(const char *filename, ssh_key *pkey)
+{
+    return ssh_pki_import_pubkey_file(filename, pkey);
+}
+
+/**
  * @brief Generates a keypair.
  *
  * @param[in] type      Type of key to create
@@ -1317,6 +1385,46 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
     return SSH_OK;
 }
 
+/**
+ * @brief Copy the certificate part of a public key into a private key.
+ *
+ * @param[in]  certkey  The certificate key.
+ *
+ * @param[in]  privkey  The target private key to copy the certificate to.
+ *
+ * @returns SSH_OK on success, SSH_ERROR otherwise.
+ **/
+int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
+  ssh_buffer cert_buffer;
+  int rc;
+
+  if (certkey == NULL || privkey == NULL) {
+      return SSH_ERROR;
+  }
+
+  if (privkey->cert != NULL) {
+      return SSH_ERROR;
+  }
+
+  if (certkey->cert == NULL) {
+      return SSH_ERROR;
+  }
+
+  cert_buffer = ssh_buffer_new();
+  if (cert_buffer == NULL) {
+      return SSH_ERROR;
+  }
+
+  rc = buffer_add_buffer(cert_buffer, certkey->cert);
+  if (rc != 0) {
+      return SSH_ERROR;
+  }
+
+  privkey->cert = cert_buffer;
+  privkey->cert_type = certkey->type;
+  return SSH_OK;
+}
+
 int ssh_pki_export_pubkey_rsa1(const ssh_key key,
                                const char *host,
                                char *rsa1,
-- 
1.9.1

From 63b9c5983357cc1e891a6d3585d87d1061b5d289 Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 17:53:55 +0100
Subject: [PATCH 4/6] Client cert auth: torture tests for the new ssh_pki cert
 functions.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 tests/torture.c               |  59 ++++++++++++++++++++++
 tests/unittests/torture_pki.c | 110 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 169 insertions(+)

diff --git a/tests/torture.c b/tests/torture.c
index b0c7a59..4eb738c 100644
--- a/tests/torture.c
+++ b/tests/torture.c
@@ -80,6 +80,32 @@ static const char torture_rsa_testkey_pub[] =
         "QMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg52DVm4esZ "
         "aris@aris-air\n";
 
+static const char torture_rsa_testkey_cert[] =
+        "ssh-rsa-cert-v01@xxxxxxxxxxx AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNz"
+        "aC5jb20AAAAgL77S/SgY969FbEtNBsbLvvtGFgnEHaPb+V7ajwuf+R0AAAADAQABA"
+        "AABAQCsA5ERRaUFckApnmEAFjLGdFrINk/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34"
+        "lHJaO+WXbDYYj7duW3SP7H9lbCMwq79BhzJxinkcvTWCjE7G66xluL4qIdEYHrPQQ"
+        "x1cztTzZTuUD+P/8fJmmnIONQOeJZptdAmB7ySwZcZOIV4An/rzu5X4klyMY/EAYV"
+        "DHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0si5qI45HIsZJbh24csM3vwSawmfCqDaAl"
+        "CZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6BQMRjUBThzmDXWeHMfMGL2ow63kPOtlCkP"
+        "iPSADYs4ekeGg52DVm4esZAAAAAAAAAAAAAAABAAAADmxpYnNzaF90b3J0dXJlAAA"
+        "AAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRp"
+        "bmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtc"
+        "G9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdX"
+        "Nlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAoowcv2Gn8tO"
+        "eDyw/lgdMpoBsLtHTTdVVOOo5HwMFvj/lFkbZlb6J2n9GIE64HNPE45vSnIdJZwz4"
+        "UYfTvtnNKNHp1MgMrjK1Z6EjyZsGqDZ+BhmvcKA6IckkhBJnDV7U9dMrovAWha61Z"
+        "9GpDqB1naRfbwqJQwSRHF1p71Cnf0fZKxOhAVx0ophmYGz3x3qq4PeOZv3Yl0AHTV"
+        "dRmqmeELDUxeuXN2bgSyb881zEgdaKHH5oWySykP4uwjn6T7ETuL2MsDdG3HZHDhn"
+        "LzLmfzOZ/cNadMCrgauMluQKc5dYF2TSeDaUxwun/NPMQBVZdETHLAMBgkGmhRUku"
+        "flVDIQAAAQ8AAAAHc3NoLXJzYQAAAQADSp4b/Zta8zs6v47iwmxV2Gbucvt1kDrvT"
+        "vKAKSbGN0+zoMyXiNfMHM/OvZObDS/WWGs4GMRqbJavwO3ja/dQY17oJss23lZ+Rc"
+        "Lw4Rqsi3/ZEPCnX6ficiRS/yRN/LAkoXvx9vBx9QHfxlzF6JXq07wTt21zxW0tntd"
+        "8dL+JI9ZZ9YylnxF3gHqfRFe2ahJpiywmxm0yOZgDmimOhep59i6BH5zHiPALvpge"
+        "Mbk075oA5K9XKsHTflCcsQRQH+pXqaNQGL37z2CFz9oezxQYvIqqKF0w/eeRIARoA"
+        "neB6OdgTpKFsmgPZVtqrvhjw+b5T8a4W4iWSl+6wg6gowAm "
+        "rsa_privkey.pub\n";
+
 static const char torture_dsa_testkey[] =
         "-----BEGIN DSA PRIVATE KEY-----\n"
         "MIIBuwIBAAKBgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n"
@@ -106,6 +132,35 @@ static const char torture_dsa_testkey_pub[] =
         "7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIg== "
         "aris@aris-air\n";
 
+static const char torture_dsa_testkey_cert[] =
+        "ssh-dss-cert-v01@xxxxxxxxxxx AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNza"
+        "C5jb20AAAAgKAd9MpIBrzctQyJvCYYJ2WUD5fyWlXMSv1G/3VihbCAAAACBAJTK9U8"
+        "SSfdSdkOPMLNLNIelOW3OvQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fA"
+        "C7ltzHDxOQhKrthE5DjT0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5"
+        "qX3HUnFOZzpPQYGrWXK74JNRTKHblo0MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzx"
+        "wAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWT"
+        "MN4BlTJzPfVaoYvzJev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+"
+        "UGeAruNBEhVSDcXfXTh9tVravBqeIuXgZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJA"
+        "b7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+"
+        "gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hD"
+        "V055pbwQhtxupUatThyDzIgAAAAAAAAAAAAAAAQAAAA5saWJzc2hfdG9ydHVyZQAAA"
+        "AAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5"
+        "nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvc"
+        "nQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXI"
+        "tcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKKMHL9hp/LTng8sP"
+        "5YHTKaAbC7R003VVTjqOR8DBb4/5RZG2ZW+idp/RiBOuBzTxOOb0pyHSWcM+FGH077"
+        "ZzSjR6dTIDK4ytWehI8mbBqg2fgYZr3CgOiHJJIQSZw1e1PXTK6LwFoWutWfRqQ6gd"
+        "Z2kX28KiUMEkRxdae9Qp39H2SsToQFcdKKYZmBs98d6quD3jmb92JdAB01XUZqpnhC"
+        "w1MXrlzdm4Esm/PNcxIHWihx+aFskspD+LsI5+k+xE7i9jLA3Rtx2Rw4Zy8y5n8zmf"
+        "3DWnTAq4GrjJbkCnOXWBdk0ng2lMcLp/zTzEAVWXRExywDAYJBpoUVJLn5VQyEAAAE"
+        "PAAAAB3NzaC1yc2EAAAEAAt4V9aGqeahOfUvhG7M8/Mn26aLB/HXbICYFJF7dY6urm"
+        "SIoS2KBqISCFGXTituiwGlZeAJ+pVgCMYo07Nxtd6oqIjsgKfJqDNx7e4pGw/YJnkm"
+        "BqMO/k/ygu2mLmQF0lnpmG2KyjKEljMibHaKlFkcVNbwfOb4p8N3OHm66g5mbCUTRZ"
+        "DHqMSJb3YtnObLexD13RydwxkG5AfCnOWxy5O4agXGEYwr/48AQBHYg9obGtpD1qyF"
+        "4mMXgzaLViFtcwah6wHGlW0UPQMvrq/RqigAkyUszSccfibkIXJ+wGAgsRYhVAMwME"
+        "JqPZ6GHOEIjLBKUegsclHb7Pk0YO8Auaw== "
+        "aris@aris-air\n";
+
 static const char torture_rsa_testkey_pp[] =
         "-----BEGIN RSA PRIVATE KEY-----\n"
         "Proc-Type: 4,ENCRYPTED\n"
@@ -622,6 +677,10 @@ static const char *torture_get_testkey_internal(enum ssh_keytypes_e type,
             return torture_ed25519_testkey_pp;
         }
         return torture_ed25519_testkey;
+    case SSH_KEYTYPE_DSS_CERT01:
+        return torture_dsa_testkey_cert;
+    case SSH_KEYTYPE_RSA_CERT01:
+        return torture_rsa_testkey_cert;
     case SSH_KEYTYPE_RSA1:
     case SSH_KEYTYPE_UNKNOWN:
         return NULL;
diff --git a/tests/unittests/torture_pki.c b/tests/unittests/torture_pki.c
index d880a5a..dea5156 100644
--- a/tests/unittests/torture_pki.c
+++ b/tests/unittests/torture_pki.c
@@ -17,11 +17,14 @@ static void setup_rsa_key(void **state) {
 
     unlink(LIBSSH_RSA_TESTKEY);
     unlink(LIBSSH_RSA_TESTKEY ".pub");
+    unlink(LIBSSH_RSA_TESTKEY "-cert.pub");
 
     torture_write_file(LIBSSH_RSA_TESTKEY,
                        torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0));
     torture_write_file(LIBSSH_RSA_TESTKEY ".pub",
                        torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0));
+    torture_write_file(LIBSSH_RSA_TESTKEY "-cert.pub",
+                       torture_get_testkey_pub(SSH_KEYTYPE_RSA_CERT01, 0));
 }
 
 static void setup_dsa_key(void **state) {
@@ -29,11 +32,14 @@ static void setup_dsa_key(void **state) {
 
     unlink(LIBSSH_DSA_TESTKEY);
     unlink(LIBSSH_DSA_TESTKEY ".pub");
+    unlink(LIBSSH_DSA_TESTKEY "-cert.pub");
 
     torture_write_file(LIBSSH_DSA_TESTKEY,
                        torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0));
     torture_write_file(LIBSSH_DSA_TESTKEY ".pub",
                        torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0));
+    torture_write_file(LIBSSH_DSA_TESTKEY "-cert.pub",
+                       torture_get_testkey_pub(SSH_KEYTYPE_DSS_CERT01, 0));
 }
 
 #ifdef HAVE_OPENSSL_ECC
@@ -88,9 +94,11 @@ static void teardown(void **state) {
 
     unlink(LIBSSH_DSA_TESTKEY);
     unlink(LIBSSH_DSA_TESTKEY ".pub");
+    unlink(LIBSSH_DSA_TESTKEY "-cert.pub");
 
     unlink(LIBSSH_RSA_TESTKEY);
     unlink(LIBSSH_RSA_TESTKEY ".pub");
+    unlink(LIBSSH_RSA_TESTKEY "-cert.pub");
 
     unlink(LIBSSH_ECDSA_TESTKEY);
     unlink(LIBSSH_ECDSA_TESTKEY ".pub");
@@ -517,6 +525,97 @@ static void torture_pki_publickey_from_privatekey_ECDSA(void **state) {
 }
 #endif
 
+static void torture_pki_copy_cert_to_privkey(void **state) {
+    /* Tests copying a cert loaded into a public key to a private key.
+       The function is encryption type agnostic, no need to run this
+       against all supported key types.
+     */
+    int rc;
+    const char *passphrase = torture_get_testkey_passphrase();
+    ssh_key pubkey;
+    ssh_key privkey;
+    ssh_key cert;
+
+    (void) state; /* unused */
+
+    rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY "-cert.pub", &cert);
+    assert_true(rc == SSH_OK);
+
+    rc = ssh_pki_import_pubkey_file(LIBSSH_RSA_TESTKEY ".pub", &pubkey);
+    assert_true(rc == SSH_OK);
+
+    rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0),
+				       passphrase,
+				       NULL,
+				       NULL,
+				       &privkey);
+    assert_true(rc == SSH_OK);
+
+    /* Basic sanity. */
+    rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
+    assert_true(rc == SSH_ERROR);
+
+    rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
+    assert_true(rc == SSH_ERROR);
+
+    /* A public key doesn't have a cert, copy should fail. */
+    assert_true(pubkey->cert == NULL);
+    rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
+    assert_true(rc == SSH_ERROR);
+
+    /* Copying the cert to non-cert keys should work fine. */
+    rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
+    assert_true(rc == SSH_OK);
+    rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+    assert_true(rc == SSH_OK);
+
+    /* The private key's cert is already set, another copy should fail. */
+    rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+    assert_true(rc == SSH_ERROR);
+
+    ssh_key_free(cert);
+    ssh_key_free(privkey);
+    ssh_key_free(pubkey);
+}
+
+static void torture_pki_import_cert_file_rsa(void **state) {
+    int rc;
+    ssh_key cert;
+    enum ssh_keytypes_e type;
+
+    (void) state; /* unused */
+
+    rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY "-cert.pub", &cert);
+    assert_true(rc == 0);
+
+    type = ssh_key_type(cert);
+    assert_true(type == SSH_KEYTYPE_RSA_CERT01);
+
+    rc = ssh_key_is_public(cert);
+    assert_true(rc == 1);
+
+    ssh_key_free(cert);
+}
+
+static void torture_pki_import_cert_file_dsa(void **state) {
+    int rc;
+    ssh_key cert;
+    enum ssh_keytypes_e type;
+
+    (void) state; /* unused */
+
+    rc = ssh_pki_import_cert_file(LIBSSH_DSA_TESTKEY "-cert.pub", &cert);
+    assert_true(rc == 0);
+
+    type = ssh_key_type(cert);
+    assert_true(type == SSH_KEYTYPE_DSS_CERT01);
+
+    rc = ssh_key_is_public(cert);
+    assert_true(rc == 1);
+
+    ssh_key_free(cert);
+}
+
 static void torture_pki_publickey_dsa_base64(void **state)
 {
     enum ssh_keytypes_e type;
@@ -1530,6 +1629,17 @@ int torture_run_tests(void) {
         unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_ed25519,
                                  setup_ed25519_key,
                                  teardown),
+        /* cert */
+        unit_test_setup_teardown(torture_pki_copy_cert_to_privkey,
+                                 setup_rsa_key,
+				 teardown),
+        unit_test_setup_teardown(torture_pki_import_cert_file_rsa,
+                                 setup_rsa_key,
+                                 teardown),
+        unit_test_setup_teardown(torture_pki_import_cert_file_dsa,
+                                 setup_dsa_key,
+                                 teardown),
+
         /* public key */
         unit_test_setup_teardown(torture_pki_publickey_dsa_base64,
                                  setup_dsa_key,
-- 
1.9.1

From 23dda7440ff7c734b242130f206f482b50a8987b Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 18:07:50 +0100
Subject: [PATCH 5/6] Client cert auth: enable cert based authentication.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 src/agent.c      |  4 ++++
 src/auth.c       | 14 ++++++++++----
 src/pki_crypto.c | 10 ++++++++++
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/agent.c b/src/agent.c
index 922d753..66e0b1d 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -469,6 +469,10 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
 
             /* get key from blob */
             rc = ssh_pki_import_pubkey_blob(blob, &key);
+	    if (rc == SSH_ERROR) {
+	        /* Try again as a cert. */
+                rc = ssh_pki_import_cert_blob(blob, &key);
+            }
             ssh_string_free(blob);
             if (rc == SSH_ERROR) {
                 return NULL;
diff --git a/src/auth.c b/src/auth.c
index 1381774..06fed59 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -524,7 +524,7 @@ fail:
 }
 
 /**
- * @brief Authenticate with public/private key.
+ * @brief Authenticate with public/private key or cert.
  *
  * @param[in] session     The SSH session.
  *
@@ -553,6 +553,8 @@ int ssh_userauth_publickey(ssh_session session,
 {
     ssh_string str = NULL;
     int rc;
+    const char *type_c;
+    enum ssh_keytypes_e key_type;
 
     if (session == NULL) {
         return SSH_AUTH_ERROR;
@@ -588,7 +590,11 @@ int ssh_userauth_publickey(ssh_session session,
         return SSH_AUTH_ERROR;
     }
 
-    /* public key */
+    /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
+    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
+    type_c = ssh_key_type_to_char(key_type);
+
+    /* get public key or cert */
     rc = ssh_pki_export_pubkey_blob(privkey, &str);
     if (rc < 0) {
         goto fail;
@@ -601,8 +607,8 @@ int ssh_userauth_publickey(ssh_session session,
             "ssh-connection",
             "publickey",
             1, /* private key */
-            privkey->type_c, /* algo */
-            str /* public key */
+            type_c, /* algo */
+            str /* public key or cert */
             );
     if (rc < 0) {
         goto fail;
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index dcce3b5..aa3bc50 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -879,6 +879,15 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
         return NULL;
     }
 
+    if (key->cert != NULL) {
+        rc = buffer_add_buffer(buffer, key->cert);
+        if (rc < 0) {
+            ssh_buffer_free(buffer);
+            return NULL;
+        }
+        goto makestring;
+    }
+
     type_s = ssh_string_from_char(key->type_c);
     if (type_s == NULL) {
         ssh_buffer_free(buffer);
@@ -1031,6 +1040,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
             goto fail;
     }
 
+makestring:
     str = ssh_string_new(buffer_get_rest_len(buffer));
     if (str == NULL) {
         goto fail;
-- 
1.9.1

From 02f7c6d8bbe466b2aa23211fb622aaef03306947 Mon Sep 17 00:00:00 2001
From: Axel Eppe <aeppe@xxxxxxxxxx>
Date: Sun, 23 Aug 2015 18:17:27 +0100
Subject: [PATCH 6/6] Client cert auth: adding auth test.

Signed-off-by: Axel Eppe <aeppe@xxxxxxxxxx>
---
 tests/client/torture_auth.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 7f5167e..e2694ab 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -362,6 +362,42 @@ static void torture_auth_agent_nonblocking(void **state) {
     assert_true(rc == SSH_AUTH_SUCCESS);
 }
 
+static void torture_auth_agent_with_cert(void **state) {
+    /* This test assumes:
+       - TrustedUserCAKeys is configured on the SSH server we test against
+       - $TORTURE_CERT_USER was used as a principal during signing (i.e.
+         ssh-keygen -s user_ca -I key_id -n $TORTURE_CERT_USER key.pub).
+    */
+    ssh_session session = *state;
+    char *user = getenv("TORTURE_CERT_USER");
+    int rc;
+
+    if (user == NULL) {
+        print_message("*** Please set the environment variable TORTURE_CERT_USER"
+                      " to enable this test!!\n");
+        return;
+    }
+    if (!agent_is_running(session)){
+        print_message("*** Agent not running. Test ignored\n");
+        return;
+    }
+    rc = ssh_options_set(session, SSH_OPTIONS_USER, user);
+    assert_true(rc == SSH_OK);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+
+    rc = ssh_userauth_none(session,NULL);
+    /* This request should return a SSH_REQUEST_DENIED error */
+    if (rc == SSH_ERROR) {
+        assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED);
+    }
+    rc = ssh_userauth_list(session, NULL);
+    assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+    rc = ssh_userauth_agent(session, NULL);
+    assert_true(rc == SSH_AUTH_SUCCESS);
+}
 
 static void torture_auth_none(void **state) {
     ssh_session session = *state;
@@ -429,6 +465,7 @@ int torture_run_tests(void) {
         unit_test_setup_teardown(torture_auth_autopubkey, setup, teardown),
         unit_test_setup_teardown(torture_auth_autopubkey_nonblocking, setup, teardown),
         unit_test_setup_teardown(torture_auth_agent, setup, teardown),
+        unit_test_setup_teardown(torture_auth_agent_with_cert, setup, teardown),
         unit_test_setup_teardown(torture_auth_agent_nonblocking, setup, teardown),
         unit_test_setup_teardown(torture_auth_none, setup, teardown),
         unit_test_setup_teardown(torture_auth_none_nonblocking, setup, teardown),
-- 
1.9.1


Follow-Ups:
Re: [PATCH] basic client certificate support for libsshAndreas Schneider <asn@xxxxxxxxxxxxxx>
References:
Re: [PATCH] basic client certificate support for libsshAndreas Schneider <asn@xxxxxxxxxxxxxx>
Re: [PATCH] basic client certificate support for libsshAxel Eppe <aeppe@xxxxxxxxxx>
Re: [PATCH] basic client certificate support for libsshAndreas Schneider <asn@xxxxxxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org