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

[PATCH] basic client certificate support for libssh


Hi,

Please find attached a patch that adds basic openssh.com certificate
support for libssh clients. This patch is very simple and key type
agnostic: it sends the certificate blob as-is to the server, instead of the
public key. This can be a first step before a more in-depth certificate
support that includes the server side as well.

Agent authentication works out of the box without any modification. File
and base64-encoded blob methods only require 2 additional steps, i.e.
loading the cert as a public key, and copying it into the private key, via
a new "ssh_pki_copy_cert_to_privkey" function:

ssh_key privkey = ssh_pki_import_privkey_file(path_to_privkey_file, NULL,
NULL, NULL, &privkey);
ssh_key cert = ssh_pki_import_pubkey_file(path_to_pubkey_file, &pubkey);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index f4e4fe9..ad90b6d 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -253,7 +253,11 @@ enum ssh_keytypes_e{
   SSH_KEYTYPE_RSA,
   SSH_KEYTYPE_RSA1,
   SSH_KEYTYPE_ECDSA,
-  SSH_KEYTYPE_ED25519
+  SSH_KEYTYPE_ED25519,
+  SSH_KEYTYPE_DSS_CERT00,
+  SSH_KEYTYPE_RSA_CERT00,
+  SSH_KEYTYPE_DSS_CERT01,
+  SSH_KEYTYPE_RSA_CERT01
 };
 
 enum ssh_keycmp_e {
@@ -560,6 +564,9 @@ LIBSSH_API int ssh_pki_export_pubkey_base64(const ssh_key key,
 LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key,
                                           const char *filename);
 
+LIBSSH_API int ssh_pki_copy_cert_to_privkey(const ssh_key cert_key,
+                                            ssh_key privkey);
+
 LIBSSH_API const char *ssh_pki_key_ecdsa_name(const ssh_key key);
 
 LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len);
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/auth.c b/src/auth.c
index d56111d..be49776 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,10 @@ int ssh_userauth_publickey(ssh_session session,
         return SSH_AUTH_ERROR;
     }
 
-    /* public key */
+    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 +606,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.c b/src/pki.c
index ceddaa2..1dd9d55 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -162,6 +162,7 @@ void ssh_key_clean (ssh_key key){
         SAFE_FREE(key->ed25519_privkey);
     }
     SAFE_FREE(key->ed25519_pubkey);
+    if (key->cert != NULL) ssh_string_free(key->cert);
     key->flags=SSH_KEY_FLAG_EMPTY;
     key->type=SSH_KEYTYPE_UNKNOWN;
     key->ecdsa_nid = 0;
@@ -214,6 +215,14 @@ 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_CERT00:
+      return "ssh-dss-cert-v00@xxxxxxxxxxx";
+    case SSH_KEYTYPE_RSA_CERT00:
+      return "ssh-rsa-cert-v00@xxxxxxxxxxx";
+    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 +263,14 @@ 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-v00@xxxxxxxxxxx") == 0){
+        return SSH_KEYTYPE_DSS_CERT00;
+    } else if (strcmp(name, "ssh-rsa-cert-v00@xxxxxxxxxxx") == 0){
+        return SSH_KEYTYPE_RSA_CERT00;
+    } 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 +387,10 @@ void ssh_signature_free(ssh_signature sig)
         case SSH_KEYTYPE_ED25519:
             SAFE_FREE(sig->ed25519_sig);
             break;
+        case SSH_KEYTYPE_DSS_CERT00:
+        case SSH_KEYTYPE_RSA_CERT00:
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             break;
     }
@@ -718,6 +739,22 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
                 }
             }
             break;
+        case SSH_KEYTYPE_DSS_CERT00:
+        case SSH_KEYTYPE_RSA_CERT00:
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
+            {
+                ssh_string cert;
+                uint32_t buffer_len = ssh_buffer_get_len(buffer);
+
+                cert = ssh_string_new(buffer_len);
+                if (cert == NULL) {
+                    goto fail;
+                }
+                ssh_string_fill(cert, ssh_buffer_get_begin(buffer), buffer_len);
+                key->cert = (void*) cert;
+            }
+            break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
             {
@@ -1075,6 +1112,10 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
                 goto error;
             }
             break;
+        case SSH_KEYTYPE_DSS_CERT00:
+        case SSH_KEYTYPE_RSA_CERT00:
+        case SSH_KEYTYPE_DSS_CERT01:
+        case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_UNKNOWN:
             goto error;
     }
@@ -1248,6 +1289,28 @@ 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_string cert_string;
+
+  cert_string = ssh_string_copy(certkey->cert);
+  if (cert_string == NULL) {
+    return SSH_ERROR;
+  }
+
+  privkey->cert = cert_string;
+  privkey->cert_type = certkey->type;
+  return SSH_OK;
+}
+
 int ssh_pki_export_pubkey_rsa1(const ssh_key key,
                                const char *host,
                                char *rsa1,
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index c2aface..b5e3ae3 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -123,6 +123,10 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
         ssh_pki_log("Unsupported private key method %s", key->type_c);
         goto fail;
     case SSH_KEYTYPE_UNKNOWN:
+    case SSH_KEYTYPE_DSS_CERT00:
+    case SSH_KEYTYPE_RSA_CERT00:
+    case SSH_KEYTYPE_DSS_CERT01:
+    case SSH_KEYTYPE_RSA_CERT01:
         ssh_pki_log("Unknown private key protocol %s", key->type_c);
         goto fail;
     }
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index b53bba2..0581370 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -651,6 +651,10 @@ 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_CERT00:
+        case SSH_KEYTYPE_RSA_CERT00:
+        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 +781,10 @@ 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_CERT00:
+        case SSH_KEYTYPE_RSA_CERT00:
+        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);
@@ -875,6 +883,18 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
         return NULL;
     }
 
+    if (key->cert != NULL) {
+      ssh_string cert = (ssh_string)key->cert;
+      size_t cert_len = ssh_string_len(cert);
+
+      rc = ssh_buffer_add_data(buffer, ssh_string_data(cert), cert_len);
+      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);
@@ -1027,6 +1047,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;

Follow-Ups:
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