[libvirt] [PATCH 04/15] Generic module for handling TLS encryption and x509 certs

Daniel P. Berrange berrange at redhat.com
Thu Dec 16 11:21:56 UTC 2010


This provides two modules for handling TLS

 * virNetTLSContext provides the process-wide state, in particular
   all the x509 credentials, DH params and x509 whitelists
 * virNetTLSSession provides the per-connection state, ie the
   TLS session itself.

The virNetTLSContext provides APIs for validating a TLS session's
x509 credentials. The virNetTLSSession includes APIs for performing
the initial TLS handshake and sending/recving encrypted data

* src/Makefile.am: Add to libvirt-net-rpc.la
* src/rpc/virnettlscontext.c, src/rpc/virnettlscontext.h: Generic
  TLS handling code
---
 po/POTFILES.in             |    1 +
 src/Makefile.am            |    6 +-
 src/rpc/virnettlscontext.c |  664 ++++++++++++++++++++++++++++++++++++++++++++
 src/rpc/virnettlscontext.h |   90 ++++++
 4 files changed, 760 insertions(+), 1 deletions(-)
 create mode 100644 src/rpc/virnettlscontext.c
 create mode 100644 src/rpc/virnettlscontext.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index bc45b3d..39cecdd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -60,6 +60,7 @@ src/qemu/qemu_security_dac.c
 src/remote/remote_driver.c
 src/rpc/virnetmessage.c
 src/rpc/virnetsocket.c
+src/rpc/virnettlscontext.c
 src/secret/secret_driver.c
 src/security/security_apparmor.c
 src/security/security_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index a19b304..10fce7d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1121,12 +1121,16 @@ EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE)
 noinst_LTLIBRARIES += libvirt-net-rpc.la
 
 libvirt_net_rpc_la_SOURCES = \
+	../daemon/event.c \
 	rpc/virnetmessage.h rpc/virnetmessage.c \
 	rpc/virnetprotocol.h rpc/virnetprotocol.c \
-	rpc/virnetsocket.h rpc/virnetsocket.c
+	rpc/virnetsocket.h rpc/virnetsocket.c \
+	rpc/virnettlscontext.h rpc/virnettlscontext.c
 libvirt_net_rpc_la_CFLAGS = \
+			$(GNUTLS_CFLAGS) \
 			$(AM_CFLAGS)
 libvirt_net_rpc_la_LDFLAGS = \
