[libvirt] [PATCH v5 0/9] Add native TLS encrypted chardev TCP support

v4: http://www.redhat.com/archives/libvir-list/2016-June/msg01709.html Since I have it on a branch and have been updating, I figured I'd post the most recent stuff. Patches 1-4 were "partially" ACK'd in v2 of this series, but there's been changes to the conf handling upstream. Patch 5 adds a new secret type 'tls'. Previous incarnations of these changes borrowed a common secret type, but this one is specific. It's more or less what got removed for LUKS with the names changed to protect the innocent (reference Dragnet). Patches 6-9 is what was mostly missing in the earlier series. Differences to v4... mostly updates/merges with the numerous changes to master since that time. I lost track of whether the desire was to have /etc/pki/libvirt-%s or /etc/pki/qemu-%s directories... I think we've been using the libvirt-%s for vnc/spice so far, so I just followed that for chardev although there was a comment at one time to use qemu-chardev during review of patch 2 of the v2 series: http://www.redhat.com/archives/libvir-list/2016-June/msg01072.html John Ferlan (9): conf: Add new default TLS X.509 certificate default directory conf: Introduce chartcp_tls_x509_cert_dir qemu: Add support for TLS X.509 path to TCP chardev backend qemu: Add the ability to hotplug the TLS X.509 environment conf: Add new secret type "tls" conf: Add new secret element for tcp chardev qemu: Introduce qemuDomainChardevPrivatePtr qemu: Add a secret object to/for a chardev tcp with secret qemu: Add the ability to hotplug a secret object for TCP chardev TLS docs/aclpolkit.html.in | 4 + docs/formatdomain.html.in | 29 +++++ docs/formatsecret.html.in | 59 ++++++++- docs/schemas/domaincommon.rng | 21 +++ docs/schemas/secret.rng | 10 ++ include/libvirt/libvirt-secret.h | 1 + src/access/viraccessdriverpolkit.c | 13 ++ src/conf/domain_conf.c | 64 ++++++++-- src/conf/domain_conf.h | 8 +- src/conf/secret_conf.c | 23 +++- src/conf/secret_conf.h | 1 + src/conf/virsecretobj.c | 5 + src/libxl/libxl_domain.c | 2 +- src/lxc/lxc_native.c | 2 +- src/qemu/libvirtd_qemu.aug | 11 +- src/qemu/qemu.conf | 83 +++++++++--- src/qemu/qemu_alias.c | 16 +++ src/qemu/qemu_alias.h | 3 + src/qemu/qemu_command.c | 141 ++++++++++++++++++++- src/qemu/qemu_command.h | 9 ++ src/qemu/qemu_conf.c | 57 ++++++++- src/qemu/qemu_conf.h | 7 + src/qemu/qemu_domain.c | 124 +++++++++++++++++- src/qemu/qemu_domain.h | 22 ++++ src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_hotplug.c | 104 ++++++++++++++- src/qemu/qemu_hotplug.h | 3 +- src/qemu/qemu_monitor_json.c | 9 ++ src/qemu/qemu_parse_command.c | 4 +- src/qemu/qemu_process.c | 2 +- src/qemu/test_libvirtd_qemu.aug.in | 5 + src/vz/vz_sdk.c | 2 +- src/xenconfig/xen_sxpr.c | 2 +- tests/qemuhotplugtest.c | 2 +- .../qemuxml2argv-serial-tcp-tlsx509-chardev.args | 33 +++++ .../qemuxml2argv-serial-tcp-tlsx509-chardev.xml | 41 ++++++ ...xml2argv-serial-tcp-tlsx509-secret-chardev.args | 38 ++++++ ...uxml2argv-serial-tcp-tlsx509-secret-chardev.xml | 51 ++++++++ tests/qemuxml2argvtest.c | 21 +++ .../qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml | 50 ++++++++ ...ml2xmlout-serial-tcp-tlsx509-secret-chardev.xml | 1 + tests/qemuxml2xmltest.c | 2 + tests/secretxml2xmlin/usage-tls.xml | 7 + tests/secretxml2xmltest.c | 1 + 44 files changed, 1038 insertions(+), 57 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml create mode 100644 tests/secretxml2xmlin/usage-tls.xml -- 2.7.4

Rather than specify perhaps multiple TLS X.509 certificate directories, let's create a "default" directory which can then be used if the service (e.g. for now vnc and spice) does not supply a default directory. Since the default for vnc and spice may have existed before without being supplied, the default check will first check if the service specific path exists and if so, set the cfg entry to that; otherwise, the default will be set to the (now) new defaultTLSx509certdir. Additionally add a "default_tls_x509_verify" entry which can also be used to force the peer verification option (for vnc it's a x509verify option). Add/alter the macro for the option being found in the config file to accept the default value. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 6 ++++- src/qemu/qemu.conf | 55 +++++++++++++++++++++++++------------- src/qemu/qemu_conf.c | 48 ++++++++++++++++++++++++++++----- src/qemu/qemu_conf.h | 3 +++ src/qemu/test_libvirtd_qemu.aug.in | 2 ++ 5 files changed, 88 insertions(+), 26 deletions(-) diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 8bc23ba..06d9b98 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -24,6 +24,9 @@ module Libvirtd_qemu = (* Config entry grouped by function - same order as example config *) + let default_tls_entry = str_entry "default_tls_x509_cert_dir" + | bool_entry "default_tls_x509_verify" + let vnc_entry = str_entry "vnc_listen" | bool_entry "vnc_auto_unix_socket" | bool_entry "vnc_tls" @@ -93,7 +96,8 @@ module Libvirtd_qemu = let nvram_entry = str_array_entry "nvram" (* Each entry in the config is one of the following ... *) - let entry = vnc_entry + let entry = default_tls_entry + | vnc_entry | spice_entry | nogfx_entry | remote_display_entry diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 7964273..fb6b843 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -2,6 +2,32 @@ # All settings described here are optional - if omitted, sensible # defaults are used. +# Use of TLS requires that x509 certificates be issued. The default is +# to keep them in /etc/pki/qemu. This directory must contain +# +# ca-cert.pem - the CA master certificate +# server-cert.pem - the server certificate signed with ca-cert.pem +# server-key.pem - the server private key +# +# and optionally may contain +# +# dh-params.pem - the DH params configuration file +# +#default_tls_x509_cert_dir = "/etc/pki/qemu" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect. +# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/qemu/ca-cert.pem +# +#default_tls_x509_verify = 1 + # VNC is configured to listen on 127.0.0.1 by default. # To make it listen on all public interfaces, uncomment # this next option. @@ -32,15 +58,10 @@ #vnc_tls = 1 -# Use of TLS requires that x509 certificates be issued. The -# default it to keep them in /etc/pki/libvirt-vnc. This directory -# must contain -# -# ca-cert.pem - the CA master certificate -# server-cert.pem - the server certificate signed with ca-cert.pem -# server-key.pem - the server private key -# -# This option allows the certificate directory to be changed +# In order to override the default TLS certificate location for +# vnc certificates, supply a valid path to the certificate directory. +# If the provided path does not exist then the default_tls_x509_cert_dir +# path will be used. # #vnc_tls_x509_cert_dir = "/etc/pki/libvirt-vnc" @@ -55,6 +76,9 @@ # Enabling this option will reject any client who does not have a # certificate signed by the CA in /etc/pki/libvirt-vnc/ca-cert.pem # +# If this option is not supplied, it will be set to the value of +# "default_tls_x509_verify". +# #vnc_tls_x509_verify = 1 @@ -117,15 +141,10 @@ #spice_tls = 1 -# Use of TLS requires that x509 certificates be issued. The -# default it to keep them in /etc/pki/libvirt-spice. This directory -# must contain -# -# ca-cert.pem - the CA master certificate -# server-cert.pem - the server certificate signed with ca-cert.pem -# server-key.pem - the server private key -# -# This option allows the certificate directory to be changed. +# In order to override the default TLS certificate location for +# spice certificates, supply a valid path to the certificate directory. +# If the provided path does not exist then the default_tls_x509_cert_dir +# path will be used. # #spice_tls_x509_cert_dir = "/etc/pki/libvirt-spice" diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index fa9d65e..9409924 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -236,19 +236,44 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) if (virAsprintf(&cfg->autostartDir, "%s/qemu/autostart", cfg->configBaseDir) < 0) goto error; - - if (VIR_STRDUP(cfg->vncListen, "127.0.0.1") < 0) + /* Set the default directory to find TLS X.509 certificates. + * This will then be used as a fallback if the service specific + * directory doesn't exist (although we don't check if this exists). + */ + if (VIR_STRDUP(cfg->defaultTLSx509certdir, + SYSCONFDIR "/pki/qemu") < 0) goto error; - if (VIR_STRDUP(cfg->vncTLSx509certdir, SYSCONFDIR "/pki/libvirt-vnc") < 0) + if (VIR_STRDUP(cfg->vncListen, "127.0.0.1") < 0) goto error; if (VIR_STRDUP(cfg->spiceListen, "127.0.0.1") < 0) goto error; - if (VIR_STRDUP(cfg->spiceTLSx509certdir, - SYSCONFDIR "/pki/libvirt-spice") < 0) - goto error; + /* + * If a "SYSCONFDIR" + "pki/libvirt-<val>" exists, then assume someone + * has created a val specific area to place service specific certificates. + * + * If the service specific directory doesn't exist, 'assume' that the + * user has created and populated the "SYSCONFDIR" + "pki/libvirt-default". + */ +#define SET_TLS_X509_CERT_DEFAULT(val) \ + do { \ + if (virFileExists(SYSCONFDIR "/pki/libvirt-"#val)) { \ + if (VIR_STRDUP(cfg->val ## TLSx509certdir, \ + SYSCONFDIR "/pki/libvirt-"#val) < 0) \ + goto error; \ + } else { \ + if (VIR_STRDUP(cfg->val ## TLSx509certdir, \ + cfg->defaultTLSx509certdir) < 0) \ + goto error; \ + } \ + } while (false); + + SET_TLS_X509_CERT_DEFAULT(vnc); + SET_TLS_X509_CERT_DEFAULT(spice); + +#undef SET_TLS_X509_CERT_DEFAULT cfg->remotePortMin = QEMU_REMOTE_PORT_MIN; cfg->remotePortMax = QEMU_REMOTE_PORT_MAX; @@ -333,6 +358,8 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->channelTargetDir); VIR_FREE(cfg->nvramDir); + VIR_FREE(cfg->defaultTLSx509certdir); + VIR_FREE(cfg->vncTLSx509certdir); VIR_FREE(cfg->vncListen); VIR_FREE(cfg->vncPassword); @@ -387,6 +414,7 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, { virConfPtr conf = NULL; int ret = -1; + int rv; size_t i, j; char *stdioHandler = NULL; char *user = NULL, *group = NULL; @@ -405,12 +433,18 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (!(conf = virConfReadFile(filename, 0))) goto cleanup; + if (virConfGetValueString(conf, "default_tls_x509_cert_dir", &cfg->defaultTLSx509certdir) < 0) + goto cleanup; + if (virConfGetValueBool(conf, "default_tls_x509_verify", &cfg->defaultTLSx509verify) < 0) + goto cleanup; if (virConfGetValueBool(conf, "vnc_auto_unix_socket", &cfg->vncAutoUnixSocket) < 0) goto cleanup; if (virConfGetValueBool(conf, "vnc_tls", &cfg->vncTLS) < 0) goto cleanup; - if (virConfGetValueBool(conf, "vnc_tls_x509_verify", &cfg->vncTLSx509verify) < 0) + if ((rv = virConfGetValueBool(conf, "vnc_tls_x509_verify", &cfg->vncTLSx509verify)) < 0) goto cleanup; + if (rv == 0) + cfg->vncTLSx509verify = cfg->defaultTLSx509verify; if (virConfGetValueString(conf, "vnc_tls_x509_cert_dir", &cfg->vncTLSx509certdir) < 0) goto cleanup; if (virConfGetValueString(conf, "vnc_listen", &cfg->vncListen) < 0) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 510cd9a..7465fcf 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -109,6 +109,9 @@ struct _virQEMUDriverConfig { char *channelTargetDir; char *nvramDir; + char *defaultTLSx509certdir; + bool defaultTLSx509verify; + bool vncAutoUnixSocket; bool vncTLS; bool vncTLSx509verify; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index c4d4f19..01351b4 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -2,6 +2,8 @@ module Test_libvirtd_qemu = ::CONFIG:: test Libvirtd_qemu.lns get conf = +{ "default_tls_x509_cert_dir" = "/etc/pki/qemu" } +{ "default_tls_x509_verify" = "1" } { "vnc_listen" = "0.0.0.0" } { "vnc_auto_unix_socket" = "1" } { "vnc_tls" = "1" } -- 2.7.4

