aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-09-15 15:42:58 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-09-15 15:42:58 +0100
commit619622424dba749feef752d76d79ef2569f7f250 (patch)
treeb5b3e718e5075781da039fe3f6966aaec4922cdd
parent1078f5db8ada5097600443838492ef07103790c2 (diff)
parent3e305e4a4752f70c0b5c3cf5b43ec957881714f7 (diff)
Merge remote-tracking branch 'remotes/berrange/tags/vnc-crypto-v9-for-upstream' into staging
Merge vnc-crypto-v9 # gpg: Signature made Tue 15 Sep 2015 15:32:38 BST using RSA key ID 15104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" * remotes/berrange/tags/vnc-crypto-v9-for-upstream: ui: convert VNC server to use QCryptoTLSSession ui: fix return type for VNC I/O functions to be ssize_t crypto: introduce new module for handling TLS sessions crypto: add sanity checking of TLS x509 credentials crypto: introduce new module for TLS x509 credentials crypto: introduce new module for TLS anonymous credentials crypto: introduce new base module for TLS credentials qom: allow QOM to be linked into tools binaries crypto: move crypto objects out of libqemuutil.la tests: remove repetition in unit test object deps qapi: allow override of default enum prefix naming Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--Makefile11
-rw-r--r--Makefile.objs11
-rw-r--r--Makefile.target6
-rwxr-xr-xconfigure53
-rw-r--r--crypto/Makefile.objs17
-rw-r--r--crypto/tlscreds.c251
-rw-r--r--crypto/tlscredsanon.c223
-rw-r--r--crypto/tlscredspriv.h42
-rw-r--r--crypto/tlscredsx509.c809
-rw-r--r--crypto/tlssession.c574
-rw-r--r--docs/qapi-code-gen.txt8
-rw-r--r--include/crypto/tlscreds.h68
-rw-r--r--include/crypto/tlscredsanon.h112
-rw-r--r--include/crypto/tlscredsx509.h113
-rw-r--r--include/crypto/tlssession.h322
-rw-r--r--qapi-schema.json3
-rw-r--r--qapi/crypto.json21
-rw-r--r--qemu-options.hx75
-rw-r--r--qom/Makefile.objs7
-rw-r--r--scripts/qapi-types.py16
-rw-r--r--scripts/qapi.py10
-rw-r--r--tests/.gitignore7
-rw-r--r--tests/Makefile106
-rw-r--r--tests/crypto-tls-x509-helpers.c485
-rw-r--r--tests/crypto-tls-x509-helpers.h133
-rw-r--r--tests/pkix_asn1_tab.c1104
-rw-r--r--tests/qapi-schema/enum-bad-prefix.err1
-rw-r--r--tests/qapi-schema/enum-bad-prefix.exit1
-rw-r--r--tests/qapi-schema/enum-bad-prefix.json2
-rw-r--r--tests/qapi-schema/enum-bad-prefix.out0
-rw-r--r--tests/qapi-schema/qapi-schema-test.json5
-rw-r--r--tests/qapi-schema/qapi-schema-test.out2
-rw-r--r--tests/test-crypto-tlscredsx509.c731
-rw-r--r--tests/test-crypto-tlssession.c535
-rw-r--r--trace-events18
-rw-r--r--ui/Makefile.objs2
-rw-r--r--ui/vnc-auth-sasl.c36
-rw-r--r--ui/vnc-auth-vencrypt.c80
-rw-r--r--ui/vnc-tls.c474
-rw-r--r--ui/vnc-tls.h69
-rw-r--r--ui/vnc-ws.c84
-rw-r--r--ui/vnc-ws.h2
-rw-r--r--ui/vnc.c362
-rw-r--r--ui/vnc.h21
44 files changed, 6132 insertions, 880 deletions
diff --git a/Makefile b/Makefile
index 9ce3972d84..36943da15c 100644
--- a/Makefile
+++ b/Makefile
@@ -152,6 +152,9 @@ dummy := $(call unnest-vars,, \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
+ crypto-obj-y \
+ crypto-aes-obj-y \
+ qom-obj-y \
common-obj-y \
common-obj-m)
@@ -173,6 +176,8 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
+$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
+$(SOFTMMU_SUBDIR_RULES): $(qom-obj-y)
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
subdir-%:
@@ -227,9 +232,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)'
qemu-img.o: qemu-img-cmds.h
-qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a
-qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
-qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a
+qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
+qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
diff --git a/Makefile.objs b/Makefile.objs
index f094eff15e..3df2efca1c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,6 @@
# Common libraries for tools and emulators
stub-obj-y = stubs/
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
-util-obj-y += crypto/
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
@@ -21,6 +20,16 @@ block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
block-obj-m = block/
+#######################################################################
+# crypto-obj-y is code used by both qemu system emulation and qemu-img
+
+crypto-obj-y = crypto/
+crypto-aes-obj-y = crypto/
+
+#######################################################################
+# qom-obj-y is code used by both qemu system emulation and qemu-img
+
+qom-obj-y = qom/
######################################################################
# smartcard
diff --git a/Makefile.target b/Makefile.target
index dc322942f4..edd136fed6 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -170,12 +170,18 @@ target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \
block-obj-y \
block-obj-m \
+ crypto-obj-y \
+ crypto-aes-obj-y \
+ qom-obj-y \
common-obj-y \
common-obj-m)
target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
+all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
+all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
$(QEMU_PROG_BUILD): config-devices.mak
diff --git a/configure b/configure
index d7c24cdaa0..018ba0b78f 100755
--- a/configure
+++ b/configure
@@ -242,7 +242,6 @@ vnc="yes"
sparse="no"
uuid=""
vde=""
-vnc_tls=""
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
@@ -416,6 +415,9 @@ if test "$debug_info" = "yes"; then
LDFLAGS="-g $LDFLAGS"
fi
+test_cflags=""
+test_libs=""
+
# make source path absolute
source_path=`cd "$source_path"; pwd`
@@ -880,10 +882,6 @@ for opt do
;;
--disable-strip) strip_opt="no"
;;
- --disable-vnc-tls) vnc_tls="no"
- ;;
- --enable-vnc-tls) vnc_tls="yes"
- ;;
--disable-vnc-sasl) vnc_sasl="no"
;;
--enable-vnc-sasl) vnc_sasl="yes"
@@ -2249,6 +2247,19 @@ if test "$gnutls_nettle" != "no"; then
fi
fi
+##########################################
+# libtasn1 - only for the TLS creds/session test suite
+
+tasn1=yes
+if $pkg_config --exists "libtasn1"; then
+ tasn1_cflags=`$pkg_config --cflags libtasn1`
+ tasn1_libs=`$pkg_config --libs libtasn1`
+ test_cflags="$test_cflags $tasn1_cflags"
+ test_libs="$test_libs $tasn1_libs"
+else
+ tasn1=no
+fi
+
##########################################
# VTE probe
@@ -2393,28 +2404,6 @@ EOF
fi
fi
-##########################################
-# VNC TLS/WS detection
-if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then
- cat > $TMPC <<EOF
-#include <gnutls/gnutls.h>
-int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
-EOF
- vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
- vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
- if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then
- if test "$vnc_tls" != "no" ; then
- vnc_tls=yes
- fi
- libs_softmmu="$vnc_tls_libs $libs_softmmu"
- QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
- else
- if test "$vnc_tls" = "yes" ; then
- feature_not_found "vnc-tls" "Install gnutls devel"
- fi
- vnc_tls=no
- fi
-fi
##########################################
# VNC SASL detection
@@ -4574,6 +4563,7 @@ echo "GNUTLS support $gnutls"
echo "GNUTLS hash $gnutls_hash"
echo "GNUTLS gcrypt $gnutls_gcrypt"
echo "GNUTLS nettle $gnutls_nettle ${gnutls_nettle+($nettle_version)}"
+echo "libtasn1 $tasn1"
echo "VTE support $vte"
echo "curses support $curses"
echo "curl support $curl"
@@ -4584,7 +4574,6 @@ echo "Block whitelist (ro) $block_drv_ro_whitelist"
echo "VirtFS support $virtfs"
echo "VNC support $vnc"
if test "$vnc" = "yes" ; then
- echo "VNC TLS support $vnc_tls"
echo "VNC SASL support $vnc_sasl"
echo "VNC JPEG support $vnc_jpeg"
echo "VNC PNG support $vnc_png"
@@ -4793,9 +4782,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
if test "$vnc" = "yes" ; then
echo "CONFIG_VNC=y" >> $config_host_mak
fi
-if test "$vnc_tls" = "yes" ; then
- echo "CONFIG_VNC_TLS=y" >> $config_host_mak
-fi
if test "$vnc_sasl" = "yes" ; then
echo "CONFIG_VNC_SASL=y" >> $config_host_mak
fi
@@ -4945,6 +4931,9 @@ if test "$gnutls_nettle" = "yes" ; then
echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak
echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak
fi
+if test "$tasn1" = "yes" ; then
+ echo "CONFIG_TASN1=y" >> $config_host_mak
+fi
if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
@@ -5268,6 +5257,8 @@ echo "EXESUF=$EXESUF" >> $config_host_mak
echo "DSOSUF=$DSOSUF" >> $config_host_mak
echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak
echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
+echo "TEST_LIBS=$test_libs" >> $config_host_mak
+echo "TEST_CFLAGS=$test_cflags" >> $config_host_mak
echo "POD2MAN=$POD2MAN" >> $config_host_mak
echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
if test "$gcov" = "yes" ; then
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs
index b05013831b..b2a0e0b38e 100644
--- a/crypto/Makefile.objs
+++ b/crypto/Makefile.objs
@@ -1,5 +1,12 @@
-util-obj-y += init.o
-util-obj-y += hash.o
-util-obj-y += aes.o
-util-obj-y += desrfb.o
-util-obj-y += cipher.o
+crypto-obj-y = init.o
+crypto-obj-y += hash.o
+crypto-obj-y += aes.o
+crypto-obj-y += desrfb.o
+crypto-obj-y += cipher.o
+crypto-obj-y += tlscreds.o
+crypto-obj-y += tlscredsanon.o
+crypto-obj-y += tlscredsx509.o
+crypto-obj-y += tlssession.o
+
+# Let the userspace emulators avoid linking gnutls/etc
+crypto-aes-obj-y = aes.o
diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
new file mode 100644
index 0000000000..5ec982c6ee
--- /dev/null
+++ b/crypto/tlscreds.c
@@ -0,0 +1,251 @@
+/*
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2015 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 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 "crypto/tlscredspriv.h"
+#include "trace.h"
+
+#define DH_BITS 2048
+
+#ifdef CONFIG_GNUTLS
+int
+qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
+ const char *filename,
+ gnutls_dh_params_t *dh_params,
+ Error **errp)
+{
+ int ret;
+
+ trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>");
+
+ if (filename == NULL) {
+ ret = gnutls_dh_params_init(dh_params);
+ if (ret < 0) {
+ error_setg(errp, "Unable to initialize DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ ret = gnutls_dh_params_generate2(*dh_params, DH_BITS);
+ if (ret < 0) {
+ gnutls_dh_params_deinit(*dh_params);
+ *dh_params = NULL;
+ error_setg(errp, "Unable to generate DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ } else {
+ GError *gerr = NULL;
+ gchar *contents;
+ gsize len;
+ gnutls_datum_t data;
+ if (!g_file_get_contents(filename,
+ &contents,
+ &len,
+ &gerr)) {
+
+ error_setg(errp, "%s", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+ data.data = (unsigned char *)contents;
+ data.size = len;
+ ret = gnutls_dh_params_init(dh_params);
+ if (ret < 0) {
+ g_free(contents);
+ error_setg(errp, "Unable to initialize DH parameters: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+ ret = gnutls_dh_params_import_pkcs3(*dh_params,
+ &data,
+ GNUTLS_X509_FMT_PEM);
+ g_free(contents);
+ if (ret < 0) {
+ gnutls_dh_params_deinit(*dh_params);
+ *dh_params = NULL;
+ error_setg(errp, "Unable to load DH parameters from %s: %s",
+ filename, gnutls_strerror(ret));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
+ const char *filename,
+ bool required,
+ char **cred,
+ Error **errp)
+{
+ struct stat sb;
+ int ret = -1;
+
+ if (!creds->dir) {
+ if (required) {
+ error_setg(errp, "Missing 'dir' property value");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ *cred = g_strdup_printf("%s/%s", creds->dir, filename);
+
+ if (stat(*cred, &sb) < 0) {
+ if (errno == ENOENT && !required) {
+ ret = 0;
+ } else {
+ error_setg_errno(errp, errno,
+ "Unable to access credentials %s",
+ *cred);
+ }
+ g_free(*cred);
+ *cred = NULL;
+ goto cleanup;
+ }
+
+ trace_qcrypto_tls_creds_get_path(creds, filename,
+ *cred ? *cred : "<none>");
+ ret = 0;
+ cleanup:
+ return ret;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_prop_set_verify(Object *obj,
+ bool value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->verifyPeer = value;
+}
+
+
+static bool
+qcrypto_tls_creds_prop_get_verify(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return creds->verifyPeer;
+}
+
+
+static void
+qcrypto_tls_creds_prop_set_dir(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->dir = g_strdup(value);
+}
+
+
+static char *
+qcrypto_tls_creds_prop_get_dir(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return g_strdup(creds->dir);
+}
+
+
+static void
+qcrypto_tls_creds_prop_set_endpoint(Object *obj,
+ int value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->endpoint = value;
+}
+
+
+static int
+qcrypto_tls_creds_prop_get_endpoint(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ return creds->endpoint;
+}
+
+
+static void
+qcrypto_tls_creds_init(Object *obj)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->verifyPeer = true;
+
+ object_property_add_bool(obj, "verify-peer",
+ qcrypto_tls_creds_prop_get_verify,
+ qcrypto_tls_creds_prop_set_verify,
+ NULL);
+ object_property_add_str(obj, "dir",
+ qcrypto_tls_creds_prop_get_dir,
+ qcrypto_tls_creds_prop_set_dir,
+ NULL);
+ object_property_add_enum(obj, "endpoint",
+ "QCryptoTLSCredsEndpoint",
+ QCryptoTLSCredsEndpoint_lookup,
+ qcrypto_tls_creds_prop_get_endpoint,
+ qcrypto_tls_creds_prop_set_endpoint,
+ NULL);
+}
+
+
+static void
+qcrypto_tls_creds_finalize(Object *obj)
+{
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ g_free(creds->dir);
+}
+
+
+static const TypeInfo qcrypto_tls_creds_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QCRYPTO_TLS_CREDS,
+ .instance_size = sizeof(QCryptoTLSCreds),
+ .instance_init = qcrypto_tls_creds_init,
+ .instance_finalize = qcrypto_tls_creds_finalize,
+ .class_size = sizeof(QCryptoTLSCredsClass),
+ .abstract = true,
+};
+
+
+static void
+qcrypto_tls_creds_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_info);
+}
+
+
+type_init(qcrypto_tls_creds_register_types);
diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c
new file mode 100644
index 0000000000..c3fcdaff06
--- /dev/null
+++ b/crypto/tlscredsanon.c
@@ -0,0 +1,223 @@
+/*
+ * QEMU crypto TLS anonymous credential support
+ *
+ * Copyright (c) 2015 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 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 "crypto/tlscredsanon.h"
+#include "crypto/tlscredspriv.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static int
+qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds,
+ Error **errp)
+{
+ char *dhparams = NULL;
+ int ret;
+ int rv = -1;
+
+ trace_qcrypto_tls_creds_anon_load(creds,
+ creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_DH_PARAMS,
+ false, &dhparams, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = gnutls_anon_allocate_server_credentials(&creds->data.server);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: %s",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
+ &creds->parent_obj.dh_params,
+ errp) < 0) {
+ goto cleanup;
+ }
+
+ gnutls_anon_set_server_dh_params(creds->data.server,
+ creds->parent_obj.dh_params);
+ } else {
+ ret = gnutls_anon_allocate_client_credentials(&creds->data.client);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: %s",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ rv = 0;
+ cleanup:
+ g_free(dhparams);
+ return rv;
+}
+
+
+static void
+qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds)
+{
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+ if (creds->data.client) {
+ gnutls_anon_free_client_credentials(creds->data.client);
+ creds->data.client = NULL;
+ }
+ } else {
+ if (creds->data.server) {
+ gnutls_anon_free_server_credentials(creds->data.server);
+ creds->data.server = NULL;
+ }
+ }
+ if (creds->parent_obj.dh_params) {
+ gnutls_dh_params_deinit(creds->parent_obj.dh_params);
+ creds->parent_obj.dh_params = NULL;
+ }
+}
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS credentials support requires GNUTLS");
+}
+
+
+static void
+qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED)
+{
+ /* nada */
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ if (value) {
+ qcrypto_tls_creds_anon_load(creds, errp);
+ } else {
+ qcrypto_tls_creds_anon_unload(creds);
+ }
+}
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static bool
+qcrypto_tls_creds_anon_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ return creds->data.server != NULL;
+ } else {
+ return creds->data.client != NULL;
+ }
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static bool
+qcrypto_tls_creds_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED,
+ Error **errp G_GNUC_UNUSED)
+{
+ return false;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_tls_creds_anon_init(Object *obj)
+{
+ object_property_add_bool(obj, "loaded",
+ qcrypto_tls_creds_anon_prop_get_loaded,
+ qcrypto_tls_creds_anon_prop_set_loaded,
+ NULL);
+}
+
+
+static void
+qcrypto_tls_creds_anon_finalize(Object *obj)
+{
+ QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj);
+
+ qcrypto_tls_creds_anon_unload(creds);
+}
+
+
+static void
+qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_tls_creds_anon_complete;
+}
+
+
+static const TypeInfo qcrypto_tls_creds_anon_info = {
+ .parent = TYPE_QCRYPTO_TLS_CREDS,
+ .name = TYPE_QCRYPTO_TLS_CREDS_ANON,
+ .instance_size = sizeof(QCryptoTLSCredsAnon),
+ .instance_init = qcrypto_tls_creds_anon_init,
+ .instance_finalize = qcrypto_tls_creds_anon_finalize,
+ .class_size = sizeof(QCryptoTLSCredsAnonClass),
+ .class_init = qcrypto_tls_creds_anon_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_tls_creds_anon_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_anon_info);
+}
+
+
+type_init(qcrypto_tls_creds_anon_register_types);
diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
new file mode 100644
index 0000000000..9222be4a9e
--- /dev/null
+++ b/crypto/tlscredspriv.h
@@ -0,0 +1,42 @@
+/*
+ * QEMU crypto TLS credential support private helpers
+ *
+ * Copyright (c) 2015 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 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_TLSCRED_PRIV_H__
+#define QCRYPTO_TLSCRED_PRIV_H__
+
+#include "crypto/tlscreds.h"
+
+#ifdef CONFIG_GNUTLS
+
+int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds,
+ const char *filename,
+ bool required,
+ char **cred,
+ Error **errp);
+
+int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
+ const char *filename,
+ gnutls_dh_params_t *dh_params,
+ Error **errp);
+
+#endif
+
+#endif /* QCRYPTO_TLSCRED_PRIV_H__ */
+
diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
new file mode 100644
index 0000000000..dc46bc40f7
--- /dev/null
+++ b/crypto/tlscredsx509.c
@@ -0,0 +1,809 @@
+/*
+ * QEMU crypto TLS x509 credential support
+ *
+ * Copyright (c) 2015 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 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 "crypto/tlscredsx509.h"
+#include "crypto/tlscredspriv.h"
+#include "qom/object_interfaces.h"
+#include "trace.h"
+
+
+#ifdef CONFIG_GNUTLS
+
+#include <gnutls/x509.h>
+
+
+static int
+qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ time_t now = time(NULL);
+
+ if (now == ((time_t)-1)) {
+ error_setg_errno(errp, errno, "cannot get current time");
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+ error_setg(errp,
+ (isCA ?
+ "The CA certificate %s has expired" :
+ (isServer ?
+ "The server certificate %s has expired" :
+ "The client certificate %s has expired")),
+ certFile);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp,
+ (isCA ?
+ "The CA certificate %s is not yet active" :
+ (isServer ?
+ "The server certificate %s is not yet active" :
+ "The client certificate %s is not yet active")),
+ certFile);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#if LIBGNUTLS_VERSION_NUMBER >= 2
+/*
+ * The gnutls_x509_crt_get_basic_constraints function isn't
+ * available in GNUTLS 1.0.x branches. This isn't critical
+ * though, since gnutls_certificate_verify_peers2 will do
+ * pretty much the same check at runtime, so we can just
+ * disable this code
+ */
+static int
+qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ int status;
+
+ status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL);
+ trace_qcrypto_tls_creds_x509_check_basic_constraints(
+ creds, certFile, status);
+
+ if (status > 0) { /* It is a CA cert */
+ if (!isCA) {
+ error_setg(errp, isServer ?
+ "The certificate %s basic constraints show a CA, "
+ "but we need one for a server" :
+ "The certificate %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) {
+ error_setg(errp,
+ "The certificate %s basic constraints do not "
+ "show a CA",
+ certFile);
+ return -1;
+ }
+ } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ /* Missing basicConstraints */
+ if (isCA) {
+ error_setg(errp,
+ "The certificate %s is missing basic constraints "
+ "for a CA",
+ certFile);
+ return -1;
+ }
+ } else { /* General error */
+ error_setg(errp,
+ "Unable to query certificate %s basic constraints: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int
+qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isCA,
+ Error **errp)
+{
+ int status;
+ unsigned int usage = 0;
+ unsigned int critical = 0;
+
+ status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
+ trace_qcrypto_tls_creds_x509_check_key_usage(
+ creds, 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 {
+ error_setg(errp,
+ "Unable to query certificate %s key usage: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+ }
+
+ if (isCA) {
+ if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit "
+ "certificate signing", certFile);
+ return -1;
+ }
+ }
+ } else {
+ if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit digital "
+ "signature", certFile);
+ return -1;
+ }
+ }
+ if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s usage does not permit key "
+ "encipherment", certFile);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ Error **errp)
+{
+ 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) {
+
+ /* 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) {
+ error_setg(errp,
+ "Unable to query certificate %s key purpose: %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) {
+ trace_qcrypto_tls_creds_x509_check_key_purpose(
+ creds, certFile, status, "<none>", purposeCritical);
+ g_free(buffer);
+ error_setg(errp,
+ "Unable to query certificate %s key purpose: %s",
+ certFile, gnutls_strerror(status));
+ return -1;
+ }
+ trace_qcrypto_tls_creds_x509_check_key_purpose(
+ creds, certFile, status, buffer, purposeCritical);
+ if (purposeCritical) {
+ critical = true;
+ }
+
+ if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
+ allowServer = true;
+ } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
+ allowClient = true;
+ } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) {
+ allowServer = allowClient = true;
+ }
+
+ g_free(buffer);
+ }
+
+ if (isServer) {
+ if (!allowServer) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s purpose does not allow "
+ "use with a TLS server", certFile);
+ return -1;
+ }
+ }
+ } else {
+ if (!allowClient) {
+ if (critical) {
+ error_setg(errp,
+ "Certificate %s purpose does not allow use "
+ "with a TLS client", certFile);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
+ gnutls_x509_crt_t cert,
+ const char *certFile,
+ bool isServer,
+ bool isCA,
+ Error **errp)
+{
+ if (qcrypto_tls_creds_check_cert_times(cert, certFile,
+ isServer, isCA,
+ errp) < 0) {
+ return -1;
+ }
+
+#if LIBGNUTLS_VERSION_NUMBER >= 2
+ if (qcrypto_tls_creds_check_cert_basic_constraints(creds,
+ cert, certFile,
+ isServer, isCA,
+ errp) < 0) {
+ return -1;
+ }
+#endif
+
+ if (qcrypto_tls_creds_check_cert_key_usage(creds,
+ cert, certFile,
+ isCA, errp) < 0) {
+ return -1;
+ }
+
+ if (!isCA &&
+ qcrypto_tls_creds_check_cert_key_purpose(creds,
+ cert, certFile,
+ isServer, errp) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert,
+ const char *certFile,
+ gnutls_x509_crt_t *cacerts,
+ size_t ncacerts,
+ const char *cacertFile,
+ bool isServer,
+ Error **errp)
+{
+ unsigned int status;
+
+ if (gnutls_x509_crt_list_verify(&cert, 1,
+ cacerts, ncacerts,
+ NULL, 0,
+ 0, &status) < 0) {
+ error_setg(errp, isServer ?
+ "Unable to verify server certificate %s against "
+ "CA certificate %s" :
+ "Unable to verify client certificate %s against "
+ "CA certificate %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";
+ }
+
+#ifndef GNUTLS_1_0_COMPAT
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+ reason = "The certificate uses an insecure algorithm";
+ }
+#endif
+
+ error_setg(errp,
+ "Our own certificate %s failed validation against %s: %s",
+ certFile, cacertFile, reason);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static gnutls_x509_crt_t
+qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds,
+ const char *certFile,
+ bool isServer,
+ Error **errp)
+{
+ gnutls_datum_t data;
+ gnutls_x509_crt_t cert = NULL;
+ char *buf = NULL;
+ gsize buflen;
+ GError *gerr;
+ int ret = -1;
+
+ trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile);
+
+ if (gnutls_x509_crt_init(&cert) < 0) {
+ error_setg(errp, "Unable to initialize certificate");
+ goto cleanup;
+ }
+
+ if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
+ error_setg(errp, "Cannot load CA cert list %s: %s",
+ certFile, gerr->message);
+ g_error_free(gerr);
+ goto cleanup;
+ }
+
+ data.data = (unsigned char *)buf;
+ data.size = strlen(buf);
+
+ if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) {
+ error_setg(errp, isServer ?
+ "Unable to import server certificate %s" :
+ "Unable to import client certificate %s",
+ certFile);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret != 0) {
+ gnutls_x509_crt_deinit(cert);
+ cert = NULL;
+ }
+ g_free(buf);
+ return cert;
+}
+
+
+static int
+qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds,
+ const char *certFile,
+ gnutls_x509_crt_t *certs,
+ unsigned int certMax,
+ size_t *ncerts,
+ Error **errp)
+{
+ gnutls_datum_t data;
+ char *buf = NULL;
+ gsize buflen;
+ int ret = -1;
+ GError *gerr = NULL;
+
+ *ncerts = 0;
+ trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile);
+
+ if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) {
+ error_setg(errp, "Cannot load CA cert list %s: %s",
+ certFile, gerr->message);
+ g_error_free(gerr);
+ goto cleanup;
+ }
+
+ data.data = (unsigned char *)buf;
+ data.size = strlen(buf);
+
+ if (gnutls_x509_crt_list_import(certs, &certMax, &data,
+ GNUTLS_X509_FMT_PEM, 0) < 0) {
+ error_setg(errp,
+ "Unable to import CA certificate list %s",
+ certFile);
+ goto cleanup;
+ }
+ *ncerts = certMax;
+
+ ret = 0;
+
+ cleanup:
+ g_free(buf);
+ return ret;
+}
+
+
+#define MAX_CERTS 16
+static int
+qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds,
+ bool isServer,
+ const char *cacertFile,
+ const char *certFile,
+ Error **errp)
+{
+ gnutls_x509_crt_t cert = NULL;
+ gnutls_x509_crt_t cacerts[MAX_CERTS];
+ size_t ncacerts = 0;
+ size_t i;
+ int ret = -1;
+
+ memset(cacerts, 0, sizeof(cacerts));
+ if (access(certFile, R_OK) == 0) {
+ cert = qcrypto_tls_creds_load_cert(creds,
+ certFile, isServer,
+ errp);
+ if (!cert) {
+ goto cleanup;
+ }
+ }
+ if (access(cacertFile, R_OK) == 0) {
+ if (qcrypto_tls_creds_load_ca_cert_list(creds,
+ cacertFile, cacerts,
+ MAX_CERTS, &ncacerts,
+ errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (cert &&
+ qcrypto_tls_creds_check_cert(creds,
+ cert, certFile, isServer,
+ false, errp) < 0) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < ncacerts; i++) {
+ if (qcrypto_tls_creds_check_cert(creds,
+ cacerts[i], cacertFile,
+ isServer, true, errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (cert && ncacerts &&
+ qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts,
+ ncacerts, cacertFile,
+ isServer, errp) < 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
+qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
+ Error **errp)
+{
+ char *cacert = NULL, *cacrl = NULL, *cert = NULL,
+ *key = NULL, *dhparams = NULL;
+ int ret;
+ int rv = -1;
+
+ trace_qcrypto_tls_creds_x509_load(creds,
+ creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CRL,
+ false, &cacrl, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
+ true, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
+ true, &key, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_DH_PARAMS,
+ false, &dhparams, errp) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
+ false, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
+ false, &key, errp) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (creds->sanityCheck &&
+ qcrypto_tls_creds_x509_sanity_check(creds,
+ creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ cacert, cert, errp) < 0) {
+ goto cleanup;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&creds->data);
+ if (ret < 0) {
+ error_setg(errp, "Cannot allocate credentials: '%s'",
+ gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ ret = gnutls_certificate_set_x509_trust_file(creds->data,
+ cacert,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ error_setg(errp, "Cannot load CA certificate '%s': %s",
+ cacert, gnutls_strerror(ret));
+ goto cleanup;
+ }
+
+ if (cert != NULL && key != NULL) {
+ ret = gnutls_certificate_set_x509_key_file(creds->data,
+ cert, key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
+ cert, key, gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ if (cacrl != NULL) {
+ ret = gnutls_certificate_set_x509_crl_file(creds->data,
+ cacrl,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ error_setg(errp, "Cannot load CRL '%s': %s",
+ cacrl, gnutls_strerror(ret));
+ goto cleanup;
+ }
+ }
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
+ &creds->parent_obj.dh_params,
+ errp) < 0) {
+ goto cleanup;
+ }
+ gnutls_certificate_set_dh_params(creds->data,
+ creds->parent_obj.dh_params);
+ }
+
+ rv = 0;
+ cleanup:
+ g_free(cacert);
+ g_free(cacrl);
+ g_free(cert);
+ g_free(key);
+ g_free(dhparams);
+ return rv;
+}
+
+
+static void
+qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds)
+{
+ if (creds->data) {
+ gnutls_certificate_free_credentials(creds->data);
+ creds->data = NULL;
+ }
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS credentials support requires GNUTLS");
+}
+
+
+static void
+qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED)
+{
+ /* nada */
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_prop_set_loaded(Object *obj,
+ bool value,
+ Error **errp)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ if (value) {
+ qcrypto_tls_creds_x509_load(creds, errp);
+ } else {
+ qcrypto_tls_creds_x509_unload(creds);
+ }
+}
+
+
+#ifdef CONFIG_GNUTLS
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_loaded(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return creds->data != NULL;
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED,
+ Error **errp G_GNUC_UNUSED)
+{
+ return false;
+}
+
+
+#endif /* ! CONFIG_GNUTLS */
+
+
+static void
+qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
+ bool value,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->sanityCheck = value;
+}
+
+
+static bool
+qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
+ Error **errp G_GNUC_UNUSED)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ return creds->sanityCheck;
+}
+
+
+static void
+qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp)
+{
+ object_property_set_bool(OBJECT(uc), true, "loaded", errp);
+}
+
+
+static void
+qcrypto_tls_creds_x509_init(Object *obj)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ creds->sanityCheck = true;
+
+ object_property_add_bool(obj, "loaded",
+ qcrypto_tls_creds_x509_prop_get_loaded,
+ qcrypto_tls_creds_x509_prop_set_loaded,
+ NULL);
+ object_property_add_bool(obj, "sanity-check",
+ qcrypto_tls_creds_x509_prop_get_sanity,
+ qcrypto_tls_creds_x509_prop_set_sanity,
+ NULL);
+}
+
+
+static void
+qcrypto_tls_creds_x509_finalize(Object *obj)
+{
+ QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
+
+ qcrypto_tls_creds_x509_unload(creds);
+}
+
+
+static void
+qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = qcrypto_tls_creds_x509_complete;
+}
+
+
+static const TypeInfo qcrypto_tls_creds_x509_info = {
+ .parent = TYPE_QCRYPTO_TLS_CREDS,
+ .name = TYPE_QCRYPTO_TLS_CREDS_X509,
+ .instance_size = sizeof(QCryptoTLSCredsX509),
+ .instance_init = qcrypto_tls_creds_x509_init,
+ .instance_finalize = qcrypto_tls_creds_x509_finalize,
+ .class_size = sizeof(QCryptoTLSCredsX509Class),
+ .class_init = qcrypto_tls_creds_x509_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+
+static void
+qcrypto_tls_creds_x509_register_types(void)
+{
+ type_register_static(&qcrypto_tls_creds_x509_info);
+}
+
+
+type_init(qcrypto_tls_creds_x509_register_types);
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
new file mode 100644
index 0000000000..ffc5c47949
--- /dev/null
+++ b/crypto/tlssession.c
@@ -0,0 +1,574 @@
+/*
+ * QEMU crypto TLS session support
+ *
+ * Copyright (c) 2015 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 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 "crypto/tlssession.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredsx509.h"
+#include "qemu/acl.h"
+#include "trace.h"
+
+#ifdef CONFIG_GNUTLS
+
+
+#include <gnutls/x509.h>
+
+
+struct QCryptoTLSSession {
+ QCryptoTLSCreds *creds;
+ gnutls_session_t handle;
+ char *hostname;
+ char *aclname;
+ bool handshakeComplete;
+ QCryptoTLSSessionWriteFunc writeFunc;
+ QCryptoTLSSessionReadFunc readFunc;
+ void *opaque;
+ char *peername;
+};
+
+
+void
+qcrypto_tls_session_free(QCryptoTLSSession *session)
+{
+ if (!session) {
+ return;
+ }
+
+ gnutls_deinit(session->handle);
+ g_free(session->hostname);
+ g_free(session->peername);
+ g_free(session->aclname);
+ object_unref(OBJECT(session->creds));
+ g_free(session);
+}
+
+
+static ssize_t
+qcrypto_tls_session_push(void *opaque, const void *buf, size_t len)
+{
+ QCryptoTLSSession *session = opaque;
+
+ if (!session->writeFunc) {
+ errno = EIO;
+ return -1;
+ };
+
+ return session->writeFunc(buf, len, session->opaque);
+}
+
+
+static ssize_t
+qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
+{
+ QCryptoTLSSession *session = opaque;
+
+ if (!session->readFunc) {
+ errno = EIO;
+ return -1;
+ };
+
+ return session->readFunc(buf, len, session->opaque);
+}
+
+
+QCryptoTLSSession *
+qcrypto_tls_session_new(QCryptoTLSCreds *creds,
+ const char *hostname,
+ const char *aclname,
+ QCryptoTLSCredsEndpoint endpoint,
+ Error **errp)
+{
+ QCryptoTLSSession *session;
+ int ret;
+
+ session = g_new0(QCryptoTLSSession, 1);
+ trace_qcrypto_tls_session_new(
+ session, creds, hostname ? hostname : "<none>",
+ aclname ? aclname : "<none>", endpoint);
+
+ if (hostname) {
+ session->hostname = g_strdup(hostname);
+ }
+ if (aclname) {
+ session->aclname = g_strdup(aclname);
+ }
+ session->creds = creds;
+ object_ref(OBJECT(creds));
+
+ if (creds->endpoint != endpoint) {
+ error_setg(errp, "Credentials endpoint doesn't match session");
+ goto error;
+ }
+
+ if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ ret = gnutls_init(&session->handle, GNUTLS_SERVER);
+ } else {
+ ret = gnutls_init(&session->handle, GNUTLS_CLIENT);
+ }
+ if (ret < 0) {
+ error_setg(errp, "Cannot initialize TLS session: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (object_dynamic_cast(OBJECT(creds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
+ QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds);
+
+ ret = gnutls_priority_set_direct(session->handle,
+ "NORMAL:+ANON-DH", NULL);
+ if (ret < 0) {
+ error_setg(errp, "Unable to set TLS session priority: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_ANON,
+ acreds->data.server);
+ } else {
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_ANON,
+ acreds->data.client);
+ }
+ if (ret < 0) {
+ error_setg(errp, "Cannot set session credentials: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ } else if (object_dynamic_cast(OBJECT(creds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds);
+
+ ret = gnutls_set_default_priority(session->handle);
+ if (ret < 0) {
+ error_setg(errp, "Cannot set default TLS session priority: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ ret = gnutls_credentials_set(session->handle,
+ GNUTLS_CRD_CERTIFICATE,
+ tcreds->data);
+ if (ret < 0) {
+ error_setg(errp, "Cannot set session credentials: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ /* This requests, but does not enforce a client cert.
+ * The cert checking code later does enforcement */
+ gnutls_certificate_server_set_request(session->handle,
+ GNUTLS_CERT_REQUEST);
+ }
+ } else {
+ error_setg(errp, "Unsupported TLS credentials type %s",
+ object_get_typename(OBJECT(creds)));
+ goto error;
+ }
+
+ gnutls_transport_set_ptr(session->handle, session);
+ gnutls_transport_set_push_function(session->handle,
+ qcrypto_tls_session_push);
+ gnutls_transport_set_pull_function(session->handle,
+ qcrypto_tls_session_pull);
+
+ return session;
+
+ error:
+ qcrypto_tls_session_free(session);
+ return NULL;
+}
+
+static int
+qcrypto_tls_session_check_certificate(QCryptoTLSSession *session,
+ Error **errp)
+{
+ int ret;
+ unsigned int status;
+ const gnutls_datum_t *certs;
+ unsigned int nCerts, i;
+ time_t now;
+ gnutls_x509_crt_t cert = NULL;
+
+ now = time(NULL);
+ if (now == ((time_t)-1)) {
+ error_setg_errno(errp, errno, "Cannot get current time");
+ return -1;
+ }
+
+ ret = gnutls_certificate_verify_peers2(session->handle, &status);
+ if (ret < 0) {
+ error_setg(errp, "Verify failed: %s", gnutls_strerror(ret));
+ 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";
+ }
+
+ error_setg(errp, "%s", reason);
+ return -1;
+ }
+
+ certs = gnutls_certificate_get_peers(session->handle, &nCerts);
+ if (!certs) {
+ error_setg(errp, "No certificate peers");
+ return -1;
+ }
+
+ for (i = 0; i < nCerts; i++) {
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0) {
+ error_setg(errp, "Cannot initialize certificate: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+
+ ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ error_setg(errp, "Cannot import certificate: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time(cert) < now) {
+ error_setg(errp, "The certificate has expired");
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp, "The certificate is not yet activated");
+ goto error;
+ }
+
+ if (gnutls_x509_crt_get_activation_time(cert) > now) {
+ error_setg(errp, "The certificate is not yet activated");
+ goto error;
+ }
+
+ if (i == 0) {
+ size_t dnameSize = 1024;
+ session->peername = g_malloc(dnameSize);
+ requery:
+ ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ session->peername = g_realloc(session->peername,
+ dnameSize);
+ goto requery;
+ }
+ error_setg(errp, "Cannot get client distinguished name: %s",
+ gnutls_strerror(ret));
+ goto error;
+ }
+ if (session->aclname) {
+ qemu_acl *acl = qemu_acl_find(session->aclname);
+ int allow;
+ if (!acl) {
+ error_setg(errp, "Cannot find ACL %s",
+ session->aclname);
+ goto error;
+ }
+
+ allow = qemu_acl_party_is_allowed(acl, session->peername);
+
+ error_setg(errp, "TLS x509 ACL check for %s is %s",
+ session->peername, allow ? "allowed" : "denied");
+ if (!allow) {
+ goto error;
+ }
+ }
+ if (session->hostname) {
+ if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) {
+ error_setg(errp,
+ "Certificate does not match the hostname %s",
+ session->hostname);
+ goto error;
+ }
+ }
+ }
+
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ return 0;
+
+ error:
+ gnutls_x509_crt_deinit(cert);
+ return -1;
+}
+
+
+int
+qcrypto_tls_session_check_credentials(QCryptoTLSSession *session,
+ Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(session->creds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
+ return 0;
+ } else if (object_dynamic_cast(OBJECT(session->creds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ if (session->creds->verifyPeer) {
+ return qcrypto_tls_session_check_certificate(session,
+ errp);
+ } else {
+ return 0;
+ }
+ } else {
+ error_setg(errp, "Unexpected credential type %s",
+ object_get_typename(OBJECT(session->creds)));
+ return -1;
+ }
+}
+
+
+void
+qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session,
+ QCryptoTLSSessionWriteFunc writeFunc,
+ QCryptoTLSSessionReadFunc readFunc,
+ void *opaque)
+{
+ session->writeFunc = writeFunc;
+ session->readFunc = readFunc;
+ session->opaque = opaque;
+}
+
+
+ssize_t
+qcrypto_tls_session_write(QCryptoTLSSession *session,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret = gnutls_record_send(session->handle, buf, len);
+
+ if (ret < 0) {
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ errno = EAGAIN;
+ break;
+ case GNUTLS_E_INTERRUPTED:
+ errno = EINTR;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+ssize_t
+qcrypto_tls_session_read(QCryptoTLSSession *session,
+ char *buf,
+ size_t len)
+{
+ ssize_t ret = gnutls_record_recv(session->handle, buf, len);
+
+ if (ret < 0) {
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ errno = EAGAIN;
+ break;
+ case GNUTLS_E_INTERRUPTED:
+ errno = EINTR;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int
+qcrypto_tls_session_handshake(QCryptoTLSSession *session,
+ Error **errp)
+{
+ int ret = gnutls_handshake(session->handle);
+ if (ret == 0) {
+ session->handshakeComplete = true;
+ } else {
+ if (ret == GNUTLS_E_INTERRUPTED ||
+ ret == GNUTLS_E_AGAIN) {
+ ret = 1;
+ } else {
+ error_setg(errp, "TLS handshake failed: %s",
+ gnutls_strerror(ret));
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+QCryptoTLSSessionHandshakeStatus
+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session)
+{
+ if (session->handshakeComplete) {
+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
+ } else if (gnutls_record_get_direction(session->handle) == 0) {
+ return QCRYPTO_TLS_HANDSHAKE_RECVING;
+ } else {
+ return QCRYPTO_TLS_HANDSHAKE_SENDING;
+ }
+}
+
+
+int
+qcrypto_tls_session_get_key_size(QCryptoTLSSession *session,
+ Error **errp)
+{
+ gnutls_cipher_algorithm_t cipher;
+ int ssf;
+
+ cipher = gnutls_cipher_get(session->handle);
+ ssf = gnutls_cipher_get_key_size(cipher);
+ if (!ssf) {
+ error_setg(errp, "Cannot get TLS cipher key size");
+ return -1;
+ }
+ return ssf;
+}
+
+
+char *
+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session)
+{
+ if (session->peername) {
+ return g_strdup(session->peername);
+ }
+ return NULL;
+}
+
+
+#else /* ! CONFIG_GNUTLS */
+
+
+QCryptoTLSSession *
+qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED,
+ const char *hostname G_GNUC_UNUSED,
+ const char *aclname G_GNUC_UNUSED,
+ QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return NULL;
+}
+
+
+void
+qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED)
+{
+}
+
+
+int
+qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+void
+qcrypto_tls_session_set_callbacks(
+ QCryptoTLSSession *sess G_GNUC_UNUSED,
+ QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED,
+ QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED)
+{
+}
+
+
+ssize_t
+qcrypto_tls_session_write(QCryptoTLSSession *sess,
+ const char *buf,
+ size_t len)
+{
+ errno = -EIO;
+ return -1;
+}
+
+
+ssize_t
+qcrypto_tls_session_read(QCryptoTLSSession *sess,
+ char *buf,
+ size_t len)
+{
+ errno = -EIO;
+ return -1;
+}
+
+
+int
+qcrypto_tls_session_handshake(QCryptoTLSSession *sess,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+QCryptoTLSSessionHandshakeStatus
+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess)
+{
+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE;
+}
+
+
+int
+qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess,
+ Error **errp)
+{
+ error_setg(errp, "TLS requires GNUTLS support");
+ return -1;
+}
+
+
+char *
+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess)
+{
+ return NULL;
+}
+
+#endif
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index ff16df2456..d960dc0234 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -236,6 +236,7 @@ both fields like this:
=== Enumeration types ===
Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING }
+ { 'enum': STRING, '*prefix': STRING, 'data': ARRAY-OF-STRING }
An enumeration type is a dictionary containing a single 'data' key
whose value is a list of strings. An example enumeration is:
@@ -247,6 +248,13 @@ useful. The list of strings should be lower case; if an enum name
represents multiple words, use '-' between words. The string 'max' is
not allowed as an enum value, and values should not be repeated.
+The enum constants will be named by using a heuristic to turn the
+type name into a set of underscore separated words. For the example
+above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name
+of 'MY_ENUM_VALUE1' for the first value. If the default heuristic
+does not result in a desirable name, the optional 'prefix' field
+can be used when defining the enum.
+
The enumeration values are passed as strings over the Client JSON
Protocol, but are encoded as C enum integral values in generated code.
While the C code starts numbering at 0, it is better to use explicit
diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
new file mode 100644
index 0000000000..21761b7ce1
--- /dev/null
+++ b/include/crypto/tlscreds.h
@@ -0,0 +1,68 @@
+/*
+ * QEMU crypto TLS credential support
+ *
+ * Copyright (c) 2015 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 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_TLSCRED_H__
+#define QCRYPTO_TLSCRED_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#ifdef CONFIG_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
+#define TYPE_QCRYPTO_TLS_CREDS "tls-creds"
+#define QCRYPTO_TLS_CREDS(obj) \
+ OBJECT_CHECK(QCryptoTLSCreds, (obj), TYPE_QCRYPTO_TLS_CREDS)
+
+typedef struct QCryptoTLSCreds QCryptoTLSCreds;
+typedef struct QCryptoTLSCredsClass QCryptoTLSCredsClass;
+
+#define QCRYPTO_TLS_CREDS_DH_PARAMS "dh-params.pem"
+
+
+/**
+ * QCryptoTLSCreds:
+ *
+ * The QCryptoTLSCreds object is an abstract base for different
+ * types of TLS handshake credentials. Most commonly the
+ * QCryptoTLSCredsX509 subclass will be used to provide x509
+ * certificate credentials.
+ */
+
+struct QCryptoTLSCreds {
+ Object parent_obj;
+ char *dir;
+ QCryptoTLSCredsEndpoint endpoint;
+#ifdef CONFIG_GNUTLS
+ gnutls_dh_params_t dh_params;
+#endif
+ bool verifyPeer;
+};
+
+
+struct QCryptoTLSCredsClass {
+ ObjectClass parent_class;
+};
+
+
+#endif /* QCRYPTO_TLSCRED_H__ */
+
diff --git a/include/crypto/tlscredsanon.h b/include/crypto/tlscredsanon.h
new file mode 100644
index 0000000000..d3976b84b9
--- /dev/null
+++ b/include/crypto/tlscredsanon.h
@@ -0,0 +1,112 @@
+/*
+ * QEMU crypto TLS anonymous credential support
+ *
+ * Copyright (c) 2015 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 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_TLSCRED_ANON_H__
+#define QCRYPTO_TLSCRED_ANON_H__
+
+#include "crypto/tlscreds.h"
+
+#define TYPE_QCRYPTO_TLS_CREDS_ANON "tls-creds-anon"
+#define QCRYPTO_TLS_CREDS_ANON(obj) \
+ OBJECT_CHECK(QCryptoTLSCredsAnon, (obj), TYPE_QCRYPTO_TLS_CREDS_ANON)
+
+
+typedef struct QCryptoTLSCredsAnon QCryptoTLSCredsAnon;
+typedef struct QCryptoTLSCredsAnonClass QCryptoTLSCredsAnonClass;
+
+/**
+ * QCryptoTLSCredsAnon:
+ *
+ * The QCryptoTLSCredsAnon object provides a representation
+ * of anonymous credentials used perform a TLS handshake.
+ * This is primarily provided for backwards compatibility and
+ * its use is discouraged as it has poor security characteristics
+ * due to lacking MITM attack protection amongst other problems.
+ *
+ * This is a user creatable object, which can be instantiated
+ * via object_new_propv():
+ *
+ * <example>
+ * <title>Creating anonymous TLS credential objects in code</title>
+ * <programlisting>
+ * Object *obj;
+ * Error *err = NULL;
+ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_ANON,
+ * "tlscreds0",
+ * &err,
+ * "endpoint", "server",
+ * "dir", "/path/x509/cert/dir",
+ * "verify-peer", "yes",
+ * NULL);
+ * </programlisting>
+ * </example>
+ *
+ * Or via QMP:
+ *
+ * <example>
+ * <title>Creating anonymous TLS credential objects via QMP</title>
+ * <programlisting>
+ * {
+ * "execute": "object-add", "arguments": {
+ * "id": "tlscreds0",
+ * "qom-type": "tls-creds-anon",
+ * "props": {
+ * "endpoint": "server",
+ * "dir": "/path/to/x509/cert/dir",
+ * "verify-peer": false
+ * }
+ * }
+ * }
+ * </programlisting>
+ * </example>
+ *
+ *
+ * Or via the CLI:
+ *
+ * <example>
+ * <title>Creating anonymous TLS credential objects via CLI</title>
+ * <programlisting>
+ * qemu-system-x86_64 -object tls-creds-anon,id=tlscreds0,\
+ * endpoint=server,verify-peer=off,\
+ * dir=/path/to/x509/certdir/
+ * </programlisting>
+ * </example>
+ *
+ */
+
+
+struct QCryptoTLSCredsAnon {
+ QCryptoTLSCreds parent_obj;
+#ifdef CONFIG_GNUTLS
+ union {
+ gnutls_anon_server_credentials_t server;
+ gnutls_anon_client_credentials_t client;
+ } data;
+#endif
+};
+
+
+struct QCryptoTLSCredsAnonClass {
+ QCryptoTLSCredsClass parent_class;
+};
+
+
+#endif /* QCRYPTO_TLSCRED_H__ */
+
diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h
new file mode 100644
index 0000000000..b9785fddcf
--- /dev/null
+++ b/include/crypto/tlscredsx509.h
@@ -0,0 +1,113 @@
+/*
+ * QEMU crypto TLS x509 credential support
+ *
+ * Copyright (c) 2015 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 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_TLSCRED_X509_H__
+#define QCRYPTO_TLSCRED_X509_H__
+
+#include "crypto/tlscreds.h"
+
+#define TYPE_QCRYPTO_TLS_CREDS_X509 "tls-creds-x509"
+#define QCRYPTO_TLS_CREDS_X509(obj) \
+ OBJECT_CHECK(QCryptoTLSCredsX509, (obj), TYPE_QCRYPTO_TLS_CREDS_X509)
+
+typedef struct QCryptoTLSCredsX509 QCryptoTLSCredsX509;
+typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class;
+
+#define QCRYPTO_TLS_CREDS_X509_CA_CERT "ca-cert.pem"
+#define QCRYPTO_TLS_CREDS_X509_CA_CRL "ca-crl.pem"
+#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY "server-key.pem"
+#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem"
+#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem"
+#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem"
+
+
+/**
+ * QCryptoTLSCredsX509:
+ *
+ * The QCryptoTLSCredsX509 object provides a representation
+ * of x509 credentials used to perform a TLS handshake.
+ *
+ * This is a user creatable object, which can be instantiated
+ * via object_new_propv():
+ *
+ * <example>
+ * <title>Creating x509 TLS credential objects in code</title>
+ * <programlisting>
+ * Object *obj;
+ * Error *err = NULL;
+ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_X509,
+ * "tlscreds0",
+ * &err,
+ * "endpoint", "server",
+ * "dir", "/path/x509/cert/dir",
+ * "verify-peer", "yes",
+ * NULL);
+ * </programlisting>
+ * </example>
+ *
+ * Or via QMP:
+ *
+ * <example>
+ * <title>Creating x509 TLS credential objects via QMP</title>
+ * <programlisting>
+ * {
+ * "execute": "object-add", "arguments": {
+ * "id": "tlscreds0",
+ * "qom-type": "tls-creds-x509",
+ * "props": {
+ * "endpoint": "server",
+ * "dir": "/path/to/x509/cert/dir",
+ * "verify-peer": false
+ * }
+ * }
+ * }
+ * </programlisting>
+ * </example>
+ *
+ *
+ * Or via the CLI:
+ *
+ * <example>
+ * <title>Creating x509 TLS credential objects via CLI</title>
+ * <programlisting>
+ * qemu-system-x86_64 -object tls-creds-x509,id=tlscreds0,\
+ * endpoint=server,verify-peer=off,\
+ * dir=/path/to/x509/certdir/
+ * </programlisting>
+ * </example>
+ *
+ */
+
+struct QCryptoTLSCredsX509 {
+ QCryptoTLSCreds parent_obj;
+#ifdef CONFIG_GNUTLS
+ gnutls_certificate_credentials_t data;
+#endif
+ bool sanityCheck;
+};
+
+
+struct QCryptoTLSCredsX509Class {
+ QCryptoTLSCredsClass parent_class;
+};
+
+
+#endif /* QCRYPTO_TLSCRED_X509_H__ */
+
diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h
new file mode 100644
index 0000000000..b38fe6954d
--- /dev/null
+++ b/include/crypto/tlssession.h
@@ -0,0 +1,322 @@
+/*
+ * QEMU crypto TLS session support
+ *
+ * Copyright (c) 2015 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 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_TLS_SESSION_H__
+#define QCRYPTO_TLS_SESSION_H__
+
+#include "crypto/tlscreds.h"
+
+/**
+ * QCryptoTLSSession:
+ *
+ * The QCryptoTLSSession object encapsulates the
+ * logic to integrate with a TLS providing library such
+ * as GNUTLS, to setup and run TLS sessions.
+ *
+ * The API is designed such that it has no assumption about
+ * the type of transport it is running over. It may be a
+ * traditional TCP socket, or something else entirely. The
+ * only requirement is a full-duplex stream of some kind.
+ *
+ * <example>
+ * <title>Using TLS session objects</title>
+ * <programlisting>
+ * static ssize_t mysock_send(const char *buf, size_t len,
+ * void *opaque)
+ * {
+ * int fd = GPOINTER_TO_INT(opaque);
+ *
+ * return write(*fd, buf, len);
+ * }
+ *
+ * static ssize_t mysock_recv(const char *buf, size_t len,
+ * void *opaque)
+ * {
+ * int fd = GPOINTER_TO_INT(opaque);
+ *
+ * return read(*fd, buf, len);
+ * }
+ *
+ * static int mysock_run_tls(int sockfd,
+ * QCryptoTLSCreds *creds,
+ * Error *erp)
+ * {
+ * QCryptoTLSSession *sess;
+ *
+ * sess = qcrypto_tls_session_new(creds,
+ * "vnc.example.com",
+ * NULL,
+ * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
+ * errp);
+ * if (sess == NULL) {
+ * return -1;
+ * }
+ *
+ * qcrypto_tls_session_set_callbacks(sess,
+ * mysock_send,
+ * mysock_recv
+ * GINT_TO_POINTER(fd));
+ *
+ * while (1) {
+ * if (qcrypto_tls_session_handshake(sess, errp) < 0) {
+ * qcrypto_tls_session_free(sess);
+ * return -1;
+ * }
+ *
+ * switch(qcrypto_tls_session_get_handshake_status(sess)) {
+ * case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
+ * if (qcrypto_tls_session_check_credentials(sess, errp) < )) {
+ * qcrypto_tls_session_free(sess);
+ * return -1;
+ * }
+ * goto done;
+ * case QCRYPTO_TLS_HANDSHAKE_RECVING:
+ * ...wait for GIO_IN event on fd...
+ * break;
+ * case QCRYPTO_TLS_HANDSHAKE_SENDING:
+ * ...wait for GIO_OUT event on fd...
+ * break;
+ * }
+ * }
+ * done:
+ *
+ * ....send/recv payload data on sess...
+ *
+ * qcrypto_tls_session_free(sess):
+ * }
+ * </programlisting>
+ * </example>
+ */
+
+typedef struct QCryptoTLSSession QCryptoTLSSession;
+
+
+/**
+ * qcrypto_tls_session_new:
+ * @creds: pointer to a TLS credentials object
+ * @hostname: optional hostname to validate
+ * @aclname: optional ACL to validate peer credentials against
+ * @endpoint: role of the TLS session, client or server
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a new TLS session object that will be used to
+ * negotiate a TLS session over an arbitrary data channel.
+ * The session object can operate as either the server or
+ * client, according to the value of the @endpoint argument.
+ *
+ * For clients, the @hostname parameter should hold the full
+ * unmodified hostname as requested by the user. This will
+ * be used to verify the against the hostname reported in
+ * the server's credentials (aka x509 certificate).
+ *
+ * The @aclname parameter (optionally) specifies the name
+ * of an access control list that will be used to validate
+ * the peer's credentials. For x509 credentials, the ACL
+ * will be matched against the CommonName shown in the peer's
+ * certificate. If the session is acting as a server, setting
+ * an ACL will require that the client provide a validate
+ * x509 client certificate.
+ *
+ * After creating the session object, the I/O callbacks
+ * must be set using the qcrypto_tls_session_set_callbacks()
+ * method. A TLS handshake sequence must then be completed
+ * using qcrypto_tls_session_handshake(), before payload
+ * data is permitted to be sent/received.
+ *
+ * The session object must be released by calling
+ * qcrypto_tls_session_free() when no longer required
+ *
+ * Returns: a TLS session object, or NULL on error.
+ */
+QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds,
+ const char *hostname,
+ const char *aclname,
+ QCryptoTLSCredsEndpoint endpoint,
+ Error **errp);
+
+/**
+ * qcrypto_tls_session_free:
+ * @sess: the TLS session object
+ *
+ * Release all memory associated with the TLS session
+ * object previously allocated by qcrypto_tls_session_new()
+ */
+void qcrypto_tls_session_free(QCryptoTLSSession *sess);
+
+/**
+ * qcrypto_tls_session_check_credentials:
+ * @sess: the TLS session object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Validate the peer's credentials after a successful
+ * TLS handshake. It is an error to call this before
+ * qcrypto_tls_session_get_handshake_status() returns
+ * QCRYPTO_TLS_HANDSHAKE_COMPLETE
+ *
+ * Returns 0 if the credentials validated, -1 on error
+ */
+int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess,
+ Error **errp);
+
+typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf,
+ size_t len,
+ void *opaque);
+typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf,
+ size_t len,
+ void *opaque);
+
+/**
+ * qcrypto_tls_session_set_callbacks:
+ * @sess: the TLS session object
+ * @writeFunc: callback for sending data
+ * @readFunc: callback to receiving data
+ * @opaque: data to pass to callbacks
+ *
+ * Sets the callback functions that are to be used for sending
+ * and receiving data on the underlying data channel. Typically
+ * the callbacks to write/read to/from a TCP socket, but there
+ * is no assumption made about the type of channel used.
+ *
+ * The @writeFunc callback will be passed the encrypted
+ * data to send to the remote peer.
+ *
+ * The @readFunc callback will be passed a pointer to fill
+ * with encrypted data received from the remote peer
+ */
+void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
+ QCryptoTLSSessionWriteFunc writeFunc,
+ QCryptoTLSSessionReadFunc readFunc,
+ void *opaque);
+
+/**
+ * qcrypto_tls_session_write:
+ * @sess: the TLS session object
+ * @buf: the plain text to send
+ * @len: the length of @buf
+ *
+ * Encrypt @len bytes of the data in @buf and send
+ * it to the remote peer using the callback previously
+ * registered with qcrypto_tls_session_set_callbacks()
+ *
+ * It is an error to call this before
+ * qcrypto_tls_session_get_handshake_status() returns
+ * QCRYPTO_TLS_HANDSHAKE_COMPLETE
+ *
+ * Returns: the number of bytes sent, or -1 on error
+ */
+ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess,
+ const char *buf,
+ size_t len);
+
+/**
+ * qcrypto_tls_session_read:
+ * @sess: the TLS session object
+ * @buf: to fill with plain text received
+ * @len: the length of @buf
+ *
+ * Receive up to @len bytes of data from the remote peer
+ * using the callback previously registered with
+ * qcrypto_tls_session_set_callbacks(), decrypt it and
+ * store it in @buf.
+ *
+ * It is an error to call this before
+ * qcrypto_tls_session_get_handshake_status() returns
+ * QCRYPTO_TLS_HANDSHAKE_COMPLETE
+ *
+ * Returns: the number of bytes received, or -1 on error
+ */
+ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess,
+ char *buf,
+ size_t len);
+
+/**
+ * qcrypto_tls_session_handshake:
+ * @sess: the TLS session object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Start, or continue, a TLS handshake sequence. If
+ * the underlying data channel is non-blocking, then
+ * this method may return control before the handshake
+ * is complete. On non-blocking channels the
+ * qcrypto_tls_session_get_handshake_status() method
+ * should be used to determine whether the handshake
+ * has completed, or is waiting to send or receive
+ * data. In the latter cases, the caller should setup
+ * an event loop watch and call this method again
+ * once the underlying data channel is ready to read
+ * or write again
+ */
+int qcrypto_tls_session_handshake(QCryptoTLSSession *sess,
+ Error **errp);
+
+typedef enum {
+ QCRYPTO_TLS_HANDSHAKE_COMPLETE,
+ QCRYPTO_TLS_HANDSHAKE_SENDING,
+ QCRYPTO_TLS_HANDSHAKE_RECVING,
+} QCryptoTLSSessionHandshakeStatus;
+
+/**
+ * qcrypto_tls_session_get_handshake_status:
+ * @sess: the TLS session object
+ *
+ * Check the status of the TLS handshake. This
+ * is used with non-blocking data channels to
+ * determine whether the handshake is waiting
+ * to send or receive further data to/from the
+ * remote peer.
+ *
+ * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE
+ * it is permitted to send/receive payload data on
+ * the channel
+ */
+QCryptoTLSSessionHandshakeStatus
+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess);
+
+/**
+ * qcrypto_tls_session_get_key_size:
+ * @sess: the TLS session object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Check the size of the data channel encryption key
+ *
+ * Returns: the length in bytes of the encryption key
+ * or -1 on error
+ */
+int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess,
+ Error **errp);
+
+/**
+ * qcrypto_tls_session_get_peer_name:
+ * @sess: the TLS session object
+ *
+ * Get the identified name of the remote peer. If the
+ * TLS session was negotiated using x509 certificate
+ * credentials, this will return the CommonName from
+ * the peer's certificate. If no identified name is
+ * available it will return NULL.
+ *
+ * The returned data must be released with g_free()
+ * when no longer required.
+ *
+ * Returns: the peer's name or NULL.
+ */
+char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess);
+
+#endif /* QCRYPTO_TLS_SESSION_H__ */
diff --git a/qapi-schema.json b/qapi-schema.json
index 4060b78386..2bada607f1 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5,6 +5,9 @@
# QAPI common definitions
{ 'include': 'qapi/common.json' }
+# QAPI crypto definitions
+{ 'include': 'qapi/crypto.json' }
+
# QAPI block definitions
{ 'include': 'qapi/block.json' }
diff --git a/qapi/crypto.json b/qapi/crypto.json
new file mode 100644
index 0000000000..b058b14d7b
--- /dev/null
+++ b/qapi/crypto.json
@@ -0,0 +1,21 @@
+# -*- Mode: Python -*-
+#
+# QAPI crypto definitions
+
+##
+# QCryptoTLSCredsEndpoint:
+#
+# The type of network endpoint that will be using the credentials.
+# Most types of credential require different setup / structures
+# depending on whether they will be used in a server versus a
+# client.
+#
+# @client: the network endpoint is acting as the client
+#
+# @server: the network endpoint is acting as the server
+#
+# Since: 2.5
+##
+{ 'enum': 'QCryptoTLSCredsEndpoint',
+ 'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT',
+ 'data': ['client', 'server']}
diff --git a/qemu-options.hx b/qemu-options.hx
index 166eae6784..7e147b8aac 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1217,8 +1217,9 @@ By definition the Websocket port is 5700+@var{display}. If @var{host} is
specified connections will only be allowed from this host.
As an alternative the Websocket port could be specified by using
@code{websocket}=@var{port}.
-TLS encryption for the Websocket connection is supported if the required
-certificates are specified with the VNC option @option{x509}.
+If no TLS credentials are provided, the websocket connection runs in
+unencrypted mode. If TLS credentials are provided, the websocket connection
+requires encrypted client connections.
@item password
@@ -1239,6 +1240,20 @@ date and time).
You can also use keywords "now" or "never" for the expiration time to
allow <protocol> password to expire immediately or never expire.
+@item tls-creds=@var{ID}
+
+Provides the ID of a set of TLS credentials to use to secure the
+VNC server. They will apply to both the normal VNC server socket
+and the websocket socket (if enabled). Setting TLS credentials
+will cause the VNC server socket to enable the VeNCrypt auth
+mechanism. The credentials should have been previously created
+using the @option{-object tls-creds} argument.
+
+The @option{tls-creds} parameter obsoletes the @option{tls},
+@option{x509}, and @option{x509verify} options, and as such
+it is not permitted to set both new and old type options at
+the same time.
+
@item tls
Require that client use TLS when communicating with the VNC server. This
@@ -1246,6 +1261,9 @@ uses anonymous TLS credentials so is susceptible to a man-in-the-middle
attack. It is recommended that this option be combined with either the
@option{x509} or @option{x509verify} options.
+This option is now deprecated in favor of using the @option{tls-creds}
+argument.
+
@item x509=@var{/path/to/certificate/dir}
Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1255,6 +1273,9 @@ to provide authentication of the client when this is used. The path following
this option specifies where the x509 certificates are to be loaded from.
See the @ref{vnc_security} section for details on generating certificates.
+This option is now deprecated in favour of using the @option{tls-creds}
+argument.
+
@item x509verify=@var{/path/to/certificate/dir}
Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1268,6 +1289,9 @@ path following this option specifies where the x509 certificates are to
be loaded from. See the @ref{vnc_security} section for details on generating
certificates.
+This option is now deprecated in favour of using the @option{tls-creds}
+argument.
+
@item sasl
Require that the client use SASL to authenticate with the VNC server.
@@ -3571,6 +3595,53 @@ the @option{virtio-rng} device. The @option{chardev} parameter is
the unique ID of a character device backend that provides the connection
to the RNG daemon.
+@item -object tls-creds-anon,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}
+
+Creates a TLS anonymous credentials object, which can be used to provide
+TLS support on network backends. The @option{id} parameter is a unique
+ID which network backends will use to access the credentials. The
+@option{endpoint} is either @option{server} or @option{client} depending
+on whether the QEMU network backend that uses the credentials will be
+acting as a client or as a server. If @option{verify-peer} is enabled
+(the default) then once the handshake is completed, the peer credentials
+will be verified, though this is a no-op for anonymous credentials.
+
+The @var{dir} parameter tells QEMU where to find the credential
+files. For server endpoints, this directory may contain a file
+@var{dh-params.pem} providing diffie-hellman parameters to use
+for the TLS server. If the file is missing, QEMU will generate
+a set of DH parameters at startup. This is a computationally
+expensive operation that consumes random pool entropy, so it is
+recommended that a persistent set of parameters be generated
+upfront and saved.
+
+@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}
+
+Creates a TLS anonymous credentials object, which can be used to provide
+TLS support on network backends. The @option{id} parameter is a unique
+ID which network backends will use to access the credentials. The
+@option{endpoint} is either @option{server} or @option{client} depending
+on whether the QEMU network backend that uses the credentials will be
+acting as a client or as a server. If @option{verify-peer} is enabled
+(the default) then once the handshake is completed, the peer credentials
+will be verified. With x509 certificates, this implies that the clients
+must be provided with valid client certificates too.
+
+The @var{dir} parameter tells QEMU where to find the credential
+files. For server endpoints, this directory may contain a file
+@var{dh-params.pem} providing diffie-hellman parameters to use
+for the TLS server. If the file is missing, QEMU will generate
+a set of DH parameters at startup. This is a computationally
+expensive operation that consumes random pool entropy, so it is
+recommended that a persistent set of parameters be generated
+upfront and saved.
+
+For x509 certificate credentials the directory will contain further files
+providing the x509 certificates. The certificates must be stored
+in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional),
+@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers),
+@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients).
+
@end table
ETEXI
diff --git a/qom/Makefile.objs b/qom/Makefile.objs
index 985003bd03..516349eec3 100644
--- a/qom/Makefile.objs
+++ b/qom/Makefile.objs
@@ -1,3 +1,4 @@
-common-obj-y = object.o container.o qom-qobject.o
-common-obj-y += cpu.o
-common-obj-y += object_interfaces.o
+qom-obj-y = object.o container.o qom-qobject.o
+qom-obj-y += object_interfaces.o
+
+common-obj-y = cpu.o
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index f2428f3807..a8453d1365 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -101,20 +101,20 @@ struct %(name)s {
return ret
-def generate_enum_lookup(name, values):
+def generate_enum_lookup(name, values, prefix=None):
ret = mcgen('''
const char *const %(name)s_lookup[] = {
''',
name=c_name(name))
for value in values:
- index = c_enum_const(name, value)
+ index = c_enum_const(name, value, prefix)
ret += mcgen('''
[%(index)s] = "%(value)s",
''',
index = index, value = value)
- max_index = c_enum_const(name, 'MAX')
+ max_index = c_enum_const(name, 'MAX', prefix)
ret += mcgen('''
[%(max_index)s] = NULL,
};
@@ -122,7 +122,7 @@ const char *const %(name)s_lookup[] = {
max_index=max_index)
return ret
-def generate_enum(name, values):
+def generate_enum(name, values, prefix=None):
name = c_name(name)
lookup_decl = mcgen('''
@@ -141,7 +141,7 @@ typedef enum %(name)s {
i = 0
for value in enum_values:
- enum_full_value = c_enum_const(name, value)
+ enum_full_value = c_enum_const(name, value, prefix)
enum_decl += mcgen('''
%(enum_full_value)s = %(i)d,
''',
@@ -348,9 +348,11 @@ for expr in exprs:
if expr.has_key('struct'):
ret += generate_fwd_struct(expr['struct'])
elif expr.has_key('enum'):
- ret += generate_enum(expr['enum'], expr['data'])
+ ret += generate_enum(expr['enum'], expr['data'],
+ expr.get('prefix'))
ret += generate_fwd_enum_struct(expr['enum'])
- fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
+ fdef.write(generate_enum_lookup(expr['enum'], expr['data'],
+ expr.get('prefix')))
elif expr.has_key('union'):
ret += generate_fwd_struct(expr['union'])
enum_define = discriminator_find_enum_define(expr)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 88fa073184..c4423b740c 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -634,11 +634,15 @@ def check_alternate(expr, expr_info):
def check_enum(expr, expr_info):
name = expr['enum']
members = expr.get('data')
+ prefix = expr.get('prefix')
values = { 'MAX': '(automatic)' }
if not isinstance(members, list):
raise QAPIExprError(expr_info,
"Enum '%s' requires an array for 'data'" % name)
+ if prefix is not None and not isinstance(prefix, str):
+ raise QAPIExprError(expr_info,
+ "Enum '%s' requires a string for 'prefix'" % name)
for member in members:
check_name(expr_info, "Member of enum '%s'" %name, member,
enum_member=True)
@@ -693,7 +697,7 @@ def check_exprs(exprs):
expr = expr_elem['expr']
info = expr_elem['info']
if expr.has_key('enum'):
- check_keys(expr_elem, 'enum', ['data'])
+ check_keys(expr_elem, 'enum', ['data'], ['prefix'])
add_enum(expr['enum'], info, expr['data'])
elif expr.has_key('union'):
check_keys(expr_elem, 'union', ['data'],
@@ -813,7 +817,9 @@ def camel_to_upper(value):
new_name += c
return new_name.lstrip('_').upper()
-def c_enum_const(type_name, const_name):
+def c_enum_const(type_name, const_name, prefix=None):
+ if prefix is not None:
+ type_name = prefix
return camel_to_upper(type_name + '_' + const_name)
c_name_trans = string.maketrans('.-', '__')
diff --git a/tests/.gitignore b/tests/.gitignore
index ccc92e4761..2c5e2c31e4 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -12,6 +12,13 @@ test-bitops
test-coroutine
test-crypto-cipher
test-crypto-hash
+test-crypto-tlscredsx509
+test-crypto-tlscredsx509-work/
+test-crypto-tlscredsx509-certs/
+test-crypto-tlssession
+test-crypto-tlssession-work/
+test-crypto-tlssession-client/
+test-crypto-tlssession-server/
test-cutils
test-hbitmap
test-int128
diff --git a/tests/Makefile b/tests/Makefile
index 34c6136db3..7c6025a2c5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,5 +1,7 @@
export SRC_PATH
+qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
+
# Get the list of all supported sysemu targets
SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
$(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak)))
@@ -76,6 +78,8 @@ check-unit-y += tests/test-write-threshold$(EXESUF)
gcov-files-test-write-threshold-y = block/write-threshold.c
check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
check-unit-y += tests/test-crypto-cipher$(EXESUF)
+check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
+check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -221,7 +225,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
comments.json empty.json enum-empty.json enum-missing-data.json \
enum-wrong-data.json enum-int-member.json enum-dict-member.json \
enum-clash-member.json enum-max-member.json enum-union-clash.json \
- enum-bad-name.json funny-char.json indented-expr.json \
+ enum-bad-name.json enum-bad-prefix.json \
+ funny-char.json indented-expr.json \
missing-type.json bad-ident.json ident-with-escape.json \
escape-outside-string.json unknown-escape.json \
escape-too-short.json escape-too-big.json unicode-str.json \
@@ -275,47 +280,50 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
tests/test-opts-visitor.o tests/test-qmp-event.o \
tests/rcutorture.o tests/test-rcu-list.o
-test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
- tests/test-qapi-event.o
-
$(test-obj-y): QEMU_INCLUDES += -Itests
QEMU_CFLAGS += -I$(SRC_PATH)/tests
-qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o qom/object_interfaces.o
-
-tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a
-tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a
-tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a
-tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a
-tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a
-tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
-tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a
-tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(qom-core-obj) libqemuutil.a libqemustub.a
-tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
-tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
-tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o libqemuutil.a libqemustub.a
-tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a
-tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a
-tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
-tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a
+
+
+# Deps that are common to various different sets of tests below
+test-util-obj-y = libqemuutil.a libqemustub.a
+test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
+test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
+ tests/test-qapi-event.o \
+ $(test-qom-obj-y)
+test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
+test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y)
+
+tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y)
+tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
+tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
+tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
+tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y)
+tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
+tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
+tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
+tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
+tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
+tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y)
+tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
+tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
+tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
+tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y)
tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o
-tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o libqemuutil.a
+tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o $(test-util-obj-y)
tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o
tests/test-int128$(EXESUF): tests/test-int128.o
-tests/rcutorture$(EXESUF): tests/rcutorture.o libqemuutil.a libqemustub.a
-tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o libqemuutil.a libqemustub.a
+tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y)
+tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y)
tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\
hw/core/irq.o \
hw/core/fw-path-provider.o \
- $(qom-core-obj) \
- $(test-qapi-obj-y) \
- libqemuutil.a libqemustub.a
+ $(test-qapi-obj-y)
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \
migration/qemu-file-unix.o qjson.o \
- $(qom-core-obj) \
- libqemuutil.a libqemustub.a
+ $(test-qom-obj-y)
tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@@ -338,20 +346,24 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve
$(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
-tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
-
-tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
-tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a
-tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a
-tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a
+tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
+tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
+tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
+tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
+tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
+tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
+
+tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
+tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
+tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y)
+tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y)
+tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
+ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
+tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
+ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
@@ -414,12 +426,14 @@ tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
-tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a
-tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a
+tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
+tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
ifeq ($(CONFIG_POSIX),y)
LIBS += -lutil
endif
+LIBS += $(TEST_LIBS)
+CFLAGS += $(TEST_CFLAGS)
# QTest rules
@@ -429,7 +443,7 @@ QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TA
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
endif
-qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a
+qtest-obj-y = tests/libqtest.o $(test-util-obj-y)
$(check-qtest-y): $(qtest-obj-y)
.PHONY: check-help
diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c
new file mode 100644
index 0000000000..c5de67baaf
--- /dev/null
+++ b/tests/crypto-tls-x509-helpers.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "config-host.h"
+#include "crypto-tls-x509-helpers.h"
+#include "qemu/sockets.h"
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+
+/*
+ * This stores some static data that is needed when
+ * encoding extensions in the x509 certs
+ */
+ASN1_TYPE pkix_asn1;
+
+/*
+ * To avoid consuming random entropy to generate keys,
+ * here's one we prepared earlier :-)
+ */
+gnutls_x509_privkey_t privkey;
+# define PRIVATE_KEY \
+ "-----BEGIN PRIVATE KEY-----\n" \
+ "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \
+ "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \
+ "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \
+ "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \
+ "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \
+ "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \
+ "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \
+ "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \
+ "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \
+ "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \
+ "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \
+ "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \
+ "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \
+ "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \
+ "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \
+ "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \
+ "-----END PRIVATE KEY-----\n"
+
+/*
+ * This loads the private key we defined earlier
+ */
+static gnutls_x509_privkey_t test_tls_load_key(void)
+{
+ gnutls_x509_privkey_t key;
+ const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY,
+ strlen(PRIVATE_KEY) };
+ int err;
+
+ err = gnutls_x509_privkey_init(&key);
+ if (err < 0) {
+ g_critical("Failed to init key %s", gnutls_strerror(err));
+ abort();
+ }
+
+ err = gnutls_x509_privkey_import(key, &data,
+ GNUTLS_X509_FMT_PEM);
+ if (err < 0) {
+ if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR &&
+ err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ g_critical("Failed to import key %s", gnutls_strerror(err));
+ abort();
+ }
+
+ err = gnutls_x509_privkey_import_pkcs8(
+ key, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (err < 0) {
+ g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err));
+ abort();
+ }
+ }
+
+ return key;
+}
+
+
+void test_tls_init(const char *keyfile)
+{
+ gnutls_global_init();
+
+ if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) {
+ abort();
+ }
+
+ privkey = test_tls_load_key();
+ if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) {
+ abort();
+ }
+}
+
+
+void test_tls_cleanup(const char *keyfile)
+{
+ asn1_delete_structure(&pkix_asn1);
+ unlink(keyfile);
+}
+
+/*
+ * Turns an ASN1 object into a DER encoded byte array
+ */
+static void test_tls_der_encode(ASN1_TYPE src,
+ const char *src_name,
+ gnutls_datum_t *res)
+{
+ int size;
+ char *data = NULL;
+
+ size = 0;
+ asn1_der_coding(src, src_name, NULL, &size, NULL);
+
+ data = g_new0(char, size);
+
+ asn1_der_coding(src, src_name, data, &size, NULL);
+
+ res->data = (unsigned char *)data;
+ res->size = size;
+}
+
+
+static void
+test_tls_get_ipaddr(const char *addrstr,
+ char **data,
+ int *datalen)
+{
+ struct addrinfo *res;
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0);
+
+ *datalen = res->ai_addrlen;
+ *data = g_new(char, *datalen);
+ memcpy(*data, res->ai_addr, *datalen);
+}
+
+/*
+ * This is a fairly lame x509 certificate generator.
+ *
+ * Do not copy/use this code for generating real certificates
+ * since it leaves out many things that you would want in
+ * certificates for real world usage.
+ *
+ * This is good enough only for doing tests of the QEMU
+ * TLS certificate code
+ */
+void
+test_tls_generate_cert(QCryptoTLSTestCertReq *req,
+ gnutls_x509_crt_t ca)
+{
+ gnutls_x509_crt_t crt;
+ int err;
+ static char buffer[1024 * 1024];
+ size_t size = sizeof(buffer);
+ char serial[5] = { 1, 2, 3, 4, 0 };
+ gnutls_datum_t der;
+ time_t start = time(NULL) + (60 * 60 * req->start_offset);
+ time_t expire = time(NULL) + (60 * 60 * (req->expire_offset
+ ? req->expire_offset : 24));
+
+ /*
+ * Prepare our new certificate object
+ */
+ err = gnutls_x509_crt_init(&crt);
+ if (err < 0) {
+ g_critical("Failed to initialize certificate %s", gnutls_strerror(err));
+ abort();
+ }
+ err = gnutls_x509_crt_set_key(crt, privkey);
+ if (err < 0) {
+ g_critical("Failed to set certificate key %s", gnutls_strerror(err));
+ abort();
+ }
+
+ /*
+ * A v3 certificate is required in order to be able
+ * set any of the basic constraints, key purpose and
+ * key usage data
+ */
+ gnutls_x509_crt_set_version(crt, 3);
+
+ if (req->country) {
+ err = gnutls_x509_crt_set_dn_by_oid(
+ crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
+ req->country, strlen(req->country));
+ if (err < 0) {
+ g_critical("Failed to set certificate country name %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ }
+ if (req->cn) {
+ err = gnutls_x509_crt_set_dn_by_oid(
+ crt, GNUTLS_OID_X520_COMMON_NAME, 0,
+ req->cn, strlen(req->cn));
+ if (err < 0) {
+ g_critical("Failed to set certificate common name %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ }
+
+ /*
+ * Setup the subject altnames, which are used
+ * for hostname checks in live sessions
+ */
+ if (req->altname1) {
+ err = gnutls_x509_crt_set_subject_alt_name(
+ crt, GNUTLS_SAN_DNSNAME,
+ req->altname1,
+ strlen(req->altname1),
+ GNUTLS_FSAN_APPEND);
+ if (err < 0) {
+ g_critical("Failed to set certificate alt name %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ }
+ if (req->altname2) {
+ err = gnutls_x509_crt_set_subject_alt_name(
+ crt, GNUTLS_SAN_DNSNAME,
+ req->altname2,
+ strlen(req->altname2),
+ GNUTLS_FSAN_APPEND);
+ if (err < 0) {
+ g_critical("Failed to set certificate %s alt name",
+ gnutls_strerror(err));
+ abort();
+ }
+ }
+
+ /*
+ * IP address need to be put into the cert in their
+ * raw byte form, not strings, hence this is a little
+ * more complicated
+ */
+ if (req->ipaddr1) {
+ char *data;
+ int len;
+
+ test_tls_get_ipaddr(req->ipaddr1, &data, &len);
+
+ err = gnutls_x509_crt_set_subject_alt_name(
+ crt, GNUTLS_SAN_IPADDRESS,
+ data, len, GNUTLS_FSAN_APPEND);
+ if (err < 0) {
+ g_critical("Failed to set certificate alt name %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ g_free(data);
+ }
+ if (req->ipaddr2) {
+ char *data;
+ int len;
+
+ test_tls_get_ipaddr(req->ipaddr2, &data, &len);
+
+ err = gnutls_x509_crt_set_subject_alt_name(
+ crt, GNUTLS_SAN_IPADDRESS,
+ data, len, GNUTLS_FSAN_APPEND);
+ if (err < 0) {
+ g_critical("Failed to set certificate alt name %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ g_free(data);
+ }
+
+
+ /*
+ * Basic constraints are used to decide if the cert
+ * is for a CA or not. We can't use the convenient
+ * gnutls API for setting this, since it hardcodes
+ * the 'critical' field which we want control over
+ */
+ if (req->basicConstraintsEnable) {
+ ASN1_TYPE ext = ASN1_TYPE_EMPTY;
+
+ asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext);
+ asn1_write_value(ext, "cA",
+ req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1);
+ asn1_write_value(ext, "pathLenConstraint", NULL, 0);
+ test_tls_der_encode(ext, "", &der);
+ err = gnutls_x509_crt_set_extension_by_oid(
+ crt, "2.5.29.19",
+ der.data, der.size,
+ req->basicConstraintsCritical);
+ if (err < 0) {
+ g_critical("Failed to set certificate basic constraints %s",
+ gnutls_strerror(err));
+ g_free(der.data);
+ abort();
+ }
+ asn1_delete_structure(&ext);
+ g_free(der.data);
+ }
+
+ /*
+ * Next up the key usage extension. Again we can't
+ * use the gnutls API since it hardcodes the extension
+ * to be 'critical'
+ */
+ if (req->keyUsageEnable) {
+ ASN1_TYPE ext = ASN1_TYPE_EMPTY;
+ char str[2];
+
+ str[0] = req->keyUsageValue & 0xff;
+ str[1] = (req->keyUsageValue >> 8) & 0xff;
+
+ asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext);
+ asn1_write_value(ext, "", str, 9);
+ test_tls_der_encode(ext, "", &der);
+ err = gnutls_x509_crt_set_extension_by_oid(
+ crt, "2.5.29.15",
+ der.data, der.size,
+ req->keyUsageCritical);
+ if (err < 0) {
+ g_critical("Failed to set certificate key usage %s",
+ gnutls_strerror(err));
+ g_free(der.data);
+ abort();
+ }
+ asn1_delete_structure(&ext);
+ g_free(der.data);
+ }
+
+ /*
+ * Finally the key purpose extension. This time
+ * gnutls has the opposite problem, always hardcoding
+ * it to be non-critical. So once again we have to
+ * set this the hard way building up ASN1 data ourselves
+ */
+ if (req->keyPurposeEnable) {
+ ASN1_TYPE ext = ASN1_TYPE_EMPTY;
+
+ asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext);
+ if (req->keyPurposeOID1) {
+ asn1_write_value(ext, "", "NEW", 1);
+ asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1);
+ }
+ if (req->keyPurposeOID2) {
+ asn1_write_value(ext, "", "NEW", 1);
+ asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1);
+ }
+ test_tls_der_encode(ext, "", &der);
+ err = gnutls_x509_crt_set_extension_by_oid(
+ crt, "2.5.29.37",
+ der.data, der.size,
+ req->keyPurposeCritical);
+ if (err < 0) {
+ g_critical("Failed to set certificate key purpose %s",
+ gnutls_strerror(err));
+ g_free(der.data);
+ abort();
+ }
+ asn1_delete_structure(&ext);
+ g_free(der.data);
+ }
+
+ /*
+ * Any old serial number will do, so lets pick 5
+ */
+ err = gnutls_x509_crt_set_serial(crt, serial, 5);
+ if (err < 0) {
+ g_critical("Failed to set certificate serial %s",
+ gnutls_strerror(err));
+ abort();
+ }
+
+ err = gnutls_x509_crt_set_activation_time(crt, start);
+ if (err < 0) {
+ g_critical("Failed to set certificate activation %s",
+ gnutls_strerror(err));
+ abort();
+ }
+ err = gnutls_x509_crt_set_expiration_time(crt, expire);
+ if (err < 0) {
+ g_critical("Failed to set certificate expiration %s",
+ gnutls_strerror(err));
+ abort();
+ }
+
+
+ /*
+ * If no 'ca' is set then we are self signing
+ * the cert. This is done for the root CA certs
+ */
+ err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey);
+ if (err < 0) {
+ g_critical("Failed to sign certificate %s",
+ gnutls_strerror(err));
+ abort();
+ }
+
+ /*
+ * Finally write the new cert out to disk
+ */
+ err = gnutls_x509_crt_export(
+ crt, GNUTLS_X509_FMT_PEM, buffer, &size);
+ if (err < 0) {
+ g_critical("Failed to export certificate %s: %d",
+ gnutls_strerror(err), err);
+ abort();
+ }
+
+ if (!g_file_set_contents(req->filename, buffer, -1, NULL)) {
+ g_critical("Failed to write certificate %s",
+ req->filename);
+ abort();
+ }
+
+ req->crt = crt;
+}
+
+
+void test_tls_write_cert_chain(const char *filename,
+ gnutls_x509_crt_t *certs,
+ size_t ncerts)
+{
+ size_t i;
+ size_t capacity = 1024, offset = 0;
+ char *buffer = g_new0(char, capacity);
+ int err;
+
+ for (i = 0; i < ncerts; i++) {
+ size_t len = capacity - offset;
+ retry:
+ err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM,
+ buffer + offset, &len);
+ if (err < 0) {
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ buffer = g_renew(char, buffer, offset + len);
+ capacity = offset + len;
+ goto retry;
+ }
+ g_critical("Failed to export certificate chain %s: %d",
+ gnutls_strerror(err), err);
+ abort();
+ }
+ offset += len;
+ }
+
+ if (!g_file_set_contents(filename, buffer, offset, NULL)) {
+ abort();
+ }
+}
+
+
+void test_tls_discard_cert(QCryptoTLSTestCertReq *req)
+{
+ if (!req->crt) {
+ return;
+ }
+
+ gnutls_x509_crt_deinit(req->crt);
+ req->crt = NULL;
+
+ if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) {
+ unlink(req->filename);
+ }
+}
+
+#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */
diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h
new file mode 100644
index 0000000000..356b49cd5a
--- /dev/null
+++ b/tests/crypto-tls-x509-helpers.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#if !(defined WIN32) && \
+ defined(CONFIG_TASN1) && \
+ defined(LIBGNUTLS_VERSION_NUMBER) && \
+ (LIBGNUTLS_VERSION_NUMBER >= 0x020600)
+# define QCRYPTO_HAVE_TLS_TEST_SUPPORT
+#endif
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+# include <libtasn1.h>
+
+# include "qemu-common.h"
+
+/*
+ * This contains parameter about how to generate
+ * certificates.
+ */
+typedef struct QCryptoTLSTestCertReq QCryptoTLSTestCertReq;
+struct QCryptoTLSTestCertReq {
+ gnutls_x509_crt_t crt;
+
+ const char *filename;
+
+ /* Identifying information */
+ const char *country;
+ const char *cn;
+ const char *altname1;
+ const char *altname2;
+ const char *ipaddr1;
+ const char *ipaddr2;
+
+ /* Basic constraints */
+ bool basicConstraintsEnable;
+ bool basicConstraintsCritical;
+ bool basicConstraintsIsCA;
+
+ /* Key usage */
+ bool keyUsageEnable;
+ bool keyUsageCritical;
+ int keyUsageValue;
+
+ /* Key purpose (aka Extended key usage) */
+ bool keyPurposeEnable;
+ bool keyPurposeCritical;
+ const char *keyPurposeOID1;
+ const char *keyPurposeOID2;
+
+ /* zero for current time, or non-zero for hours from now */
+ int start_offset;
+ /* zero for 24 hours from now, or non-zero for hours from now */
+ int expire_offset;
+};
+
+void test_tls_generate_cert(QCryptoTLSTestCertReq *req,
+ gnutls_x509_crt_t ca);
+void test_tls_write_cert_chain(const char *filename,
+ gnutls_x509_crt_t *certs,
+ size_t ncerts);
+void test_tls_discard_cert(QCryptoTLSTestCertReq *req);
+
+void test_tls_init(const char *keyfile);
+void test_tls_cleanup(const char *keyfile);
+
+# define TLS_CERT_REQ(varname, cavarname, \
+ country, commonname, \
+ altname1, altname2, \
+ ipaddr1, ipaddr2, \
+ basicconsenable, basicconscritical, basicconsca, \
+ keyusageenable, keyusagecritical, keyusagevalue, \
+ keypurposeenable, keypurposecritical, \
+ keypurposeoid1, keypurposeoid2, \
+ startoffset, endoffset) \
+ static QCryptoTLSTestCertReq varname = { \
+ NULL, WORKDIR #varname "-ctx.pem", \
+ country, commonname, altname1, altname2, \
+ ipaddr1, ipaddr2, \
+ basicconsenable, basicconscritical, basicconsca, \
+ keyusageenable, keyusagecritical, keyusagevalue, \
+ keypurposeenable, keypurposecritical, \
+ keypurposeoid1, keypurposeoid2, \
+ startoffset, endoffset \
+ }; \
+ test_tls_generate_cert(&varname, cavarname.crt)
+
+# define TLS_ROOT_REQ(varname, \
+ country, commonname, \
+ altname1, altname2, \
+ ipaddr1, ipaddr2, \
+ basicconsenable, basicconscritical, basicconsca, \
+ keyusageenable, keyusagecritical, keyusagevalue, \
+ keypurposeenable, keypurposecritical, \
+ keypurposeoid1, keypurposeoid2, \
+ startoffset, endoffset) \
+ static QCryptoTLSTestCertReq varname = { \
+ NULL, WORKDIR #varname "-ctx.pem", \
+ country, commonname, altname1, altname2, \
+ ipaddr1, ipaddr2, \
+ basicconsenable, basicconscritical, basicconsca, \
+ keyusageenable, keyusagecritical, keyusagevalue, \
+ keypurposeenable, keypurposecritical, \
+ keypurposeoid1, keypurposeoid2, \
+ startoffset, endoffset \
+ }; \
+ test_tls_generate_cert(&varname, NULL)
+
+extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
+
+#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */
diff --git a/tests/pkix_asn1_tab.c b/tests/pkix_asn1_tab.c
new file mode 100644
index 0000000000..5b4e6b9e2d
--- /dev/null
+++ b/tests/pkix_asn1_tab.c
@@ -0,0 +1,1104 @@
+/*
+ * This file is taken from gnutls 1.6.3 under the GPLv2+
+ * and is under copyright of various GNUTLS contributors.
+ */
+
+#include <libtasn1.h>
+
+const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
+ {"PKIX1", 536875024, 0},
+ {0, 1073741836, 0},
+ {"id-ce", 1879048204, 0},
+ {"joint-iso-ccitt", 1073741825, "2"},
+ {"ds", 1073741825, "5"},
+ {0, 1, "29"},
+ {"id-ce-authorityKeyIdentifier", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "35"},
+ {"AuthorityKeyIdentifier", 1610612741, 0},
+ {"keyIdentifier", 1610637314, "KeyIdentifier"},
+ {0, 4104, "0"},
+ {"authorityCertIssuer", 1610637314, "GeneralNames"},
+ {0, 4104, "1"},
+ {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"},
+ {0, 4104, "2"},
+ {"KeyIdentifier", 1073741831, 0},
+ {"id-ce-subjectKeyIdentifier", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "14"},
+ {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"},
+ {"id-ce-keyUsage", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "15"},
+ {"KeyUsage", 1610874886, 0},
+ {"digitalSignature", 1073741825, "0"},
+ {"nonRepudiation", 1073741825, "1"},
+ {"keyEncipherment", 1073741825, "2"},
+ {"dataEncipherment", 1073741825, "3"},
+ {"keyAgreement", 1073741825, "4"},
+ {"keyCertSign", 1073741825, "5"},
+ {"cRLSign", 1073741825, "6"},
+ {"encipherOnly", 1073741825, "7"},
+ {"decipherOnly", 1, "8"},
+ {"id-ce-privateKeyUsagePeriod", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "16"},
+ {"PrivateKeyUsagePeriod", 1610612741, 0},
+ {"notBefore", 1619025937, 0},
+ {0, 4104, "0"},
+ {"notAfter", 545284113, 0},
+ {0, 4104, "1"},
+ {"id-ce-certificatePolicies", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "32"},
+ {"CertificatePolicies", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "PolicyInformation"},
+ {"PolicyInformation", 1610612741, 0},
+ {"policyIdentifier", 1073741826, "CertPolicyId"},
+ {"policyQualifiers", 538984459, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "PolicyQualifierInfo"},
+ {"CertPolicyId", 1073741836, 0},
+ {"PolicyQualifierInfo", 1610612741, 0},
+ {"policyQualifierId", 1073741826, "PolicyQualifierId"},
+ {"qualifier", 541065229, 0},
+ {"policyQualifierId", 1, 0},
+ {"PolicyQualifierId", 1073741836, 0},
+ {"CPSuri", 1073741826, "IA5String"},
+ {"UserNotice", 1610612741, 0},
+ {"noticeRef", 1073758210, "NoticeReference"},
+ {"explicitText", 16386, "DisplayText"},
+ {"NoticeReference", 1610612741, 0},
+ {"organization", 1073741826, "DisplayText"},
+ {"noticeNumbers", 536870923, 0},
+ {0, 3, 0},
+ {"DisplayText", 1610612754, 0},
+ {"visibleString", 1612709890, "VisibleString"},
+ {"200", 524298, "1"},
+ {"bmpString", 1612709890, "BMPString"},
+ {"200", 524298, "1"},
+ {"utf8String", 538968066, "UTF8String"},
+ {"200", 524298, "1"},
+ {"id-ce-policyMappings", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "33"},
+ {"PolicyMappings", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 536870917, 0},
+ {"issuerDomainPolicy", 1073741826, "CertPolicyId"},
+ {"subjectDomainPolicy", 2, "CertPolicyId"},
+ {"DirectoryString", 1610612754, 0},
+ {"teletexString", 1612709890, "TeletexString"},
+ {"MAX", 524298, "1"},
+ {"printableString", 1612709890, "PrintableString"},
+ {"MAX", 524298, "1"},
+ {"universalString", 1612709890, "UniversalString"},
+ {"MAX", 524298, "1"},
+ {"utf8String", 1612709890, "UTF8String"},
+ {"MAX", 524298, "1"},
+ {"bmpString", 1612709890, "BMPString"},
+ {"MAX", 524298, "1"},
+ {"ia5String", 538968066, "IA5String"},
+ {"MAX", 524298, "1"},
+ {"id-ce-subjectAltName", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "17"},
+ {"SubjectAltName", 1073741826, "GeneralNames"},
+ {"GeneralNames", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "GeneralName"},
+ {"GeneralName", 1610612754, 0},
+ {"otherName", 1610620930, "AnotherName"},
+ {0, 4104, "0"},
+ {"rfc822Name", 1610620930, "IA5String"},
+ {0, 4104, "1"},
+ {"dNSName", 1610620930, "IA5String"},
+ {0, 4104, "2"},
+ {"x400Address", 1610620930, "ORAddress"},
+ {0, 4104, "3"},
+ {"directoryName", 1610620930, "RDNSequence"},
+ {0, 2056, "4"},
+ {"ediPartyName", 1610620930, "EDIPartyName"},
+ {0, 4104, "5"},
+ {"uniformResourceIdentifier", 1610620930, "IA5String"},
+ {0, 4104, "6"},
+ {"iPAddress", 1610620935, 0},
+ {0, 4104, "7"},
+ {"registeredID", 536879116, 0},
+ {0, 4104, "8"},
+ {"AnotherName", 1610612741, 0},
+ {"type-id", 1073741836, 0},
+ {"value", 541073421, 0},
+ {0, 1073743880, "0"},
+ {"type-id", 1, 0},
+ {"EDIPartyName", 1610612741, 0},
+ {"nameAssigner", 1610637314, "DirectoryString"},
+ {0, 4104, "0"},
+ {"partyName", 536879106, "DirectoryString"},
+ {0, 4104, "1"},
+ {"id-ce-issuerAltName", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "18"},
+ {"IssuerAltName", 1073741826, "GeneralNames"},
+ {"id-ce-subjectDirectoryAttributes", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "9"},
+ {"SubjectDirectoryAttributes", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "Attribute"},
+ {"id-ce-basicConstraints", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "19"},
+ {"BasicConstraints", 1610612741, 0},
+ {"cA", 1610645508, 0},
+ {0, 131081, 0},
+ {"pathLenConstraint", 537411587, 0},
+ {"0", 10, "MAX"},
+ {"id-ce-nameConstraints", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "30"},
+ {"NameConstraints", 1610612741, 0},
+ {"permittedSubtrees", 1610637314, "GeneralSubtrees"},
+ {0, 4104, "0"},
+ {"excludedSubtrees", 536895490, "GeneralSubtrees"},
+ {0, 4104, "1"},
+ {"GeneralSubtrees", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "GeneralSubtree"},
+ {"GeneralSubtree", 1610612741, 0},
+ {"base", 1073741826, "GeneralName"},
+ {"minimum", 1610653698, "BaseDistance"},
+ {0, 1073741833, "0"},
+ {0, 4104, "0"},
+ {"maximum", 536895490, "BaseDistance"},
+ {0, 4104, "1"},
+ {"BaseDistance", 1611137027, 0},
+ {"0", 10, "MAX"},
+ {"id-ce-policyConstraints", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "36"},
+ {"PolicyConstraints", 1610612741, 0},
+ {"requireExplicitPolicy", 1610637314, "SkipCerts"},
+ {0, 4104, "0"},
+ {"inhibitPolicyMapping", 536895490, "SkipCerts"},
+ {0, 4104, "1"},
+ {"SkipCerts", 1611137027, 0},
+ {"0", 10, "MAX"},
+ {"id-ce-cRLDistributionPoints", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "31"},
+ {"CRLDistributionPoints", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "DistributionPoint"},
+ {"DistributionPoint", 1610612741, 0},
+ {"distributionPoint", 1610637314, "DistributionPointName"},
+ {0, 2056, "0"},
+ {"reasons", 1610637314, "ReasonFlags"},
+ {0, 4104, "1"},
+ {"cRLIssuer", 536895490, "GeneralNames"},
+ {0, 4104, "2"},
+ {"DistributionPointName", 1610612754, 0},
+ {"fullName", 1610620930, "GeneralNames"},
+ {0, 4104, "0"},
+ {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"},
+ {0, 4104, "1"},
+ {"ReasonFlags", 1610874886, 0},
+ {"unused", 1073741825, "0"},
+ {"keyCompromise", 1073741825, "1"},
+ {"cACompromise", 1073741825, "2"},
+ {"affiliationChanged", 1073741825, "3"},
+ {"superseded", 1073741825, "4"},
+ {"cessationOfOperation", 1073741825, "5"},
+ {"certificateHold", 1073741825, "6"},
+ {"privilegeWithdrawn", 1073741825, "7"},
+ {"aACompromise", 1, "8"},
+ {"id-ce-extKeyUsage", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "37"},
+ {"ExtKeyUsageSyntax", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "KeyPurposeId"},
+ {"KeyPurposeId", 1073741836, 0},
+ {"id-kp-serverAuth", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "1"},
+ {"id-kp-clientAuth", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "2"},
+ {"id-kp-codeSigning", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "3"},
+ {"id-kp-emailProtection", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "4"},
+ {"id-kp-ipsecEndSystem", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "5"},
+ {"id-kp-ipsecTunnel", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "6"},
+ {"id-kp-ipsecUser", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "7"},
+ {"id-kp-timeStamping", 1879048204, 0},
+ {0, 1073741825, "id-kp"},
+ {0, 1, "8"},
+ {"id-pe-authorityInfoAccess", 1879048204, 0},
+ {0, 1073741825, "id-pe"},
+ {0, 1, "1"},
+ {"AuthorityInfoAccessSyntax", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "AccessDescription"},
+ {"AccessDescription", 1610612741, 0},
+ {"accessMethod", 1073741836, 0},
+ {"accessLocation", 2, "GeneralName"},
+ {"id-ce-cRLNumber", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "20"},
+ {"CRLNumber", 1611137027, 0},
+ {"0", 10, "MAX"},
+ {"id-ce-issuingDistributionPoint", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "28"},
+ {"IssuingDistributionPoint", 1610612741, 0},
+ {"distributionPoint", 1610637314, "DistributionPointName"},
+ {0, 4104, "0"},
+ {"onlyContainsUserCerts", 1610653700, 0},
+ {0, 1073872905, 0},
+ {0, 4104, "1"},
+ {"onlyContainsCACerts", 1610653700, 0},
+ {0, 1073872905, 0},
+ {0, 4104, "2"},
+ {"onlySomeReasons", 1610637314, "ReasonFlags"},
+ {0, 4104, "3"},
+ {"indirectCRL", 536911876, 0},
+ {0, 1073872905, 0},
+ {0, 4104, "4"},
+ {"id-ce-deltaCRLIndicator", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "27"},
+ {"BaseCRLNumber", 1073741826, "CRLNumber"},
+ {"id-ce-cRLReasons", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "21"},
+ {"CRLReason", 1610874901, 0},
+ {"unspecified", 1073741825, "0"},
+ {"keyCompromise", 1073741825, "1"},
+ {"cACompromise", 1073741825, "2"},
+ {"affiliationChanged", 1073741825, "3"},
+ {"superseded", 1073741825, "4"},
+ {"cessationOfOperation", 1073741825, "5"},
+ {"certificateHold", 1073741825, "6"},
+ {"removeFromCRL", 1, "8"},
+ {"id-ce-certificateIssuer", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "29"},
+ {"CertificateIssuer", 1073741826, "GeneralNames"},
+ {"id-ce-holdInstructionCode", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "23"},
+ {"HoldInstructionCode", 1073741836, 0},
+ {"holdInstruction", 1879048204, 0},
+ {"joint-iso-itu-t", 1073741825, "2"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"x9cm", 1073741825, "10040"},
+ {0, 1, "2"},
+ {"id-holdinstruction-none", 1879048204, 0},
+ {0, 1073741825, "holdInstruction"},
+ {0, 1, "1"},
+ {"id-holdinstruction-callissuer", 1879048204, 0},
+ {0, 1073741825, "holdInstruction"},
+ {0, 1, "2"},
+ {"id-holdinstruction-reject", 1879048204, 0},
+ {0, 1073741825, "holdInstruction"},
+ {0, 1, "3"},
+ {"id-ce-invalidityDate", 1879048204, 0},
+ {0, 1073741825, "id-ce"},
+ {0, 1, "24"},
+ {"InvalidityDate", 1082130449, 0},
+ {"VisibleString", 1610620935, 0},
+ {0, 4360, "26"},
+ {"NumericString", 1610620935, 0},
+ {0, 4360, "18"},
+ {"IA5String", 1610620935, 0},
+ {0, 4360, "22"},
+ {"TeletexString", 1610620935, 0},
+ {0, 4360, "20"},
+ {"PrintableString", 1610620935, 0},
+ {0, 4360, "19"},
+ {"UniversalString", 1610620935, 0},
+ {0, 4360, "28"},
+ {"BMPString", 1610620935, 0},
+ {0, 4360, "30"},
+ {"UTF8String", 1610620935, 0},
+ {0, 4360, "12"},
+ {"id-pkix", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"identified-organization", 1073741825, "3"},
+ {"dod", 1073741825, "6"},
+ {"internet", 1073741825, "1"},
+ {"security", 1073741825, "5"},
+ {"mechanisms", 1073741825, "5"},
+ {"pkix", 1, "7"},
+ {"id-pe", 1879048204, 0},
+ {0, 1073741825, "id-pkix"},
+ {0, 1, "1"},
+ {"id-qt", 1879048204, 0},
+ {0, 1073741825, "id-pkix"},
+ {0, 1, "2"},
+ {"id-kp", 1879048204, 0},
+ {0, 1073741825, "id-pkix"},
+ {0, 1, "3"},
+ {"id-ad", 1879048204, 0},
+ {0, 1073741825, "id-pkix"},
+ {0, 1, "48"},
+ {"id-qt-cps", 1879048204, 0},
+ {0, 1073741825, "id-qt"},
+ {0, 1, "1"},
+ {"id-qt-unotice", 1879048204, 0},
+ {0, 1073741825, "id-qt"},
+ {0, 1, "2"},
+ {"id-ad-ocsp", 1879048204, 0},
+ {0, 1073741825, "id-ad"},
+ {0, 1, "1"},
+ {"id-ad-caIssuers", 1879048204, 0},
+ {0, 1073741825, "id-ad"},
+ {0, 1, "2"},
+ {"Attribute", 1610612741, 0},
+ {"type", 1073741826, "AttributeType"},
+ {"values", 536870927, 0},
+ {0, 2, "AttributeValue"},
+ {"AttributeType", 1073741836, 0},
+ {"AttributeValue", 1614807053, 0},
+ {"type", 1, 0},
+ {"AttributeTypeAndValue", 1610612741, 0},
+ {"type", 1073741826, "AttributeType"},
+ {"value", 2, "AttributeValue"},
+ {"id-at", 1879048204, 0},
+ {"joint-iso-ccitt", 1073741825, "2"},
+ {"ds", 1073741825, "5"},
+ {0, 1, "4"},
+ {"id-at-initials", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "43"},
+ {"X520initials", 1073741826, "DirectoryString"},
+ {"id-at-generationQualifier", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "44"},
+ {"X520generationQualifier", 1073741826, "DirectoryString"},
+ {"id-at-surname", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "4"},
+ {"X520surName", 1073741826, "DirectoryString"},
+ {"id-at-givenName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "42"},
+ {"X520givenName", 1073741826, "DirectoryString"},
+ {"id-at-name", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "41"},
+ {"X520name", 1073741826, "DirectoryString"},
+ {"id-at-commonName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "3"},
+ {"X520CommonName", 1073741826, "DirectoryString"},
+ {"id-at-localityName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "7"},
+ {"X520LocalityName", 1073741826, "DirectoryString"},
+ {"id-at-stateOrProvinceName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "8"},
+ {"X520StateOrProvinceName", 1073741826, "DirectoryString"},
+ {"id-at-organizationName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "10"},
+ {"X520OrganizationName", 1073741826, "DirectoryString"},
+ {"id-at-organizationalUnitName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "11"},
+ {"X520OrganizationalUnitName", 1073741826, "DirectoryString"},
+ {"id-at-title", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "12"},
+ {"X520Title", 1073741826, "DirectoryString"},
+ {"id-at-description", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "13"},
+ {"X520Description", 1073741826, "DirectoryString"},
+ {"id-at-dnQualifier", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "46"},
+ {"X520dnQualifier", 1073741826, "PrintableString"},
+ {"id-at-countryName", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "6"},
+ {"X520countryName", 1612709890, "PrintableString"},
+ {0, 1048586, "2"},
+ {"id-at-serialNumber", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "5"},
+ {"X520serialNumber", 1073741826, "PrintableString"},
+ {"id-at-telephoneNumber", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "20"},
+ {"X520telephoneNumber", 1073741826, "PrintableString"},
+ {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "23"},
+ {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"},
+ {"id-at-pseudonym", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "65"},
+ {"X520pseudonym", 1073741826, "DirectoryString"},
+ {"id-at-name", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "41"},
+ {"X520name", 1073741826, "DirectoryString"},
+ {"id-at-streetAddress", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "9"},
+ {"X520streetAddress", 1073741826, "DirectoryString"},
+ {"id-at-postalAddress", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-at"},
+ {0, 1, "16"},
+ {"X520postalAddress", 1073741826, "PostalAddress"},
+ {"PostalAddress", 1610612747, 0},
+ {0, 2, "DirectoryString"},
+ {"pkcs", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"rsadsi", 1073741825, "113549"},
+ {"pkcs", 1, "1"},
+ {"pkcs-9", 1879048204, 0},
+ {0, 1073741825, "pkcs"},
+ {0, 1, "9"},
+ {"emailAddress", 1880096780, "AttributeType"},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "1"},
+ {"Pkcs9email", 1612709890, "IA5String"},
+ {"ub-emailaddress-length", 524298, "1"},
+ {"Name", 1610612754, 0},
+ {"rdnSequence", 2, "RDNSequence"},
+ {"RDNSequence", 1610612747, 0},
+ {0, 2, "RelativeDistinguishedName"},
+ {"DistinguishedName", 1073741826, "RDNSequence"},
+ {"RelativeDistinguishedName", 1612709903, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "AttributeTypeAndValue"},
+ {"Certificate", 1610612741, 0},
+ {"tbsCertificate", 1073741826, "TBSCertificate"},
+ {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ {"signature", 6, 0},
+ {"TBSCertificate", 1610612741, 0},
+ {"version", 1610653698, "Version"},
+ {0, 1073741833, "v1"},
+ {0, 2056, "0"},
+ {"serialNumber", 1073741826, "CertificateSerialNumber"},
+ {"signature", 1073741826, "AlgorithmIdentifier"},
+ {"issuer", 1073741826, "Name"},
+ {"validity", 1073741826, "Validity"},
+ {"subject", 1073741826, "Name"},
+ {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"},
+ {"issuerUniqueID", 1610637314, "UniqueIdentifier"},
+ {0, 4104, "1"},
+ {"subjectUniqueID", 1610637314, "UniqueIdentifier"},
+ {0, 4104, "2"},
+ {"extensions", 536895490, "Extensions"},
+ {0, 2056, "3"},
+ {"Version", 1610874883, 0},
+ {"v1", 1073741825, "0"},
+ {"v2", 1073741825, "1"},
+ {"v3", 1, "2"},
+ {"CertificateSerialNumber", 1073741827, 0},
+ {"Validity", 1610612741, 0},
+ {"notBefore", 1073741826, "Time"},
+ {"notAfter", 2, "Time"},
+ {"Time", 1610612754, 0},
+ {"utcTime", 1090519057, 0},
+ {"generalTime", 8388625, 0},
+ {"UniqueIdentifier", 1073741830, 0},
+ {"SubjectPublicKeyInfo", 1610612741, 0},
+ {"algorithm", 1073741826, "AlgorithmIdentifier"},
+ {"subjectPublicKey", 6, 0},
+ {"Extensions", 1612709899, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "Extension"},
+ {"Extension", 1610612741, 0},
+ {"extnID", 1073741836, 0},
+ {"critical", 1610645508, 0},
+ {0, 131081, 0},
+ {"extnValue", 7, 0},
+ {"CertificateList", 1610612741, 0},
+ {"tbsCertList", 1073741826, "TBSCertList"},
+ {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ {"signature", 6, 0},
+ {"TBSCertList", 1610612741, 0},
+ {"version", 1073758210, "Version"},
+ {"signature", 1073741826, "AlgorithmIdentifier"},
+ {"issuer", 1073741826, "Name"},
+ {"thisUpdate", 1073741826, "Time"},
+ {"nextUpdate", 1073758210, "Time"},
+ {"revokedCertificates", 1610629131, 0},
+ {0, 536870917, 0},
+ {"userCertificate", 1073741826, "CertificateSerialNumber"},
+ {"revocationDate", 1073741826, "Time"},
+ {"crlEntryExtensions", 16386, "Extensions"},
+ {"crlExtensions", 536895490, "Extensions"},
+ {0, 2056, "0"},
+ {"AlgorithmIdentifier", 1610612741, 0},
+ {"algorithm", 1073741836, 0},
+ {"parameters", 541081613, 0},
+ {"algorithm", 1, 0},
+ {"pkcs-1", 1879048204, 0},
+ {0, 1073741825, "pkcs"},
+ {0, 1, "1"},
+ {"rsaEncryption", 1879048204, 0},
+ {0, 1073741825, "pkcs-1"},
+ {0, 1, "1"},
+ {"md2WithRSAEncryption", 1879048204, 0},
+ {0, 1073741825, "pkcs-1"},
+ {0, 1, "2"},
+ {"md5WithRSAEncryption", 1879048204, 0},
+ {0, 1073741825, "pkcs-1"},
+ {0, 1, "4"},
+ {"sha1WithRSAEncryption", 1879048204, 0},
+ {0, 1073741825, "pkcs-1"},
+ {0, 1, "5"},
+ {"id-dsa-with-sha1", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"x9-57", 1073741825, "10040"},
+ {"x9algorithm", 1073741825, "4"},
+ {0, 1, "3"},
+ {"Dss-Sig-Value", 1610612741, 0},
+ {"r", 1073741827, 0},
+ {"s", 3, 0},
+ {"dhpublicnumber", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"ansi-x942", 1073741825, "10046"},
+ {"number-type", 1073741825, "2"},
+ {0, 1, "1"},
+ {"DomainParameters", 1610612741, 0},
+ {"p", 1073741827, 0},
+ {"g", 1073741827, 0},
+ {"q", 1073741827, 0},
+ {"j", 1073758211, 0},
+ {"validationParms", 16386, "ValidationParms"},
+ {"ValidationParms", 1610612741, 0},
+ {"seed", 1073741830, 0},
+ {"pgenCounter", 3, 0},
+ {"id-dsa", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"x9-57", 1073741825, "10040"},
+ {"x9algorithm", 1073741825, "4"},
+ {0, 1, "1"},
+ {"Dss-Parms", 1610612741, 0},
+ {"p", 1073741827, 0},
+ {"q", 1073741827, 0},
+ {"g", 3, 0},
+ {"ORAddress", 1610612741, 0},
+ {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"},
+ {"built-in-domain-defined-attributes", 1073758210,
+ "BuiltInDomainDefinedAttributes"},
+ {"extension-attributes", 16386, "ExtensionAttributes"},
+ {"BuiltInStandardAttributes", 1610612741, 0},
+ {"country-name", 1073758210, "CountryName"},
+ {"administration-domain-name", 1073758210, "AdministrationDomainName"},
+ {"network-address", 1610637314, "NetworkAddress"},
+ {0, 2056, "0"},
+ {"terminal-identifier", 1610637314, "TerminalIdentifier"},
+ {0, 2056, "1"},
+ {"private-domain-name", 1610637314, "PrivateDomainName"},
+ {0, 2056, "2"},
+ {"organization-name", 1610637314, "OrganizationName"},
+ {0, 2056, "3"},
+ {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"},
+ {0, 2056, "4"},
+ {"personal-name", 1610637314, "PersonalName"},
+ {0, 2056, "5"},
+ {"organizational-unit-names", 536895490, "OrganizationalUnitNames"},
+ {0, 2056, "6"},
+ {"CountryName", 1610620946, 0},
+ {0, 1073746952, "1"},
+ {"x121-dcc-code", 1612709890, "NumericString"},
+ {0, 1048586, "ub-country-name-numeric-length"},
+ {"iso-3166-alpha2-code", 538968066, "PrintableString"},
+ {0, 1048586, "ub-country-name-alpha-length"},
+ {"AdministrationDomainName", 1610620946, 0},
+ {0, 1073744904, "2"},
+ {"numeric", 1612709890, "NumericString"},
+ {"ub-domain-name-length", 524298, "0"},
+ {"printable", 538968066, "PrintableString"},
+ {"ub-domain-name-length", 524298, "0"},
+ {"NetworkAddress", 1073741826, "X121Address"},
+ {"X121Address", 1612709890, "NumericString"},
+ {"ub-x121-address-length", 524298, "1"},
+ {"TerminalIdentifier", 1612709890, "PrintableString"},
+ {"ub-terminal-id-length", 524298, "1"},
+ {"PrivateDomainName", 1610612754, 0},
+ {"numeric", 1612709890, "NumericString"},
+ {"ub-domain-name-length", 524298, "1"},
+ {"printable", 538968066, "PrintableString"},
+ {"ub-domain-name-length", 524298, "1"},
+ {"OrganizationName", 1612709890, "PrintableString"},
+ {"ub-organization-name-length", 524298, "1"},
+ {"NumericUserIdentifier", 1612709890, "NumericString"},
+ {"ub-numeric-user-id-length", 524298, "1"},
+ {"PersonalName", 1610612750, 0},
+ {"surname", 1814044674, "PrintableString"},
+ {0, 1073745928, "0"},
+ {"ub-surname-length", 524298, "1"},
+ {"given-name", 1814061058, "PrintableString"},
+ {0, 1073745928, "1"},
+ {"ub-given-name-length", 524298, "1"},
+ {"initials", 1814061058, "PrintableString"},
+ {0, 1073745928, "2"},
+ {"ub-initials-length", 524298, "1"},
+ {"generation-qualifier", 740319234, "PrintableString"},
+ {0, 1073745928, "3"},
+ {"ub-generation-qualifier-length", 524298, "1"},
+ {"OrganizationalUnitNames", 1612709899, 0},
+ {"ub-organizational-units", 1074266122, "1"},
+ {0, 2, "OrganizationalUnitName"},
+ {"OrganizationalUnitName", 1612709890, "PrintableString"},
+ {"ub-organizational-unit-name-length", 524298, "1"},
+ {"BuiltInDomainDefinedAttributes", 1612709899, 0},
+ {"ub-domain-defined-attributes", 1074266122, "1"},
+ {0, 2, "BuiltInDomainDefinedAttribute"},
+ {"BuiltInDomainDefinedAttribute", 1610612741, 0},
+ {"type", 1612709890, "PrintableString"},
+ {"ub-domain-defined-attribute-type-length", 524298, "1"},
+ {"value", 538968066, "PrintableString"},
+ {"ub-domain-defined-attribute-value-length", 524298, "1"},
+ {"ExtensionAttributes", 1612709903, 0},
+ {"ub-extension-attributes", 1074266122, "1"},
+ {0, 2, "ExtensionAttribute"},
+ {"ExtensionAttribute", 1610612741, 0},
+ {"extension-attribute-type", 1611145219, 0},
+ {0, 1073743880, "0"},
+ {"0", 10, "ub-extension-attributes"},
+ {"extension-attribute-value", 541073421, 0},
+ {0, 1073743880, "1"},
+ {"extension-attribute-type", 1, 0},
+ {"common-name", 1342177283, "1"},
+ {"CommonName", 1612709890, "PrintableString"},
+ {"ub-common-name-length", 524298, "1"},
+ {"teletex-common-name", 1342177283, "2"},
+ {"TeletexCommonName", 1612709890, "TeletexString"},
+ {"ub-common-name-length", 524298, "1"},
+ {"teletex-organization-name", 1342177283, "3"},
+ {"TeletexOrganizationName", 1612709890, "TeletexString"},
+ {"ub-organization-name-length", 524298, "1"},
+ {"teletex-personal-name", 1342177283, "4"},
+ {"TeletexPersonalName", 1610612750, 0},
+ {"surname", 1814044674, "TeletexString"},
+ {0, 1073743880, "0"},
+ {"ub-surname-length", 524298, "1"},
+ {"given-name", 1814061058, "TeletexString"},
+ {0, 1073743880, "1"},
+ {"ub-given-name-length", 524298, "1"},
+ {"initials", 1814061058, "TeletexString"},
+ {0, 1073743880, "2"},
+ {"ub-initials-length", 524298, "1"},
+ {"generation-qualifier", 740319234, "TeletexString"},
+ {0, 1073743880, "3"},
+ {"ub-generation-qualifier-length", 524298, "1"},
+ {"teletex-organizational-unit-names", 1342177283, "5"},
+ {"TeletexOrganizationalUnitNames", 1612709899, 0},
+ {"ub-organizational-units", 1074266122, "1"},
+ {0, 2, "TeletexOrganizationalUnitName"},
+ {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"},
+ {"ub-organizational-unit-name-length", 524298, "1"},
+ {"pds-name", 1342177283, "7"},
+ {"PDSName", 1612709890, "PrintableString"},
+ {"ub-pds-name-length", 524298, "1"},
+ {"physical-delivery-country-name", 1342177283, "8"},
+ {"PhysicalDeliveryCountryName", 1610612754, 0},
+ {"x121-dcc-code", 1612709890, "NumericString"},
+ {0, 1048586, "ub-country-name-numeric-length"},
+ {"iso-3166-alpha2-code", 538968066, "PrintableString"},
+ {0, 1048586, "ub-country-name-alpha-length"},
+ {"postal-code", 1342177283, "9"},
+ {"PostalCode", 1610612754, 0},
+ {"numeric-code", 1612709890, "NumericString"},
+ {"ub-postal-code-length", 524298, "1"},
+ {"printable-code", 538968066, "PrintableString"},
+ {"ub-postal-code-length", 524298, "1"},
+ {"physical-delivery-office-name", 1342177283, "10"},
+ {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"},
+ {"physical-delivery-office-number", 1342177283, "11"},
+ {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"},
+ {"extension-OR-address-components", 1342177283, "12"},
+ {"ExtensionORAddressComponents", 1073741826, "PDSParameter"},
+ {"physical-delivery-personal-name", 1342177283, "13"},
+ {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"},
+ {"physical-delivery-organization-name", 1342177283, "14"},
+ {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"},
+ {"extension-physical-delivery-address-components", 1342177283, "15"},
+ {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"},
+ {"unformatted-postal-address", 1342177283, "16"},
+ {"UnformattedPostalAddress", 1610612750, 0},
+ {"printable-address", 1814052875, 0},
+ {"ub-pds-physical-address-lines", 1074266122, "1"},
+ {0, 538968066, "PrintableString"},
+ {"ub-pds-parameter-length", 524298, "1"},
+ {"teletex-string", 740311042, "TeletexString"},
+ {"ub-unformatted-address-length", 524298, "1"},
+ {"street-address", 1342177283, "17"},
+ {"StreetAddress", 1073741826, "PDSParameter"},
+ {"post-office-box-address", 1342177283, "18"},
+ {"PostOfficeBoxAddress", 1073741826, "PDSParameter"},
+ {"poste-restante-address", 1342177283, "19"},
+ {"PosteRestanteAddress", 1073741826, "PDSParameter"},
+ {"unique-postal-name", 1342177283, "20"},
+ {"UniquePostalName", 1073741826, "PDSParameter"},
+ {"local-postal-attributes", 1342177283, "21"},
+ {"LocalPostalAttributes", 1073741826, "PDSParameter"},
+ {"PDSParameter", 1610612750, 0},
+ {"printable-string", 1814052866, "PrintableString"},
+ {"ub-pds-parameter-length", 524298, "1"},
+ {"teletex-string", 740311042, "TeletexString"},
+ {"ub-pds-parameter-length", 524298, "1"},
+ {"extended-network-address", 1342177283, "22"},
+ {"ExtendedNetworkAddress", 1610612754, 0},
+ {"e163-4-address", 1610612741, 0},
+ {"number", 1612718082, "NumericString"},
+ {0, 1073743880, "0"},
+ {"ub-e163-4-number-length", 524298, "1"},
+ {"sub-address", 538992642, "NumericString"},
+ {0, 1073743880, "1"},
+ {"ub-e163-4-sub-address-length", 524298, "1"},
+ {"psap-address", 536879106, "PresentationAddress"},
+ {0, 2056, "0"},
+ {"PresentationAddress", 1610612741, 0},
+ {"pSelector", 1610637319, 0},
+ {0, 2056, "0"},
+ {"sSelector", 1610637319, 0},
+ {0, 2056, "1"},
+ {"tSelector", 1610637319, 0},
+ {0, 2056, "2"},
+ {"nAddresses", 538976271, 0},
+ {0, 1073743880, "3"},
+ {"MAX", 1074266122, "1"},
+ {0, 7, 0},
+ {"terminal-type", 1342177283, "23"},
+ {"TerminalType", 1610874883, 0},
+ {"telex", 1073741825, "3"},
+ {"teletex", 1073741825, "4"},
+ {"g3-facsimile", 1073741825, "5"},
+ {"g4-facsimile", 1073741825, "6"},
+ {"ia5-terminal", 1073741825, "7"},
+ {"videotex", 1, "8"},
+ {"teletex-domain-defined-attributes", 1342177283, "6"},
+ {"TeletexDomainDefinedAttributes", 1612709899, 0},
+ {"ub-domain-defined-attributes", 1074266122, "1"},
+ {0, 2, "TeletexDomainDefinedAttribute"},
+ {"TeletexDomainDefinedAttribute", 1610612741, 0},
+ {"type", 1612709890, "TeletexString"},
+ {"ub-domain-defined-attribute-type-length", 524298, "1"},
+ {"value", 538968066, "TeletexString"},
+ {"ub-domain-defined-attribute-value-length", 524298, "1"},
+ {"ub-name", 1342177283, "32768"},
+ {"ub-common-name", 1342177283, "64"},
+ {"ub-locality-name", 1342177283, "128"},
+ {"ub-state-name", 1342177283, "128"},
+ {"ub-organization-name", 1342177283, "64"},
+ {"ub-organizational-unit-name", 1342177283, "64"},
+ {"ub-title", 1342177283, "64"},
+ {"ub-match", 1342177283, "128"},
+ {"ub-emailaddress-length", 1342177283, "128"},
+ {"ub-common-name-length", 1342177283, "64"},
+ {"ub-country-name-alpha-length", 1342177283, "2"},
+ {"ub-country-name-numeric-length", 1342177283, "3"},
+ {"ub-domain-defined-attributes", 1342177283, "4"},
+ {"ub-domain-defined-attribute-type-length", 1342177283, "8"},
+ {"ub-domain-defined-attribute-value-length", 1342177283, "128"},
+ {"ub-domain-name-length", 1342177283, "16"},
+ {"ub-extension-attributes", 1342177283, "256"},
+ {"ub-e163-4-number-length", 1342177283, "15"},
+ {"ub-e163-4-sub-address-length", 1342177283, "40"},
+ {"ub-generation-qualifier-length", 1342177283, "3"},
+ {"ub-given-name-length", 1342177283, "16"},
+ {"ub-initials-length", 1342177283, "5"},
+ {"ub-integer-options", 1342177283, "256"},
+ {"ub-numeric-user-id-length", 1342177283, "32"},
+ {"ub-organization-name-length", 1342177283, "64"},
+ {"ub-organizational-unit-name-length", 1342177283, "32"},
+ {"ub-organizational-units", 1342177283, "4"},
+ {"ub-pds-name-length", 1342177283, "16"},
+ {"ub-pds-parameter-length", 1342177283, "30"},
+ {"ub-pds-physical-address-lines", 1342177283, "6"},
+ {"ub-postal-code-length", 1342177283, "16"},
+ {"ub-surname-length", 1342177283, "40"},
+ {"ub-terminal-id-length", 1342177283, "24"},
+ {"ub-unformatted-address-length", 1342177283, "180"},
+ {"ub-x121-address-length", 1342177283, "16"},
+ {"pkcs-7-ContentInfo", 1610612741, 0},
+ {"contentType", 1073741826, "pkcs-7-ContentType"},
+ {"content", 541073421, 0},
+ {0, 1073743880, "0"},
+ {"contentType", 1, 0},
+ {"pkcs-7-DigestInfo", 1610612741, 0},
+ {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"},
+ {"digest", 2, "pkcs-7-Digest"},
+ {"pkcs-7-Digest", 1073741831, 0},
+ {"pkcs-7-ContentType", 1073741836, 0},
+ {"pkcs-7-SignedData", 1610612741, 0},
+ {"version", 1073741826, "pkcs-7-CMSVersion"},
+ {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"},
+ {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"},
+ {"certificates", 1610637314, "pkcs-7-CertificateSet"},
+ {0, 4104, "0"},
+ {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"},
+ {0, 4104, "1"},
+ {"signerInfos", 2, "pkcs-7-SignerInfos"},
+ {"pkcs-7-CMSVersion", 1610874883, 0},
+ {"v0", 1073741825, "0"},
+ {"v1", 1073741825, "1"},
+ {"v2", 1073741825, "2"},
+ {"v3", 1073741825, "3"},
+ {"v4", 1, "4"},
+ {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0},
+ {0, 2, "pkcs-7-DigestAlgorithmIdentifier"},
+ {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+ {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0},
+ {"eContentType", 1073741826, "pkcs-7-ContentType"},
+ {"eContent", 536895495, 0},
+ {0, 2056, "0"},
+ {"pkcs-7-CertificateRevocationLists", 1610612751, 0},
+ {0, 13, 0},
+ {"pkcs-7-CertificateChoices", 1610612754, 0},
+ {"certificate", 13, 0},
+ {"pkcs-7-CertificateSet", 1610612751, 0},
+ {0, 2, "pkcs-7-CertificateChoices"},
+ {"pkcs-7-SignerInfos", 1610612751, 0},
+ {0, 13, 0},
+ {"pkcs-10-CertificationRequestInfo", 1610612741, 0},
+ {"version", 1610874883, 0},
+ {"v1", 1, "0"},
+ {"subject", 1073741826, "Name"},
+ {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"},
+ {"attributes", 536879106, "Attributes"},
+ {0, 4104, "0"},
+ {"Attributes", 1610612751, 0},
+ {0, 2, "Attribute"},
+ {"pkcs-10-CertificationRequest", 1610612741, 0},
+ {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"},
+ {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ {"signature", 6, 0},
+ {"pkcs-9-ub-challengePassword", 1342177283, "255"},
+ {"pkcs-9-certTypes", 1879048204, 0},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "22"},
+ {"pkcs-9-crlTypes", 1879048204, 0},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "23"},
+ {"pkcs-9-at-challengePassword", 1879048204, 0},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "7"},
+ {"pkcs-9-challengePassword", 1610612754, 0},
+ {"printableString", 1612709890, "PrintableString"},
+ {"pkcs-9-ub-challengePassword", 524298, "1"},
+ {"utf8String", 538968066, "UTF8String"},
+ {"pkcs-9-ub-challengePassword", 524298, "1"},
+ {"pkcs-9-at-localKeyId", 1879048204, 0},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "21"},
+ {"pkcs-9-localKeyId", 1073741831, 0},
+ {"pkcs-9-at-friendlyName", 1879048204, 0},
+ {0, 1073741825, "pkcs-9"},
+ {0, 1, "20"},
+ {"pkcs-9-friendlyName", 1612709890, "BMPString"},
+ {"255", 524298, "1"},
+ {"pkcs-8-PrivateKeyInfo", 1610612741, 0},
+ {"version", 1073741826, "pkcs-8-Version"},
+ {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ {"privateKey", 1073741826, "pkcs-8-PrivateKey"},
+ {"attributes", 536895490, "Attributes"},
+ {0, 4104, "0"},
+ {"pkcs-8-Version", 1610874883, 0},
+ {"v1", 1, "0"},
+ {"pkcs-8-PrivateKey", 1073741831, 0},
+ {"pkcs-8-Attributes", 1610612751, 0},
+ {0, 2, "Attribute"},
+ {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0},
+ {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"},
+ {"encryptedData", 2, "pkcs-8-EncryptedData"},
+ {"pkcs-8-EncryptedData", 1073741831, 0},
+ {"pkcs-5", 1879048204, 0},
+ {0, 1073741825, "pkcs"},
+ {0, 1, "5"},
+ {"pkcs-5-encryptionAlgorithm", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"rsadsi", 1073741825, "113549"},
+ {0, 1, "3"},
+ {"pkcs-5-des-EDE3-CBC", 1879048204, 0},
+ {0, 1073741825, "pkcs-5-encryptionAlgorithm"},
+ {0, 1, "7"},
+ {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0},
+ {0, 1048586, "8"},
+ {"pkcs-5-id-PBES2", 1879048204, 0},
+ {0, 1073741825, "pkcs-5"},
+ {0, 1, "13"},
+ {"pkcs-5-PBES2-params", 1610612741, 0},
+ {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
+ {"encryptionScheme", 2, "AlgorithmIdentifier"},
+ {"pkcs-5-id-PBKDF2", 1879048204, 0},
+ {0, 1073741825, "pkcs-5"},
+ {0, 1, "12"},
+ {"pkcs-5-PBKDF2-params", 1610612741, 0},
+ {"salt", 1610612754, 0},
+ {"specified", 1073741831, 0},
+ {"otherSource", 2, "AlgorithmIdentifier"},
+ {"iterationCount", 1611137027, 0},
+ {"1", 10, "MAX"},
+ {"keyLength", 1611153411, 0},
+ {"1", 10, "MAX"},
+ {"prf", 16386, "AlgorithmIdentifier"},
+ {"pkcs-12", 1879048204, 0},
+ {0, 1073741825, "pkcs"},
+ {0, 1, "12"},
+ {"pkcs-12-PFX", 1610612741, 0},
+ {"version", 1610874883, 0},
+ {"v3", 1, "3"},
+ {"authSafe", 1073741826, "pkcs-7-ContentInfo"},
+ {"macData", 16386, "pkcs-12-MacData"},
+ {"pkcs-12-PbeParams", 1610612741, 0},
+ {"salt", 1073741831, 0},
+ {"iterations", 3, 0},
+ {"pkcs-12-MacData", 1610612741, 0},
+ {"mac", 1073741826, "pkcs-7-DigestInfo"},
+ {"macSalt", 1073741831, 0},
+ {"iterations", 536903683, 0},
+ {0, 9, "1"},
+ {"pkcs-12-AuthenticatedSafe", 1610612747, 0},
+ {0, 2, "pkcs-7-ContentInfo"},
+ {"pkcs-12-SafeContents", 1610612747, 0},
+ {0, 2, "pkcs-12-SafeBag"},
+ {"pkcs-12-SafeBag", 1610612741, 0},
+ {"bagId", 1073741836, 0},
+ {"bagValue", 1614815245, 0},
+ {0, 1073743880, "0"},
+ {"badId", 1, 0},
+ {"bagAttributes", 536887311, 0},
+ {0, 2, "pkcs-12-PKCS12Attribute"},
+ {"pkcs-12-bagtypes", 1879048204, 0},
+ {0, 1073741825, "pkcs-12"},
+ {0, 1073741825, "10"},
+ {0, 1, "1"},
+ {"pkcs-12-keyBag", 1879048204, 0},
+ {0, 1073741825, "pkcs-12-bagtypes"},
+ {0, 1, "1"},
+ {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0},
+ {0, 1073741825, "pkcs-12-bagtypes"},
+ {0, 1, "2"},
+ {"pkcs-12-certBag", 1879048204, 0},
+ {0, 1073741825, "pkcs-12-bagtypes"},
+ {0, 1, "3"},
+ {"pkcs-12-crlBag", 1879048204, 0},
+ {0, 1073741825, "pkcs-12-bagtypes"},
+ {0, 1, "4"},
+ {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"},
+ {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"},
+ {"pkcs-12-CertBag", 1610612741, 0},
+ {"certId", 1073741836, 0},
+ {"certValue", 541073421, 0},
+ {0, 1073743880, "0"},
+ {"certId", 1, 0},
+ {"pkcs-12-CRLBag", 1610612741, 0},
+ {"crlId", 1073741836, 0},
+ {"crlValue", 541073421, 0},
+ {0, 1073743880, "0"},
+ {"crlId", 1, 0},
+ {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"},
+ {"pkcs-7-data", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"rsadsi", 1073741825, "113549"},
+ {"pkcs", 1073741825, "1"},
+ {"pkcs7", 1073741825, "7"},
+ {0, 1, "1"},
+ {"pkcs-7-encryptedData", 1879048204, 0},
+ {"iso", 1073741825, "1"},
+ {"member-body", 1073741825, "2"},
+ {"us", 1073741825, "840"},
+ {"rsadsi", 1073741825, "113549"},
+ {"pkcs", 1073741825, "1"},
+ {"pkcs7", 1073741825, "7"},
+ {0, 1, "6"},
+ {"pkcs-7-Data", 1073741831, 0},
+ {"pkcs-7-EncryptedData", 1610612741, 0},
+ {"version", 1073741826, "pkcs-7-CMSVersion"},
+ {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"},
+ {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"},
+ {0, 4104, "1"},
+ {"pkcs-7-EncryptedContentInfo", 1610612741, 0},
+ {"contentType", 1073741826, "pkcs-7-ContentType"},
+ {"contentEncryptionAlgorithm", 1073741826,
+ "pkcs-7-ContentEncryptionAlgorithmIdentifier"},
+ {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"},
+ {0, 4104, "0"},
+ {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826,
+ "AlgorithmIdentifier"},
+ {"pkcs-7-EncryptedContent", 1073741831, 0},
+ {"pkcs-7-UnprotectedAttributes", 1612709903, 0},
+ {"MAX", 1074266122, "1"},
+ {0, 2, "Attribute"},
+ {"id-at-ldap-DC", 1880096780, "AttributeType"},
+ {0, 1073741825, "0"},
+ {0, 1073741825, "9"},
+ {0, 1073741825, "2342"},
+ {0, 1073741825, "19200300"},
+ {0, 1073741825, "100"},
+ {0, 1073741825, "1"},
+ {0, 1, "25"},
+ {"ldap-DC", 1073741826, "IA5String"},
+ {"id-at-ldap-UID", 1880096780, "AttributeType"},
+ {0, 1073741825, "0"},
+ {0, 1073741825, "9"},
+ {0, 1073741825, "2342"},
+ {0, 1073741825, "19200300"},
+ {0, 1073741825, "100"},
+ {0, 1073741825, "1"},
+ {0, 1, "1"},
+ {"ldap-UID", 1073741826, "DirectoryString"},
+ {"id-pda", 1879048204, 0},
+ {0, 1073741825, "id-pkix"},
+ {0, 1, "9"},
+ {"id-pda-dateOfBirth", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-pda"},
+ {0, 1, "1"},
+ {"DateOfBirth", 1082130449, 0},
+ {"id-pda-placeOfBirth", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-pda"},
+ {0, 1, "2"},
+ {"PlaceOfBirth", 1073741826, "DirectoryString"},
+ {"id-pda-gender", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-pda"},
+ {0, 1, "3"},
+ {"Gender", 1612709890, "PrintableString"},
+ {0, 1048586, "1"},
+ {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-pda"},
+ {0, 1, "4"},
+ {"CountryOfCitizenship", 1612709890, "PrintableString"},
+ {0, 1048586, "2"},
+ {"id-pda-countryOfResidence", 1880096780, "AttributeType"},
+ {0, 1073741825, "id-pda"},
+ {0, 1, "5"},
+ {"CountryOfResidence", 538968066, "PrintableString"},
+ {0, 1048586, "2"},
+ {0, 0, 0}
+};
diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err
new file mode 100644
index 0000000000..399f5f7af5
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-prefix.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
diff --git a/tests/qapi-schema/enum-bad-prefix.exit b/tests/qapi-schema/enum-bad-prefix.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-prefix.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-bad-prefix.json b/tests/qapi-schema/enum-bad-prefix.json
new file mode 100644
index 0000000000..996f628f6d
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-prefix.json
@@ -0,0 +1,2 @@
+# The prefix must be a string type
+{ 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] }
diff --git a/tests/qapi-schema/enum-bad-prefix.out b/tests/qapi-schema/enum-bad-prefix.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-prefix.out
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index a9e5aab927..677190c1ef 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -6,6 +6,11 @@
{ 'struct': 'NestedEnumsOne',
'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
+# for testing override of default naming heuristic
+{ 'enum': 'QEnumTwo',
+ 'prefix': 'QENUM_TWO',
+ 'data': [ 'value1', 'value2' ] }
+
# for testing nested structs
{ 'struct': 'UserDefOne',
'base': 'UserDefZero', # intentional forward reference
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index b0b7187226..32f17c5278 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,5 +1,6 @@
[OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]),
OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
+ OrderedDict([('enum', 'QEnumTwo'), ('prefix', 'QENUM_TWO'), ('data', ['value1', 'value2'])]),
OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
@@ -33,6 +34,7 @@
OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]),
OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])]
[{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']},
+ {'enum_name': 'QEnumTwo', 'enum_values': ['value1', 'value2']},
{'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']},
{'enum_name': 'UserDefAlternateKind', 'enum_values': None},
{'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None},
diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c
new file mode 100644
index 0000000000..c70aa5563a
--- /dev/null
+++ b/tests/test-crypto-tlscredsx509.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2015 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "config-host.h"
+#include "crypto-tls-x509-helpers.h"
+#include "crypto/tlscredsx509.h"
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+
+#define WORKDIR "tests/test-crypto-tlscredsx509-work/"
+#define KEYFILE WORKDIR "key-ctx.pem"
+
+struct QCryptoTLSCredsTestData {
+ bool isServer;
+ const char *cacrt;
+ const char *crt;
+ bool expectFail;
+};
+
+
+static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
+ const char *certdir,
+ Error **errp)
+{
+ Object *parent = object_get_objects_root();
+ Object *creds = object_new_with_props(
+ TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ "testtlscreds",
+ errp,
+ "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
+ "server" : "client"),
+ "dir", certdir,
+ "verify-peer", "yes",
+ "sanity-check", "yes",
+ NULL);
+
+ if (*errp) {
+ return NULL;
+ }
+ return QCRYPTO_TLS_CREDS(creds);
+}
+
+/*
+ * This tests sanity checking of our own certificates
+ *
+ * The code being tested is used when TLS creds are created,
+ * and aim to ensure QMEU has been configured with sane
+ * certificates. This allows us to give much much much
+ * clearer error messages to the admin when they misconfigure
+ * things.
+ */
+static void test_tls_creds(const void *opaque)
+{
+ struct QCryptoTLSCredsTestData *data =
+ (struct QCryptoTLSCredsTestData *)opaque;
+ QCryptoTLSCreds *creds;
+ Error *err = NULL;
+
+#define CERT_DIR "tests/test-crypto-tlscredsx509-certs/"
+ mkdir(CERT_DIR, 0700);
+
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ if (data->isServer) {
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+ } else {
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+ }
+
+ if (access(data->cacrt, R_OK) == 0) {
+ g_assert(link(data->cacrt,
+ CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
+ }
+ if (data->isServer) {
+ if (access(data->crt, R_OK) == 0) {
+ g_assert(link(data->crt,
+ CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
+ }
+ g_assert(link(KEYFILE,
+ CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
+ } else {
+ if (access(data->crt, R_OK) == 0) {
+ g_assert(link(data->crt,
+ CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
+ }
+ g_assert(link(KEYFILE,
+ CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
+ }
+
+ creds = test_tls_creds_create(
+ (data->isServer ?
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER :
+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT),
+ CERT_DIR,
+ &err);
+
+ if (data->expectFail) {
+ error_free(err);
+ g_assert(creds == NULL);
+ } else {
+ if (err) {
+ g_printerr("Failed to generate creds: %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ }
+ g_assert(creds != NULL);
+ }
+
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ if (data->isServer) {
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+ } else {
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+ }
+ rmdir(CERT_DIR);
+ if (creds) {
+ object_unparent(OBJECT(creds));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+ setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
+
+ mkdir(WORKDIR, 0700);
+
+ test_tls_init(KEYFILE);
+
+# define TLS_TEST_REG(name, isServer, caCrt, crt, expectFail) \
+ struct QCryptoTLSCredsTestData name = { \
+ isServer, caCrt, crt, expectFail \
+ }; \
+ g_test_add_data_func("/qcrypto/tlscredsx509/" # name, \
+ &name, test_tls_creds); \
+
+ /* A perfect CA, perfect client & perfect server */
+
+ /* Basic:CA:critical */
+ TLS_ROOT_REQ(cacertreq,
+ "UK", "qemu CA", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+
+ TLS_CERT_REQ(servercertreq, cacertreq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(clientcertreq, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ TLS_TEST_REG(perfectserver, true,
+ cacertreq.filename, servercertreq.filename, false);
+ TLS_TEST_REG(perfectclient, false,
+ cacertreq.filename, clientcertreq.filename, false);
+
+
+ /* Some other CAs which are good */
+
+ /* Basic:CA:critical */
+ TLS_ROOT_REQ(cacert1req,
+ "UK", "qemu CA 1", NULL, NULL, NULL, NULL,
+ true, true, true,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert1req, cacert1req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+
+ /* Basic:CA:not-critical */
+ TLS_ROOT_REQ(cacert2req,
+ "UK", "qemu CA 2", NULL, NULL, NULL, NULL,
+ true, false, true,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert2req, cacert2req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+
+ /* Key usage:cert-sign:critical */
+ TLS_ROOT_REQ(cacert3req,
+ "UK", "qemu CA 3", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert3req, cacert3req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+
+ TLS_TEST_REG(goodca1, true,
+ cacert1req.filename, servercert1req.filename, false);
+ TLS_TEST_REG(goodca2, true,
+ cacert2req.filename, servercert2req.filename, false);
+ TLS_TEST_REG(goodca3, true,
+ cacert3req.filename, servercert3req.filename, false);
+
+ /* Now some bad certs */
+
+ /* Key usage:dig-sig:not-critical */
+ TLS_ROOT_REQ(cacert4req,
+ "UK", "qemu CA 4", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, false, GNUTLS_KEY_DIGITAL_SIGNATURE,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert4req, cacert4req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* no-basic */
+ TLS_ROOT_REQ(cacert5req,
+ "UK", "qemu CA 5", NULL, NULL, NULL, NULL,
+ false, false, false,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert5req, cacert5req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* Key usage:dig-sig:critical */
+ TLS_ROOT_REQ(cacert6req,
+ "UK", "qemu CA 6", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_DIGITAL_SIGNATURE,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercert6req, cacert6req,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+
+ /* Technically a CA cert with basic constraints
+ * key purpose == key signing + non-critical should
+ * be rejected. GNUTLS < 3.1 does not reject it and
+ * we don't anticipate them changing this behaviour
+ */
+ TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename,
+ (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 1) ||
+ GNUTLS_VERSION_MAJOR > 3);
+ TLS_TEST_REG(badca2, true,
+ cacert5req.filename, servercert5req.filename, true);
+ TLS_TEST_REG(badca3, true,
+ cacert6req.filename, servercert6req.filename, true);
+
+
+ /* Various good servers */
+ /* no usage or purpose */
+ TLS_CERT_REQ(servercert7req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* usage:cert-sign+dig-sig+encipher:critical */
+ TLS_CERT_REQ(servercert8req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT |
+ GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* usage:cert-sign:not-critical */
+ TLS_CERT_REQ(servercert9req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, false, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* purpose:server:critical */
+ TLS_CERT_REQ(servercert10req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* purpose:server:not-critical */
+ TLS_CERT_REQ(servercert11req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* purpose:client+server:critical */
+ TLS_CERT_REQ(servercert12req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true,
+ GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER,
+ 0, 0);
+ /* purpose:client+server:not-critical */
+ TLS_CERT_REQ(servercert13req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, false,
+ GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER,
+ 0, 0);
+
+ TLS_TEST_REG(goodserver1, true,
+ cacertreq.filename, servercert7req.filename, false);
+ TLS_TEST_REG(goodserver2, true,
+ cacertreq.filename, servercert8req.filename, false);
+ TLS_TEST_REG(goodserver3, true,
+ cacertreq.filename, servercert9req.filename, false);
+ TLS_TEST_REG(goodserver4, true,
+ cacertreq.filename, servercert10req.filename, false);
+ TLS_TEST_REG(goodserver5, true,
+ cacertreq.filename, servercert11req.filename, false);
+ TLS_TEST_REG(goodserver6, true,
+ cacertreq.filename, servercert12req.filename, false);
+ TLS_TEST_REG(goodserver7, true,
+ cacertreq.filename, servercert13req.filename, false);
+
+ /* Bad servers */
+
+ /* usage:cert-sign:critical */
+ TLS_CERT_REQ(servercert14req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* purpose:client:critical */
+ TLS_CERT_REQ(servercert15req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+ /* usage: none:critical */
+ TLS_CERT_REQ(servercert16req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+
+ TLS_TEST_REG(badserver1, true,
+ cacertreq.filename, servercert14req.filename, true);
+ TLS_TEST_REG(badserver2, true,
+ cacertreq.filename, servercert15req.filename, true);
+ TLS_TEST_REG(badserver3, true,
+ cacertreq.filename, servercert16req.filename, true);
+
+
+
+ /* Various good clients */
+ /* no usage or purpose */
+ TLS_CERT_REQ(clientcert1req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* usage:cert-sign+dig-sig+encipher:critical */
+ TLS_CERT_REQ(clientcert2req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT |
+ GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* usage:cert-sign:not-critical */
+ TLS_CERT_REQ(clientcert3req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, false, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* purpose:client:critical */
+ TLS_CERT_REQ(clientcert4req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+ /* purpose:client:not-critical */
+ TLS_CERT_REQ(clientcert5req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+ /* purpose:client+client:critical */
+ TLS_CERT_REQ(clientcert6req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true,
+ GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER,
+ 0, 0);
+ /* purpose:client+client:not-critical */
+ TLS_CERT_REQ(clientcert7req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, false,
+ GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER,
+ 0, 0);
+
+ TLS_TEST_REG(goodclient1, false,
+ cacertreq.filename, clientcert1req.filename, false);
+ TLS_TEST_REG(goodclient2, false,
+ cacertreq.filename, clientcert2req.filename, false);
+ TLS_TEST_REG(goodclient3, false,
+ cacertreq.filename, clientcert3req.filename, false);
+ TLS_TEST_REG(goodclient4, false,
+ cacertreq.filename, clientcert4req.filename, false);
+ TLS_TEST_REG(goodclient5, false,
+ cacertreq.filename, clientcert5req.filename, false);
+ TLS_TEST_REG(goodclient6, false,
+ cacertreq.filename, clientcert6req.filename, false);
+ TLS_TEST_REG(goodclient7, false,
+ cacertreq.filename, clientcert7req.filename, false);
+
+ /* Bad clients */
+
+ /* usage:cert-sign:critical */
+ TLS_CERT_REQ(clientcert8req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ /* purpose:client:critical */
+ TLS_CERT_REQ(clientcert9req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ false, false, 0,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* usage: none:critical */
+ TLS_CERT_REQ(clientcert10req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+
+ TLS_TEST_REG(badclient1, false,
+ cacertreq.filename, clientcert8req.filename, true);
+ TLS_TEST_REG(badclient2, false,
+ cacertreq.filename, clientcert9req.filename, true);
+ TLS_TEST_REG(badclient3, false,
+ cacertreq.filename, clientcert10req.filename, true);
+
+
+
+ /* Expired stuff */
+
+ TLS_ROOT_REQ(cacertexpreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, -1);
+ TLS_CERT_REQ(servercertexpreq, cacertexpreq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercertexp1req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, -1);
+ TLS_CERT_REQ(clientcertexp1req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, -1);
+
+ TLS_TEST_REG(expired1, true,
+ cacertexpreq.filename, servercertexpreq.filename, true);
+ TLS_TEST_REG(expired2, true,
+ cacertreq.filename, servercertexp1req.filename, true);
+ TLS_TEST_REG(expired3, false,
+ cacertreq.filename, clientcertexp1req.filename, true);
+
+
+ /* Not activated stuff */
+
+ TLS_ROOT_REQ(cacertnewreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 1, 2);
+ TLS_CERT_REQ(servercertnewreq, cacertnewreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercertnew1req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 1, 2);
+ TLS_CERT_REQ(clientcertnew1req, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 1, 2);
+
+ TLS_TEST_REG(inactive1, true,
+ cacertnewreq.filename, servercertnewreq.filename, true);
+ TLS_TEST_REG(inactive2, true,
+ cacertreq.filename, servercertnew1req.filename, true);
+ TLS_TEST_REG(inactive3, false,
+ cacertreq.filename, clientcertnew1req.filename, true);
+
+ TLS_ROOT_REQ(cacertrootreq,
+ "UK", "qemu root", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel1areq, cacertrootreq,
+ "UK", "qemu level 1a", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel1breq, cacertrootreq,
+ "UK", "qemu level 1b", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq,
+ "UK", "qemu level 2a", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq,
+ "UK", "qemu client level 2b", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ gnutls_x509_crt_t certchain[] = {
+ cacertrootreq.crt,
+ cacertlevel1areq.crt,
+ cacertlevel1breq.crt,
+ cacertlevel2areq.crt,
+ };
+
+ test_tls_write_cert_chain(WORKDIR "cacertchain-ctx.pem",
+ certchain,
+ G_N_ELEMENTS(certchain));
+
+ TLS_TEST_REG(chain1, true,
+ WORKDIR "cacertchain-ctx.pem",
+ servercertlevel3areq.filename, false);
+ TLS_TEST_REG(chain2, false,
+ WORKDIR "cacertchain-ctx.pem",
+ clientcertlevel2breq.filename, false);
+
+ /* Some missing certs - first two are fatal, the last
+ * is ok
+ */
+ TLS_TEST_REG(missingca, true,
+ "cacertdoesnotexist.pem",
+ servercert1req.filename, true);
+ TLS_TEST_REG(missingserver, true,
+ cacert1req.filename,
+ "servercertdoesnotexist.pem", true);
+ TLS_TEST_REG(missingclient, false,
+ cacert1req.filename,
+ "clientcertdoesnotexist.pem", false);
+
+ ret = g_test_run();
+
+ test_tls_discard_cert(&cacertreq);
+ test_tls_discard_cert(&cacert1req);
+ test_tls_discard_cert(&cacert2req);
+ test_tls_discard_cert(&cacert3req);
+ test_tls_discard_cert(&cacert4req);
+ test_tls_discard_cert(&cacert5req);
+ test_tls_discard_cert(&cacert6req);
+
+ test_tls_discard_cert(&servercertreq);
+ test_tls_discard_cert(&servercert1req);
+ test_tls_discard_cert(&servercert2req);
+ test_tls_discard_cert(&servercert3req);
+ test_tls_discard_cert(&servercert4req);
+ test_tls_discard_cert(&servercert5req);
+ test_tls_discard_cert(&servercert6req);
+ test_tls_discard_cert(&servercert7req);
+ test_tls_discard_cert(&servercert8req);
+ test_tls_discard_cert(&servercert9req);
+ test_tls_discard_cert(&servercert10req);
+ test_tls_discard_cert(&servercert11req);
+ test_tls_discard_cert(&servercert12req);
+ test_tls_discard_cert(&servercert13req);
+ test_tls_discard_cert(&servercert14req);
+ test_tls_discard_cert(&servercert15req);
+ test_tls_discard_cert(&servercert16req);
+
+ test_tls_discard_cert(&clientcertreq);
+ test_tls_discard_cert(&clientcert1req);
+ test_tls_discard_cert(&clientcert2req);
+ test_tls_discard_cert(&clientcert3req);
+ test_tls_discard_cert(&clientcert4req);
+ test_tls_discard_cert(&clientcert5req);
+ test_tls_discard_cert(&clientcert6req);
+ test_tls_discard_cert(&clientcert7req);
+ test_tls_discard_cert(&clientcert8req);
+ test_tls_discard_cert(&clientcert9req);
+ test_tls_discard_cert(&clientcert10req);
+
+ test_tls_discard_cert(&cacertexpreq);
+ test_tls_discard_cert(&servercertexpreq);
+ test_tls_discard_cert(&servercertexp1req);
+ test_tls_discard_cert(&clientcertexp1req);
+
+ test_tls_discard_cert(&cacertnewreq);
+ test_tls_discard_cert(&servercertnewreq);
+ test_tls_discard_cert(&servercertnew1req);
+ test_tls_discard_cert(&clientcertnew1req);
+
+ test_tls_discard_cert(&cacertrootreq);
+ test_tls_discard_cert(&cacertlevel1areq);
+ test_tls_discard_cert(&cacertlevel1breq);
+ test_tls_discard_cert(&cacertlevel2areq);
+ test_tls_discard_cert(&servercertlevel3areq);
+ test_tls_discard_cert(&clientcertlevel2breq);
+ unlink(WORKDIR "cacertchain-ctx.pem");
+
+ test_tls_cleanup(KEYFILE);
+ rmdir(WORKDIR);
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
+
+int
+main(void)
+{
+ return EXIT_SUCCESS;
+}
+
+#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c
new file mode 100644
index 0000000000..4524128fca
--- /dev/null
+++ b/tests/test-crypto-tlssession.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2015 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "config-host.h"
+#include "crypto-tls-x509-helpers.h"
+#include "crypto/tlscredsx509.h"
+#include "crypto/tlssession.h"
+#include "qom/object_interfaces.h"
+#include "qemu/sockets.h"
+#include "qemu/acl.h"
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+
+#define WORKDIR "tests/test-crypto-tlssession-work/"
+#define KEYFILE WORKDIR "key-ctx.pem"
+
+struct QCryptoTLSSessionTestData {
+ const char *servercacrt;
+ const char *clientcacrt;
+ const char *servercrt;
+ const char *clientcrt;
+ bool expectServerFail;
+ bool expectClientFail;
+ const char *hostname;
+ const char *const *wildcards;
+};
+
+
+static ssize_t testWrite(const char *buf, size_t len, void *opaque)
+{
+ int *fd = opaque;
+
+ return write(*fd, buf, len);
+}
+
+static ssize_t testRead(char *buf, size_t len, void *opaque)
+{
+ int *fd = opaque;
+
+ return read(*fd, buf, len);
+}
+
+static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
+ const char *certdir,
+ Error **errp)
+{
+ Error *err = NULL;
+ Object *parent = object_get_objects_root();
+ Object *creds = object_new_with_props(
+ TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
+ "testtlscredsserver" : "testtlscredsclient"),
+ &err,
+ "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
+ "server" : "client"),
+ "dir", certdir,
+ "verify-peer", "yes",
+ /* We skip initial sanity checks here because we
+ * want to make sure that problems are being
+ * detected at the TLS session validation stage,
+ * and the test-crypto-tlscreds test already
+ * validate the sanity check code.
+ */
+ "sanity-check", "no",
+ NULL
+ );
+
+ if (err) {
+ error_propagate(errp, err);
+ return NULL;
+ }
+ return QCRYPTO_TLS_CREDS(creds);
+}
+
+
+/*
+ * This tests validation checking of peer certificates
+ *
+ * This is replicating the checks that are done for an
+ * active TLS session after handshake completes. To
+ * simulate that we create our TLS contexts, skipping
+ * sanity checks. We then get a socketpair, and
+ * initiate a TLS session across them. Finally do
+ * do actual cert validation tests
+ */
+static void test_crypto_tls_session(const void *opaque)
+{
+ struct QCryptoTLSSessionTestData *data =
+ (struct QCryptoTLSSessionTestData *)opaque;
+ QCryptoTLSCreds *clientCreds;
+ QCryptoTLSCreds *serverCreds;
+ QCryptoTLSSession *clientSess = NULL;
+ QCryptoTLSSession *serverSess = NULL;
+ qemu_acl *acl;
+ const char * const *wildcards;
+ int channel[2];
+ bool clientShake = false;
+ bool serverShake = false;
+ Error *err = NULL;
+ int ret;
+
+ /* We'll use this for our fake client-server connection */
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel);
+ g_assert(ret == 0);
+
+ /*
+ * We have an evil loop to do the handshake in a single
+ * thread, so we need these non-blocking to avoid deadlock
+ * of ourselves
+ */
+ qemu_set_nonblock(channel[0]);
+ qemu_set_nonblock(channel[1]);
+
+#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/"
+#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/"
+ mkdir(CLIENT_CERT_DIR, 0700);
+ mkdir(SERVER_CERT_DIR, 0700);
+
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+
+ g_assert(link(data->servercacrt,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
+ g_assert(link(data->servercrt,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
+ g_assert(link(KEYFILE,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
+
+ g_assert(link(data->clientcacrt,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
+ g_assert(link(data->clientcrt,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
+ g_assert(link(KEYFILE,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
+
+ clientCreds = test_tls_creds_create(
+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
+ CLIENT_CERT_DIR,
+ &err);
+ g_assert(clientCreds != NULL);
+
+ serverCreds = test_tls_creds_create(
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ SERVER_CERT_DIR,
+ &err);
+ g_assert(serverCreds != NULL);
+
+ acl = qemu_acl_init("tlssessionacl");
+ qemu_acl_reset(acl);
+ wildcards = data->wildcards;
+ while (wildcards && *wildcards) {
+ qemu_acl_append(acl, 0, *wildcards);
+ wildcards++;
+ }
+
+ /* Now the real part of the test, setup the sessions */
+ clientSess = qcrypto_tls_session_new(
+ clientCreds, data->hostname, NULL,
+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err);
+ serverSess = qcrypto_tls_session_new(
+ serverCreds, NULL,
+ data->wildcards ? "tlssessionacl" : NULL,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err);
+
+ g_assert(clientSess != NULL);
+ g_assert(serverSess != NULL);
+
+ /* For handshake to work, we need to set the I/O callbacks
+ * to read/write over the socketpair
+ */
+ qcrypto_tls_session_set_callbacks(serverSess,
+ testWrite, testRead,
+ &channel[0]);
+ qcrypto_tls_session_set_callbacks(clientSess,
+ testWrite, testRead,
+ &channel[1]);
+
+ /*
+ * Finally we loop around & around doing handshake on each
+ * session until we get an error, or the handshake completes.
+ * This relies on the socketpair being nonblocking to avoid
+ * deadlocking ourselves upon handshake
+ */
+ do {
+ int rv;
+ if (!serverShake) {
+ rv = qcrypto_tls_session_handshake(serverSess,
+ &err);
+ g_assert(rv >= 0);
+ if (qcrypto_tls_session_get_handshake_status(serverSess) ==
+ QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
+ serverShake = true;
+ }
+ }
+ if (!clientShake) {
+ rv = qcrypto_tls_session_handshake(clientSess,
+ &err);
+ g_assert(rv >= 0);
+ if (qcrypto_tls_session_get_handshake_status(clientSess) ==
+ QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
+ clientShake = true;
+ }
+ }
+ } while (!clientShake && !serverShake);
+
+
+ /* Finally make sure the server validation does what
+ * we were expecting
+ */
+ if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) {
+ g_assert(data->expectServerFail);
+ error_free(err);
+ err = NULL;
+ } else {
+ g_assert(!data->expectServerFail);
+ }
+
+ /*
+ * And the same for the client validation check
+ */
+ if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) {
+ g_assert(data->expectClientFail);
+ error_free(err);
+ err = NULL;
+ } else {
+ g_assert(!data->expectClientFail);
+ }
+
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+
+ rmdir(CLIENT_CERT_DIR);
+ rmdir(SERVER_CERT_DIR);
+
+ object_unparent(OBJECT(serverCreds));
+ object_unparent(OBJECT(clientCreds));
+
+ qcrypto_tls_session_free(serverSess);
+ qcrypto_tls_session_free(clientSess);
+
+ close(channel[0]);
+ close(channel[1]);
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+ setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
+
+ mkdir(WORKDIR, 0700);
+
+ test_tls_init(KEYFILE);
+
+# define TEST_SESS_REG(name, caCrt, \
+ serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards) \
+ struct QCryptoTLSSessionTestData name = { \
+ caCrt, caCrt, serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards \
+ }; \
+ g_test_add_data_func("/qcrypto/tlssession/" # name, \
+ &name, test_crypto_tls_session); \
+
+
+# define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \
+ serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards) \
+ struct QCryptoTLSSessionTestData name = { \
+ serverCaCrt, clientCaCrt, serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards \
+ }; \
+ g_test_add_data_func("/qcrypto/tlssession/" # name, \
+ &name, test_crypto_tls_session); \
+
+ /* A perfect CA, perfect client & perfect server */
+
+ /* Basic:CA:critical */
+ TLS_ROOT_REQ(cacertreq,
+ "UK", "qemu CA", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+
+ TLS_ROOT_REQ(altcacertreq,
+ "UK", "qemu CA 1", NULL, NULL, NULL, NULL,
+ true, true, true,
+ false, false, 0,
+ false, false, NULL, NULL,
+ 0, 0);
+
+ TLS_CERT_REQ(servercertreq, cacertreq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(clientcertreq, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ TLS_CERT_REQ(clientcertaltreq, altcacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ TEST_SESS_REG(basicca, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ false, false, "qemu.org", NULL);
+ TEST_SESS_REG_EXT(differentca, cacertreq.filename,
+ altcacertreq.filename, servercertreq.filename,
+ clientcertaltreq.filename, true, true, "qemu.org", NULL);
+
+
+ /* When an altname is set, the CN is ignored, so it must be duplicated
+ * as an altname for it to match */
+ TLS_CERT_REQ(servercertalt1req, cacertreq,
+ "UK", "qemu.org", "www.qemu.org", "qemu.org",
+ "192.168.122.1", "fec0::dead:beaf",
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ /* This intentionally doesn't replicate */
+ TLS_CERT_REQ(servercertalt2req, cacertreq,
+ "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org",
+ "192.168.122.1", "fec0::dead:beaf",
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+
+ TEST_SESS_REG(altname1, cacertreq.filename,
+ servercertalt1req.filename, clientcertreq.filename,
+ false, false, "qemu.org", NULL);
+ TEST_SESS_REG(altname2, cacertreq.filename,
+ servercertalt1req.filename, clientcertreq.filename,
+ false, false, "www.qemu.org", NULL);
+ TEST_SESS_REG(altname3, cacertreq.filename,
+ servercertalt1req.filename, clientcertreq.filename,
+ false, true, "wiki.qemu.org", NULL);
+
+ TEST_SESS_REG(altname4, cacertreq.filename,
+ servercertalt2req.filename, clientcertreq.filename,
+ false, true, "qemu.org", NULL);
+ TEST_SESS_REG(altname5, cacertreq.filename,
+ servercertalt2req.filename, clientcertreq.filename,
+ false, false, "www.qemu.org", NULL);
+ TEST_SESS_REG(altname6, cacertreq.filename,
+ servercertalt2req.filename, clientcertreq.filename,
+ false, false, "wiki.qemu.org", NULL);
+
+ const char *const wildcards1[] = {
+ "C=UK,CN=dogfood",
+ NULL,
+ };
+ const char *const wildcards2[] = {
+ "C=UK,CN=qemu",
+ NULL,
+ };
+ const char *const wildcards3[] = {
+ "C=UK,CN=dogfood",
+ "C=UK,CN=qemu",
+ NULL,
+ };
+ const char *const wildcards4[] = {
+ "C=UK,CN=qemustuff",
+ NULL,
+ };
+ const char *const wildcards5[] = {
+ "C=UK,CN=qemu*",
+ NULL,
+ };
+ const char *const wildcards6[] = {
+ "C=UK,CN=*emu*",
+ NULL,
+ };
+
+ TEST_SESS_REG(wildcard1, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ true, false, "qemu.org", wildcards1);
+ TEST_SESS_REG(wildcard2, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ false, false, "qemu.org", wildcards2);
+ TEST_SESS_REG(wildcard3, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ false, false, "qemu.org", wildcards3);
+ TEST_SESS_REG(wildcard4, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ true, false, "qemu.org", wildcards4);
+ TEST_SESS_REG(wildcard5, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ false, false, "qemu.org", wildcards5);
+ TEST_SESS_REG(wildcard6, cacertreq.filename,
+ servercertreq.filename, clientcertreq.filename,
+ false, false, "qemu.org", wildcards6);
+
+ TLS_ROOT_REQ(cacertrootreq,
+ "UK", "qemu root", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel1areq, cacertrootreq,
+ "UK", "qemu level 1a", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel1breq, cacertrootreq,
+ "UK", "qemu level 1b", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq,
+ "UK", "qemu level 2a", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq,
+ "UK", "qemu client level 2b", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ gnutls_x509_crt_t certchain[] = {
+ cacertrootreq.crt,
+ cacertlevel1areq.crt,
+ cacertlevel1breq.crt,
+ cacertlevel2areq.crt,
+ };
+
+ test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem",
+ certchain,
+ G_N_ELEMENTS(certchain));
+
+ TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem",
+ servercertlevel3areq.filename, clientcertlevel2breq.filename,
+ false, false, "qemu.org", NULL);
+
+ ret = g_test_run();
+
+ test_tls_discard_cert(&clientcertreq);
+ test_tls_discard_cert(&clientcertaltreq);
+
+ test_tls_discard_cert(&servercertreq);
+ test_tls_discard_cert(&servercertalt1req);
+ test_tls_discard_cert(&servercertalt2req);
+
+ test_tls_discard_cert(&cacertreq);
+ test_tls_discard_cert(&altcacertreq);
+
+ test_tls_discard_cert(&cacertrootreq);
+ test_tls_discard_cert(&cacertlevel1areq);
+ test_tls_discard_cert(&cacertlevel1breq);
+ test_tls_discard_cert(&cacertlevel2areq);
+ test_tls_discard_cert(&servercertlevel3areq);
+ test_tls_discard_cert(&clientcertlevel2breq);
+ unlink(WORKDIR "cacertchain-sess.pem");
+
+ test_tls_cleanup(KEYFILE);
+ rmdir(WORKDIR);
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
+
+int
+main(void)
+{
+ return EXIT_SUCCESS;
+}
+
+#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
diff --git a/trace-events b/trace-events
index 3d1857b270..88a2f141e5 100644
--- a/trace-events
+++ b/trace-events
@@ -1667,3 +1667,21 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
# audio/ossaudio.c
oss_version(int version) "OSS version = %#x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
+
+# crypto/tlscreds.c
+qcrypto_tls_creds_load_dh(void *creds, const char *filename) "TLS creds load DH creds=%p filename=%s"
+qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) "TLS creds path creds=%p filename=%s path=%s"
+
+# crypto/tlscredsanon.c
+qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s"
+
+# crypto/tlscredsx509.c
+qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s"
+qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d"
+qcrypto_tls_creds_x509_check_key_usage(void *creds, const char *file, int status, int usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%d critical=%d"
+qcrypto_tls_creds_x509_check_key_purpose(void *creds, const char *file, int status, const char *usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%s critical=%d"
+qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "TLS creds x509 load cert creds=%p isServer=%d file=%s"
+qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s"
+
+# crypto/tlssession.c
+qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d"
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index c62d4d9722..0034fbb49f 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -2,7 +2,7 @@ vnc-obj-y += vnc.o
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o
-vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+vnc-obj-y += vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 62a5fc4bf1..fc732bdbac 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -525,21 +525,24 @@ void start_auth_sasl(VncState *vs)
goto authabort;
}
-#ifdef CONFIG_VNC_TLS
/* Inform SASL that we've got an external SSF layer from TLS/x509 */
if (vs->auth == VNC_AUTH_VENCRYPT &&
vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
- gnutls_cipher_algorithm_t cipher;
+ Error *local_err = NULL;
+ int keysize;
sasl_ssf_t ssf;
- cipher = gnutls_cipher_get(vs->tls.session);
- if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
- VNC_DEBUG("%s", "cannot TLS get cipher size\n");
+ keysize = qcrypto_tls_session_get_key_size(vs->tls,
+ &local_err);
+ if (keysize < 0) {
+ VNC_DEBUG("cannot TLS get cipher size: %s\n",
+ error_get_pretty(local_err));
+ error_free(local_err);
sasl_dispose(&vs->sasl.conn);
vs->sasl.conn = NULL;
goto authabort;
}
- ssf *= 8; /* tls key size is bytes, sasl wants bits */
+ ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
if (err != SASL_OK) {
@@ -549,20 +552,19 @@ void start_auth_sasl(VncState *vs)
vs->sasl.conn = NULL;
goto authabort;
}
- } else
-#endif /* CONFIG_VNC_TLS */
+ } else {
vs->sasl.wantSSF = 1;
+ }
memset (&secprops, 0, sizeof secprops);
- /* Inform SASL that we've got an external SSF layer from TLS */
- if (vs->vd->is_unix
-#ifdef CONFIG_VNC_TLS
- /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
- is not sufficiently strong */
- || (vs->auth == VNC_AUTH_VENCRYPT &&
- vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-#endif /* CONFIG_VNC_TLS */
- ) {
+ /* Inform SASL that we've got an external SSF layer from TLS.
+ *
+ * Disable SSF, if using TLS+x509+SASL only. TLS without x509
+ * is not sufficiently strong
+ */
+ if (vs->vd->is_unix ||
+ (vs->auth == VNC_AUTH_VENCRYPT &&
+ vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
secprops.min_ssf = 0;
secprops.max_ssf = 0;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 8fc965b4ad..44ac2fae63 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -67,38 +67,42 @@ static void vnc_tls_handshake_io(void *opaque);
static int vnc_start_vencrypt_handshake(VncState *vs)
{
- int ret;
-
- if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session))
- qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
- else
- qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
- vnc_client_error(vs);
- return -1;
+ Error *err = NULL;
+
+ if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
+ goto error;
}
- if (vs->vd->tls.x509verify) {
- if (vnc_tls_validate_certificate(vs) < 0) {
- VNC_DEBUG("Client verification failed\n");
- vnc_client_error(vs);
- return -1;
- } else {
- VNC_DEBUG("Client verification passed\n");
+ switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
+ case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
+ VNC_DEBUG("Handshake done, checking credentials\n");
+ if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
+ goto error;
}
- }
+ VNC_DEBUG("Client verification passed, starting TLS I/O\n");
+ qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+
+ start_auth_vencrypt_subauth(vs);
+ break;
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+ case QCRYPTO_TLS_HANDSHAKE_RECVING:
+ VNC_DEBUG("Handshake interrupted (blocking read)\n");
+ qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
+ break;
- start_auth_vencrypt_subauth(vs);
+ case QCRYPTO_TLS_HANDSHAKE_SENDING:
+ VNC_DEBUG("Handshake interrupted (blocking write)\n");
+ qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
+ break;
+ }
return 0;
+
+ error:
+ VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return -1;
}
static void vnc_tls_handshake_io(void *opaque)
@@ -110,14 +114,6 @@ static void vnc_tls_handshake_io(void *opaque)
}
-
-#define NEED_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-
-
static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
{
int auth = read_u32(data, 0);
@@ -128,15 +124,29 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
vnc_flush(vs);
vnc_client_error(vs);
} else {
+ Error *err = NULL;
VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
vnc_write_u8(vs, 1); /* Accept auth */
vnc_flush(vs);
- if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
- VNC_DEBUG("Failed to setup TLS\n");
+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
+ NULL,
+ vs->vd->tlsaclname,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ &err);
+ if (!vs->tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
return 0;
}
+ qcrypto_tls_session_set_callbacks(vs->tls,
+ vnc_tls_push,
+ vnc_tls_pull,
+ vs);
+
VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
if (vnc_start_vencrypt_handshake(vs) < 0) {
VNC_DEBUG("Failed to start TLS handshake\n");
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
deleted file mode 100644
index 028fc4db1f..0000000000
--- a/ui/vnc-tls.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * QEMU VNC display driver: TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu-x509.h"
-#include "vnc.h"
-#include "qemu/sockets.h"
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
-/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
-static void vnc_debug_gnutls_log(int level, const char* str) {
- VNC_DEBUG("%d %s", level, str);
-}
-#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
-
-
-#define DH_BITS 1024
-static gnutls_dh_params_t dh_params;
-
-static int vnc_tls_initialize(void)
-{
- static int tlsinitialized = 0;
-
- if (tlsinitialized)
- return 1;
-
- if (gnutls_global_init () < 0)
- return 0;
-
- /* XXX ought to re-generate diffie-hellman params periodically */
- if (gnutls_dh_params_init (&dh_params) < 0)
- return 0;
- if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
- return 0;
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
- gnutls_global_set_log_level(10);
- gnutls_global_set_log_function(vnc_debug_gnutls_log);
-#endif
-
- tlsinitialized = 1;
-
- return 1;
-}
-
-static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
- const void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = send(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
- void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = qemu_recv(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void)
-{
- gnutls_anon_server_credentials_t anon_cred;
- int ret;
-
- if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
-
- gnutls_anon_set_server_dh_params(anon_cred, dh_params);
-
- return anon_cred;
-}
-
-
-static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
-{
- gnutls_certificate_credentials_t x509_cred;
- int ret;
-
- if (!vd->tls.x509cacert) {
- VNC_DEBUG("No CA x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509cert) {
- VNC_DEBUG("No server x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509key) {
- VNC_DEBUG("No server private key specified\n");
- return NULL;
- }
-
- if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
- if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
- vd->tls.x509cacert,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
- vd->tls.x509cert,
- vd->tls.x509key,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if (vd->tls.x509cacrl) {
- if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
- vd->tls.x509cacrl,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
- }
-
- gnutls_certificate_set_dh_params (x509_cred, dh_params);
-
- return x509_cred;
-}
-
-
-int vnc_tls_validate_certificate(VncState *vs)
-{
- int ret;
- unsigned int status;
- const gnutls_datum_t *certs;
- unsigned int nCerts, i;
- time_t now;
-
- VNC_DEBUG("Validating client certificate\n");
- if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
- VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
- return -1;
- }
-
- if ((now = time(NULL)) == ((time_t)-1)) {
- return -1;
- }
-
- if (status != 0) {
- if (status & GNUTLS_CERT_INVALID)
- VNC_DEBUG("The certificate is not trusted.\n");
-
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- VNC_DEBUG("The certificate hasn't got a known issuer.\n");
-
- if (status & GNUTLS_CERT_REVOKED)
- VNC_DEBUG("The certificate has been revoked.\n");
-
- if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
- VNC_DEBUG("The certificate uses an insecure algorithm\n");
-
- return -1;
- } else {
- VNC_DEBUG("Certificate is valid!\n");
- }
-
- /* Only support x509 for now */
- if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
- return -1;
-
- if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
- return -1;
-
- for (i = 0 ; i < nCerts ; i++) {
- gnutls_x509_crt_t cert;
- VNC_DEBUG ("Checking certificate chain %d\n", i);
- if (gnutls_x509_crt_init (&cert) < 0)
- return -1;
-
- if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_expiration_time (cert) < now) {
- VNC_DEBUG("The certificate has expired\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (i == 0) {
- size_t dnameSize = 1024;
- vs->tls.dname = g_malloc(dnameSize);
- requery:
- if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
- if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
- vs->tls.dname = g_realloc(vs->tls.dname, dnameSize);
- goto requery;
- }
- gnutls_x509_crt_deinit (cert);
- VNC_DEBUG("Cannot get client distinguished name: %s",
- gnutls_strerror (ret));
- return -1;
- }
-
- if (vs->vd->tls.x509verify) {
- int allow;
- if (!vs->vd->tls.acl) {
- VNC_DEBUG("no ACL activated, allowing access");
- gnutls_x509_crt_deinit (cert);
- continue;
- }
-
- allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
- vs->tls.dname);
-
- VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
- vs->tls.dname, allow ? "allowed" : "denied");
- if (!allow) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
- }
- }
-
- gnutls_x509_crt_deinit (cert);
- }
-
- return 0;
-}
-
-#if defined(GNUTLS_VERSION_NUMBER) && \
- GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH";
- int rc;
-
- rc = gnutls_priority_set_direct(s, priority, NULL);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#else
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- static const int cert_types[] = { GNUTLS_CRT_X509, 0 };
- static const int protocols[] = {
- GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
- };
- static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 };
- static const int kx_x509[] = {
- GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
- GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0
- };
- int rc;
-
- rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_certificate_type_set_priority(s, cert_types);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_protocol_set_priority(s, protocols);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#endif
-
-int vnc_tls_client_setup(VncState *vs,
- int needX509Creds) {
- VNC_DEBUG("Do TLS setup\n");
- if (vnc_tls_initialize() < 0) {
- VNC_DEBUG("Failed to init TLS\n");
- vnc_client_error(vs);
- return -1;
- }
- if (vs->tls.session == NULL) {
- if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
- vnc_client_error(vs);
- return -1;
- }
-
- if (gnutls_set_default_priority(vs->tls.session) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (needX509Creds) {
- gnutls_certificate_server_credentials x509_cred =
- vnc_tls_initialize_x509_cred(vs->vd);
- if (!x509_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_certificate_free_credentials(x509_cred);
- vnc_client_error(vs);
- return -1;
- }
- if (vs->vd->tls.x509verify) {
- VNC_DEBUG("Requesting a client certificate\n");
- gnutls_certificate_server_set_request(vs->tls.session,
- GNUTLS_CERT_REQUEST);
- }
-
- } else {
- gnutls_anon_server_credentials_t anon_cred =
- vnc_tls_initialize_anon_cred();
- if (!anon_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_ANON, anon_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_anon_free_server_credentials(anon_cred);
- vnc_client_error(vs);
- return -1;
- }
- }
-
- gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
- gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
- gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
- }
- return 0;
-}
-
-
-void vnc_tls_client_cleanup(VncState *vs)
-{
- if (vs->tls.session) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- }
- g_free(vs->tls.dname);
-}
-
-
-
-static int vnc_set_x509_credential(VncDisplay *vd,
- const char *certdir,
- const char *filename,
- char **cred,
- int ignoreMissing)
-{
- struct stat sb;
-
- g_free(*cred);
- *cred = g_malloc(strlen(certdir) + strlen(filename) + 2);
-
- strcpy(*cred, certdir);
- strcat(*cred, "/");
- strcat(*cred, filename);
-
- VNC_DEBUG("Check %s\n", *cred);
- if (stat(*cred, &sb) < 0) {
- g_free(*cred);
- *cred = NULL;
- if (ignoreMissing && errno == ENOENT)
- return 0;
- return -1;
- }
-
- return 0;
-}
-
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *certdir)
-{
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
- goto cleanup;
-
- return 0;
-
- cleanup:
- g_free(vd->tls.x509cacert);
- g_free(vd->tls.x509cacrl);
- g_free(vd->tls.x509cert);
- g_free(vd->tls.x509key);
- vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
- return -1;
-}
-
diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h
deleted file mode 100644
index f9829c7824..0000000000
--- a/ui/vnc-tls.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * QEMU VNC display driver. TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-#ifndef __QEMU_VNC_TLS_H__
-#define __QEMU_VNC_TLS_H__
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#include "qemu/acl.h"
-
-typedef struct VncDisplayTLS VncDisplayTLS;
-typedef struct VncStateTLS VncStateTLS;
-
-/* Server state */
-struct VncDisplayTLS {
- int x509verify; /* Non-zero if server requests & validates client cert */
- qemu_acl *acl;
-
- /* Paths to x509 certs/keys */
- char *x509cacert;
- char *x509cacrl;
- char *x509cert;
- char *x509key;
-};
-
-/* Per client state */
-struct VncStateTLS {
- gnutls_session_t session;
-
- /* Client's Distinguished Name from the x509 cert */
- char *dname;
-};
-
-int vnc_tls_client_setup(VncState *vs, int x509Creds);
-void vnc_tls_client_cleanup(VncState *vs);
-
-int vnc_tls_validate_certificate(VncState *vs);
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *path);
-
-
-#endif /* __QEMU_VNC_TLS_H__ */
-
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index b4cb6bde70..175ea50b4b 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -22,60 +22,70 @@
#include "qemu/main-loop.h"
#include "crypto/hash.h"
-#ifdef CONFIG_VNC_TLS
-#include "qemu/sockets.h"
-
static int vncws_start_tls_handshake(VncState *vs)
{
- int ret = gnutls_handshake(vs->tls.session);
-
- if (ret < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session)) {
- qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
- NULL, vs);
- } else {
- qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
- vs);
- }
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
- vnc_client_error(vs);
- return -1;
+ Error *err = NULL;
+
+ if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
+ goto error;
}
- if (vs->vd->tls.x509verify) {
- if (vnc_tls_validate_certificate(vs) < 0) {
- VNC_DEBUG("Client verification failed\n");
- vnc_client_error(vs);
- return -1;
- } else {
- VNC_DEBUG("Client verification passed\n");
+ switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
+ case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
+ VNC_DEBUG("Handshake done, checking credentials\n");
+ if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
+ goto error;
}
+ VNC_DEBUG("Client verification passed, starting TLS I/O\n");
+ qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
+ break;
+
+ case QCRYPTO_TLS_HANDSHAKE_RECVING:
+ VNC_DEBUG("Handshake interrupted (blocking read)\n");
+ qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
+ break;
+
+ case QCRYPTO_TLS_HANDSHAKE_SENDING:
+ VNC_DEBUG("Handshake interrupted (blocking write)\n");
+ qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs);
+ break;
}
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
-
return 0;
+
+ error:
+ VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return -1;
}
void vncws_tls_handshake_io(void *opaque)
{
VncState *vs = (VncState *)opaque;
+ Error *err = NULL;
- if (!vs->tls.session) {
- VNC_DEBUG("TLS Websocket setup\n");
- if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) {
- return;
- }
+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
+ NULL,
+ vs->vd->tlsaclname,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ &err);
+ if (!vs->tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return;
}
- VNC_DEBUG("Handshake IO continue\n");
+
+ qcrypto_tls_session_set_callbacks(vs->tls,
+ vnc_tls_push,
+ vnc_tls_pull,
+ vs);
+
+ VNC_DEBUG("Start TLS WS handshake process\n");
vncws_start_tls_handshake(vs);
}
-#endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque)
{
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 94942258ec..4ab0a8c899 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -72,9 +72,7 @@ enum {
WS_OPCODE_PONG = 0xA
};
-#ifdef CONFIG_VNC_TLS
void vncws_tls_handshake_io(void *opaque);
-#endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque);
long vnc_client_write_ws(VncState *vs);
long vnc_client_read_ws(VncState *vs);
diff --git a/ui/vnc.c b/ui/vnc.c
index caf82f56f1..d73966afc5 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -41,6 +41,9 @@
#include "ui/input.h"
#include "qapi-event.h"
#include "crypto/hash.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredsx509.h"
+#include "qom/object_interfaces.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
@@ -222,7 +225,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
case VNC_AUTH_TLS:
return "tls";
case VNC_AUTH_VENCRYPT:
-#ifdef CONFIG_VNC_TLS
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
return "vencrypt+plain";
@@ -245,9 +247,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
default:
return "vencrypt";
}
-#else
- return "vencrypt";
-#endif
case VNC_AUTH_SASL:
return "sasl";
}
@@ -275,13 +274,12 @@ static void vnc_client_cache_auth(VncState *client)
return;
}
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session &&
- client->tls.dname) {
- client->info->has_x509_dname = true;
- client->info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ client->info->x509_dname =
+ qcrypto_tls_session_get_peer_name(client->tls);
+ client->info->has_x509_dname =
+ client->info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn &&
client->sasl.username) {
@@ -358,12 +356,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
info->base->family = inet_netfamily(sa.ss_family);
info->base->websocket = client->websocket;
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session && client->tls.dname) {
- info->has_x509_dname = true;
- info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
+ info->has_x509_dname = info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn && client->sasl.username) {
info->has_sasl_username = true;
@@ -513,7 +509,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
break;
case VNC_AUTH_VENCRYPT:
info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
-#ifdef CONFIG_VNC_TLS
info->has_vencrypt = true;
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
@@ -547,7 +542,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
info->has_vencrypt = false;
break;
}
-#endif
break;
case VNC_AUTH_SASL:
info->auth = VNC_PRIMARY_AUTH_SASL;
@@ -1237,9 +1231,7 @@ void vnc_disconnect_finish(VncState *vs)
vnc_tight_clear(vs);
vnc_zrle_clear(vs);
-#ifdef CONFIG_VNC_TLS
- vnc_tls_client_cleanup(vs);
-#endif /* CONFIG_VNC_TLS */
+ qcrypto_tls_session_free(vs->tls);
#ifdef CONFIG_VNC_SASL
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
@@ -1268,7 +1260,7 @@ void vnc_disconnect_finish(VncState *vs)
g_free(vs);
}
-int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno)
{
if (ret == 0 || ret == -1) {
if (ret == -1) {
@@ -1284,7 +1276,7 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno)
}
}
- VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
+ VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n",
ret, ret < 0 ? last_errno : 0);
vnc_disconnect_start(vs);
@@ -1300,23 +1292,40 @@ void vnc_client_error(VncState *vs)
vnc_disconnect_start(vs);
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_write_tls(gnutls_session_t *session,
- const uint8_t *data,
- size_t datalen)
+
+ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
{
- long ret = gnutls_write(*session, data, datalen);
+ VncState *vs = opaque;
+ ssize_t ret;
+
+ retry:
+ ret = qemu_recv(vs->csock, buf, len, 0);
if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
+ if (errno == EINTR) {
+ goto retry;
+ }
+ return -1;
+ }
+ return ret;
+}
+
+
+ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
+{
+ VncState *vs = opaque;
+ ssize_t ret;
+
+ retry:
+ ret = send(vs->csock, buf, len, 0);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ goto retry;
}
- ret = -1;
+ return -1;
}
return ret;
}
-#endif /* CONFIG_VNC_TLS */
+
/*
* Called to write a chunk of data to the client socket. The data may
@@ -1333,20 +1342,23 @@ static long vnc_client_write_tls(gnutls_session_t *session,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
+ ssize_t ret;
+ int err = 0;
+ if (vs->tls) {
+ ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen);
+ if (ret < 0) {
+ err = errno;
+ }
} else {
-#endif /* CONFIG_VNC_TLS */
ret = send(vs->csock, (const void *)data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
+ if (ret < 0) {
+ err = socket_error();
+ }
}
-#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, err);
}
@@ -1360,9 +1372,9 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
* the buffered output data if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-static long vnc_client_write_plain(VncState *vs)
+static ssize_t vnc_client_write_plain(VncState *vs)
{
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
@@ -1435,22 +1447,6 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
vs->read_handler_expect = expecting;
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
- size_t datalen)
-{
- long ret = gnutls_read(*session, data, datalen);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
- }
- ret = -1;
- }
- return ret;
-}
-#endif /* CONFIG_VNC_TLS */
/*
* Called to read a chunk of data from the client socket. The data may
@@ -1467,20 +1463,23 @@ static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
+ ssize_t ret;
+ int err = -1;
+ if (vs->tls) {
+ ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen);
+ if (ret < 0) {
+ err = errno;
+ }
} else {
-#endif /* CONFIG_VNC_TLS */
ret = qemu_recv(vs->csock, data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
+ if (ret < 0) {
+ err = socket_error();
+ }
}
-#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, err);
}
@@ -1492,9 +1491,9 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
* Returns the number of bytes read. Returns -1 on error, and
* disconnects the client socket.
*/
-static long vnc_client_read_plain(VncState *vs)
+static ssize_t vnc_client_read_plain(VncState *vs)
{
- int ret;
+ ssize_t ret;
VNC_DEBUG("Read plain %p size %zd offset %zd\n",
vs->input.buffer, vs->input.capacity, vs->input.offset);
buffer_reserve(&vs->input, 4096);
@@ -1520,7 +1519,7 @@ static void vnc_jobs_bh(void *opaque)
void vnc_client_read(void *opaque)
{
VncState *vs = opaque;
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn && vs->sasl.runSSF)
@@ -2631,12 +2630,10 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
start_auth_vnc(vs);
break;
-#ifdef CONFIG_VNC_TLS
case VNC_AUTH_VENCRYPT:
VNC_DEBUG("Accept VeNCrypt auth\n");
start_auth_vencrypt(vs);
break;
-#endif /* CONFIG_VNC_TLS */
#ifdef CONFIG_VNC_SASL
case VNC_AUTH_SASL:
@@ -3033,12 +3030,9 @@ static void vnc_connect(VncDisplay *vd, int csock,
qemu_set_nonblock(vs->csock);
if (websocket) {
vs->websocket = 1;
-#ifdef CONFIG_VNC_TLS
if (vd->ws_tls) {
qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
- } else
-#endif /* CONFIG_VNC_TLS */
- {
+ } else {
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
}
} else
@@ -3194,9 +3188,11 @@ static void vnc_display_close(VncDisplay *vs)
}
vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID;
-#ifdef CONFIG_VNC_TLS
- vs->tls.x509verify = 0;
-#endif
+ if (vs->tlscreds) {
+ object_unparent(OBJECT(vs->tlscreds));
+ }
+ g_free(vs->tlsaclname);
+ vs->tlsaclname = NULL;
}
int vnc_display_password(const char *id, const char *password)
@@ -3250,6 +3246,10 @@ static QemuOptsList qemu_vnc_opts = {
.name = "websocket",
.type = QEMU_OPT_STRING,
},{
+ .name = "tls-creds",
+ .type = QEMU_OPT_STRING,
+ },{
+ /* Deprecated in favour of tls-creds */
.name = "x509",
.type = QEMU_OPT_STRING,
},{
@@ -3286,9 +3286,11 @@ static QemuOptsList qemu_vnc_opts = {
.name = "sasl",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "tls",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "x509verify",
.type = QEMU_OPT_STRING,
},{
@@ -3306,13 +3308,12 @@ static QemuOptsList qemu_vnc_opts = {
};
-static void
+static int
vnc_display_setup_auth(VncDisplay *vs,
bool password,
bool sasl,
- bool tls,
- bool x509,
- bool websocket)
+ bool websocket,
+ Error **errp)
{
/*
* We have a choice of 3 authentication options
@@ -3362,17 +3363,24 @@ vnc_display_setup_auth(VncDisplay *vs,
* result has the same security characteristics.
*/
if (password) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with password auth\n");
@@ -3385,17 +3393,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else if (sasl) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with SASL auth\n");
@@ -3408,17 +3423,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with no auth\n");
@@ -3431,8 +3453,55 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
}
+ return 0;
+}
+
+
+/*
+ * Handle back compat with old CLI syntax by creating some
+ * suitable QCryptoTLSCreds objects
+ */
+static QCryptoTLSCreds *
+vnc_display_create_creds(bool x509,
+ bool x509verify,
+ const char *dir,
+ const char *id,
+ Error **errp)
+{
+ gchar *credsid = g_strdup_printf("tlsvnc%s", id);
+ Object *parent = object_get_objects_root();
+ Object *creds;
+ Error *err = NULL;
+
+ if (x509) {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ "dir", dir,
+ "verify-peer", x509verify ? "yes" : "no",
+ NULL);
+ } else {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ NULL);
+ }
+
+ g_free(credsid);
+
+ if (err) {
+ error_propagate(errp, err);
+ return NULL;
+ }
+
+ return QCRYPTO_TLS_CREDS(creds);
}
+
void vnc_display_open(const char *id, Error **errp)
{
VncDisplay *vs = vnc_display_find(id);
@@ -3447,18 +3516,13 @@ void vnc_display_open(const char *id, Error **errp)
char *h;
bool has_ipv4 = false;
bool has_ipv6 = false;
+ const char *credid;
const char *websocket;
- bool tls = false, x509 = false;
-#ifdef CONFIG_VNC_TLS
- const char *path;
-#endif
bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr;
#endif
-#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
-#endif
int lock_key_sync = 1;
if (!vs) {
@@ -3539,32 +3603,67 @@ void vnc_display_open(const char *id, Error **errp)
goto fail;
}
#endif /* CONFIG_VNC_SASL */
- tls = qemu_opt_get_bool(opts, "tls", false);
-#ifdef CONFIG_VNC_TLS
- path = qemu_opt_get(opts, "x509");
- if (!path) {
- path = qemu_opt_get(opts, "x509verify");
- if (path) {
- vs->tls.x509verify = true;
- }
- }
- if (path) {
- x509 = true;
- if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
- error_setg(errp, "Failed to find x509 certificates/keys in %s",
- path);
+ credid = qemu_opt_get(opts, "tls-creds");
+ if (credid) {
+ Object *creds;
+ if (qemu_opt_get(opts, "tls") ||
+ qemu_opt_get(opts, "x509") ||
+ qemu_opt_get(opts, "x509verify")) {
+ error_setg(errp,
+ "'credid' parameter is mutually exclusive with "
+ "'tls', 'x509' and 'x509verify' parameters");
goto fail;
}
+
+ creds = object_resolve_path_component(
+ object_get_objects_root(), credid);
+ if (!creds) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ credid);
+ goto fail;
+ }
+ vs->tlscreds = (QCryptoTLSCreds *)
+ object_dynamic_cast(creds,
+ TYPE_QCRYPTO_TLS_CREDS);
+ if (!vs->tlscreds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ credid);
+ goto fail;
+ }
+ object_ref(OBJECT(vs->tlscreds));
+
+ if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ goto fail;
+ }
+ } else {
+ const char *path;
+ bool tls = false, x509 = false, x509verify = false;
+ tls = qemu_opt_get_bool(opts, "tls", false);
+ if (tls) {
+ path = qemu_opt_get(opts, "x509");
+
+ if (path) {
+ x509 = true;
+ } else {
+ path = qemu_opt_get(opts, "x509verify");
+ if (path) {
+ x509 = true;
+ x509verify = true;
+ }
+ }
+ vs->tlscreds = vnc_display_create_creds(x509,
+ x509verify,
+ path,
+ vs->id,
+ errp);
+ if (!vs->tlscreds) {
+ goto fail;
+ }
+ }
}
-#else /* ! CONFIG_VNC_TLS */
- if (tls) {
- error_setg(errp, "VNC TLS auth requires gnutls support");
- goto fail;
- }
-#endif /* ! CONFIG_VNC_TLS */
-#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
acl = qemu_opt_get_bool(opts, "acl", false);
-#endif
share = qemu_opt_get(opts, "share");
if (share) {
@@ -3604,19 +3703,14 @@ void vnc_display_open(const char *id, Error **errp)
vs->non_adaptive = true;
}
-#ifdef CONFIG_VNC_TLS
- if (acl && x509 && vs->tls.x509verify) {
- char *aclname;
-
+ if (acl) {
if (strcmp(vs->id, "default") == 0) {
- aclname = g_strdup("vnc.x509dname");
+ vs->tlsaclname = g_strdup("vnc.x509dname");
} else {
- aclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
+ vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
}
- vs->tls.acl = qemu_acl_init(aclname);
- g_free(aclname);
- }
-#endif
+ qemu_acl_init(vs->tlsaclname);
+ }
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
char *aclname;
@@ -3631,7 +3725,9 @@ void vnc_display_open(const char *id, Error **errp)
}
#endif
- vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket);
+ if (vnc_display_setup_auth(vs, password, sasl, websocket, errp) < 0) {
+ goto fail;
+ }
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
diff --git a/ui/vnc.h b/ui/vnc.h
index 814d720df2..4dd769cddb 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -33,6 +33,7 @@
#include "ui/console.h"
#include "audio/audio.h"
#include "qemu/bitmap.h"
+#include "crypto/tlssession.h"
#include <zlib.h>
#include <stdbool.h>
@@ -101,10 +102,7 @@ typedef void VncSendHextileTile(VncState *vs,
typedef struct VncDisplay VncDisplay;
-#ifdef CONFIG_VNC_TLS
-#include "vnc-tls.h"
#include "vnc-auth-vencrypt.h"
-#endif
#ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h"
#endif
@@ -181,9 +179,8 @@ struct VncDisplay
bool ws_tls; /* Used by websockets */
bool lossy;
bool non_adaptive;
-#ifdef CONFIG_VNC_TLS
- VncDisplayTLS tls;
-#endif
+ QCryptoTLSCreds *tlscreds;
+ char *tlsaclname;
#ifdef CONFIG_VNC_SASL
VncDisplaySASL sasl;
#endif
@@ -284,9 +281,7 @@ struct VncState
int auth;
int subauth; /* Used by VeNCrypt */
char challenge[VNC_AUTH_CHALLENGE_SIZE];
-#ifdef CONFIG_VNC_TLS
- VncStateTLS tls;
-#endif
+ QCryptoTLSSession *tls;
#ifdef CONFIG_VNC_SASL
VncStateSASL sasl;
#endif
@@ -513,8 +508,10 @@ enum {
void vnc_client_read(void *opaque);
void vnc_client_write(void *opaque);
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque);
+ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque);
/* Protocol I/O functions */
void vnc_write(VncState *vs, const void *data, size_t len);
@@ -533,7 +530,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);
/* Protocol stage functions */
void vnc_client_error(VncState *vs);
-int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);
void start_client_init(VncState *vs);
void start_auth_vnc(VncState *vs);