[PATCH 3/9] rpc: split TLS cert validation into separate file

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

 



The TLS cert validation logic will be reused for the new impl of the
virt-pki-validate tool.

Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx>
---
 po/POTFILES                |   1 +
 src/rpc/meson.build        |   1 +
 src/rpc/virnettlscert.c    | 553 +++++++++++++++++++++++++++++++++++++
 src/rpc/virnettlscert.h    |  42 +++
 src/rpc/virnettlscontext.c | 535 +----------------------------------
 5 files changed, 606 insertions(+), 526 deletions(-)
 create mode 100644 src/rpc/virnettlscert.c
 create mode 100644 src/rpc/virnettlscert.h

diff --git a/po/POTFILES b/po/POTFILES
index 4ad7e19e08..0f68c652eb 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -219,6 +219,7 @@ src/rpc/virnetserverprogram.c
 src/rpc/virnetserverservice.c
 src/rpc/virnetsocket.c
 src/rpc/virnetsshsession.c
+src/rpc/virnettlscert.c
 src/rpc/virnettlscontext.c
 src/secret/secret_driver.c
 src/security/security_apparmor.c
diff --git a/src/rpc/meson.build b/src/rpc/meson.build
index d11d532d0f..8bdbf5c88f 100644
--- a/src/rpc/meson.build
+++ b/src/rpc/meson.build
@@ -2,6 +2,7 @@ gendispatch_prog = find_program('gendispatch.pl')
 
 tlsconfig_sources = [
     files('virnettlsconfig.c'),
+    files('virnettlscert.c'),
 ]
 
 socket_sources = tlsconfig_sources + [
diff --git a/src/rpc/virnettlscert.c b/src/rpc/virnettlscert.c
new file mode 100644
index 0000000000..2e1e4c56d5
--- /dev/null
+++ b/src/rpc/virnettlscert.c
@@ -0,0 +1,553 @@
+/*
+ * virnettlscert.c: TLS x509 certificate helpers
+ *
+ * Copyright (C) 2010-2024 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "virnettlscert.h"
+
+#include "viralloc.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "virerror.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("rpc.nettlscert");
+
+static int virNetTLSCertCheckTimes(gnutls_x509_crt_t cert,
+                                   const char *certFile,
+                                   bool isServer,
+                                   bool isCA)
+{
+    time_t now;
+
+    if ((now = time(NULL)) == ((time_t)-1)) {
+        virReportSystemError(errno, "%s",
+                             _("cannot get current time"));
+        return -1;
+    }
+
+    if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       (isCA ?
+                        _("The CA certificate %1$s has expired") :
+                        (isServer ?
+                         _("The server certificate %1$s has expired") :
+                         _("The client certificate %1$s has expired"))),
+                       certFile);
+        return -1;
+    }
+
+    if (gnutls_x509_crt_get_activation_time(cert) > now) {
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       (isCA ?
+                        _("The CA certificate %1$s is not yet active") :
+                        (isServer ?
+                         _("The server certificate %1$s is not yet active") :
+                         _("The client certificate %1$s is not yet active"))),
+                       certFile);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int virNetTLSCertCheckBasicConstraints(gnutls_x509_crt_t cert,
+                                              const char *certFile,
+                                              bool isServer,
+                                              bool isCA)
+{
+    int status;
+
+    status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL);
+    VIR_DEBUG("Cert %s basic constraints %d", certFile, status);
+
+    if (status > 0) { /* It is a CA cert */
+        if (!isCA) {
+            virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
+                           _("The certificate %1$s basic constraints show a CA, but we need one for a server") :
+                           _("The certificate %1$s basic constraints show a CA, but we need one for a client"),
+                           certFile);
+            return -1;
+        }
+    } else if (status == 0) { /* It is not a CA cert */
+        if (isCA) {
+            virReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("The certificate %1$s basic constraints do not show a CA"),
+                           certFile);
+            return -1;
+        }
+    } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */
+        if (isCA) {
+            virReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("The certificate %1$s is missing basic constraints for a CA"),
+                           certFile);
+            return -1;
+        }
+    } else { /* General error */
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       _("Unable to query certificate %1$s basic constraints %2$s"),
+                       certFile, gnutls_strerror(status));
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int virNetTLSCertCheckKeyUsage(gnutls_x509_crt_t cert,
+                                      const char *certFile,
+                                      bool isCA)
+{
+    int status;
+    unsigned int usage = 0;
+    unsigned int critical = 0;
+
+    status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
+
+    VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical);
+    if (status < 0) {
+        if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+            usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN :
+                GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT;
+        } else {
+            virReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("Unable to query certificate %1$s key usage %2$s"),
+                           certFile, gnutls_strerror(status));
+            return -1;
+        }
+    }
+
+    if (isCA) {
+        if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
+            if (critical) {
+                virReportError(VIR_ERR_SYSTEM_ERROR,
+                               _("Certificate %1$s usage does not permit certificate signing"),
+                               certFile);
+                return -1;
+            } else {
+                VIR_WARN("Certificate %s usage does not permit certificate signing",
+                         certFile);
+            }
+        }
+    } else {
+        if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
+            if (critical) {
+                virReportError(VIR_ERR_SYSTEM_ERROR,
+                               _("Certificate %1$s usage does not permit digital signature"),
+                               certFile);
+                return -1;
+            } else {
+                VIR_WARN("Certificate %s usage does not permit digital signature",
+                         certFile);
+            }
+        }
+        if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
+            if (critical) {
+                virReportError(VIR_ERR_SYSTEM_ERROR,
+                               _("Certificate %1$s usage does not permit key encipherment"),
+                               certFile);
+                return -1;
+            } else {
+                VIR_WARN("Certificate %s usage does not permit key encipherment",
+                         certFile);
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static int virNetTLSCertCheckKeyPurpose(gnutls_x509_crt_t cert,
+                                        const char *certFile,
+                                        bool isServer)
+{
+    int status;
+    size_t i;
+    unsigned int purposeCritical;
+    unsigned int critical;
+    char *buffer = NULL;
+    size_t size;
+    bool allowClient = false, allowServer = false;
+
+    critical = 0;
+    for (i = 0; ; i++) {
+        size = 0;
+        status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL);
+
+        if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+            VIR_DEBUG("No key purpose data available at slot %zu", i);
+
+            /* If there is no data at all, then we must allow client/server to pass */
+            if (i == 0)
+                allowServer = allowClient = true;
+            break;
+        }
+        if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+            virReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("Unable to query certificate %1$s key purpose %2$s"),
+                           certFile, gnutls_strerror(status));
+            return -1;
+        }
+
+        buffer = g_new0(char, size);
+        status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical);
+        if (status < 0) {
+            VIR_FREE(buffer);
+            virReportError(VIR_ERR_SYSTEM_ERROR,
+                           _("Unable to query certificate %1$s key purpose %2$s"),
+                           certFile, gnutls_strerror(status));
+            return -1;
+        }
+        if (purposeCritical)
+            critical = true;
+
+        VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical);
+        if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
+            allowServer = true;
+        } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
+            allowClient = true;
+        } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) {
+            allowServer = allowClient = true;
+        }
+
+        VIR_FREE(buffer);
+    }
+
+    if (isServer) {
+        if (!allowServer) {
+            if (critical) {
+                virReportError(VIR_ERR_SYSTEM_ERROR,
+                               _("Certificate %1$s purpose does not allow use for with a TLS server"),
+                               certFile);
+                return -1;
+            } else {
+                VIR_WARN("Certificate %s purpose does not allow use for with a TLS server",
+                         certFile);
+            }
+        }
+    } else {
+        if (!allowClient) {
+            if (critical) {
+                virReportError(VIR_ERR_SYSTEM_ERROR,
+                               _("Certificate %1$s purpose does not allow use for with a TLS client"),
+                               certFile);
+                return -1;
+            } else {
+                VIR_WARN("Certificate %s purpose does not allow use for with a TLS client",
+                         certFile);
+            }
+        }
+    }
+
+    return 0;
+}
+
+/* Check DN is on tls_allowed_dn_list. */
+static int
+virNetTLSCertCheckDNACL(const char *dname,
+                        const char *const *wildcards)
+{
+    while (*wildcards) {
+        if (g_pattern_match_simple(*wildcards, dname))
+            return 1;
+
+        wildcards++;
+    }
+
+    /* Log the client's DN for debugging */
+    VIR_DEBUG("Failed ACL check for client DN '%s'", dname);
+
+    /* This is the most common error: make it informative. */
+    virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+                   _("Client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list).  Use 'virt-pki-query-dn clientcert.pem' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option."));
+    return 0;
+}
+
+
+static int
+virNetTLSCertCheckDN(gnutls_x509_crt_t cert,
+                     const char *certFile,
+                     const char *hostname,
+                     const char *dname,
+                     const char *const *acl)
+{
+    if (acl && dname &&
+        virNetTLSCertCheckDNACL(dname, acl) <= 0)
+        return -1;
+
+    if (hostname &&
+        !gnutls_x509_crt_check_hostname(cert, hostname)) {
+        virReportError(VIR_ERR_RPC,
+                       _("Certificate %1$s owner does not match the hostname %2$s"),
+                       certFile, hostname);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int virNetTLSCertCheck(gnutls_x509_crt_t cert,
+                              const char *certFile,
+                              bool isServer,
+                              bool isCA)
+{
+    if (virNetTLSCertCheckTimes(cert, certFile, isServer, isCA) < 0)
+        return -1;
+
+    if (virNetTLSCertCheckBasicConstraints(cert, certFile, isServer, isCA) < 0)
+        return -1;
+
+    if (virNetTLSCertCheckKeyUsage(cert, certFile, isCA) < 0)
+        return -1;
+
+    if (!isCA &&
+        virNetTLSCertCheckKeyPurpose(cert, certFile, isServer) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int virNetTLSCertCheckPair(gnutls_x509_crt_t cert,
+                                         const char *certFile,
+                                         gnutls_x509_crt_t *cacerts,
+                                         size_t ncacerts,
+                                         const char *cacertFile,
+                                         bool isServer)
+{
+    unsigned int status;
+
+    if (gnutls_x509_crt_list_verify(&cert, 1,
+                                    cacerts, ncacerts,
+                                    NULL, 0,
+                                    0, &status) < 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
+                       _("Unable to verify server certificate %1$s against CA certificate %2$s") :
+                       _("Unable to verify client certificate %1$s against CA certificate %2$s"),
+                       certFile, cacertFile);
+        return -1;
+    }
+
+    if (status != 0) {
+        const char *reason = _("Invalid certificate");
+
+        if (status & GNUTLS_CERT_INVALID)
+            reason = _("The certificate is not trusted.");
+
+        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+            reason = _("The certificate hasn't got a known issuer.");
+
+        if (status & GNUTLS_CERT_REVOKED)
+            reason = _("The certificate has been revoked.");
+
+        if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+            reason = _("The certificate uses an insecure algorithm");
+
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       _("Our own certificate %1$s failed validation against %2$s: %3$s"),
+                       certFile, cacertFile, reason);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+gnutls_x509_crt_t virNetTLSCertLoadFromFile(const char *certFile,
+                                            bool isServer)
+{
+    gnutls_datum_t data;
+    gnutls_x509_crt_t cert = NULL;
+    g_autofree char *buf = NULL;
+    int ret = -1;
+
+    VIR_DEBUG("isServer %d certFile %s",
+              isServer, certFile);
+
+    if (gnutls_x509_crt_init(&cert) < 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+                       _("Unable to initialize certificate"));
+        goto cleanup;
+    }
+
+    if (virFileReadAll(certFile, (1<<16), &buf) < 0)
+        goto cleanup;
+
+    data.data = (unsigned char *)buf;
+    data.size = strlen(buf);
+
+    if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
+                       _("Unable to import server certificate %1$s") :
+                       _("Unable to import client certificate %1$s"),
+                       certFile);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (ret != 0) {
+        g_clear_pointer(&cert, gnutls_x509_crt_deinit);
+    }
+    return cert;
+}
+
+
+static int virNetTLSCertLoadCAListFromFile(const char *certFile,
+                                           gnutls_x509_crt_t *certs,
+                                           unsigned int certMax,
+                                           size_t *ncerts)
+{
+    gnutls_datum_t data;
+    g_autofree char *buf = NULL;
+
+    *ncerts = 0;
+    VIR_DEBUG("certFile %s", certFile);
+
+    if (virFileReadAll(certFile, (1<<16), &buf) < 0)
+        return -1;
+
+    data.data = (unsigned char *)buf;
+    data.size = strlen(buf);
+
+    if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       _("Unable to import CA certificate list %1$s"),
+                       certFile);
+        return -1;
+    }
+    *ncerts = certMax;
+
+    return 0;
+}
+
+
+#define MAX_CERTS 16
+int virNetTLSCertSanityCheck(bool isServer,
+                             const char *cacertFile,
+                             const char *certFile)
+{
+    gnutls_x509_crt_t cert = NULL;
+    gnutls_x509_crt_t cacerts[MAX_CERTS] = { 0 };
+    size_t ncacerts = 0;
+    size_t i;
+    int ret = -1;
+
+    if ((access(certFile, R_OK) == 0) &&
+        !(cert = virNetTLSCertLoadFromFile(certFile, isServer)))
+        goto cleanup;
+    if ((access(cacertFile, R_OK) == 0) &&
+        virNetTLSCertLoadCAListFromFile(cacertFile, cacerts,
+                                        MAX_CERTS, &ncacerts) < 0)
+        goto cleanup;
+
+    if (cert &&
+        virNetTLSCertCheck(cert, certFile, isServer, false) < 0)
+        goto cleanup;
+
+    for (i = 0; i < ncacerts; i++) {
+        if (virNetTLSCertCheck(cacerts[i], cacertFile, isServer, true) < 0)
+            goto cleanup;
+    }
+
+    if (cert && ncacerts &&
+        virNetTLSCertCheckPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    if (cert)
+        gnutls_x509_crt_deinit(cert);
+    for (i = 0; i < ncacerts; i++)
+        gnutls_x509_crt_deinit(cacerts[i]);
+    return ret;
+}
+
+int virNetTLSCertValidateCA(gnutls_x509_crt_t cert,
+                            bool isServer)
+{
+    if (virNetTLSCertCheckTimes(cert, "[session]",
+                                isServer, true) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+char *virNetTLSCertValidate(gnutls_x509_crt_t cert,
+                            bool isServer,
+                            const char *hostname,
+                            const char *const *x509dnACL)
+{
+    size_t dnamesize = 256;
+    g_autofree char *dname = g_new0(char, dnamesize);
+    int ret;
+
+    if (virNetTLSCertCheckTimes(cert, "[session]",
+                                isServer, false) < 0) {
+        return NULL;
+    }
+
+    ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize);
+    if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+        VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize);
+        dname = g_realloc(dname, dnamesize);
+        ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize);
+    }
+    if (ret != 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR,
+                       _("Failed to get certificate %1$s distinguished name: %2$s"),
+                       "[session]", gnutls_strerror(ret));
+        return NULL;
+    }
+
+    VIR_DEBUG("Peer DN is %s", dname);
+
+    if (virNetTLSCertCheckDN(cert, "[session]", hostname,
+                             dname, x509dnACL) < 0) {
+        return NULL;
+    }
+
+    /* !isServer, since on the client, we're validating the
+     * server's cert, and on the server, the client's cert
+     */
+    if (virNetTLSCertCheckBasicConstraints(cert, "[session]",
+                                           !isServer, false) < 0) {
+        return NULL;
+    }
+
+    if (virNetTLSCertCheckKeyUsage(cert, "[session]",
+                                   false) < 0) {
+        return NULL;
+    }
+
+    /* !isServer - as above */
+    if (virNetTLSCertCheckKeyPurpose(cert, "[session]",
+                                     !isServer) < 0) {
+        return NULL;
+    }
+
+    return g_steal_pointer(&dname);
+}
diff --git a/src/rpc/virnettlscert.h b/src/rpc/virnettlscert.h
new file mode 100644
index 0000000000..0ac511a141
--- /dev/null
+++ b/src/rpc/virnettlscert.h
@@ -0,0 +1,42 @@
+/*
+ * virnettlscert.h: TLS x509 certificate helpers
+ *
+ * Copyright (C) 2010-2024 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/x509.h>
+
+#include "internal.h"
+
+int virNetTLSCertSanityCheck(bool isServer,
+                             const char *cacertFile,
+                             const char *certFile);
+
+int virNetTLSCertValidateCA(gnutls_x509_crt_t cert,
+                            bool isServer);
+
+char *virNetTLSCertValidate(gnutls_x509_crt_t cert,
+                            bool isServer,
+                            const char *hostname,
+                            const char *const *x509dnACL);
+
+gnutls_x509_crt_t virNetTLSCertLoadFromFile(const char *certFile,
+                                            bool isServer);
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
index a56abee009..e8023133b4 100644
--- a/src/rpc/virnettlscontext.c
+++ b/src/rpc/virnettlscontext.c
@@ -28,6 +28,7 @@
 
 #include "virnettlscontext.h"
 #include "virnettlsconfig.h"
+#include "virnettlscert.h"
 #include "virstring.h"
 
 #include "viralloc.h"
@@ -110,466 +111,6 @@ static void virNetTLSLog(int level G_GNUC_UNUSED,
 }
 
 
-static int virNetTLSContextCheckCertTimes(gnutls_x509_crt_t cert,
-                                          const char *certFile,
-                                          bool isServer,
-                                          bool isCA)
-{
-    time_t now;
-
-    if ((now = time(NULL)) == ((time_t)-1)) {
-        virReportSystemError(errno, "%s",
-                             _("cannot get current time"));
-        return -1;
-    }
-
-    if (gnutls_x509_crt_get_expiration_time(cert) < now) {
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       (isCA ?
-                        _("The CA certificate %1$s has expired") :
-                        (isServer ?
-                         _("The server certificate %1$s has expired") :
-                         _("The client certificate %1$s has expired"))),
-                       certFile);
-        return -1;
-    }
-
-    if (gnutls_x509_crt_get_activation_time(cert) > now) {
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       (isCA ?
-                        _("The CA certificate %1$s is not yet active") :
-                        (isServer ?
-                         _("The server certificate %1$s is not yet active") :
-                         _("The client certificate %1$s is not yet active"))),
-                       certFile);
-        return -1;
-    }
-
-    return 0;
-}
-
-
-static int virNetTLSContextCheckCertBasicConstraints(gnutls_x509_crt_t cert,
-                                                     const char *certFile,
-                                                     bool isServer,
-                                                     bool isCA)
-{
-    int status;
-
-    status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL);
-    VIR_DEBUG("Cert %s basic constraints %d", certFile, status);
-
-    if (status > 0) { /* It is a CA cert */
-        if (!isCA) {
-            virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
-                           _("The certificate %1$s basic constraints show a CA, but we need one for a server") :
-                           _("The certificate %1$s basic constraints show a CA, but we need one for a client"),
-                           certFile);
-            return -1;
-        }
-    } else if (status == 0) { /* It is not a CA cert */
-        if (isCA) {
-            virReportError(VIR_ERR_SYSTEM_ERROR,
-                           _("The certificate %1$s basic constraints do not show a CA"),
-                           certFile);
-            return -1;
-        }
-    } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */
-        if (isCA) {
-            virReportError(VIR_ERR_SYSTEM_ERROR,
-                           _("The certificate %1$s is missing basic constraints for a CA"),
-                           certFile);
-            return -1;
-        }
-    } else { /* General error */
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       _("Unable to query certificate %1$s basic constraints %2$s"),
-                       certFile, gnutls_strerror(status));
-        return -1;
-    }
-
-    return 0;
-}
-
-
-static int virNetTLSContextCheckCertKeyUsage(gnutls_x509_crt_t cert,
-                                             const char *certFile,
-                                             bool isCA)
-{
-    int status;
-    unsigned int usage = 0;
-    unsigned int critical = 0;
-
-    status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
-
-    VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical);
-    if (status < 0) {
-        if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
-            usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN :
-                GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT;
-        } else {
-            virReportError(VIR_ERR_SYSTEM_ERROR,
-                           _("Unable to query certificate %1$s key usage %2$s"),
-                           certFile, gnutls_strerror(status));
-            return -1;
-        }
-    }
-
-    if (isCA) {
-        if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
-            if (critical) {
-                virReportError(VIR_ERR_SYSTEM_ERROR,
-                               _("Certificate %1$s usage does not permit certificate signing"),
-                               certFile);
-                return -1;
-            } else {
-                VIR_WARN("Certificate %s usage does not permit certificate signing",
-                         certFile);
-            }
-        }
-    } else {
-        if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
-            if (critical) {
-                virReportError(VIR_ERR_SYSTEM_ERROR,
-                               _("Certificate %1$s usage does not permit digital signature"),
-                               certFile);
-                return -1;
-            } else {
-                VIR_WARN("Certificate %s usage does not permit digital signature",
-                         certFile);
-            }
-        }
-        if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
-            if (critical) {
-                virReportError(VIR_ERR_SYSTEM_ERROR,
-                               _("Certificate %1$s usage does not permit key encipherment"),
-                               certFile);
-                return -1;
-            } else {
-                VIR_WARN("Certificate %s usage does not permit key encipherment",
-                         certFile);
-            }
-        }
-    }
-
-    return 0;
-}
-
-
-static int virNetTLSContextCheckCertKeyPurpose(gnutls_x509_crt_t cert,
-                                               const char *certFile,
-                                               bool isServer)
-{
-    int status;
-    size_t i;
-    unsigned int purposeCritical;
-    unsigned int critical;
-    char *buffer = NULL;
-    size_t size;
-    bool allowClient = false, allowServer = false;
-
-    critical = 0;
-    for (i = 0; ; i++) {
-        size = 0;
-        status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL);
-
-        if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
-            VIR_DEBUG("No key purpose data available at slot %zu", i);
-
-            /* If there is no data at all, then we must allow client/server to pass */
-            if (i == 0)
-                allowServer = allowClient = true;
-            break;
-        }
-        if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) {
-            virReportError(VIR_ERR_SYSTEM_ERROR,
-                           _("Unable to query certificate %1$s key purpose %2$s"),
-                           certFile, gnutls_strerror(status));
-            return -1;
-        }
-
-        buffer = g_new0(char, size);
-        status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical);
-        if (status < 0) {
-            VIR_FREE(buffer);
-            virReportError(VIR_ERR_SYSTEM_ERROR,
-                           _("Unable to query certificate %1$s key purpose %2$s"),
-                           certFile, gnutls_strerror(status));
-            return -1;
-        }
-        if (purposeCritical)
-            critical = true;
-
-        VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical);
-        if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
-            allowServer = true;
-        } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
-            allowClient = true;
-        } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) {
-            allowServer = allowClient = true;
-        }
-
-        VIR_FREE(buffer);
-    }
-
-    if (isServer) {
-        if (!allowServer) {
-            if (critical) {
-                virReportError(VIR_ERR_SYSTEM_ERROR,
-                               _("Certificate %1$s purpose does not allow use for with a TLS server"),
-                               certFile);
-                return -1;
-            } else {
-                VIR_WARN("Certificate %s purpose does not allow use for with a TLS server",
-                         certFile);
-            }
-        }
-    } else {
-        if (!allowClient) {
-            if (critical) {
-                virReportError(VIR_ERR_SYSTEM_ERROR,
-                               _("Certificate %1$s purpose does not allow use for with a TLS client"),
-                               certFile);
-                return -1;
-            } else {
-                VIR_WARN("Certificate %s purpose does not allow use for with a TLS client",
-                         certFile);
-            }
-        }
-    }
-
-    return 0;
-}
-
-/* Check DN is on tls_allowed_dn_list. */
-static int
-virNetTLSContextCheckCertDNACL(const char *dname,
-                               const char *const *wildcards)
-{
-    while (*wildcards) {
-        if (g_pattern_match_simple(*wildcards, dname))
-            return 1;
-
-        wildcards++;
-    }
-
-    /* Log the client's DN for debugging */
-    VIR_DEBUG("Failed ACL check for client DN '%s'", dname);
-
-    /* This is the most common error: make it informative. */
-    virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
-                   _("Client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list).  Use 'virt-pki-query-dn clientcert.pem' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option."));
-    return 0;
-}
-
-
-static int
-virNetTLSContextCheckCertDN(gnutls_x509_crt_t cert,
-                            const char *certFile,
-                            const char *hostname,
-                            const char *dname,
-                            const char *const *acl)
-{
-    if (acl && dname &&
-        virNetTLSContextCheckCertDNACL(dname, acl) <= 0)
-        return -1;
-
-    if (hostname &&
-        !gnutls_x509_crt_check_hostname(cert, hostname)) {
-        virReportError(VIR_ERR_RPC,
-                       _("Certificate %1$s owner does not match the hostname %2$s"),
-                       certFile, hostname);
-        return -1;
-    }
-
-    return 0;
-}
-
-
-static int virNetTLSContextCheckCert(gnutls_x509_crt_t cert,
-                                     const char *certFile,
-                                     bool isServer,
-                                     bool isCA)
-{
-    if (virNetTLSContextCheckCertTimes(cert, certFile,
-                                       isServer, isCA) < 0)
-        return -1;
-
-    if (virNetTLSContextCheckCertBasicConstraints(cert, certFile,
-                                                  isServer, isCA) < 0)
-        return -1;
-
-    if (virNetTLSContextCheckCertKeyUsage(cert, certFile,
-                                          isCA) < 0)
-        return -1;
-
-    if (!isCA &&
-        virNetTLSContextCheckCertKeyPurpose(cert, certFile,
-                                            isServer) < 0)
-        return -1;
-
-    return 0;
-}
-
-
-static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert,
-                                         const char *certFile,
-                                         gnutls_x509_crt_t *cacerts,
-                                         size_t ncacerts,
-                                         const char *cacertFile,
-                                         bool isServer)
-{
-    unsigned int status;
-
-    if (gnutls_x509_crt_list_verify(&cert, 1,
-                                    cacerts, ncacerts,
-                                    NULL, 0,
-                                    0, &status) < 0) {
-        virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
-                       _("Unable to verify server certificate %1$s against CA certificate %2$s") :
-                       _("Unable to verify client certificate %1$s against CA certificate %2$s"),
-                       certFile, cacertFile);
-        return -1;
-    }
-
-    if (status != 0) {
-        const char *reason = _("Invalid certificate");
-
-        if (status & GNUTLS_CERT_INVALID)
-            reason = _("The certificate is not trusted.");
-
-        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-            reason = _("The certificate hasn't got a known issuer.");
-
-        if (status & GNUTLS_CERT_REVOKED)
-            reason = _("The certificate has been revoked.");
-
-        if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
-            reason = _("The certificate uses an insecure algorithm");
-
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       _("Our own certificate %1$s failed validation against %2$s: %3$s"),
-                       certFile, cacertFile, reason);
-        return -1;
-    }
-
-    return 0;
-}
-
-
-static gnutls_x509_crt_t virNetTLSContextLoadCertFromFile(const char *certFile,
-                                                          bool isServer)
-{
-    gnutls_datum_t data;
-    gnutls_x509_crt_t cert = NULL;
-    g_autofree char *buf = NULL;
-    int ret = -1;
-
-    VIR_DEBUG("isServer %d certFile %s",
-              isServer, certFile);
-
-    if (gnutls_x509_crt_init(&cert) < 0) {
-        virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
-                       _("Unable to initialize certificate"));
-        goto cleanup;
-    }
-
-    if (virFileReadAll(certFile, (1<<16), &buf) < 0)
-        goto cleanup;
-
-    data.data = (unsigned char *)buf;
-    data.size = strlen(buf);
-
-    if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) {
-        virReportError(VIR_ERR_SYSTEM_ERROR, isServer ?
-                       _("Unable to import server certificate %1$s") :
-                       _("Unable to import client certificate %1$s"),
-                       certFile);
-        goto cleanup;
-    }
-
-    ret = 0;
-
- cleanup:
-    if (ret != 0) {
-        g_clear_pointer(&cert, gnutls_x509_crt_deinit);
-    }
-    return cert;
-}
-
-
-static int virNetTLSContextLoadCACertListFromFile(const char *certFile,
-                                                  gnutls_x509_crt_t *certs,
-                                                  unsigned int certMax,
-                                                  size_t *ncerts)
-{
-    gnutls_datum_t data;
-    g_autofree char *buf = NULL;
-
-    *ncerts = 0;
-    VIR_DEBUG("certFile %s", certFile);
-
-    if (virFileReadAll(certFile, (1<<16), &buf) < 0)
-        return -1;
-
-    data.data = (unsigned char *)buf;
-    data.size = strlen(buf);
-
-    if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) {
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       _("Unable to import CA certificate list %1$s"),
-                       certFile);
-        return -1;
-    }
-    *ncerts = certMax;
-
-    return 0;
-}
-
-
-#define MAX_CERTS 16
-static int virNetTLSContextSanityCheckCredentials(bool isServer,
-                                                  const char *cacertFile,
-                                                  const char *certFile)
-{
-    gnutls_x509_crt_t cert = NULL;
-    gnutls_x509_crt_t cacerts[MAX_CERTS] = { 0 };
-    size_t ncacerts = 0;
-    size_t i;
-    int ret = -1;
-
-    if ((access(certFile, R_OK) == 0) &&
-        !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer)))
-        goto cleanup;
-    if ((access(cacertFile, R_OK) == 0) &&
-        virNetTLSContextLoadCACertListFromFile(cacertFile, cacerts,
-                                               MAX_CERTS, &ncacerts) < 0)
-        goto cleanup;
-
-    if (cert &&
-        virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0)
-        goto cleanup;
-
-    for (i = 0; i < ncacerts; i++) {
-        if (virNetTLSContextCheckCert(cacerts[i], cacertFile, isServer, true) < 0)
-            goto cleanup;
-    }
-
-    if (cert && ncacerts &&
-        virNetTLSContextCheckCertPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0)
-        goto cleanup;
-
-    ret = 0;
-
- cleanup:
-    if (cert)
-        gnutls_x509_crt_deinit(cert);
-    for (i = 0; i < ncacerts; i++)
-        gnutls_x509_crt_deinit(cacerts[i]);
-    return ret;
-}
-
-
 static int virNetTLSContextLoadCredentials(virNetTLSContext *ctxt,
                                            bool isServer,
                                            const char *cacert,
@@ -683,7 +224,7 @@ static virNetTLSContext *virNetTLSContextNew(const char *cacert,
     }
 
     if (sanityCheckCert &&
-        virNetTLSContextSanityCheckCredentials(isServer, cacert, cert) < 0)
+        virNetTLSCertSanityCheck(isServer, cacert, cert) < 0)
         goto error;
 
     if (virNetTLSContextLoadCredentials(ctxt, isServer, cacert, cacrl, cert, key) < 0)