+			$(GNUTLS_LIBS) \
 			$(AM_LDFLAGS) \
 			$(CYGWIN_EXTRA_LDFLAGS) \
 			$(MINGW_EXTRA_LDFLAGS)
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
new file mode 100644
index 0000000..02da41a
--- /dev/null
+++ b/src/rpc/virnettlscontext.c
@@ -0,0 +1,664 @@
+/*
+ * virnettlscontext.c: TLS encryption/x509 handling
+ *
+ * Copyright (C) 2010 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "gnutls_1_0_compat.h"
+
+#include "virnettlscontext.h"
+
+#include "memory.h"
+#include "virterror_internal.h"
+#include "util.h"
+#include "logging.h"
+
+#define DH_BITS 1024
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+#define virNetError(code, ...)                                    \
+    virReportErrorHelper(NULL, VIR_FROM_RPC, code, __FILE__,      \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+struct _virNetTLSContext {
+    int refs;
+
+    gnutls_certificate_credentials_t x509cred;
+    gnutls_dh_params_t dhParams;
+
+    bool isServer;
+    bool requireValidCert;
+    const char *const*x509dnWhitelist;
+};
+
+struct _virNetTLSSession {
+    int refs;
+
+    bool handshakeComplete;
+
+    char *hostname;
+    gnutls_session_t session;
+    virNetTLSSessionWriteFunc writeFunc;
+    virNetTLSSessionReadFunc readFunc;
+    void *opaque;
+};
+
+
+static int
+virNetTLSContextCheckCertFile(const char *type, const char *file)
+{
+    if (access(file, R_OK) < 0) {
+        virReportSystemError(errno,
+                             _("Cannot read %s '%s'"),
+                             type, file);
+        return -1;
+    }
+    return 0;
+}
+
+
+static void virNetTLSLog(int level, const char *str) {
+    VIR_DEBUG("%d %s", level, str);
+}
+
+static virNetTLSContextPtr virNetTLSContextNew(const char *ca_file,
+                                               const char *crl_file,
+                                               const char *cert_file,
+                                               const char *key_file,
+                                               const char *const*x509dnWhitelist,
+                                               bool requireValidCert,
+                                               bool isServer)
+{
+    virNetTLSContextPtr ctxt;
+    char *gnutlsdebug;
+    int err;
+
+    if (VIR_ALLOC(ctxt) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    ctxt->refs = 1;
+
+    /* Initialise GnuTLS. */
+    gnutls_global_init();
+
+    if ((gnutlsdebug = getenv("LIBVIRT_GNUTLS_DEBUG")) != NULL) {
+        int val;
+        if (virStrToLong_i(gnutlsdebug, NULL, 10, &val) < 0)
+            val = 10;
+        gnutls_global_set_log_level(val);
+        gnutls_global_set_log_function(virNetTLSLog);
+        VIR_DEBUG0("Enabled GNUTLS debug");
+    }
+
+
+    err = gnutls_certificate_allocate_credentials(&ctxt->x509cred);
+    if (err) {
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Unable to allocate x509 credentials: %s"),
+                    gnutls_strerror (err));
+        goto error;
+    }
+
+    if (ca_file && ca_file[0] != '\0') {
+        if (virNetTLSContextCheckCertFile("CA certificate", ca_file) < 0)
+            goto error;
+
+        VIR_DEBUG("loading CA cert from %s", ca_file);
+        err = gnutls_certificate_set_x509_trust_file(ctxt->x509cred,
+                                                     ca_file,
+                                                     GNUTLS_X509_FMT_PEM);
+        if (err < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR,
+                        _("Unable to set x509 CA certificate: %s"),
+                        gnutls_strerror (err));
+            goto error;
+        }
+    }
+
+    if (crl_file && crl_file[0] != '\0') {
+        if (virNetTLSContextCheckCertFile("CA revocation list", crl_file) < 0)
+            goto error;
+
+        VIR_DEBUG("loading CRL from %s", crl_file);
+        err = gnutls_certificate_set_x509_crl_file(ctxt->x509cred,
+                                                   crl_file,
+                                                   GNUTLS_X509_FMT_PEM);
+        if (err < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR,
+                        _("Unable to set x509 certificate revocation list: %s"),
+                        gnutls_strerror (err));
+            goto error;
+        }
+    }
+
+    if (cert_file && cert_file[0] != '\0' && key_file && key_file[0] != '\0') {
+        if (virNetTLSContextCheckCertFile("server certificate", cert_file) < 0)
+            goto error;
+        if (virNetTLSContextCheckCertFile("server key", key_file) < 0)
+            goto error;
+        VIR_DEBUG("loading cert and key from %s and %s", cert_file, key_file);
+        err =
+            gnutls_certificate_set_x509_key_file(ctxt->x509cred,
+                                                 cert_file, key_file,
+                                                 GNUTLS_X509_FMT_PEM);
+        if (err < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR,
+                        _("Unable to set x509 key and certificate: %s"),
+                        gnutls_strerror (err));
+            goto error;
+        }
+    }
+
+    /* Generate Diffie Hellman parameters - for use with DHE
+     * kx algorithms. These should be discarded and regenerated
+     * once a day, once a week or once a month. Depending on the
+     * security requirements.
+     */
+    if (isServer) {
+        err = gnutls_dh_params_init(&ctxt->dhParams);
+        if (err < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR,
+                        _("Unable to initialize diffie-hellman parameters: %s"),
+                        gnutls_strerror (err));
+            goto error;
+        }
+        err = gnutls_dh_params_generate2(ctxt->dhParams, DH_BITS);
+        if (err < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR,
+                        _("Unable to generate diffie-hellman parameters: %s"),
+                        gnutls_strerror (err));
+            goto error;
+        }
+
+        gnutls_certificate_set_dh_params(ctxt->x509cred,
+                                         ctxt->dhParams);
+    }
+
+    ctxt->requireValidCert = requireValidCert;
+    ctxt->x509dnWhitelist = x509dnWhitelist;
+    ctxt->isServer = isServer;
+
+    return ctxt;
+
+error:
+    if (isServer)
+        gnutls_dh_params_deinit(ctxt->dhParams);
+    gnutls_certificate_free_credentials(ctxt->x509cred);
+    VIR_FREE(ctxt);
+    return NULL;
+}
+
+
+virNetTLSContextPtr virNetTLSContextNewServer(const char *ca_file,
+                                              const char *crl_file,
+                                              const char *cert_file,
+                                              const char *key_file,
+                                              const char *const*x509dnWhitelist,
+                                              bool requireValidCert)
+{
+    return virNetTLSContextNew(ca_file, crl_file, cert_file, key_file,
+                               x509dnWhitelist, requireValidCert, true);
+}
+
+virNetTLSContextPtr virNetTLSContextNewClient(const char *ca_file,
+                                              const char *cert_file,
+                                              const char *key_file,
+                                              bool requireValidCert)
+{
+    return virNetTLSContextNew(ca_file, NULL, cert_file, key_file,
+                               NULL, requireValidCert, false);
+}
+
+
+void virNetTLSContextRef(virNetTLSContextPtr ctxt)
+{
+    ctxt->refs++;
+}
+
+
+/* Check DN is on tls_allowed_dn_list. */
+static int
+virNetTLSContextCheckDN(virNetTLSContextPtr ctxt,
+                        const char *dname)
+{
+    const char *const*wildcards;
+
+    /* If the list is not set, allow any DN. */
+    wildcards = ctxt->x509dnWhitelist;
+    if (!wildcards)
+        return 1;
+
+    while (*wildcards) {
+        if (fnmatch (*wildcards, dname, 0) == 0)
+            return 1;
+        wildcards++;
+    }
+
+    /* Print the client's DN. */
+    DEBUG(_("Failed whitelist check for client DN '%s'"), dname);
+
+    return 0; // Not found.
+}
+
+static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt,
+                                            virNetTLSSessionPtr sess)
+{
+    int ret;
+    unsigned int status;
+    const gnutls_datum_t *certs;
+    unsigned int nCerts, i;
+    time_t now;
+    char name[256];
+    size_t namesize = sizeof name;
+
+    memset(name, 0, namesize);
+
+    if ((ret = gnutls_certificate_verify_peers2(sess->session, &status)) < 0){
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Unable to verify TLS peer: %s"),
+                    gnutls_strerror(ret));
+        goto authdeny;
+    }
+
+    if ((now = time(NULL)) == ((time_t)-1)) {
+        virReportSystemError(errno, "%s",
+                             _("cannot get current time"));
+        goto authfail;
+    }
+
+    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.");
+
+#ifndef GNUTLS_1_0_COMPAT
+        if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+            reason = _("The certificate uses an insecure algorithm");
+#endif
+
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Certificate failed validation: %s"),
+                    reason);
+        goto authdeny;
+    }
+
+    if (gnutls_certificate_type_get(sess->session) != GNUTLS_CRT_X509) {
+        virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                    _("Only x509 certificates are supported"));
+        goto authdeny;
+    }
+
+    if (!(certs = gnutls_certificate_get_peers(sess->session, &nCerts))) {
+        virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                    _("The certificate has no peers"));
+        goto authdeny;
+    }
+
+    for (i = 0; i < nCerts; i++) {
+        gnutls_x509_crt_t cert;
+
+        if (gnutls_x509_crt_init (&cert) < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                        _("Unable to initialize certificate"));
+            goto authfail;
+        }
+
+        if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+            virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                        _("Unable to load certificate"));
+            gnutls_x509_crt_deinit(cert);
+            goto authfail;
+        }
+
+        if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+            virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                        _("The client certificate has expired"));
+            gnutls_x509_crt_deinit(cert);
+            goto authdeny;
+        }
+
+        if (gnutls_x509_crt_get_activation_time(cert) > now) {
+            virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                        _("The client certificate is not yet active"));
+            gnutls_x509_crt_deinit(cert);
+            goto authdeny;
+        }
+
+        if (i == 0) {
+            ret = gnutls_x509_crt_get_dn(cert, name, &namesize);
+            if (ret != 0) {
+                virNetError(VIR_ERR_SYSTEM_ERROR,
+                            _("Failed to get certificate distinguished name: %s"),
+                            gnutls_strerror(ret));
+                gnutls_x509_crt_deinit(cert);
+                goto authfail;
+            }
+
+            if (!virNetTLSContextCheckDN(ctxt, name)) {
+                /* This is the most common error: make it informative. */
+                virNetError(VIR_ERR_SYSTEM_ERROR, "%s",
+                            _("Client's Distinguished Name is not on the list "
+                              "of allowed clients (tls_allowed_dn_list).  Use "
+                              "'certtool -i --infile clientcert.pem' to view the"
+                              "Distinguished Name field in the client certificate,"
+                              "or run this daemon with --verbose option."));
+                gnutls_x509_crt_deinit(cert);
+                goto authdeny;
+            }
+
+            if (sess->hostname &&
+                !gnutls_x509_crt_check_hostname(cert, sess->hostname)) {
+                virNetError(VIR_ERR_RPC,
+                            _("Certificate's owner does not match the hostname (%s)"),
+                            sess->hostname);
+                gnutls_x509_crt_deinit(cert);
+                goto authdeny;
+            }
+        }
+    }
+
+#if 0
+    PROBE(CLIENT_TLS_ALLOW, "fd=%d, name=%s",
+          virNetServerClientGetFD(client), name);
+#endif
+    return 0;
+
+authdeny:
+#if 0
+    PROBE(CLIENT_TLS_DENY, "fd=%d, name=%s",
+          virNetServerClientGetFD(client), name);
+#endif
+    return -1;
+
+authfail:
+#if 0
+    PROBE(CLIENT_TLS_FAIL, "fd=%d",
+          virNetServerClientGetFD(client));
+#endif
+    return -1;
+}
+
+int virNetTLSContextCheckCertificate(virNetTLSContextPtr ctxt,
+                                     virNetTLSSessionPtr sess) {
+    if (virNetTLSContextValidCertificate(ctxt, sess) < 0) {
+        if (ctxt->requireValidCert) {
+            virNetError(VIR_ERR_AUTH_FAILED, "%s",
+                        _("Failed to verify peer's certificate"));
+            return -1;
+        }
+        VIR_INFO0(_("Ignoring bad certificate at user request"));
+    }
+    return 0;
+}
+
+void virNetTLSContextFree(virNetTLSContextPtr ctxt)
+{
+    if (!ctxt)
+        return;
+
+    ctxt->refs--;
+    if (ctxt->refs > 0)
+        return;
+
+    gnutls_dh_params_deinit(ctxt->dhParams);
+    gnutls_certificate_free_credentials(ctxt->x509cred);
+    VIR_FREE(ctxt);
+}
+
+
+
+static ssize_t
+virNetTLSSessionPush(void *opaque, const void *buf, size_t len)
+{
+    virNetTLSSessionPtr sess = opaque;
+    if (!sess->writeFunc) {
+        VIR_WARN0("TLS session push with missing read function");
+        errno = EIO;
+        return -1;
+    };
+
+    return sess->writeFunc(buf, len, sess->opaque);
+}
+
+
+static ssize_t
+virNetTLSSessionPull(void *opaque, void *buf, size_t len)
+{
+    virNetTLSSessionPtr sess = opaque;
+    if (!sess->readFunc) {
+        VIR_WARN0("TLS session pull with missing read function");
+        errno = EIO;
+        return -1;
+    };
+
+    return sess->readFunc(buf, len, sess->opaque);
+}
+
+
+virNetTLSSessionPtr virNetTLSSessionNew(virNetTLSContextPtr ctxt,
+                                        const char *hostname)
+{
+    virNetTLSSessionPtr sess;
+    int err;
+    static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+
+    if (VIR_ALLOC(sess) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    sess->refs = 1;
+    if (hostname &&
+        !(sess->hostname = strdup(hostname))) {
+        virReportOOMError();
+        goto error;
+    }
+
+    if ((err = gnutls_init(&sess->session,
+                           ctxt->isServer ? GNUTLS_SERVER : GNUTLS_CLIENT)) != 0) {
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Failed to initialize TLS session: %s"),
+                    gnutls_strerror(err));
+        goto error;
+    }
+
+    /* avoid calling all the priority functions, since the defaults
+     * are adequate.
+     */
+    if ((err = gnutls_set_default_priority(sess->session)) != 0 ||
+        (err = gnutls_certificate_type_set_priority(sess->session,
+                                                    cert_type_priority))) {
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Failed to set TLS session priority %s"),
+                    gnutls_strerror(err));
+        goto error;
+    }
+
+    if ((err = gnutls_credentials_set(sess->session,
+                                      GNUTLS_CRD_CERTIFICATE,
+                                      ctxt->x509cred)) != 0) {
+        virNetError(VIR_ERR_SYSTEM_ERROR,
+                    _("Failed set TLS x509 credentials: %s"),
+                    gnutls_strerror(err));
+        goto error;
+    }
+
+    /* request client certificate if any.
+     */
+    if (ctxt->isServer) {
+        gnutls_certificate_server_set_request(sess->session, GNUTLS_CERT_REQUEST);
+
+        gnutls_dh_set_prime_bits(sess->session, DH_BITS);
+    }
+
+    gnutls_transport_set_ptr(sess->session, sess);
+    gnutls_transport_set_push_function(sess->session,
+                                       virNetTLSSessionPush);
+    gnutls_transport_set_pull_function(sess->session,
+                                       virNetTLSSessionPull);
+
+    return sess;
+
+error:
+    virNetTLSSessionFree(sess);
+    return NULL;
+}
+
+
+void virNetTLSSessionRef(virNetTLSSessionPtr sess)
+{
+    sess->refs++;
+}
+
+void virNetTLSSessionSetIOCallbacks(virNetTLSSessionPtr sess,
+                                    virNetTLSSessionWriteFunc writeFunc,
+                                    virNetTLSSessionReadFunc readFunc,
+                                    void *opaque)
+{
+    sess->writeFunc = writeFunc;
+    sess->readFunc = readFunc;
+    sess->opaque = opaque;
+}
+
+
+ssize_t virNetTLSSessionWrite(virNetTLSSessionPtr sess,
+                              const char *buf, size_t len)
+{
+    int ret;
+    ret = gnutls_record_send(sess->session, buf, len);
+
+    switch (ret) {
+    case GNUTLS_E_AGAIN:
+        errno = EAGAIN;
+        break;
+    case GNUTLS_E_INTERRUPTED:
+        errno = EINTR;
+        break;
+    case 0:
+        break;
+    default:
+        errno = EIO;
+    }
+
+    return ret >= 0 ? ret : -1;
+}
+
+ssize_t virNetTLSSessionRead(virNetTLSSessionPtr sess,
+                             char *buf, size_t len)
+{
+    int ret;
+
+    ret = gnutls_record_recv(sess->session, buf, len);
+
+    switch (ret) {
+    case GNUTLS_E_AGAIN:
+        errno = EAGAIN;
+        break;
+    case GNUTLS_E_INTERRUPTED:
+        errno = EINTR;
+        break;
+    case 0:
+        break;
+    default:
+        errno = EIO;
+    }
+
+    return ret >= 0 ? ret : -1;
+}
+
+int virNetTLSSessionHandshake(virNetTLSSessionPtr sess)
+{
+    VIR_DEBUG("sess=%p", sess);
+    int ret = gnutls_handshake(sess->session);
+
+    if (ret == 0) {
+        sess->handshakeComplete = true;
+        VIR_DEBUG0("Handshake is complete");
+        return 0;
+    }
+    if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
+        return 1;
+
+#if 0
+    PROBE(CLIENT_TLS_FAIL, "fd=%d",
+          virNetServerClientGetFD(client));
+#endif
+
+    virNetError(VIR_ERR_AUTH_FAILED,
+                _("TLS handshake failed %s"),
+                gnutls_strerror (ret));
+    return -1;
+}
+
+virNetTLSSessionHandshakeStatus
+virNetTLSSessionGetHandshakeStatus(virNetTLSSessionPtr sess)
+{
+    if (sess->handshakeComplete)
+        return VIR_NET_TLS_HANDSHAKE_COMPLETE;
+    else if (gnutls_record_get_direction (sess->session) == 0)
+        return VIR_NET_TLS_HANDSHAKE_RECVING;
+    else
+        return VIR_NET_TLS_HANDSHAKE_SENDING;
+}
+
+int virNetTLSSessionGetKeySize(virNetTLSSessionPtr sess)
+{
+    gnutls_cipher_algorithm_t cipher;
+    int ssf;
+
+    cipher = gnutls_cipher_get(sess->session);
+    if (!(ssf = gnutls_cipher_get_key_size(cipher))) {
+        virNetError(VIR_ERR_INTERNAL_ERROR, "%s",
+                    _("invalid cipher size for TLS session"));
+        return -1;
+    }
+
+    return ssf;
+}
+
+
+void virNetTLSSessionFree(virNetTLSSessionPtr sess)
+{
+    if (!sess)
+        return;
+
+    sess->refs--;
+    if (sess->refs > 0)
+        return;
+
+    VIR_FREE(sess->hostname);
+    gnutls_deinit(sess->session);
+    VIR_FREE(sess);
+}
diff --git a/src/rpc/virnettlscontext.h b/src/rpc/virnettlscontext.h
new file mode 100644
index 0000000..88db141
--- /dev/null
+++ b/src/rpc/virnettlscontext.h
@@ -0,0 +1,90 @@
+/*
+ * virnettlscontext.h: TLS encryption/x509 handling
+ *
+ * Copyright (C) 2010 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __VIR_NET_TLS_CONTEXT_H__
+# define __VIR_NET_TLS_CONTEXT_H__
+
+# include <stdbool.h>
+# include <sys/types.h>
+
+typedef struct _virNetTLSContext virNetTLSContext;
+typedef virNetTLSContext *virNetTLSContextPtr;
+
+typedef struct _virNetTLSSession virNetTLSSession;
+typedef virNetTLSSession *virNetTLSSessionPtr;
+
+
+virNetTLSContextPtr virNetTLSContextNewServer(const char *ca_file,
+                                              const char *crl_file,
+                                              const char *cert_file,
+                                              const char *key_file,
+                                              const char *const*x509dnWhitelist,
+                                              bool requireValidCert);
+
+virNetTLSContextPtr virNetTLSContextNewClient(const char *ca_file,
+                                              const char *cert_file,
+                                              const char *key_file,
+                                              bool requireValidCert);
+
+void virNetTLSContextRef(virNetTLSContextPtr ctxt);
+
+int virNetTLSContextCheckCertificate(virNetTLSContextPtr ctxt,
+                                     virNetTLSSessionPtr sess);
+
+void virNetTLSContextFree(virNetTLSContextPtr ctxt);
+
+
+typedef ssize_t (*virNetTLSSessionWriteFunc)(const char *buf, size_t len,
+                                             void *opaque);
+typedef ssize_t (*virNetTLSSessionReadFunc)(char *buf, size_t len,
+                                            void *opaque);
+
+virNetTLSSessionPtr virNetTLSSessionNew(virNetTLSContextPtr ctxt,
+                                        const char *hostname);
+
+void virNetTLSSessionSetIOCallbacks(virNetTLSSessionPtr sess,
+                                    virNetTLSSessionWriteFunc writeFunc,
+                                    virNetTLSSessionReadFunc readFunc,
+                                    void *opaque);
+
+void virNetTLSSessionRef(virNetTLSSessionPtr sess);
+
+ssize_t virNetTLSSessionWrite(virNetTLSSessionPtr sess,
+                              const char *buf, size_t len);
+ssize_t virNetTLSSessionRead(virNetTLSSessionPtr sess,
+                             char *buf, size_t len);
+
+int virNetTLSSessionHandshake(virNetTLSSessionPtr sess);
+
+typedef enum {
+    VIR_NET_TLS_HANDSHAKE_COMPLETE,
+    VIR_NET_TLS_HANDSHAKE_SENDING,
+    VIR_NET_TLS_HANDSHAKE_RECVING,
+} virNetTLSSessionHandshakeStatus;
+
+virNetTLSSessionHandshakeStatus
+virNetTLSSessionGetHandshakeStatus(virNetTLSSessionPtr sess);
+
+int virNetTLSSessionGetKeySize(virNetTLSSessionPtr sess);
+
+void virNetTLSSessionFree(virNetTLSSessionPtr sess);
+
+
+#endif
-- 
1.7.2.3




More information about the libvir-list mailing list