On Thu, Aug 04, 2016 at 11:21:19AM -0400, John Ferlan wrote:
Rather than specify perhaps multiple TLS X.509 certificate directories, let's create a "default" directory which can then be used if the service (e.g. for now vnc and spice) does not supply a default directory.
Since the default for vnc and spice may have existed before without being supplied, the default check will first check if the service specific path exists and if so, set the cfg entry to that; otherwise, the default will be set to the (now) new defaultTLSx509certdir.
Additionally add a "default_tls_x509_verify" entry which can also be used to force the peer verification option (for vnc it's a x509verify option). Add/alter the macro for the option being found in the config file to accept the default value.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 6 ++++- src/qemu/qemu.conf | 55 +++++++++++++++++++++++++------------- src/qemu/qemu_conf.c | 48 ++++++++++++++++++++++++++++----- src/qemu/qemu_conf.h | 3 +++ src/qemu/test_libvirtd_qemu.aug.in | 2 ++ 5 files changed, 88 insertions(+), 26 deletions(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add a new TLS X.509 certificate type - "chardev". This will handle the creation of a TLS certificate capability (and possibly repository) for properly configured character device TCP backends. Unlike the vnc and spice there is no "listen" or "passwd" associated. The credentials will be handled via a libvirt secret provided to a specific backend. Make use of the default verify option as well. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++ src/qemu/qemu.conf | 28 ++++++++++++ src/qemu/qemu_conf.c | 9 ++++ src/qemu/qemu_conf.h | 4 ++ src/qemu/test_libvirtd_qemu.aug.in | 3 ++ .../qemuxml2argv-serial-tcp-tlsx509-chardev.xml | 41 ++++++++++++++++++ .../qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml | 50 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 06d9b98..25b4645 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -45,6 +45,10 @@ module Libvirtd_qemu = | bool_entry "spice_sasl" | str_entry "spice_sasl_dir" + let chardev_entry = bool_entry "chardev_tls" + | str_entry "chardev_tls_x509_cert_dir" + | bool_entry "chardev_tls_x509_verify" + let nogfx_entry = bool_entry "nographics_allow_host_audio" let remote_display_entry = int_entry "remote_display_port_min" @@ -99,6 +103,7 @@ module Libvirtd_qemu = let entry = default_tls_entry | vnc_entry | spice_entry + | chardev_entry | nogfx_entry | remote_display_entry | security_entry diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index fb6b843..6a4f6c3 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -185,6 +185,34 @@ # #spice_sasl_dir = "/some/directory/sasl2" +# Enable use of TLS encryption on the chardev TCP transports. +# +# It is necessary to setup CA and issue a server certificate +# before enabling this. +# +#chardev_tls = 1 + + +# In order to override the default TLS certificate location for character +# device TCP certificates, supply a valid path to the certificate directory. +# If the provided path does not exist then the default_tls_x509_cert_dir +# path will be used. +# +#chardev_tls_x509_cert_dir = "/etc/pki/libvirt-chardev" + + +# The default TLS configuration only uses certificates for the server +# allowing the client to verify the server's identity and establish +# an encrypted channel. +# +# It is possible to use x509 certificates for authentication too, by +# issuing a x509 certificate to every client who needs to connect. +# +# Enabling this option will reject any client who does not have a +# certificate signed by the CA in /etc/pki/libvirt-chardev/ca-cert.pem +# +#chardev_tls_x509_verify = 1 + # By default, if no graphical front end is configured, libvirt will disable # QEMU audio output since directly talking to alsa/pulseaudio may not work diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 9409924..20a30e3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -272,6 +272,7 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) SET_TLS_X509_CERT_DEFAULT(vnc); SET_TLS_X509_CERT_DEFAULT(spice); + SET_TLS_X509_CERT_DEFAULT(chardev); #undef SET_TLS_X509_CERT_DEFAULT @@ -370,6 +371,8 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->spicePassword); VIR_FREE(cfg->spiceSASLdir); + VIR_FREE(cfg->chardevTLSx509certdir); + while (cfg->nhugetlbfs) { cfg->nhugetlbfs--; VIR_FREE(cfg->hugetlbfs[cfg->nhugetlbfs].mnt_dir); @@ -496,6 +499,12 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0) goto cleanup; + if (virConfGetValueString(conf, "chardev_tls_x509_cert_dir", &cfg->chardevTLSx509certdir) < 0) + goto cleanup; + if ((rv = virConfGetValueBool(conf, "chardev_tls_x509_verify", &cfg->chardevTLSx509verify)) < 0) + goto cleanup; + if (rv == 0) + cfg->chardevTLSx509verify = cfg->defaultTLSx509verify; if (virConfGetValueUInt(conf, "remote_websocket_port_min", &cfg->webSocketPortMin) < 0) goto cleanup; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 7465fcf..46e582b 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -129,6 +129,10 @@ struct _virQEMUDriverConfig { char *spicePassword; bool spiceAutoUnixSocket; + bool chardevTLS; + char *chardevTLSx509certdir; + bool chardevTLSx509verify; + unsigned int remotePortMin; unsigned int remotePortMax; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 01351b4..60bf9e6 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -20,6 +20,9 @@ module Test_libvirtd_qemu = { "spice_password" = "XYZ12345" } { "spice_sasl" = "1" } { "spice_sasl_dir" = "/some/directory/sasl2" } +{ "chardev_tls" = "1" } +{ "chardev_tls_x509_cert_dir" = "/etc/pki/libvirt-chardev" } +{ "chardev_tls_x509_verify" = "1" } { "nographics_allow_host_audio" = "1" } { "remote_display_port_min" = "5900" } { "remote_display_port_max" = "65535" } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml new file mode 100644 index 0000000..1618b02 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml @@ -0,0 +1,41 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <serial type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target port='0'/> + </serial> + <serial type='tcp'> + <source mode='connect' host='127.0.0.1' service='5555'/> + <protocol type='raw'/> + <target port='0'/> + </serial> + <console type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target type='serial' port='0'/> + </console> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml new file mode 100644 index 0000000..832e2a2 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml @@ -0,0 +1,50 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <serial type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target port='0'/> + </serial> + <serial type='tcp'> + <source mode='connect' host='127.0.0.1' service='5555'/> + <protocol type='raw'/> + <target port='0'/> + </serial> + <console type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 5f04b8b..85241b9 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -540,6 +540,7 @@ mymain(void) DO_TEST("serial-tcp"); DO_TEST("serial-udp"); DO_TEST("serial-tcp-telnet"); + DO_TEST("serial-tcp-tlsx509-chardev"); DO_TEST("serial-many"); DO_TEST("serial-spiceport"); DO_TEST("serial-spiceport-nospice"); -- 2.7.4

In the subject s/chartcp/chardev/ On Thu, Aug 04, 2016 at 11:21:20AM -0400, John Ferlan wrote:
Add a new TLS X.509 certificate type - "chardev". This will handle the creation of a TLS certificate capability (and possibly repository) for properly configured character device TCP backends.
Unlike the vnc and spice there is no "listen" or "passwd" associated. The credentials will be handled via a libvirt secret provided to a specific backend.
Make use of the default verify option as well.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++ src/qemu/qemu.conf | 28 ++++++++++++ src/qemu/qemu_conf.c | 9 ++++ src/qemu/qemu_conf.h | 4 ++ src/qemu/test_libvirtd_qemu.aug.in | 3 ++ .../qemuxml2argv-serial-tcp-tlsx509-chardev.xml | 41 ++++++++++++++++++ .../qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml | 50 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 08/05/2016 04:19 AM, Daniel P. Berrange wrote:
In the subject s/chartcp/chardev/
On Thu, Aug 04, 2016 at 11:21:20AM -0400, John Ferlan wrote:
Add a new TLS X.509 certificate type - "chardev". This will handle the creation of a TLS certificate capability (and possibly repository) for properly configured character device TCP backends.
Unlike the vnc and spice there is no "listen" or "passwd" associated. The credentials will be handled via a libvirt secret provided to a specific backend.
Make use of the default verify option as well.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/libvirtd_qemu.aug | 5 +++ src/qemu/qemu.conf | 28 ++++++++++++ src/qemu/qemu_conf.c | 9 ++++ src/qemu/qemu_conf.h | 4 ++ src/qemu/test_libvirtd_qemu.aug.in | 3 ++ .../qemuxml2argv-serial-tcp-tlsx509-chardev.xml | 41 ++++++++++++++++++ .../qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml | 50 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml
ACK
Regards, Daniel
As noted in my response in 6/9, I somehow missed fetching the chardevTLS, so I'll squash the following in before pushing the ACK'd patches, so that I can repost the secret changes in a v6: diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index f2d27a1..ce2a890 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -505,6 +505,8 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (virConfGetValueBool(conf, "spice_auto_unix_socket", &cfg->spiceAutoUnixSocket) < 0) goto cleanup; + if ((rv = virConfGetValueBool(conf, "chardev_tls", &cfg->chardevTLS)) < 0) + goto cleanup; if (virConfGetValueString(conf, "chardev_tls_x509_cert_dir", &cfg->chardevTLSx509certdir) < 0) goto cleanup; if ((rv = virConfGetValueBool(conf, "chardev_tls_x509_verify", &cfg->chardevTLSx509verify)) < 0)

When building a chardev device string for tcp, add the necessary pieces to access provide the TLS X.509 path to qemu. This includes generating the 'tls-creds-x509' object and then adding the 'tls-creds' parameter to the VIR_DOMAIN_CHR_TYPE_TCP command line. Finally add the tests for the qemu command line. This test will make use of the "new(ish)" /etc/pki/qemu setting for a TLS certificate environment by *not* "resetting" the chardevTLSx509certdir prior to running the test. Also use the default "verify" option (which is "no"). Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_alias.c | 16 +++ src/qemu/qemu_alias.h | 3 + src/qemu/qemu_command.c | 114 ++++++++++++++++++++- .../qemuxml2argv-serial-tcp-tlsx509-chardev.args | 33 ++++++ tests/qemuxml2argvtest.c | 6 ++ 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 0102c96..9094f3b 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -560,3 +560,19 @@ qemuDomainGetSecretAESAlias(const char *srcalias, return alias; } + + +/* qemuAliasTLSObjFromChardevAlias + * @chardev_alias: Pointer to the chardev alias string + * + * Generate and return a string to be used as the TLS object alias + */ +char * +qemuAliasTLSObjFromChardevAlias(const char *chardev_alias) +{ + char *ret; + + ignore_value(virAsprintf(&ret, "obj%s_tls0", chardev_alias)); + + return ret; +} diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index 505c40e..c366d28 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -76,4 +76,7 @@ char *qemuDomainGetMasterKeyAlias(void); char *qemuDomainGetSecretAESAlias(const char *srcalias, bool isLuks); +char *qemuAliasTLSObjFromChardevAlias(const char *chardev_alias) + ATTRIBUTE_NONNULL(1); + #endif /* __QEMU_ALIAS_H__*/ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 197537f..33cc451 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -679,6 +679,103 @@ qemuBuildRBDSecinfoURI(virBufferPtr buf, } +/* qemuBuildTLSx509BackendProps: + * @tlspath: path to the TLS credentials + * @listen: boolen listen for client or server setting + * @verifypeer: boolean to enable peer verification (form of authorization) + * @qemuCaps: capabilities + * @propsret: json properties to return + * + * Create a backend string for the tls-creds-x509 object. + * + * Returns 0 on success, -1 on failure with error set. + */ +static int +qemuBuildTLSx509BackendProps(const char *tlspath, + bool listen, + bool verifypeer, + virQEMUCapsPtr qemuCaps, + virJSONValuePtr *propsret) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *path = NULL; + int ret = -1; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_TLS_CREDS_X509)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("tls-creds-x509 not supported in this QEMU binary")); + return -1; + } + + virQEMUBuildBufferEscapeComma(&buf, tlspath); + if (virBufferCheckError(&buf) < 0) + goto cleanup; + path = virBufferContentAndReset(&buf); + + if (virJSONValueObjectCreate(propsret, + "s:dir", path, + "s:endpoint", (listen ? "server": "client"), + "b:verify-peer", verifypeer, + NULL) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virBufferFreeAndReset(&buf); + VIR_FREE(path); + return ret; +} + + +/* qemuBuildTLSx509CommandLine: + * @cmd: Pointer to command + * @tlspath: path to the TLS credentials + * @listen: boolen listen for client or server setting + * @verifypeer: boolean to enable peer verification (form of authorization) + * @inalias: Alias for the parent to generate object alias + * @qemuCaps: capabilities + * + * Create the command line for a TLS object + * + * Returns 0 on success, -1 on failure with error set. + */ +static int +qemuBuildTLSx509CommandLine(virCommandPtr cmd, + const char *tlspath, + bool listen, + bool verifypeer, + const char *inalias, + virQEMUCapsPtr qemuCaps) +{ + int ret = -1; + char *objalias = NULL; + virJSONValuePtr props = NULL; + char *tmp = NULL; + + if (qemuBuildTLSx509BackendProps(tlspath, listen, verifypeer, + qemuCaps, &props) < 0) + return -1; + + if (!(objalias = qemuAliasTLSObjFromChardevAlias(inalias))) + goto cleanup; + + if (!(tmp = virQEMUBuildObjectCommandlineFromJSON("tls-creds-x509", + objalias, props))) + goto cleanup; + + virCommandAddArgList(cmd, "-object", tmp, NULL); + + ret = 0; + + cleanup: + virJSONValueFree(props); + VIR_FREE(objalias); + VIR_FREE(tmp); + return ret; +} + + #define QEMU_DEFAULT_NBD_PORT "10809" #define QEMU_DEFAULT_GLUSTER_PORT "24007" @@ -4865,7 +4962,7 @@ qemuBuildChrChardevFileStr(virLogManagerPtr logManager, static char * qemuBuildChrChardevStr(virLogManagerPtr logManager, virCommandPtr cmd, - virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED, + virQEMUDriverConfigPtr cfg, const virDomainDef *def, const virDomainChrSourceDef *dev, const char *alias, @@ -4948,6 +5045,21 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager, dev->data.tcp.service, telnet ? ",telnet" : "", dev->data.tcp.listen ? ",server,nowait" : ""); + + if (cfg->chardevTLS) { + char *objalias = NULL; + + if (qemuBuildTLSx509CommandLine(cmd, cfg->chardevTLSx509certdir, + dev->data.tcp.listen, + cfg->chardevTLSx509verify, + alias, qemuCaps) < 0) + goto error; + + if (!(objalias = qemuAliasTLSObjFromChardevAlias(alias))) + goto error; + virBufferAsprintf(&buf, ",tls-creds=%s", objalias); + VIR_FREE(objalias); + } break; case VIR_DOMAIN_CHR_TYPE_UNIX: diff --git a/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args new file mode 100644 index 0000000..518117b --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args @@ -0,0 +1,33 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefconfig \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-chardev udp,id=charserial0,host=127.0.0.1,port=2222,localaddr=127.0.0.1,\ +localport=1111 \ +-device isa-serial,chardev=charserial0,id=serial0 \ +-object tls-creds-x509,id=objserial1_tls0,dir=/etc/pki/qemu,endpoint=client,\ +verify-peer=no \ +-chardev socket,id=charserial1,host=127.0.0.1,port=5555,\ +tls-creds=objserial1_tls0 \ +-device isa-serial,chardev=charserial1,id=serial1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index a5d51a8..8f138e6 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1094,6 +1094,12 @@ mymain(void) QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("serial-tcp-telnet-chardev", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); + driver.config->chardevTLS = 1; + DO_TEST("serial-tcp-tlsx509-chardev", + QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_OBJECT_TLS_CREDS_X509); + driver.config->chardevTLS = 0; + VIR_FREE(driver.config->chardevTLSx509certdir); DO_TEST("serial-many-chardev", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("parallel-tcp-chardev", -- 2.7.4

On Thu, Aug 04, 2016 at 11:21:21AM -0400, John Ferlan wrote:
When building a chardev device string for tcp, add the necessary pieces to access provide the TLS X.509 path to qemu. This includes generating the 'tls-creds-x509' object and then adding the 'tls-creds' parameter to the VIR_DOMAIN_CHR_TYPE_TCP command line.
Finally add the tests for the qemu command line. This test will make use of the "new(ish)" /etc/pki/qemu setting for a TLS certificate environment by *not* "resetting" the chardevTLSx509certdir prior to running the test. Also use the default "verify" option (which is "no").
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_alias.c | 16 +++ src/qemu/qemu_alias.h | 3 + src/qemu/qemu_command.c | 114 ++++++++++++++++++++- .../qemuxml2argv-serial-tcp-tlsx509-chardev.args | 33 ++++++ tests/qemuxml2argvtest.c | 6 ++ 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

If the incoming XML defined a path to a TLS X.509 certificate environment, add the necessary 'tls-creds-x509' object to the VIR_DOMAIN_CHR_TYPE_TCP character device. Likewise, if the environment exists the hot unplug needs adjustment as well. Note that all the return ret were changed to goto cleanup since the cfg needs to be unref'd Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 2 +- src/qemu/qemu_command.h | 8 ++++++ src/qemu/qemu_hotplug.c | 59 ++++++++++++++++++++++++++++++++++++++------ src/qemu/qemu_monitor_json.c | 9 +++++++ 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b2e905d..b25e219 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1091,6 +1091,7 @@ struct _virDomainChrSourceDef { char *service; bool listen; int protocol; + bool tlscreds; } tcp; struct { char *bindHost; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 33cc451..2295175 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -690,7 +690,7 @@ qemuBuildRBDSecinfoURI(virBufferPtr buf, * * Returns 0 on success, -1 on failure with error set. */ -static int +int qemuBuildTLSx509BackendProps(const char *tlspath, bool listen, bool verifypeer, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index dcf9ba6..583f35d 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -60,10 +60,18 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, const char *domainLibDir) ATTRIBUTE_NONNULL(15); + /* Generate the object properties for a secret */ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, virJSONValuePtr *propsret); +/* Generate the object properties for a tls-creds-x509 */ +int qemuBuildTLSx509BackendProps(const char *tlspath, + bool listen, + bool verifypeer, + virQEMUCapsPtr qemuCaps, + virJSONValuePtr *propsret); + /* Generate '-device' string for chardev device */ int qemuBuildChrDeviceStr(char **deviceStr, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 00e4a75..013fbed 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1644,12 +1644,17 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, virDomainChrDefPtr chr) { int ret = -1, rc; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; virErrorPtr orig_err; virDomainDefPtr vmdef = vm->def; char *devstr = NULL; + virDomainChrSourceDefPtr dev = &chr->source; char *charAlias = NULL; bool chardevAttached = false; + bool tlsobjAdded = false; + virJSONValuePtr tlsProps = NULL; + char *tlsAlias = NULL; bool need_release = false; if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && @@ -1673,7 +1678,29 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, if (qemuDomainChrPreInsert(vmdef, chr) < 0) goto cleanup; + if (cfg->chardevTLS) { + if (qemuBuildTLSx509BackendProps(cfg->chardevTLSx509certdir, + dev->data.tcp.listen, + cfg->chardevTLSx509verify, + priv->qemuCaps, + &tlsProps) < 0) + goto cleanup; + + if (!(tlsAlias = qemuAliasTLSObjFromChardevAlias(chr->info.alias))) + goto cleanup; + dev->data.tcp.tlscreds = true; + } + qemuDomainObjEnterMonitor(driver, vm); + if (tlsAlias) { + rc = qemuMonitorAddObject(priv->mon, "tls-creds-x509", + tlsAlias, tlsProps); + tlsProps = NULL; /* qemuMonitorAddObject consumes */ + if (rc < 0) + goto exit_monitor; + tlsobjAdded = true; + } + if (qemuMonitorAttachCharDev(priv->mon, charAlias, &chr->source) < 0) goto exit_monitor; chardevAttached = true; @@ -1693,12 +1720,17 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, qemuDomainChrInsertPreAllocCleanup(vmdef, chr); if (ret < 0 && need_release) qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); + VIR_FREE(tlsAlias); + virJSONValueFree(tlsProps); VIR_FREE(charAlias); VIR_FREE(devstr); + virObjectUnref(cfg); return ret; exit_monitor: orig_err = virSaveLastError(); + if (tlsobjAdded) + ignore_value(qemuMonitorDelObject(priv->mon, tlsAlias)); /* detach associated chardev on error */ if (chardevAttached) qemuMonitorDetachCharDev(priv->mon, charAlias); @@ -4286,32 +4318,40 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainChrDefPtr chr) { int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr vmdef = vm->def; virDomainChrDefPtr tmpChr; + char *objAlias = NULL; char *devstr = NULL; if (!(tmpChr = virDomainChrFind(vmdef, chr))) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("device not present in domain configuration")); - return ret; + goto cleanup; } if (!tmpChr->info.alias && qemuAssignDeviceChrAlias(vmdef, tmpChr, -1) < 0) - return ret; + goto cleanup; sa_assert(tmpChr->info.alias); + if (cfg->chardevTLS && + !(objAlias = qemuAliasTLSObjFromChardevAlias(tmpChr->info.alias))) + goto cleanup; + if (qemuBuildChrDeviceStr(&devstr, vmdef, chr, priv->qemuCaps) < 0) - return ret; + goto cleanup; qemuDomainMarkDeviceForRemoval(vm, &tmpChr->info); qemuDomainObjEnterMonitor(driver, vm); - if (devstr && qemuMonitorDelDevice(priv->mon, tmpChr->info.alias) < 0) { - ignore_value(qemuDomainObjExitMonitor(driver, vm)); - goto cleanup; - } + if (objAlias && qemuMonitorDelObject(priv->mon, objAlias) < 0) + goto exit_monitor; + + if (devstr && qemuMonitorDelDevice(priv->mon, tmpChr->info.alias) < 0) + goto exit_monitor; + if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; @@ -4323,7 +4363,12 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, cleanup: qemuDomainResetDeviceRemoval(vm); VIR_FREE(devstr); + virObjectUnref(cfg); return ret; + + exit_monitor: + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d455adf..a081ae5 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -6137,6 +6137,7 @@ qemuMonitorJSONAttachCharDevCommand(const char *chrID, virJSONValuePtr data = NULL; virJSONValuePtr addr = NULL; const char *backend_type = NULL; + char *tlsalias = NULL; bool telnet; if (!(backend = virJSONValueNewObject()) || @@ -6182,6 +6183,13 @@ qemuMonitorJSONAttachCharDevCommand(const char *chrID, virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 || virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0) goto error; + if (chr->data.tcp.tlscreds) { + if (!(tlsalias = qemuAliasTLSObjFromChardevAlias(chrID))) + goto error; + + if (virJSONValueObjectAppendString(data, "tls-creds", tlsalias) < 0) + goto error; + } break; case VIR_DOMAIN_CHR_TYPE_UDP: @@ -6247,6 +6255,7 @@ qemuMonitorJSONAttachCharDevCommand(const char *chrID, return ret; error: + VIR_FREE(tlsalias); virJSONValueFree(addr); virJSONValueFree(data); virJSONValueFree(backend); -- 2.7.4

On Thu, Aug 04, 2016 at 11:21:22AM -0400, John Ferlan wrote:
If the incoming XML defined a path to a TLS X.509 certificate environment, add the necessary 'tls-creds-x509' object to the VIR_DOMAIN_CHR_TYPE_TCP character device.
Likewise, if the environment exists the hot unplug needs adjustment as well. Note that all the return ret were changed to goto cleanup since the cfg needs to be unref'd
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/domain_conf.h | 1 + src/qemu/qemu_command.c | 2 +- src/qemu/qemu_command.h | 8 ++++++ src/qemu/qemu_hotplug.c | 59 ++++++++++++++++++++++++++++++++++++++------ src/qemu/qemu_monitor_json.c | 9 +++++++ 5 files changed, 71 insertions(+), 8 deletions(-)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b2e905d..b25e219 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1091,6 +1091,7 @@ struct _virDomainChrSourceDef { char *service; bool listen; int protocol; + bool tlscreds; } tcp; struct { char *bindHost; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 33cc451..2295175 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -690,7 +690,7 @@ qemuBuildRBDSecinfoURI(virBufferPtr buf, * * Returns 0 on success, -1 on failure with error set. */ -static int +int qemuBuildTLSx509BackendProps(const char *tlspath, bool listen, bool verifypeer, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index dcf9ba6..583f35d 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -60,10 +60,18 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, const char *domainLibDir) ATTRIBUTE_NONNULL(15);
+ /* Generate the object properties for a secret */ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, virJSONValuePtr *propsret);
+/* Generate the object properties for a tls-creds-x509 */ +int qemuBuildTLSx509BackendProps(const char *tlspath, + bool listen, + bool verifypeer, + virQEMUCapsPtr qemuCaps, + virJSONValuePtr *propsret); + /* Generate '-device' string for chardev device */ int qemuBuildChrDeviceStr(char **deviceStr, @@ -4286,32 +4318,40 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainChrDefPtr chr) { int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr vmdef = vm->def; virDomainChrDefPtr tmpChr; + char *objAlias = NULL; char *devstr = NULL;
if (!(tmpChr = virDomainChrFind(vmdef, chr))) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("device not present in domain configuration")); - return ret; + goto cleanup; }
if (!tmpChr->info.alias && qemuAssignDeviceChrAlias(vmdef, tmpChr, -1) < 0) - return ret; + goto cleanup;
sa_assert(tmpChr->info.alias);
+ if (cfg->chardevTLS && + !(objAlias = qemuAliasTLSObjFromChardevAlias(tmpChr->info.alias))) + goto cleanup; + if (qemuBuildChrDeviceStr(&devstr, vmdef, chr, priv->qemuCaps) < 0) - return ret; + goto cleanup;
qemuDomainMarkDeviceForRemoval(vm, &tmpChr->info);
qemuDomainObjEnterMonitor(driver, vm); - if (devstr && qemuMonitorDelDevice(priv->mon, tmpChr->info.alias) < 0) { - ignore_value(qemuDomainObjExitMonitor(driver, vm)); - goto cleanup; - } + if (objAlias && qemuMonitorDelObject(priv->mon, objAlias) < 0) + goto exit_monitor; + + if (devstr && qemuMonitorDelDevice(priv->mon, tmpChr->info.alias) < 0) + goto exit_monitor;
We should really detach the device before the tls object, due to their dependancy order ACK with that swapped Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Add a new secret usage type known as "tls" - it will handle adding the secret objects for various TLS objects that need to provide some sort of passphrase in order to access the credentials. The format is: <secret ...> <uuid>...</uuid> ... <usage type='tls'> <name>mumblyfratz</name> </usage> </secret> Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/aclpolkit.html.in | 4 +++ docs/formatsecret.html.in | 59 +++++++++++++++++++++++++++++++++++-- docs/schemas/secret.rng | 10 +++++++ include/libvirt/libvirt-secret.h | 1 + src/access/viraccessdriverpolkit.c | 13 ++++++++ src/conf/secret_conf.c | 23 ++++++++++++++- src/conf/secret_conf.h | 1 + src/conf/virsecretobj.c | 5 ++++ tests/secretxml2xmlin/usage-tls.xml | 7 +++++ tests/secretxml2xmltest.c | 1 + 10 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 tests/secretxml2xmlin/usage-tls.xml diff --git a/docs/aclpolkit.html.in b/docs/aclpolkit.html.in index dae0814..dd4c5fb 100644 --- a/docs/aclpolkit.html.in +++ b/docs/aclpolkit.html.in @@ -224,6 +224,10 @@ <td>secret_usage_target</td> <td>Name of the associated iSCSI target, if any</td> </tr> + <tr> + <td>secret_usage_name</td> + <td>Name of the associated TLS secret, if any</td> + </tr> </tbody> </table> diff --git a/docs/formatsecret.html.in b/docs/formatsecret.html.in index 216a83c..a9f3e8a 100644 --- a/docs/formatsecret.html.in +++ b/docs/formatsecret.html.in @@ -41,8 +41,9 @@ <dd> Specifies what this secret is used for. A mandatory <code>type</code> attribute specifies the usage category, currently - only <code>volume</code>, <code>ceph</code>, and <code>iscsi</code> - are defined. Specific usage categories are described below. + only <code>volume</code>, <code>ceph</code>, <code>iscsi</code>, + and <code>tls</code> are defined. Specific usage categories + are described below. </dd> </dl> @@ -271,5 +272,59 @@ </auth> </pre> + <h3><a name="tlsUsageType">Usage type "tls"</a></h3> + + <p> + This secret may be used in order to provide the passphrase for the + private key used to provide TLS credentials. + The <code><usage type='tls'></code> element must contain a + single <code>name</code> element that specifies a usage name + for the secret. + <span class="since">Since 2.2.0</span>. + The following is an example of the expected XML and processing to + define the secret: + </p> + + <pre> + # cat tls-secret.xml + <secret ephemeral='no' private='yes'> + <description>sample tls secret</description> + <usage type='tls'> + <name>TLS_example</name> + </usage> + </secret> + + # virsh secret-define tls-secret.xml + Secret 718c71bd-67b5-4a2b-87ec-a24e8ca200dc created + + # virsh secret-list + UUID Usage + ----------------------------------------------------------- + 718c71bd-67b5-4a2b-87ec-a24e8ca200dc tls TLS_example + # + + </pre> + + <p> + A secret may also be defined via the + <a href="html/libvirt-libvirt-secret.html#virSecretDefineXML"> + <code>virSecretDefineXML</code></a> API. + + Once the secret is defined, a secret value will need to be set. The + secret would be the passphrase used to access the TLS credentials. + The following is a simple example of using + <code>virsh secret-set-value</code> to set the secret value. The + <a href="html/libvirt-libvirt-secret.html#virSecretSetValue"> + <code>virSecretSetValue</code></a> API may also be used to set + a more secure secret without using printable/readable characters. + </p> + + <pre> + # MYSECRET=`printf %s "letmein" | base64` + # virsh secret-set-value 718c71bd-67b5-4a2b-87ec-a24e8ca200dc $MYSECRET + Secret value set + + </pre> + </body> </html> diff --git a/docs/schemas/secret.rng b/docs/schemas/secret.rng index e21e700..1e94d66 100644 --- a/docs/schemas/secret.rng +++ b/docs/schemas/secret.rng @@ -36,6 +36,7 @@ <ref name='usagevolume'/> <ref name='usageceph'/> <ref name='usageiscsi'/> + <ref name='usagetls'/> <!-- More choices later --> </choice> </element> @@ -71,4 +72,13 @@ </element> </define> + <define name='usagetls'> + <attribute name='type'> + <value>tls</value> + </attribute> + <element name='name'> + <ref name='genericName'/> + </element> + </define> + </grammar> diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-secret.h index 02728ba..2ae36f6 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -43,6 +43,7 @@ typedef enum { VIR_SECRET_USAGE_TYPE_VOLUME = 1, VIR_SECRET_USAGE_TYPE_CEPH = 2, VIR_SECRET_USAGE_TYPE_ISCSI = 3, + VIR_SECRET_USAGE_TYPE_TLS = 4, # ifdef VIR_ENUM_SENTINELS VIR_SECRET_USAGE_TYPE_LAST diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 89bc890..0d9e0a1 100644 --- a/src/access/viraccessdriverpolkit.c +++ b/src/access/viraccessdriverpolkit.c @@ -338,6 +338,19 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, virAccessPermSecretTypeToString(perm), attrs); } break; + case VIR_SECRET_USAGE_TYPE_TLS: { + const char *attrs[] = { + "connect_driver", driverName, + "secret_uuid", uuidstr, + "secret_usage_name", secret->usage.name, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; } } diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c index d510645..e662455 100644 --- a/src/conf/secret_conf.c +++ b/src/conf/secret_conf.c @@ -30,6 +30,7 @@ #include "secret_conf.h" #include "virsecretobj.h" #include "virerror.h" +#include "virstring.h" #include "virxml.h" #include "viruuid.h" @@ -38,7 +39,7 @@ VIR_LOG_INIT("conf.secret_conf"); VIR_ENUM_IMPL(virSecretUsage, VIR_SECRET_USAGE_TYPE_LAST, - "none", "volume", "ceph", "iscsi") + "none", "volume", "ceph", "iscsi", "tls") const char * virSecretUsageIDForDef(virSecretDefPtr def) @@ -56,6 +57,9 @@ virSecretUsageIDForDef(virSecretDefPtr def) case VIR_SECRET_USAGE_TYPE_ISCSI: return def->usage.target; + case VIR_SECRET_USAGE_TYPE_TLS: + return def->usage.name; + default: return NULL; } @@ -85,6 +89,10 @@ virSecretDefFree(virSecretDefPtr def) VIR_FREE(def->usage.target); break; + case VIR_SECRET_USAGE_TYPE_TLS: + VIR_FREE(def->usage.name); + break; + default: VIR_ERROR(_("unexpected secret usage type %d"), def->usage_type); break; @@ -145,6 +153,15 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, } break; + case VIR_SECRET_USAGE_TYPE_TLS: + def->usage.name = virXPathString("string(./usage/name)", ctxt); + if (!def->usage.name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("TLS usage specified, but name is missing")); + return -1; + } + break; + default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret usage type %d"), @@ -297,6 +314,10 @@ virSecretDefFormatUsage(virBufferPtr buf, virBufferEscapeString(buf, "<target>%s</target>\n", def->usage.target); break; + case VIR_SECRET_USAGE_TYPE_TLS: + virBufferEscapeString(buf, "<name>%s</name>\n", def->usage.name); + break; + default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret usage type %d"), diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index 4584403..c34880f 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -40,6 +40,7 @@ struct _virSecretDef { char *volume; /* May be NULL */ char *ceph; char *target; + char *name; } usage; }; diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index 30a5e80..2bdfe08 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -237,6 +237,11 @@ virSecretObjSearchName(const void *payload, if (STREQ(secret->def->usage.target, data->usageID)) found = 1; break; + + case VIR_SECRET_USAGE_TYPE_TLS: + if (STREQ(secret->def->usage.name, data->usageID)) + found = 1; + break; } cleanup: diff --git a/tests/secretxml2xmlin/usage-tls.xml b/tests/secretxml2xmlin/usage-tls.xml new file mode 100644 index 0000000..88068b5 --- /dev/null +++ b/tests/secretxml2xmlin/usage-tls.xml @@ -0,0 +1,7 @@ +<secret ephemeral='no' private='no'> + <uuid>f52a81b2-424e-490c-823d-6bd4235bc572</uuid> + <description>Sample TLS Secret</description> + <usage type='tls'> + <name>mumblyfratz</name> + </usage> +</secret> diff --git a/tests/secretxml2xmltest.c b/tests/secretxml2xmltest.c index 8dcbb40..714c709 100644 --- a/tests/secretxml2xmltest.c +++ b/tests/secretxml2xmltest.c @@ -80,6 +80,7 @@ mymain(void) DO_TEST("usage-volume"); DO_TEST("usage-ceph"); DO_TEST("usage-iscsi"); + DO_TEST("usage-tls"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.7.4

On Thu, Aug 04, 2016 at 11:21:23AM -0400, John Ferlan wrote:
Add a new secret usage type known as "tls" - it will handle adding the secret objects for various TLS objects that need to provide some sort of passphrase in order to access the credentials.
The format is:
<secret ...> <uuid>...</uuid> ... <usage type='tls'> <name>mumblyfratz</name> </usage> </secret>
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/aclpolkit.html.in | 4 +++ docs/formatsecret.html.in | 59 +++++++++++++++++++++++++++++++++++-- docs/schemas/secret.rng | 10 +++++++ include/libvirt/libvirt-secret.h | 1 + src/access/viraccessdriverpolkit.c | 13 ++++++++ src/conf/secret_conf.c | 23 ++++++++++++++- src/conf/secret_conf.h | 1 + src/conf/virsecretobj.c | 5 ++++ tests/secretxml2xmlin/usage-tls.xml | 7 +++++ tests/secretxml2xmltest.c | 1 + 10 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 tests/secretxml2xmlin/usage-tls.xml
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Define, parse, and format a key secret element for a chardev tcp backend. This secret will be used in conjunction with the chartcp_tls_x509_cert_dir in order to provide the secret to the TLS encrypted TCP chardev. <secret type='tls' usage='tlsexample'/> Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatdomain.html.in | 29 ++++++++++++ docs/schemas/domaincommon.rng | 21 +++++++++ src/conf/domain_conf.c | 35 +++++++++++++++ src/conf/domain_conf.h | 3 ++ ...uxml2argv-serial-tcp-tlsx509-secret-chardev.xml | 51 ++++++++++++++++++++++ ...ml2xmlout-serial-tcp-tlsx509-secret-chardev.xml | 1 + tests/qemuxml2xmltest.c | 1 + 7 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index fa88839..b1d9741 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -6097,6 +6097,35 @@ qemu-kvm -net nic,model=? /dev/null </devices> ...</pre> + <p> + <span class="since">Since 2.2.0,</span> some hypervisors support using + a TLS X.509 certificate environment in order to encrypt the TCP + connection. In order to provide the passphrase for the certificates, + provide a <code>secret</code> element. The <code>secret</code> element + takes two required attributes <code>type</code> and either + <code>UUID</code> or <code>usage</code>. The supported <code>type</code> + is a "passphrase" secret referenced via either attribute + <code>uuid</code> or <code>usage</code>. + </p> +<pre> + ... + <devices> + <serial type="tcp"> + <source mode="connect" host="0.0.0.0" service="2445"/> + <protocol type="raw"/> + <secret type='passphrase' usage='keyexample'/> + <target port="1"/> + </serial> + ... + <serial type="tcp"> + <source mode="bind" host="127.0.0.1" service="2445"/> + <protocol type="raw"/> + <target port="1"/> + <secret type='passphrase' usage='keyexample'/> + </serial> + </devices> + ...</pre> + <h6><a name="elementsCharUDP">UDP network console</a></h6> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index cb9f134..5702e3b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3264,6 +3264,9 @@ <ref name="qemucdevTgtDef"/> </optional> <optional> + <ref name="qemucdevSecret"/> + </optional> + <optional> <ref name="alias"/> </optional> <optional> @@ -3315,6 +3318,24 @@ </element> </define> + <define name="qemucdevSecret"> + <element name='secret'> + <attribute name='type'> + <choice> + <value>tls</value> + </choice> + </attribute> + <choice> + <attribute name='uuid'> + <ref name="UUID"/> + </attribute> + <attribute name='usage'> + <ref name='genericName'/> + </attribute> + </choice> + </element> + </define> + <define name="qemucdevSrcTypeChoice"> <choice> <value>dev</value> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e58046b..53e5bae 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1899,6 +1899,7 @@ virDomainChrSourceDefClear(virDomainChrSourceDefPtr def) case VIR_DOMAIN_CHR_TYPE_TCP: VIR_FREE(def->data.tcp.host); VIR_FREE(def->data.tcp.service); + virSecretLookupDefClear(&def->data.tcp.seclookupdef); break; case VIR_DOMAIN_CHR_TYPE_UNIX: @@ -1955,6 +1956,10 @@ virDomainChrSourceDefCopy(virDomainChrSourceDefPtr dest, if (VIR_STRDUP(dest->data.tcp.service, src->data.tcp.service) < 0) return -1; + + if (virSecretLookupDefCopy(&dest->data.tcp.seclookupdef, + &src->data.tcp.seclookupdef) < 0) + return -1; break; case VIR_DOMAIN_CHR_TYPE_UNIX: @@ -9867,6 +9872,8 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, char *master = NULL; char *slave = NULL; char *append = NULL; + xmlNodePtr secret = NULL; + char *sectypestr = NULL; int remaining = 0; while (cur != NULL) { @@ -9956,6 +9963,8 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { if (!protocol) protocol = virXMLPropString(cur, "type"); + } else if (xmlStrEqual(cur->name, BAD_CAST "secret")) { + secret = cur; } else { remaining++; } @@ -10059,6 +10068,25 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, goto error; } + if (secret) { + + if (!(sectypestr = virXMLPropString(secret, "type"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TCP chardev secret type")); + goto error; + } + if ((def->data.tcp.sectype = + virSecretUsageTypeFromString(sectypestr)) != + VIR_SECRET_USAGE_TYPE_TLS) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid TCP chardev secret type '%s'"), + sectypestr); + goto error; + } + if (virSecretLookupParseSecret(secret, + &def->data.tcp.seclookupdef) < 0) + goto error; + } break; case VIR_DOMAIN_CHR_TYPE_UDP: @@ -10133,6 +10161,7 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, VIR_FREE(append); VIR_FREE(logappend); VIR_FREE(logfile); + VIR_FREE(sectypestr); return remaining; @@ -21157,6 +21186,12 @@ virDomainChrSourceDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<protocol type='%s'/>\n", virDomainChrTcpProtocolTypeToString( def->data.tcp.protocol)); + if (def->data.tcp.sectype == VIR_SECRET_USAGE_TYPE_TLS) { + const char *typestr = + virSecretUsageTypeToString(def->data.tcp.sectype); + virSecretLookupFormatSecret(buf, typestr, + &def->data.tcp.seclookupdef); + } break; case VIR_DOMAIN_CHR_TYPE_UNIX: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b25e219..0b48b8e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -53,6 +53,7 @@ # include "virprocess.h" # include "virgic.h" # include "virperf.h" +# include "virsecret.h" # include "virtypedparam.h" /* forward declarations of all device types, required by @@ -1092,6 +1093,8 @@ struct _virDomainChrSourceDef { bool listen; int protocol; bool tlscreds; + int sectype; /* virSecretUsage */ + virSecretLookupTypeDef seclookupdef; } tcp; struct { char *bindHost; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml new file mode 100644 index 0000000..62bc151 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml @@ -0,0 +1,51 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <serial type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target port='0'/> + </serial> + <serial type='tcp'> + <source mode='connect' host='127.0.0.1' service='5555'/> + <protocol type='raw'/> + <secret type='tls' usage='mycluster_myname'/> + <target port='0'/> + </serial> + <console type='udp'> + <source mode='bind' host='127.0.0.1' service='1111'/> + <source mode='connect' host='127.0.0.1' service='2222'/> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml new file mode 120000 index 0000000..974d3fb --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml @@ -0,0 +1 @@ +../qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 85241b9..67e5857 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -541,6 +541,7 @@ mymain(void) DO_TEST("serial-udp"); DO_TEST("serial-tcp-telnet"); DO_TEST("serial-tcp-tlsx509-chardev"); + DO_TEST("serial-tcp-tlsx509-secret-chardev"); DO_TEST("serial-many"); DO_TEST("serial-spiceport"); DO_TEST("serial-spiceport-nospice"); -- 2.7.4

On Thu, Aug 04, 2016 at 11:21:24AM -0400, John Ferlan wrote:
Define, parse, and format a key secret element for a chardev tcp backend. This secret will be used in conjunction with the chartcp_tls_x509_cert_dir in order to provide the secret to the TLS encrypted TCP chardev.
<secret type='tls' usage='tlsexample'/>
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatdomain.html.in | 29 ++++++++++++ docs/schemas/domaincommon.rng | 21 +++++++++ src/conf/domain_conf.c | 35 +++++++++++++++ src/conf/domain_conf.h | 3 ++ ...uxml2argv-serial-tcp-tlsx509-secret-chardev.xml | 51 ++++++++++++++++++++++ ...ml2xmlout-serial-tcp-tlsx509-secret-chardev.xml | 1 + tests/qemuxml2xmltest.c | 1 + 7 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml
Hmm, it feels little odd that we're having to give the password in the XML, for a certificate thats configured in qemu.conf. I wonder if we instead need to have the secret UUID listed in qemu.conf too Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 08/05/2016 04:25 AM, Daniel P. Berrange wrote:
On Thu, Aug 04, 2016 at 11:21:24AM -0400, John Ferlan wrote:
Define, parse, and format a key secret element for a chardev tcp backend. This secret will be used in conjunction with the chartcp_tls_x509_cert_dir in order to provide the secret to the TLS encrypted TCP chardev.
<secret type='tls' usage='tlsexample'/>
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatdomain.html.in | 29 ++++++++++++ docs/schemas/domaincommon.rng | 21 +++++++++ src/conf/domain_conf.c | 35 +++++++++++++++ src/conf/domain_conf.h | 3 ++ ...uxml2argv-serial-tcp-tlsx509-secret-chardev.xml | 51 ++++++++++++++++++++++ ...ml2xmlout-serial-tcp-tlsx509-secret-chardev.xml | 1 + tests/qemuxml2xmltest.c | 1 + 7 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml
Hmm, it feels little odd that we're having to give the password in the XML, for a certificate thats configured in qemu.conf. I wonder if we instead need to have the secret UUID listed in qemu.conf too
I knew there was something I wanted to get back to... I guess it seemed awkward to have to modify qemu.conf to list a UUID of a libvirt secret that would be generated after initial startup and thus would require a restart to read/load the secret into the cfg. I suppose that's akin to having/changing the "{spice|vnc}_password" in qemu.conf, so perhaps no different from that processing. Still Hmmm... I suppose the admin interface could handle these tasks as well. Anyway - secondarily, by adding UUID to qemu.conf, if cfg->chardevTLS was set (something I appear to have forgotten to do in patch 2 too, sigh), then that would mean every domain would use TLS. Is that desired? Or should there still be some domain XML attribute added to signify the desire for the domain to use TLS. Would there ever be a use case where multiple TLS environments would be set up for different domains with the same host? Tks - John

On Tue, Sep 06, 2016 at 06:29:38PM -0400, John Ferlan wrote:
On 08/05/2016 04:25 AM, Daniel P. Berrange wrote:
On Thu, Aug 04, 2016 at 11:21:24AM -0400, John Ferlan wrote:
Define, parse, and format a key secret element for a chardev tcp backend. This secret will be used in conjunction with the chartcp_tls_x509_cert_dir in order to provide the secret to the TLS encrypted TCP chardev.
<secret type='tls' usage='tlsexample'/>
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatdomain.html.in | 29 ++++++++++++ docs/schemas/domaincommon.rng | 21 +++++++++ src/conf/domain_conf.c | 35 +++++++++++++++ src/conf/domain_conf.h | 3 ++ ...uxml2argv-serial-tcp-tlsx509-secret-chardev.xml | 51 ++++++++++++++++++++++ ...ml2xmlout-serial-tcp-tlsx509-secret-chardev.xml | 1 + tests/qemuxml2xmltest.c | 1 + 7 files changed, 141 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.xml create mode 120000 tests/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-secret-chardev.xml
Hmm, it feels little odd that we're having to give the password in the XML, for a certificate thats configured in qemu.conf. I wonder if we instead need to have the secret UUID listed in qemu.conf too
I knew there was something I wanted to get back to...
I guess it seemed awkward to have to modify qemu.conf to list a UUID of a libvirt secret that would be generated after initial startup and thus would require a restart to read/load the secret into the cfg.
I suppose that's akin to having/changing the "{spice|vnc}_password" in qemu.conf, so perhaps no different from that processing. Still
Hmmm... I suppose the admin interface could handle these tasks as well.
Anyway - secondarily, by adding UUID to qemu.conf, if cfg->chardevTLS was set (something I appear to have forgotten to do in patch 2 too, sigh), then that would mean every domain would use TLS. Is that desired?
As default behaviour I think it is desirable that we can turn TLS on for every VM at once - I tend to view it as a host network integration task, rather than a VM configuration task. Same rationale that we use for TLS wth VNC/SPICE.
Or should there still be some domain XML attribute added to signify the desire for the domain to use TLS.
There's no reason we can't have a tri-state TLS flag against the chardev in the XML too, to override the default behaviour of cfg->chardevTLS Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Modeled after the qemuDomainHostdevPrivatePtr (commit id '27726d8c'), create a privateData pointer in the _virDomainChardevDef to allow storage of private data for a hypervisor in order to at least temporarily store secret data for usage during qemuBuildCommandLine. NB: Since the qemu_parse_command (qemuParseCommandLine) code is not expecting to restore the secret data, there's no need to add code code to handle this new structure there. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/domain_conf.c | 29 ++++++++++++++++++++-------- src/conf/domain_conf.h | 4 +++- src/libxl/libxl_domain.c | 2 +- src/lxc/lxc_native.c | 2 +- src/qemu/qemu_domain.c | 44 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 14 ++++++++++++++ src/qemu/qemu_parse_command.c | 4 ++-- src/vz/vz_sdk.c | 2 +- src/xenconfig/xen_sxpr.c | 2 +- 9 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 53e5bae..d83011b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2087,6 +2087,8 @@ void virDomainChrDefFree(virDomainChrDefPtr def) VIR_FREE(def->seclabels); } + virObjectUnref(def->privateData); + VIR_FREE(def); } @@ -10175,7 +10177,7 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, * default port. */ virDomainChrDefPtr -virDomainChrDefNew(void) +virDomainChrDefNew(virDomainXMLOptionPtr xmlopt) { virDomainChrDefPtr def = NULL; @@ -10183,6 +10185,11 @@ virDomainChrDefNew(void) return NULL; def->target.port = -1; + + if (xmlopt && xmlopt->privateData.chardevNew && + !(def->privateData = xmlopt->privateData.chardevNew())) + VIR_FREE(def); + return def; } @@ -10230,7 +10237,8 @@ virDomainChrDefNew(void) * */ static virDomainChrDefPtr -virDomainChrDefParseXML(xmlXPathContextPtr ctxt, +virDomainChrDefParseXML(virDomainXMLOptionPtr xmlopt, + xmlXPathContextPtr ctxt, xmlNodePtr node, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels, @@ -10242,7 +10250,7 @@ virDomainChrDefParseXML(xmlXPathContextPtr ctxt, virDomainChrDefPtr def; bool seenTarget = false; - if (!(def = virDomainChrDefNew())) + if (!(def = virDomainChrDefNew(xmlopt))) return NULL; type = virXMLPropString(node, "type"); @@ -13419,7 +13427,8 @@ virDomainDeviceDefParse(const char *xmlStr, goto error; break; case VIR_DOMAIN_DEVICE_CHR: - if (!(dev->data.chr = virDomainChrDefParseXML(ctxt, + if (!(dev->data.chr = virDomainChrDefParseXML(xmlopt, + ctxt, node, def->seclabels, def->nseclabels, @@ -16887,7 +16896,8 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; for (i = 0; i < n; i++) { - virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, + virDomainChrDefPtr chr = virDomainChrDefParseXML(xmlopt, + ctxt, nodes[i], def->seclabels, def->nseclabels, @@ -16914,7 +16924,8 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; for (i = 0; i < n; i++) { - virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, + virDomainChrDefPtr chr = virDomainChrDefParseXML(xmlopt, + ctxt, nodes[i], def->seclabels, def->nseclabels, @@ -16943,7 +16954,8 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; for (i = 0; i < n; i++) { - virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, + virDomainChrDefPtr chr = virDomainChrDefParseXML(xmlopt, + ctxt, nodes[i], def->seclabels, def->nseclabels, @@ -16962,7 +16974,8 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; for (i = 0; i < n; i++) { - virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, + virDomainChrDefPtr chr = virDomainChrDefParseXML(xmlopt, + ctxt, nodes[i], def->seclabels, def->nseclabels, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0b48b8e..0caee51 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1118,6 +1118,7 @@ struct _virDomainChrSourceDef { /* A complete character device, both host and domain views. */ struct _virDomainChrDef { int deviceType; /* enum virDomainChrDeviceType */ + virObjectPtr privateData; bool targetTypeAttr; int targetType; /* enum virDomainChrConsoleTargetType || @@ -2427,6 +2428,7 @@ struct _virDomainXMLPrivateDataCallbacks { virDomainXMLPrivateDataNewFunc diskNew; virDomainXMLPrivateDataNewFunc hostdevNew; virDomainXMLPrivateDataNewFunc vcpuNew; + virDomainXMLPrivateDataNewFunc chardevNew; virDomainXMLPrivateDataFormatFunc format; virDomainXMLPrivateDataParseFunc parse; }; @@ -2559,7 +2561,7 @@ bool virDomainDefHasDeviceAddress(virDomainDefPtr def, void virDomainDefFree(virDomainDefPtr vm); -virDomainChrDefPtr virDomainChrDefNew(void); +virDomainChrDefPtr virDomainChrDefNew(virDomainXMLOptionPtr xmlopt); virDomainDefPtr virDomainDefNew(void); virDomainDefPtr virDomainDefNewFull(const char *name, diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c index 57ef235..e5384a7 100644 --- a/src/libxl/libxl_domain.c +++ b/src/libxl/libxl_domain.c @@ -387,7 +387,7 @@ libxlDomainDefPostParse(virDomainDefPtr def, if (def->os.type != VIR_DOMAIN_OSTYPE_HVM && def->nconsoles == 0) { virDomainChrDefPtr chrdef; - if (!(chrdef = virDomainChrDefNew())) + if (!(chrdef = virDomainChrDefNew(NULL))) return -1; chrdef->source.type = VIR_DOMAIN_CHR_TYPE_PTY; diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index 4b22e2a..faff03a 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -703,7 +703,7 @@ lxcCreateConsoles(virDomainDefPtr def, virConfPtr properties) def->nconsoles = nbttys; for (i = 0; i < nbttys; i++) { - if (!(console = virDomainChrDefNew())) + if (!(console = virDomainChrDefNew(NULL))) goto error; console->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index bb6f21e..b215139 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -860,6 +860,49 @@ qemuDomainVcpuPrivateDispose(void *obj ATTRIBUTE_UNUSED) } +static virClassPtr qemuDomainChardevPrivateClass; +static void qemuDomainChardevPrivateDispose(void *obj); + +static int +qemuDomainChardevPrivateOnceInit(void) +{ + qemuDomainChardevPrivateClass = + virClassNew(virClassForObject(), + "qemuDomainChardevPrivate", + sizeof(qemuDomainChardevPrivate), + qemuDomainChardevPrivateDispose); + if (!qemuDomainChardevPrivateClass) + return -1; + else + return 0; +} + +VIR_ONCE_GLOBAL_INIT(qemuDomainChardevPrivate) + +static virObjectPtr +qemuDomainChardevPrivateNew(void) +{ + qemuDomainChardevPrivatePtr priv; + + if (qemuDomainChardevPrivateInitialize() < 0) + return NULL; + + if (!(priv = virObjectNew(qemuDomainChardevPrivateClass))) + return NULL; + + return (virObjectPtr) priv; +} + + +static void +qemuDomainChardevPrivateDispose(void *obj) +{ + qemuDomainChardevPrivatePtr priv = obj; + + qemuDomainSecretInfoFree(&priv->secinfo); +} + + /* qemuDomainSecretPlainSetup: * @conn: Pointer to connection * @secinfo: Pointer to secret info @@ -1763,6 +1806,7 @@ virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks = { .diskNew = qemuDomainDiskPrivateNew, .vcpuNew = qemuDomainVcpuPrivateNew, .hostdevNew = qemuDomainHostdevPrivateNew, + .chardevNew = qemuDomainChardevPrivateNew, .parse = qemuDomainObjPrivateXMLParse, .format = qemuDomainObjPrivateXMLFormat, }; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 0613093..0411ac1 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -340,6 +340,20 @@ struct _qemuDomainHostdevPrivate { qemuDomainSecretInfoPtr secinfo; }; +# define QEMU_DOMAIN_CHARDEV_PRIVATE(chardev) \ + ((qemuDomainChardevPrivatePtr) (chardev)->privateData) + +typedef struct _qemuDomainChardevPrivate qemuDomainChardevPrivate; +typedef qemuDomainChardevPrivate *qemuDomainChardevPrivatePtr; +struct _qemuDomainChardevPrivate { + virObject parent; + + /* for char devices using secret + * NB: *not* to be written to qemu domain object XML */ + qemuDomainSecretInfoPtr secinfo; +}; + + typedef enum { QEMU_PROCESS_EVENT_WATCHDOG = 0, QEMU_PROCESS_EVENT_GUESTPANIC, diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c index 82d1621..6540d60 100644 --- a/src/qemu/qemu_parse_command.c +++ b/src/qemu/qemu_parse_command.c @@ -2189,7 +2189,7 @@ qemuParseCommandLine(virCapsPtr caps, if (STRNEQ(val, "none")) { virDomainChrDefPtr chr; - if (!(chr = virDomainChrDefNew())) + if (!(chr = virDomainChrDefNew(NULL))) goto error; if (qemuParseCommandLineChr(&chr->source, val) < 0) { @@ -2208,7 +2208,7 @@ qemuParseCommandLine(virCapsPtr caps, if (STRNEQ(val, "none")) { virDomainChrDefPtr chr; - if (!(chr = virDomainChrDefNew())) + if (!(chr = virDomainChrDefNew(NULL))) goto error; if (qemuParseCommandLineChr(&chr->source, val) < 0) { diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index 38254c0..edd3f2d 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -1218,7 +1218,7 @@ prlsdkAddSerialInfo(PRL_HANDLE sdkdom, ret = PrlVmCfg_GetSerialPort(sdkdom, i, &serialPort); prlsdkCheckRetGoto(ret, cleanup); - if (!(chr = virDomainChrDefNew())) + if (!(chr = virDomainChrDefNew(NULL))) goto cleanup; if (prlsdkGetSerialInfo(serialPort, chr)) diff --git a/src/xenconfig/xen_sxpr.c b/src/xenconfig/xen_sxpr.c index 40dc53c..e586e24 100644 --- a/src/xenconfig/xen_sxpr.c +++ b/src/xenconfig/xen_sxpr.c @@ -190,7 +190,7 @@ xenParseSxprChar(const char *value, char *tmp; virDomainChrDefPtr def; - if (!(def = virDomainChrDefNew())) + if (!(def = virDomainChrDefNew(NULL))) return NULL; prefix = value; -- 2.7.4

Add the secret object prior to the chardev tcp so the 'passwordid=' can be added if the domain XML has a <secret> for the chardev TLS. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 31 ++++++++- src/qemu/qemu_command.h | 1 + src/qemu/qemu_domain.c | 80 +++++++++++++++++++++- src/qemu/qemu_domain.h | 8 +++ src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 2 +- ...xml2argv-serial-tcp-tlsx509-secret-chardev.args | 38 ++++++++++ tests/qemuxml2argvtest.c | 15 ++++ 8 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 2295175..fc729e2 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -683,6 +683,7 @@ qemuBuildRBDSecinfoURI(virBufferPtr buf, * @tlspath: path to the TLS credentials * @listen: boolen listen for client or server setting * @verifypeer: boolean to enable peer verification (form of authorization) + * @secalias: if one exists, the alias of the security object for passwordid * @qemuCaps: capabilities * @propsret: json properties to return * @@ -694,6 +695,7 @@ int qemuBuildTLSx509BackendProps(const char *tlspath, bool listen, bool verifypeer, + const char *secalias, virQEMUCapsPtr qemuCaps, virJSONValuePtr *propsret) { @@ -719,6 +721,10 @@ qemuBuildTLSx509BackendProps(const char *tlspath, NULL) < 0) goto cleanup; + if (secalias && + virJSONValueObjectAdd(*propsret, "s:passwordid", secalias, NULL) < 0) + goto cleanup; + ret = 0; cleanup: @@ -733,6 +739,7 @@ qemuBuildTLSx509BackendProps(const char *tlspath, * @tlspath: path to the TLS credentials * @listen: boolen listen for client or server setting * @verifypeer: boolean to enable peer verification (form of authorization) + * @addpasswordid: boolean to handle adding passwordid to object * @inalias: Alias for the parent to generate object alias * @qemuCaps: capabilities * @@ -745,6 +752,7 @@ qemuBuildTLSx509CommandLine(virCommandPtr cmd, const char *tlspath, bool listen, bool verifypeer, + bool addpasswordid, const char *inalias, virQEMUCapsPtr qemuCaps) { @@ -752,11 +760,16 @@ qemuBuildTLSx509CommandLine(virCommandPtr cmd, char *objalias = NULL; virJSONValuePtr props = NULL; char *tmp = NULL; + char *secalias = NULL; - if (qemuBuildTLSx509BackendProps(tlspath, listen, verifypeer, - qemuCaps, &props) < 0) + if (addpasswordid && + !(secalias = qemuDomainGetSecretAESAlias(inalias, false))) return -1; + if (qemuBuildTLSx509BackendProps(tlspath, listen, verifypeer, secalias, + qemuCaps, &props) < 0) + goto cleanup; + if (!(objalias = qemuAliasTLSObjFromChardevAlias(inalias))) goto cleanup; @@ -772,6 +785,7 @@ qemuBuildTLSx509CommandLine(virCommandPtr cmd, virJSONValueFree(props); VIR_FREE(objalias); VIR_FREE(tmp); + VIR_FREE(secalias); return ret; } @@ -5048,10 +5062,13 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager, if (cfg->chardevTLS) { char *objalias = NULL; + bool addpasswordid = (dev->data.tcp.sectype == + VIR_SECRET_USAGE_TYPE_TLS); if (qemuBuildTLSx509CommandLine(cmd, cfg->chardevTLSx509certdir, dev->data.tcp.listen, cfg->chardevTLSx509verify, + addpasswordid, alias, qemuCaps) < 0) goto error; @@ -8670,6 +8687,16 @@ qemuBuildSerialCommandLine(virLogManagerPtr logManager, /* Use -chardev with -device if they are available */ if (virQEMUCapsSupportsChardev(def, qemuCaps, serial)) { + /* Add the secret object first if necessary */ + if (serial->source.data.tcp.sectype == VIR_SECRET_USAGE_TYPE_TLS) { + qemuDomainChardevPrivatePtr chardevPriv = + QEMU_DOMAIN_CHARDEV_PRIVATE(serial); + qemuDomainSecretInfoPtr secinfo = chardevPriv->secinfo; + + if (qemuBuildObjectSecretCommandLine(cmd, secinfo) < 0) + return -1; + } + if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def, &serial->source, serial->info.alias, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 583f35d..f6e6ba2 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -69,6 +69,7 @@ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, int qemuBuildTLSx509BackendProps(const char *tlspath, bool listen, bool verifypeer, + const char *secalias, virQEMUCapsPtr qemuCaps, virJSONValuePtr *propsret); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b215139..784469b 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1038,7 +1038,8 @@ qemuDomainSecretSetup(virConnectPtr conn, if (virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC) && virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET) && (secretUsageType == VIR_SECRET_USAGE_TYPE_CEPH || - secretUsageType == VIR_SECRET_USAGE_TYPE_VOLUME)) { + secretUsageType == VIR_SECRET_USAGE_TYPE_VOLUME || + secretUsageType == VIR_SECRET_USAGE_TYPE_TLS)) { if (qemuDomainSecretAESSetup(conn, priv, secinfo, srcalias, secretUsageType, username, seclookupdef, isLuks) < 0) @@ -1219,6 +1220,74 @@ qemuDomainSecretHostdevPrepare(virConnectPtr conn, } +/* qemuDomainSecretChardevDestroy: + * @disk: Pointer to a chardev definition + * + * Clear and destroy memory associated with the secret + */ +void +qemuDomainSecretChardevDestroy(virDomainChrDefPtr chardev) +{ + qemuDomainChardevPrivatePtr chardevPriv = + QEMU_DOMAIN_CHARDEV_PRIVATE(chardev); + + if (!chardevPriv || !chardevPriv->secinfo) + return; + + qemuDomainSecretInfoFree(&chardevPriv->secinfo); +} + + +/* qemuDomainSecretChardevPrepare: + * @conn: Pointer to connection + * @priv: pointer to domain private object + * @chardev: Pointer to a chardev definition + * + * For the right character device, generate the qemuDomainSecretInfo structure. + * + * Returns 0 on success, -1 on failure + */ +int +qemuDomainSecretChardevPrepare(virConnectPtr conn, + qemuDomainObjPrivatePtr priv, + virDomainChrDefPtr chardev) +{ + qemuDomainSecretInfoPtr secinfo = NULL; + + if (conn && chardev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + chardev->source.type == VIR_DOMAIN_CHR_TYPE_TCP && + chardev->source.data.tcp.sectype == VIR_SECRET_USAGE_TYPE_TLS) { + + qemuDomainChardevPrivatePtr chardevPriv = + QEMU_DOMAIN_CHARDEV_PRIVATE(chardev); + + if (VIR_ALLOC(secinfo) < 0) + return -1; + + if (qemuDomainSecretSetup(conn, priv, secinfo, chardev->info.alias, + VIR_SECRET_USAGE_TYPE_TLS, NULL, + &chardev->source.data.tcp.seclookupdef, + false) < 0) + goto error; + + if (secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("TLS X.509 requires encrypted secrets " + "to be supported")); + goto error; + } + + chardevPriv->secinfo = secinfo; + } + + return 0; + + error: + qemuDomainSecretInfoFree(&secinfo); + return -1; +} + + /* qemuDomainSecretDestroy: * @vm: Domain object * @@ -1235,6 +1304,9 @@ qemuDomainSecretDestroy(virDomainObjPtr vm) for (i = 0; i < vm->def->nhostdevs; i++) qemuDomainSecretHostdevDestroy(vm->def->hostdevs[i]); + + for (i = 0; i < vm->def->nserials; i++) + qemuDomainSecretChardevDestroy(vm->def->serials[i]); } @@ -1268,6 +1340,12 @@ qemuDomainSecretPrepare(virConnectPtr conn, return -1; } + for (i = 0; i < vm->def->nserials; i++) { + if (qemuDomainSecretChardevPrepare(conn, priv, + vm->def->serials[i]) < 0) + return -1; + } + return 0; } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 0411ac1..8658ff3 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -712,6 +712,14 @@ int qemuDomainSecretHostdevPrepare(virConnectPtr conn, virDomainHostdevDefPtr hostdev) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +void qemuDomainSecretChardevDestroy(virDomainChrDefPtr chardev) + ATTRIBUTE_NONNULL(1); + +int qemuDomainSecretChardevPrepare(virConnectPtr conn, + qemuDomainObjPrivatePtr priv, + virDomainChrDefPtr chardev) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + void qemuDomainSecretDestroy(virDomainObjPtr vm) ATTRIBUTE_NONNULL(1); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 013fbed..534e718 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1682,6 +1682,7 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, if (qemuBuildTLSx509BackendProps(cfg->chardevTLSx509certdir, dev->data.tcp.listen, cfg->chardevTLSx509verify, + NULL, priv->qemuCaps, &tlsProps) < 0) goto cleanup; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 7481626..a18b4ea 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4801,7 +4801,7 @@ qemuProcessPrepareDomain(virConnectPtr conn, if (qemuDomainMasterKeyCreate(vm) < 0) goto cleanup; - VIR_DEBUG("Add secrets to disks and hostdevs"); + VIR_DEBUG("Add secrets to disks, hostdevs, and chardevs"); if (qemuDomainSecretPrepare(conn, vm) < 0) goto cleanup; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.args b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.args new file mode 100644 index 0000000..5859c74 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-secret-chardev.args @@ -0,0 +1,38 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name QEMUGuest1 \ +-S \ +-object secret,id=masterKey0,format=raw,\ +file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefconfig \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ +-chardev udp,id=charserial0,host=127.0.0.1,port=2222,localaddr=127.0.0.1,\ +localport=1111 \ +-device isa-serial,chardev=charserial0,id=serial0 \ +-object secret,id=serial1-secret0,\ +data=9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1,\ +keyid=masterKey0,iv=AAECAwQFBgcICQoLDA0ODw==,format=base64 \ +-object tls-creds-x509,id=objserial1_tls0,dir=/etc/pki/libvirt-chardev,\ +endpoint=client,verify-peer=yes,passwordid=serial1-secret0 \ +-chardev socket,id=charserial1,host=127.0.0.1,port=5555,\ +tls-creds=objserial1_tls0 \ +-device isa-serial,chardev=charserial1,id=serial1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 8f138e6..3cd0489 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1098,6 +1098,21 @@ mymain(void) DO_TEST("serial-tcp-tlsx509-chardev", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_OBJECT_TLS_CREDS_X509); + VIR_FREE(driver.config->chardevTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->chardevTLSx509certdir, "/etc/pki/libvirt-chardev") < 0) + return EXIT_FAILURE; + driver.config->chardevTLSx509verify = 1; +# ifdef HAVE_GNUTLS_CIPHER_ENCRYPT + DO_TEST("serial-tcp-tlsx509-secret-chardev", + QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_OBJECT_SECRET, + QEMU_CAPS_OBJECT_TLS_CREDS_X509); +# else + DO_TEST_FAILURE("serial-tcp-tlsx509-secret-chardev", + QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_OBJECT_SECRET, + QEMU_CAPS_OBJECT_TLS_CREDS_X509); +# endif driver.config->chardevTLS = 0; VIR_FREE(driver.config->chardevTLSx509certdir); DO_TEST("serial-many-chardev", -- 2.7.4

https://bugzilla.redhat.com/show_bug.cgi?id=1300776 Complete the implementation of support for TLS encryption on chardev TCP transports by adding the hotplug ability of a secret to generate the passwordid for the TLS object Likewise, add the ability to hot unplug that secret object as well Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_hotplug.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_hotplug.h | 3 ++- tests/qemuhotplugtest.c | 2 +- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5a7733c..04ac7d5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7394,7 +7394,7 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, break; case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainAttachChrDevice(driver, vm, + ret = qemuDomainAttachChrDevice(conn, driver, vm, dev->data.chr); if (!ret) { alias = dev->data.chr->info.alias; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 534e718..75ee278 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1639,7 +1639,8 @@ qemuDomainAttachChrDeviceAssignAddr(virDomainDefPtr def, return ret; } -int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, +int qemuDomainAttachChrDevice(virConnectPtr conn, + virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr) { @@ -1653,8 +1654,11 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, char *charAlias = NULL; bool chardevAttached = false; bool tlsobjAdded = false; + bool secobjAdded = false; virJSONValuePtr tlsProps = NULL; char *tlsAlias = NULL; + virJSONValuePtr secProps = NULL; + char *secAlias = NULL; bool need_release = false; if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && @@ -1678,11 +1682,28 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, if (qemuDomainChrPreInsert(vmdef, chr) < 0) goto cleanup; + if (qemuDomainSecretChardevPrepare(conn, priv, chr) < 0) + goto cleanup; + if (cfg->chardevTLS) { + /* Add a secret object in order to access the TLS environment + * if provided of course */ + if (dev->data.tcp.sectype == VIR_SECRET_USAGE_TYPE_TLS) { + qemuDomainChardevPrivatePtr chardevPriv = + QEMU_DOMAIN_CHARDEV_PRIVATE(chr); + qemuDomainSecretInfoPtr secinfo = chardevPriv->secinfo; + + if (qemuBuildSecretInfoProps(secinfo, &secProps) < 0) + goto cleanup; + + if (!(secAlias = qemuDomainGetSecretAESAlias(charAlias, false))) + goto cleanup; + } + if (qemuBuildTLSx509BackendProps(cfg->chardevTLSx509certdir, dev->data.tcp.listen, cfg->chardevTLSx509verify, - NULL, + secAlias, priv->qemuCaps, &tlsProps) < 0) goto cleanup; @@ -1693,6 +1714,15 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, } qemuDomainObjEnterMonitor(driver, vm); + if (secAlias) { + rc = qemuMonitorAddObject(priv->mon, "secret", + secAlias, secProps); + secProps = NULL; + if (rc < 0) + goto exit_monitor; + secobjAdded = true; + } + if (tlsAlias) { rc = qemuMonitorAddObject(priv->mon, "tls-creds-x509", tlsAlias, tlsProps); @@ -1723,6 +1753,8 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); VIR_FREE(tlsAlias); virJSONValueFree(tlsProps); + VIR_FREE(secAlias); + virJSONValueFree(secProps); VIR_FREE(charAlias); VIR_FREE(devstr); virObjectUnref(cfg); @@ -1730,6 +1762,8 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, exit_monitor: orig_err = virSaveLastError(); + if (secobjAdded) + ignore_value(qemuMonitorDelObject(priv->mon, secAlias)); if (tlsobjAdded) ignore_value(qemuMonitorDelObject(priv->mon, tlsAlias)); /* detach associated chardev on error */ @@ -4323,6 +4357,7 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr vmdef = vm->def; virDomainChrDefPtr tmpChr; + virDomainChrSourceDefPtr dev = &chr->source; char *objAlias = NULL; char *devstr = NULL; @@ -4347,6 +4382,15 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, qemuDomainMarkDeviceForRemoval(vm, &tmpChr->info); qemuDomainObjEnterMonitor(driver, vm); + if (dev->data.tcp.sectype == VIR_SECRET_USAGE_TYPE_TLS) { + qemuDomainChardevPrivatePtr chardevPriv = + QEMU_DOMAIN_CHARDEV_PRIVATE(chr); + qemuDomainSecretInfoPtr secinfo = chardevPriv->secinfo; + + if (qemuMonitorDelObject(priv->mon, secinfo->s.aes.alias) < 0) + goto exit_monitor; + } + if (objAlias && qemuMonitorDelObject(priv->mon, objAlias) < 0) goto exit_monitor; diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 165d345..a299ea1 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -92,7 +92,8 @@ int qemuDomainAttachLease(virQEMUDriverPtr driver, int qemuDomainDetachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease); -int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, +int qemuDomainAttachChrDevice(virConnectPtr conn, + virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr); int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 0a5f068..f84908f 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -117,7 +117,7 @@ testQemuHotplugAttach(virDomainObjPtr vm, ret = qemuDomainAttachDeviceDiskLive(NULL, &driver, vm, dev); break; case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainAttachChrDevice(&driver, vm, dev->data.chr); + ret = qemuDomainAttachChrDevice(NULL, &driver, vm, dev->data.chr); break; default: VIR_TEST_VERBOSE("device type '%s' cannot be attached\n", -- 2.7.4
participants (2)
-
Daniel P. Berrange
-
John Ferlan