@@ -846,7 +387,7 @@ int virNetTLSContextReloadForServer(virNetTLSContext *ctxt,
         goto error;
     }
 
-    if (virNetTLSContextSanityCheckCredentials(true, cacert, cert))
+    if (virNetTLSCertSanityCheck(true, cacert, cert))
         goto error;
 
     if (virNetTLSContextLoadCredentials(ctxt, true, cacert, cacrl, cert, key))
@@ -876,65 +417,6 @@ virNetTLSContext *virNetTLSContextNewClient(const char *cacert,
                                sanityCheckCert, requireValidCert, false);
 }
 
-static int virNetTLSContextCertValidateCA(gnutls_x509_crt_t cert,
-                                          bool isServer)
-{
-    if (virNetTLSContextCheckCertTimes(cert, "[session]", isServer, true) < 0)
-        return -1;
-
-    return 0;
-}
-
-static char *virNetTLSContextCertValidate(gnutls_x509_crt_t cert,
-                                          bool isServer,
-                                          const char *hostname,
-                                          const char *const *x509dnACL)
-{
-    size_t dnamesize = 256;
-    g_autofree char *dname = g_new0(char, dnamesize);
-    int ret;
-
-    if (virNetTLSContextCheckCertTimes(cert, "[session]",
-                                       isServer, false) < 0)
-        return NULL;
-
-    ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize);
-    if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
-        VIR_DEBUG("Reallocating dname to fit %zu bytes", dnamesize);
-        dname = g_realloc(dname, dnamesize);
-        ret = gnutls_x509_crt_get_dn(cert, dname, &dnamesize);
-    }
-    if (ret != 0) {
-        virReportError(VIR_ERR_SYSTEM_ERROR,
-                       _("Failed to get certificate %1$s distinguished name: %2$s"),
-                       "[session]", gnutls_strerror(ret));
-        return NULL;
-    }
-
-    VIR_DEBUG("Peer DN is %s", dname);
-
-    if (virNetTLSContextCheckCertDN(cert, "[session]", hostname,
-                                    dname, x509dnACL) < 0)
-        return NULL;
-
-    /* !isServer, since on the client, we're validating the
-     * server's cert, and on the server, the client's cert
-     */
-    if (virNetTLSContextCheckCertBasicConstraints(cert, "[session]",
-                                                  !isServer, false) < 0)
-        return NULL;
-
-    if (virNetTLSContextCheckCertKeyUsage(cert, "[session]",
-                                          false) < 0)
-        return NULL;
-
-    /* !isServer - as above */
-    if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]",
-                                            !isServer) < 0)
-        return NULL;
-
-    return g_steal_pointer(&dname);
-}
 
 static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt,
                                             virNetTLSSession *sess)
@@ -1005,15 +487,16 @@ static int virNetTLSContextValidCertificate(virNetTLSContext *ctxt,
         }
 
         if (i == 0) {
-            if (!(sess->x509dname = virNetTLSContextCertValidate(cert,
-                                                                 sess->isServer,
-                                                                 sess->hostname,
-                                                                 ctxt->x509dnACL))) {
+            if (!(sess->x509dname = virNetTLSCertValidate(cert,
+                                                          sess->isServer,
+                                                          sess->hostname,
+                                                          ctxt->x509dnACL))) {
                 gnutls_x509_crt_deinit(cert);
                 goto authdeny;
             }
         } else {
-            if (virNetTLSContextCertValidateCA(cert, sess->isServer) < 0) {
+            if (virNetTLSCertValidateCA(cert,
+                                        sess->isServer) < 0) {
                 gnutls_x509_crt_deinit(cert);
                 goto authdeny;
             }
-- 
2.43.0




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux