[libvirt] [PATCH v3 0/4] Some patches for native TLS encrypted chardev TCP support

v2: http://www.redhat.com/archives/libvir-list/2016-June/msg01044.html Patches 3 and 4 from original already pushed. Changes since v2: Patch 1: (the two bullets were ACK'd, but 3rd introduced new change so repost) * Change to using /etc/pki/qemu as the default directory name * Add description for dh-params.pem * Added default_tls_x509_verify Patch 2: * Change to using /etc/pki/qemu-chardev * Change charTCP/chartcp to chardev * Added chardev_tls_x509_verify Patch 3: (former patch 5) * Added processing for verify-peer and set in .args output Patch 4: (was ACK'd, but...) * Needed update due to new argument to qemuBuildTLSx509BackendProps John Ferlan (4): 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 src/conf/domain_conf.h | 1 + src/qemu/libvirtd_qemu.aug | 11 ++- src/qemu/qemu.conf | 83 ++++++++++++---- src/qemu/qemu_command.c | 109 ++++++++++++++++++++- src/qemu/qemu_command.h | 7 ++ src/qemu/qemu_conf.c | 59 +++++++++-- src/qemu/qemu_conf.h | 7 ++ src/qemu/qemu_hotplug.c | 30 +++++- src/qemu/qemu_monitor_json.c | 9 ++ src/qemu/test_libvirtd_qemu.aug.in | 5 + .../qemuxml2argv-serial-tcp-tlsx509-chardev.args | 33 +++++++ .../qemuxml2argv-serial-tcp-tlsx509-chardev.xml | 41 ++++++++ tests/qemuxml2argvtest.c | 6 ++ .../qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml | 50 ++++++++++ tests/qemuxml2xmltest.c | 1 + 15 files changed, 424 insertions(+), 28 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/qemuxml2xmloutdata/qemuxml2xmlout-serial-tcp-tlsx509-chardev.xml -- 2.5.5

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 | 53 +++++++++++++++++++++++++++++++----- src/qemu/qemu_conf.h | 3 +++ src/qemu/test_libvirtd_qemu.aug.in | 2 ++ 5 files changed, 93 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 6dfa738..3091843 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); @@ -436,6 +463,14 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, if (p) \ VAR = p->l != 0; +#define GET_VALUE_BOOL_DFLT(NAME, VAR, DFLT) \ + p = virConfGetValue(conf, NAME); \ + CHECK_TYPE(NAME, VIR_CONF_ULONG); \ + if (p) \ + VAR = p->l != 0; \ + else \ + VAR = DFLT; + #define GET_VALUE_STR(NAME, VAR) \ p = virConfGetValue(conf, NAME); \ CHECK_TYPE(NAME, VIR_CONF_STRING); \ @@ -445,9 +480,13 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, goto cleanup; \ } + GET_VALUE_STR("default_tls_x509_cert_dir", cfg->defaultTLSx509certdir); + GET_VALUE_BOOL("default_tls_x509_verify", cfg->defaultTLSx509verify); + GET_VALUE_BOOL("vnc_auto_unix_socket", cfg->vncAutoUnixSocket); GET_VALUE_BOOL("vnc_tls", cfg->vncTLS); - GET_VALUE_BOOL("vnc_tls_x509_verify", cfg->vncTLSx509verify); + GET_VALUE_BOOL_DFLT("vnc_tls_x509_verify", cfg->vncTLSx509verify, + cfg->defaultTLSx509verify); GET_VALUE_STR("vnc_tls_x509_cert_dir", cfg->vncTLSx509certdir); GET_VALUE_STR("vnc_listen", cfg->vncListen); GET_VALUE_STR("vnc_password", cfg->vncPassword); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index a09c81d..1843693 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.5.5

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 | 6 +++ 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, 138 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..8634e9f 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/qemu-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 3091843..b1c6215 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); @@ -547,6 +550,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr cfg, GET_VALUE_STR("spice_password", cfg->spicePassword); GET_VALUE_BOOL("spice_auto_unix_socket", cfg->spiceAutoUnixSocket); + GET_VALUE_STR("chardev_tls_x509_cert_dir", cfg->chardevTLSx509certdir); + GET_VALUE_BOOL_DFLT("chardev_tls_x509_verify", cfg->chardevTLSx509verify, + cfg->defaultTLSx509verify); GET_VALUE_ULONG("remote_websocket_port_min", cfg->webSocketPortMin); if (cfg->webSocketPortMin < QEMU_WEBSOCKET_PORT_MIN) { diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1843693..7138a7b 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; + int remotePortMin; int remotePortMax; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 01351b4..44ee451 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/qemu-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 7db9cb7..3064458 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -483,6 +483,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.5.5

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_command.c | 109 ++++++++++++++++++++- .../qemuxml2argv-serial-tcp-tlsx509-chardev.args | 33 +++++++ tests/qemuxml2argvtest.c | 6 ++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-serial-tcp-tlsx509-chardev.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 4a8def1..0ee07a9 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -701,6 +701,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; + } + + qemuBufferEscapeComma(&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 (this code will add a "_tls0" to 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 *alias = NULL; + virJSONValuePtr props = NULL; + char *tmp = NULL; + + if (qemuBuildTLSx509BackendProps(tlspath, listen, verifypeer, + qemuCaps, &props) < 0) + return -1; + + if (virAsprintf(&alias, "obj%s_tls0", inalias) < 0) + goto cleanup; + + if (!(tmp = virQEMUBuildObjectCommandlineFromJSON("tls-creds-x509", + alias, props))) + goto cleanup; + + virCommandAddArgList(cmd, "-object", tmp, NULL); + + ret = 0; + + cleanup: + virJSONValueFree(props); + VIR_FREE(alias); + VIR_FREE(tmp); + return ret; +} + + #define QEMU_DEFAULT_NBD_PORT "10809" static char * @@ -4685,7 +4782,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, @@ -4768,6 +4865,16 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager, dev->data.tcp.service, telnet ? ",telnet" : "", dev->data.tcp.listen ? ",server,nowait" : ""); + + if (cfg->chardevTLS) { + if (qemuBuildTLSx509CommandLine(cmd, cfg->chardevTLSx509certdir, + dev->data.tcp.listen, + cfg->chardevTLSx509verify, + alias, qemuCaps) < 0) + goto error; + + virBufferAsprintf(&buf, ",tls-creds=obj%s_tls0", alias); + } 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..5b3016d --- /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 \ +-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 c406b64..7c04f56 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1072,6 +1072,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.5.5

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. 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 | 7 +++++++ src/qemu/qemu_hotplug.c | 30 +++++++++++++++++++++++++++++- src/qemu/qemu_monitor_json.c | 9 +++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 15f9c80..0e07504 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1097,6 +1097,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 0ee07a9..36ad484 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -712,7 +712,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 9ff4edb..fcb720b 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -61,6 +61,13 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, const char *domainLibDir) ATTRIBUTE_NONNULL(15); +/* 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 e1c3de7..5407b3a 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1496,10 +1496,14 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, virDomainChrDefPtr chr) { int ret = -1, rc; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr vmdef = vm->def; char *devstr = NULL; + virDomainChrSourceDefPtr dev = &chr->source; char *charAlias = NULL; + virJSONValuePtr props = NULL; + char *objAlias = NULL; bool need_release = false; if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && @@ -1523,8 +1527,26 @@ 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, + &props) < 0) + goto cleanup; + + if (virAsprintf(&objAlias, "obj%s_tls0", chr->info.alias) < 0) + goto cleanup; + dev->data.tcp.tlscreds = true; + } + qemuDomainObjEnterMonitor(driver, vm); - if (qemuMonitorAttachCharDev(priv->mon, charAlias, &chr->source) < 0) + + if (objAlias && qemuMonitorAddObject(priv->mon, "tls-creds-x509", + objAlias, props) < 0) + goto failobject; + + if (qemuMonitorAttachCharDev(priv->mon, charAlias, dev) < 0) goto failchardev; if (qemuMonitorAddDevice(priv->mon, devstr) < 0) @@ -1542,14 +1564,20 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, qemuDomainChrInsertPreAllocCleanup(vm->def, chr); if (ret < 0 && need_release) qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); + VIR_FREE(objAlias); + virJSONValueFree(props); VIR_FREE(charAlias); VIR_FREE(devstr); + virObjectUnref(cfg); return ret; failadddev: /* detach associated chardev on error */ qemuMonitorDetachCharDev(priv->mon, charAlias); failchardev: + /* Remove the object */ + ignore_value(qemuMonitorDelObject(priv->mon, objAlias)); + failobject: ignore_value(qemuDomainObjExitMonitor(driver, vm)); goto audit; } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 380ddab..703622a 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 (virAsprintf(&tlsalias, "obj%s_tls0", chrID) < 0) + 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.5.5
participants (1)
-
John Ferlan