提交 61962242 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
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
/*
* 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);
/*
* 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);
/*
* 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__ */
此差异已折叠。
/*
* 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
......@@ -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
......
/*
* 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__ */
/*
* 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__ */
/*
* 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__ */
/*
* 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__ */
......@@ -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' }
......
# -*- 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']}
......@@ -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
......
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
......@@ -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)
......
......@@ -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('.-', '__')
......
......@@ -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
......
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
......
/*
* 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 */
/*
* 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 */
此差异已折叠。
tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
# The prefix must be a string type
{ 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] }
......@@ -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
......
[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},
......
此差异已折叠。
此差异已折叠。
......@@ -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"
......@@ -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
......
......@@ -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;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -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);
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册