[libvirt] [PATCH v4 00/12] remove repetition of URI path validation

This is a code repetition that I crossed a few times, then I noticed that Cole Robinson suggested a solution for it in the wiki. Here it is. changes from v3: - patch 8: fix the exception logic, move the code formatting to patch 1 - patch 9: use lowcase 'qemu' - patch 12: (optional) test case I created to aid in patch 8 logic changes from v2: - use a boolean to determine 'QEMU' and 'vbox' case to avoid block repetition (patch 8) - avoid 80+ chars lines in all patches changes from v1: - handle QEMU and vbox cases separately inside the validation function v3: https://www.redhat.com/archives/libvir-list/2019-September/msg01122.html v2: https://www.redhat.com/archives/libvir-list/2019-September/msg01007.html v1: https://www.redhat.com/archives/libvir-list/2019-September/msg00983.html Daniel Henrique Barboza (12): src/driver.c: add virConnectValidateURIPath() interface_backend_netcf.c: use virConnectValidateURIPath() interface_backend_udev.c: use virConnectValidateURIPath() bridge_driver.c: virConnectValidateURIPath() node_device_driver.c: use virConnectValidateURIPath() secret_driver.c: use virConnectValidateURIPath() storage_driver.c: use virConnectValidateURIPath() driver.c: change URI validation to handle QEMU and vbox case qemu_driver.c: use virConnectValidateURIPath() vbox_common.c: use virConnectValidateURIPath() vbox_driver.c: use virConnectValidateURIPath() tests: add a test for driver.c:virConnectValidateURIPath() src/driver.c | 38 +++++ src/driver.h | 4 + src/interface/interface_backend_netcf.c | 19 +-- src/interface/interface_backend_udev.c | 19 +-- src/libvirt_private.syms | 1 + src/network/bridge_driver.c | 19 +-- src/node_device/node_device_driver.c | 19 +-- src/qemu/qemu_driver.c | 20 +-- src/secret/secret_driver.c | 19 +-- src/storage/storage_driver.c | 19 +-- src/vbox/vbox_common.c | 16 +- src/vbox/vbox_driver.c | 16 +- tests/Makefile.am | 7 +- tests/virdriverconnvalidatetest.c | 186 ++++++++++++++++++++++++ 14 files changed, 267 insertions(+), 135 deletions(-) create mode 100644 tests/virdriverconnvalidatetest.c -- 2.21.0

The code to validate the URI path is repeated across several files. This patch creates a common validation code to be used across all of them. Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/driver.c | 26 ++++++++++++++++++++++++++ src/driver.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 31 insertions(+) diff --git a/src/driver.c b/src/driver.c index 5e8f68f6df..6b75622689 100644 --- a/src/driver.c +++ b/src/driver.c @@ -269,3 +269,29 @@ virSetConnectStorage(virConnectPtr conn) VIR_DEBUG("Override storage connection with %p", conn); return virThreadLocalSet(&connectStorage, conn); } + +bool +virConnectValidateURIPath(const char *uriPath, + const char *entityName, + bool privileged) +{ + if (privileged) { + if (STRNEQ(uriPath, "/system")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected %s URI path '%s', try " + "%s:///system"), + entityName, uriPath, entityName); + return false; + } + } else { + if (STRNEQ(uriPath, "/session")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected %s URI path '%s', try " + "%s:///session"), + entityName, uriPath, entityName); + return false; + } + } + + return true; +} diff --git a/src/driver.h b/src/driver.h index f7d667a03c..68c0004d86 100644 --- a/src/driver.h +++ b/src/driver.h @@ -127,3 +127,7 @@ int virSetConnectNWFilter(virConnectPtr conn); int virSetConnectNodeDev(virConnectPtr conn); int virSetConnectSecret(virConnectPtr conn); int virSetConnectStorage(virConnectPtr conn); + +bool virConnectValidateURIPath(const char *uriPath, + const char *entityName, + bool privileged); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 287e63bffa..3668a531a4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1343,6 +1343,7 @@ virStreamClass; # driver.h +virConnectValidateURIPath; virGetConnectInterface; virGetConnectNetwork; virGetConnectNodeDev; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/interface/interface_backend_netcf.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c index 9659e9fcf1..5ef8ac33db 100644 --- a/src/interface/interface_backend_netcf.c +++ b/src/interface/interface_backend_netcf.c @@ -200,21 +200,10 @@ netcfConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected interface URI path '%s', try interface:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected interface URI path '%s', try interface:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "interface", + driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/interface/interface_backend_udev.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c index ddc3de5347..1684b8ed83 100644 --- a/src/interface/interface_backend_udev.c +++ b/src/interface/interface_backend_udev.c @@ -1250,21 +1250,10 @@ udevConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected interface URI path '%s', try interface:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected interface URI path '%s', try interface:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "interface", + driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/network/bridge_driver.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index c54be96407..c617bbb58f 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -938,21 +938,10 @@ networkConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (network_driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected network URI path '%s', try network:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected network URI path '%s', try network:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "network", + network_driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/node_device/node_device_driver.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 8fb00d0c86..578489c5be 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -58,21 +58,10 @@ nodeConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected nodedev URI path '%s', try nodedev:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected nodedev URI path '%s', try nodedev:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "nodedev", + driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/secret/secret_driver.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 7512a51c74..ed3bd3c751 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -552,21 +552,10 @@ secretConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected secret URI path '%s', try secret:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected secret URI path '%s', try secret:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "secret", + driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/storage/storage_driver.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index ce10b55ed0..d160ff34fe 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -411,21 +411,10 @@ storageConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (driver->privileged) { - if (STRNEQ(conn->uri->path, "/system")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected storage URI path '%s', try storage:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected storage URI path '%s', try storage:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "storage", + driver->privileged)) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

The existing QEMU and vbox URI path validation consider that a privileged user can use both a "/system" and a "/session" URI. This differs from all the other drivers that forbids the root user to use "/session" URI. Let's update virConnectValidateURIPath() to handle these cases as exceptions, using the already existent 'entityName' value to handle "QEMU" and "vbox" differently. This allows us to use the validateURI function in these cases without changing the existing behavior of other drivers. Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/driver.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/driver.c b/src/driver.c index 6b75622689..ed2d943ddf 100644 --- a/src/driver.c +++ b/src/driver.c @@ -276,7 +276,19 @@ virConnectValidateURIPath(const char *uriPath, bool privileged) { if (privileged) { - if (STRNEQ(uriPath, "/system")) { + /* TODO: qemu and vbox drivers allow '/session' + * connections as root. This is not ideal, but changing + * these drivers to refuse privileged '/session' + * connections, like everyone else is already doing, can + * break existing applications. Until we decide what to do, + * for now we can handle them as exception in this validate + * function. + */ + bool compatSessionRoot = (STREQ(entityName, "qemu") || + STREQ(entityName, "vbox")) && + STREQ(uriPath, "/session"); + + if (STRNEQ(uriPath, "/system") && !compatSessionRoot) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected %s URI path '%s', try " "%s:///system"), -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_driver.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0c65414a1a..cf9c06f274 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1295,22 +1295,10 @@ static virDrvOpenStatus qemuConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_ERROR; } - if (virQEMUDriverIsPrivileged(qemu_driver)) { - if (STRNEQ(conn->uri->path, "/system") && - STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected QEMU URI path '%s', try qemu:///system"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected QEMU URI path '%s', try qemu:///session"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, + "qemu", + virQEMUDriverIsPrivileged(qemu_driver))) + return VIR_DRV_OPEN_ERROR; if (virConnectOpenEnsureACL(conn) < 0) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/vbox/vbox_common.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index ddabcb80ca..d3b8fb625f 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -517,20 +517,8 @@ vboxConnectOpen(virConnectPtr conn, virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); - if (uid != 0) { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown driver path '%s' specified (try vbox:///session)"), conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { /* root */ - if (STRNEQ(conn->uri->path, "/system") && - STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown driver path '%s' specified (try vbox:///system)"), conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, "vbox", uid == 0)) + return VIR_DRV_OPEN_ERROR; if (!(driver = vboxGetDriverConnection())) return VIR_DRV_OPEN_ERROR; -- 2.21.0

Suggested-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/vbox/vbox_driver.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/vbox/vbox_driver.c b/src/vbox/vbox_driver.c index 1f31fa28df..d7e80828ab 100644 --- a/src/vbox/vbox_driver.c +++ b/src/vbox/vbox_driver.c @@ -58,20 +58,8 @@ static virDrvOpenStatus dummyConnectOpen(virConnectPtr conn, virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); - if (uid != 0) { - if (STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown driver path '%s' specified (try vbox:///session)"), conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } else { /* root */ - if (STRNEQ(conn->uri->path, "/system") && - STRNEQ(conn->uri->path, "/session")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown driver path '%s' specified (try vbox:///system)"), conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - } + if (!virConnectValidateURIPath(conn->uri->path, "vbox", uid == 0)) + return VIR_DRV_OPEN_ERROR; virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unable to initialize VirtualBox driver API")); -- 2.21.0

Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- I've made this test file to make sure I wasn't messing up with the logic at patch 8. The idea of having this test seems okay, but probably I could do it shorter/cleaner. Feel free to discard it if it's not applicable or unnecessary. Adding this new file make the whole patch series, aimed at reduce code repetition and lines of code, add more lines than before. The irony is real. tests/Makefile.am | 7 +- tests/virdriverconnvalidatetest.c | 186 ++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/virdriverconnvalidatetest.c diff --git a/tests/Makefile.am b/tests/Makefile.am index d88ad7f686..c7f563d24d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -433,7 +433,8 @@ test_scripts += $(libvirtd_test_scripts) test_programs += \ eventtest \ - virdrivermoduletest + virdrivermoduletest \ + virdriverconnvalidatetest else ! WITH_LIBVIRTD EXTRA_DIST += $(libvirtd_test_scripts) endif ! WITH_LIBVIRTD @@ -1485,6 +1486,10 @@ if WITH_LIBVIRTD virdrivermoduletest_SOURCES = \ virdrivermoduletest.c testutils.h testutils.c virdrivermoduletest_LDADD = $(LDADDS) + +virdriverconnvalidatetest_SOURCES = \ + virdriverconnvalidatetest.c testutils.h testutils.c +virdriverconnvalidatetest_LDADD = $(LDADDS) endif WITH_LIBVIRTD if WITH_LIBVIRTD diff --git a/tests/virdriverconnvalidatetest.c b/tests/virdriverconnvalidatetest.c new file mode 100644 index 0000000000..599150dc08 --- /dev/null +++ b/tests/virdriverconnvalidatetest.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "testutils.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "driver.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("tests.driverconnvalidatetest"); + +struct testDriverConnValidateData { + const char *uriPath; + const char *entityName; + bool privileged; + bool expectFailure; +}; + + +static int testDriverConnValidate(const void *args) +{ + const struct testDriverConnValidateData *data = args; + + bool ret = virConnectValidateURIPath(data->uriPath, + data->entityName, + data->privileged); + if (data->expectFailure) + ret = !ret; + + return ret ? 0 : -1; +} + + +static int +mymain(void) +{ + int ret = 0; + struct testDriverConnValidateData data; + +#define TEST_SUCCESS(name) \ + do { \ + data.expectFailure = false; \ + if (virTestRun("Test conn URI path validate ok " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + +#define TEST_FAIL(name) \ + do { \ + data.expectFailure = true; \ + if (virTestRun("Test conn URI path validate fail " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + + /* Validation should always succeed with privileged user + * and '/system' URI path + */ + data.uriPath = "/system"; + data.privileged = true; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /system"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /system"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /system"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /system"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /system"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /system"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /system"); + + /* Fail URI path validation for all '/system' path with + * unprivileged user + */ + data.privileged = false; + + data.entityName = "interface"; + TEST_FAIL("interface unprivileged /system"); + + data.entityName = "network"; + TEST_FAIL("network unprivileged /system"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev unprivileged /system"); + + data.entityName = "secret"; + TEST_FAIL("secret unprivileged /system"); + + data.entityName = "storage"; + TEST_FAIL("storage unprivileged /system"); + + data.entityName = "qemu"; + TEST_FAIL("qemu unprivileged /system"); + + data.entityName = "vbox"; + TEST_FAIL("vbox unprivileged /system"); + + /* Validation should always succeed with unprivileged user + * and '/session' URI path + */ + data.uriPath = "/session"; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /session"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /session"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /session"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /session"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + /* Fail URI path validation for all '/session' path with + * privileged user + */ + data.privileged = true; + + data.entityName = "interface"; + TEST_FAIL("interface privileged /session"); + + data.entityName = "network"; + TEST_FAIL("network privileged /session"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_FAIL("secret privileged /session"); + + data.entityName = "storage"; + TEST_FAIL("storage privileged /session"); + + /* ... except with qemu and vbox, where root can connect + * with both /system and /session + */ + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) -- 2.21.0

On 9/26/19 10:56 AM, Daniel Henrique Barboza wrote:
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> ---
I've made this test file to make sure I wasn't messing up with the logic at patch 8. The idea of having this test seems okay, but probably I could do it shorter/cleaner.
Feel free to discard it if it's not applicable or unnecessary. Adding this new file make the whole patch series, aimed at reduce code repetition and lines of code, add more lines than before. The irony is real.
It will reduce lines if you fold the data.entityName = X bit into the TEST_ calls, passing the string value as the first argument for example We don't have a ton of fine grained unittesting like this in libvirt, but it doesn't hurt. Though in this case it seems kinda overkill IMO to call it for possible combo that it's used in. I think it's better to just call it in all the invocations to give full code coverage. If we want to test the individual driver usage of the function then we would want to find a way to test those entry points directly IMO. Maybe others have opinions. I'll let you decide what to do for v2 though - Cole
tests/Makefile.am | 7 +- tests/virdriverconnvalidatetest.c | 186 ++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/virdriverconnvalidatetest.c
diff --git a/tests/Makefile.am b/tests/Makefile.am index d88ad7f686..c7f563d24d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -433,7 +433,8 @@ test_scripts += $(libvirtd_test_scripts)
test_programs += \ eventtest \ - virdrivermoduletest + virdrivermoduletest \ + virdriverconnvalidatetest else ! WITH_LIBVIRTD EXTRA_DIST += $(libvirtd_test_scripts) endif ! WITH_LIBVIRTD @@ -1485,6 +1486,10 @@ if WITH_LIBVIRTD virdrivermoduletest_SOURCES = \ virdrivermoduletest.c testutils.h testutils.c virdrivermoduletest_LDADD = $(LDADDS) + +virdriverconnvalidatetest_SOURCES = \ + virdriverconnvalidatetest.c testutils.h testutils.c +virdriverconnvalidatetest_LDADD = $(LDADDS) endif WITH_LIBVIRTD
if WITH_LIBVIRTD diff --git a/tests/virdriverconnvalidatetest.c b/tests/virdriverconnvalidatetest.c new file mode 100644 index 0000000000..599150dc08 --- /dev/null +++ b/tests/virdriverconnvalidatetest.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "testutils.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "driver.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("tests.driverconnvalidatetest"); + +struct testDriverConnValidateData { + const char *uriPath; + const char *entityName; + bool privileged; + bool expectFailure; +}; + + +static int testDriverConnValidate(const void *args) +{ + const struct testDriverConnValidateData *data = args; + + bool ret = virConnectValidateURIPath(data->uriPath, + data->entityName, + data->privileged); + if (data->expectFailure) + ret = !ret; + + return ret ? 0 : -1; +} + + +static int +mymain(void) +{ + int ret = 0; + struct testDriverConnValidateData data; + +#define TEST_SUCCESS(name) \ + do { \ + data.expectFailure = false; \ + if (virTestRun("Test conn URI path validate ok " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + +#define TEST_FAIL(name) \ + do { \ + data.expectFailure = true; \ + if (virTestRun("Test conn URI path validate fail " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + + /* Validation should always succeed with privileged user + * and '/system' URI path + */ + data.uriPath = "/system"; + data.privileged = true; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /system"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /system"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /system"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /system"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /system"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /system"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /system"); + + /* Fail URI path validation for all '/system' path with + * unprivileged user + */ + data.privileged = false; + + data.entityName = "interface"; + TEST_FAIL("interface unprivileged /system"); + + data.entityName = "network"; + TEST_FAIL("network unprivileged /system"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev unprivileged /system"); + + data.entityName = "secret"; + TEST_FAIL("secret unprivileged /system"); + + data.entityName = "storage"; + TEST_FAIL("storage unprivileged /system"); + + data.entityName = "qemu"; + TEST_FAIL("qemu unprivileged /system"); + + data.entityName = "vbox"; + TEST_FAIL("vbox unprivileged /system"); + + /* Validation should always succeed with unprivileged user + * and '/session' URI path + */ + data.uriPath = "/session"; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /session"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /session"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /session"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /session"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + /* Fail URI path validation for all '/session' path with + * privileged user + */ + data.privileged = true; + + data.entityName = "interface"; + TEST_FAIL("interface privileged /session"); + + data.entityName = "network"; + TEST_FAIL("network privileged /session"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_FAIL("secret privileged /session"); + + data.entityName = "storage"; + TEST_FAIL("storage privileged /session"); + + /* ... except with qemu and vbox, where root can connect + * with both /system and /session + */ + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain)
- Cole

On 9/27/19 5:33 PM, Cole Robinson wrote:
On 9/26/19 10:56 AM, Daniel Henrique Barboza wrote:
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> ---
I've made this test file to make sure I wasn't messing up with the logic at patch 8. The idea of having this test seems okay, but probably I could do it shorter/cleaner.
Feel free to discard it if it's not applicable or unnecessary. Adding this new file make the whole patch series, aimed at reduce code repetition and lines of code, add more lines than before. The irony is real.
It will reduce lines if you fold the data.entityName = X bit into the TEST_ calls, passing the string value as the first argument for example
Got it.
We don't have a ton of fine grained unittesting like this in libvirt, but it doesn't hurt. Though in this case it seems kinda overkill IMO to call it for possible combo that it's used in. I think it's better to just call it in all the invocations to give full code coverage. If we want to test the individual driver usage of the function then we would want to find a way to test those entry points directly IMO. Maybe others have opinions.
Do you mean we should just test the actual usage of the function in the code instead of testing all possible fail scenarios? For example, the code does not call virConnectValidateURIPath("/system", X , false) anywhere, so it's ok to not test them since it's not being exercised anyway - otherwise we're entering TDD territory (which I don't mind - kind of a fan TBH), testing all possible stuff just for sake of testing. Is that what you're saying? I'm fine with it, and it will cut a good chunk of lines in this file too.
I'll let you decide what to do for v2 though
Thanks for the review. I'll wait to see if more people want to join in this discussion before sending the v2. DHB
- Cole
tests/Makefile.am | 7 +- tests/virdriverconnvalidatetest.c | 186 ++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/virdriverconnvalidatetest.c
diff --git a/tests/Makefile.am b/tests/Makefile.am index d88ad7f686..c7f563d24d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -433,7 +433,8 @@ test_scripts += $(libvirtd_test_scripts) test_programs += \ eventtest \ - virdrivermoduletest + virdrivermoduletest \ + virdriverconnvalidatetest else ! WITH_LIBVIRTD EXTRA_DIST += $(libvirtd_test_scripts) endif ! WITH_LIBVIRTD @@ -1485,6 +1486,10 @@ if WITH_LIBVIRTD virdrivermoduletest_SOURCES = \ virdrivermoduletest.c testutils.h testutils.c virdrivermoduletest_LDADD = $(LDADDS) + +virdriverconnvalidatetest_SOURCES = \ + virdriverconnvalidatetest.c testutils.h testutils.c +virdriverconnvalidatetest_LDADD = $(LDADDS) endif WITH_LIBVIRTD if WITH_LIBVIRTD diff --git a/tests/virdriverconnvalidatetest.c b/tests/virdriverconnvalidatetest.c new file mode 100644 index 0000000000..599150dc08 --- /dev/null +++ b/tests/virdriverconnvalidatetest.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "testutils.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "driver.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("tests.driverconnvalidatetest"); + +struct testDriverConnValidateData { + const char *uriPath; + const char *entityName; + bool privileged; + bool expectFailure; +}; + + +static int testDriverConnValidate(const void *args) +{ + const struct testDriverConnValidateData *data = args; + + bool ret = virConnectValidateURIPath(data->uriPath, + data->entityName, + data->privileged); + if (data->expectFailure) + ret = !ret; + + return ret ? 0 : -1; +} + + +static int +mymain(void) +{ + int ret = 0; + struct testDriverConnValidateData data; + +#define TEST_SUCCESS(name) \ + do { \ + data.expectFailure = false; \ + if (virTestRun("Test conn URI path validate ok " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + +#define TEST_FAIL(name) \ + do { \ + data.expectFailure = true; \ + if (virTestRun("Test conn URI path validate fail " name, \ + testDriverConnValidate, &data) < 0) \ + ret = -1; \ + } while (0) + + /* Validation should always succeed with privileged user + * and '/system' URI path + */ + data.uriPath = "/system"; + data.privileged = true; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /system"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /system"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /system"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /system"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /system"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /system"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /system"); + + /* Fail URI path validation for all '/system' path with + * unprivileged user + */ + data.privileged = false; + + data.entityName = "interface"; + TEST_FAIL("interface unprivileged /system"); + + data.entityName = "network"; + TEST_FAIL("network unprivileged /system"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev unprivileged /system"); + + data.entityName = "secret"; + TEST_FAIL("secret unprivileged /system"); + + data.entityName = "storage"; + TEST_FAIL("storage unprivileged /system"); + + data.entityName = "qemu"; + TEST_FAIL("qemu unprivileged /system"); + + data.entityName = "vbox"; + TEST_FAIL("vbox unprivileged /system"); + + /* Validation should always succeed with unprivileged user + * and '/session' URI path + */ + data.uriPath = "/session"; + + data.entityName = "interface"; + TEST_SUCCESS("interface privileged /session"); + + data.entityName = "network"; + TEST_SUCCESS("network privileged /session"); + + data.entityName = "nodedev"; + TEST_SUCCESS("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_SUCCESS("secret privileged /session"); + + data.entityName = "storage"; + TEST_SUCCESS("storage privileged /session"); + + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + /* Fail URI path validation for all '/session' path with + * privileged user + */ + data.privileged = true; + + data.entityName = "interface"; + TEST_FAIL("interface privileged /session"); + + data.entityName = "network"; + TEST_FAIL("network privileged /session"); + + data.entityName = "nodedev"; + TEST_FAIL("nodedev privileged /session"); + + data.entityName = "secret"; + TEST_FAIL("secret privileged /session"); + + data.entityName = "storage"; + TEST_FAIL("storage privileged /session"); + + /* ... except with qemu and vbox, where root can connect + * with both /system and /session + */ + data.entityName = "qemu"; + TEST_SUCCESS("qemu privileged /session"); + + data.entityName = "vbox"; + TEST_SUCCESS("vbox privileged /session"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain)
- Cole

On 9/27/19 5:32 PM, Daniel Henrique Barboza wrote:
On 9/27/19 5:33 PM, Cole Robinson wrote:
On 9/26/19 10:56 AM, Daniel Henrique Barboza wrote:
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> ---
I've made this test file to make sure I wasn't messing up with the logic at patch 8. The idea of having this test seems okay, but probably I could do it shorter/cleaner.
Feel free to discard it if it's not applicable or unnecessary. Adding this new file make the whole patch series, aimed at reduce code repetition and lines of code, add more lines than before. The irony is real.
It will reduce lines if you fold the data.entityName = X bit into the TEST_ calls, passing the string value as the first argument for example
Got it.
We don't have a ton of fine grained unittesting like this in libvirt, but it doesn't hurt. Though in this case it seems kinda overkill IMO to call it for possible combo that it's used in. I think it's better to just call it in all the invocations to give full code coverage. If we want to test the individual driver usage of the function then we would want to find a way to test those entry points directly IMO. Maybe others have opinions.
Do you mean we should just test the actual usage of the function in the code instead of testing all possible fail scenarios? For example, the code does not call virConnectValidateURIPath("/system", X , false) anywhere, so it's ok to not test them since it's not being exercised anyway - otherwise we're entering TDD territory (which I don't mind - kind of a fan TBH), testing all possible stuff just for sake of testing.
Is that what you're saying? I'm fine with it, and it will cut a good chunk of lines in this file too.
I was thinking, add test cases that are needed to hit every bit of logic in the function. So privileged=false, /session path privileged=false, non-/session path failure privileged=true, /system path privileged=true, non-/system path failure privileged=true, vbox /session privileged=true, qemu /session privileged=true, other driver /session failure - Cole

On 9/26/19 10:56 AM, Daniel Henrique Barboza wrote:
This is a code repetition that I crossed a few times, then I noticed that Cole Robinson suggested a solution for it in the wiki. Here it is.
changes from v3: - patch 8: fix the exception logic, move the code formatting to patch 1 - patch 9: use lowcase 'qemu' - patch 12: (optional) test case I created to aid in patch 8 logic
changes from v2: - use a boolean to determine 'QEMU' and 'vbox' case to avoid block repetition (patch 8) - avoid 80+ chars lines in all patches
changes from v1: - handle QEMU and vbox cases separately inside the validation function
v3: https://www.redhat.com/archives/libvir-list/2019-September/msg01122.html v2: https://www.redhat.com/archives/libvir-list/2019-September/msg01007.html v1: https://www.redhat.com/archives/libvir-list/2019-September/msg00983.html
Daniel Henrique Barboza (12): src/driver.c: add virConnectValidateURIPath() interface_backend_netcf.c: use virConnectValidateURIPath() interface_backend_udev.c: use virConnectValidateURIPath() bridge_driver.c: virConnectValidateURIPath() node_device_driver.c: use virConnectValidateURIPath() secret_driver.c: use virConnectValidateURIPath() storage_driver.c: use virConnectValidateURIPath() driver.c: change URI validation to handle QEMU and vbox case qemu_driver.c: use virConnectValidateURIPath() vbox_common.c: use virConnectValidateURIPath() vbox_driver.c: use virConnectValidateURIPath() tests: add a test for driver.c:virConnectValidateURIPath()
src/driver.c | 38 +++++ src/driver.h | 4 + src/interface/interface_backend_netcf.c | 19 +-- src/interface/interface_backend_udev.c | 19 +-- src/libvirt_private.syms | 1 + src/network/bridge_driver.c | 19 +-- src/node_device/node_device_driver.c | 19 +-- src/qemu/qemu_driver.c | 20 +-- src/secret/secret_driver.c | 19 +-- src/storage/storage_driver.c | 19 +-- src/vbox/vbox_common.c | 16 +- src/vbox/vbox_driver.c | 16 +- tests/Makefile.am | 7 +- tests/virdriverconnvalidatetest.c | 186 ++++++++++++++++++++++++ 14 files changed, 267 insertions(+), 135 deletions(-) create mode 100644 tests/virdriverconnvalidatetest.c
I pushed 1-11. I'll review the test suite bits tomorrow - Cole - Cole
participants (2)
-
Cole Robinson
-
Daniel Henrique Barboza