diff --git a/.gitignore b/.gitignore index 882ae4ca6b..a851616ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -158,6 +158,7 @@ /tests/secaatest /tests/seclabeltest /tests/securityselinuxtest +/tests/securityselinuxlabeltest /tests/sexpr2xmltest /tests/shunloadtest /tests/sockettest diff --git a/HACKING b/HACKING index bf02b65076..3b800d81e1 100644 --- a/HACKING +++ b/HACKING @@ -689,7 +689,7 @@ stick to the following general plan for all *.c source files: #include #include - #if HAVE_NUMACTL Some system includes aren't supported + #if WITH_NUMACTL Some system includes aren't supported # include everywhere so need these #if guards. #endif diff --git a/cfg.mk b/cfg.mk index ff9adcb190..e42ef28c17 100644 --- a/cfg.mk +++ b/cfg.mk @@ -795,7 +795,7 @@ exclude_file_name_regexp--sc_prohibit_nonreentrant = \ ^((po|tests)/|docs/.*py|run.in$$) exclude_file_name_regexp--sc_prohibit_raw_allocation = \ - ^(src/util/viralloc\.[ch]|examples/.*)$$ + ^(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper.c)$$ exclude_file_name_regexp--sc_prohibit_readlink = \ ^src/(util/virutil|lxc/lxc_container)\.c$$ diff --git a/configure.ac b/configure.ac index 2cff562106..27e99f40cf 100644 --- a/configure.ac +++ b/configure.ac @@ -143,6 +143,7 @@ AC_MSG_RESULT([$VERSION_SCRIPT_FLAGS]) LIBVIRT_COMPILE_WARNINGS LIBVIRT_CHECK_APPARMOR +LIBVIRT_CHECK_ATTR LIBVIRT_CHECK_AUDIT LIBVIRT_CHECK_AVAHI LIBVIRT_CHECK_BLKID @@ -2467,6 +2468,7 @@ AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) LIBVIRT_RESULT_APPARMOR +LIBVIRT_RESULT_ATTR LIBVIRT_RESULT_AUDIT LIBVIRT_RESULT_AVAHI LIBVIRT_RESULT_BLKID diff --git a/libvirt.spec.in b/libvirt.spec.in index 35869a8625..6d5e9f1cca 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -410,6 +410,7 @@ BuildRequires: ncurses-devel BuildRequires: gettext BuildRequires: libtasn1-devel BuildRequires: gnutls-devel +BuildRequires: libattr-devel %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6 # for augparse, optionally used in testing BuildRequires: augeas diff --git a/m4/virt-attr.m4 b/m4/virt-attr.m4 new file mode 100644 index 0000000000..341b35b98a --- /dev/null +++ b/m4/virt-attr.m4 @@ -0,0 +1,9 @@ +dnl The libattr.so library + +AC_DEFUN([LIBVIRT_CHECK_ATTR],[ + LIBVIRT_CHECK_LIB([ATTR], [attr], [getxattr], [attr/xattr.h]) +]) + +AC_DEFUN([LIBVIRT_RESULT_ATTR],[ + LIBVIRT_RESULT_LIB([ATTR]) +]) diff --git a/tests/Makefile.am b/tests/Makefile.am index 6f0dde5207..61b0a0c2ff 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -68,6 +68,7 @@ EXTRA_DIST = \ qemuxml2argvdata \ qemuxml2xmloutdata \ qemuxmlnsdata \ + securityselinuxlabeldata \ schematestutils.sh \ sexpr2xmldata \ storagepoolschematest \ @@ -106,6 +107,9 @@ endif if WITH_SECDRIVER_SELINUX test_programs += securityselinuxtest +if WITH_ATTR +test_programs += securityselinuxlabeltest +endif endif if WITH_DRIVER_MODULES @@ -597,10 +601,20 @@ securityselinuxtest_SOURCES = \ securityselinuxtest.c testutils.h testutils.c securityselinuxtest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) securityselinuxtest_LDADD = $(LDADDS) -securityselinuxtest_DEPENDENCIES = libsecurityselinuxhelper.la -else -EXTRA_DIST += securityselinuxtest.c securityselinuxhelper.c +securityselinuxtest_DEPENDENCIES = libsecurityselinuxhelper.la ../src/libvirt.la + +if WITH_QEMU +if WITH_ATTR +securityselinuxlabeltest_SOURCES = \ + securityselinuxlabeltest.c testutils.h testutils.c \ + testutilsqemu.h testutilsqemu.c +securityselinuxlabeltest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +securityselinuxlabeltest_LDADD = $(qemu_LDADDS) +securityselinuxlabeltest_DEPENDENCIES = libsecurityselinuxhelper.la ../src/libvirt.la endif +endif +endif +EXTRA_DIST += securityselinuxtest.c securityselinuxlabeltest.c securityselinuxhelper.c virbuftest_SOURCES = \ virbuftest.c testutils.h testutils.c diff --git a/tests/securityselinuxhelper.c b/tests/securityselinuxhelper.c index dc63ff3d3a..daad7dd43f 100644 --- a/tests/securityselinuxhelper.c +++ b/tests/securityselinuxhelper.c @@ -24,6 +24,9 @@ #include #include #include +#include + + /* * The kernel policy will not allow us to arbitrarily change * test process context. This helper is used as an LD_PRELOAD @@ -64,3 +67,33 @@ int setcon_raw(security_context_t context) { return setenv("FAKE_CONTEXT", context, 1); } + + +#if WITH_ATTR +int setfilecon(const char *path, security_context_t con) +{ + const char *constr = con; + return setxattr(path, "user.libvirt.selinux", + constr, strlen(constr), 0); +} + + +int getfilecon(const char *path, security_context_t *con) +{ + char *constr = NULL; + ssize_t len = getxattr(path, "user.libvirt.selinux", + NULL, 0); + if (len < 0) + return -1; + if (!(constr = malloc(len+1))) + return -1; + memset(constr, 0, len); + if (getxattr(path, "user.libvirt.selinux", constr, len) < 0) { + free(constr); + return -1; + } + *con = constr; + constr[len] = '\0'; + return 0; +} +#endif diff --git a/tests/securityselinuxlabeldata/chardev.txt b/tests/securityselinuxlabeldata/chardev.txt new file mode 100644 index 0000000000..3f4b6302b9 --- /dev/null +++ b/tests/securityselinuxlabeldata/chardev.txt @@ -0,0 +1,7 @@ +/plain.txt;system_u:object_r:svirt_image_t:s0:c41,c264 +/plain.dev;system_u:object_r:svirt_image_t:s0:c41,c264 +/plain.fifo;system_u:object_r:svirt_image_t:s0:c41,c264 +/nolabel.sock; +/plain.sock; +/yeslabel.sock;system_u:object_r:svirt_image_t:s0:c41,c264 +/altlabel.sock;system_u:object_r:svirt_image_custom_t:s0:c41,c264 diff --git a/tests/securityselinuxlabeldata/chardev.xml b/tests/securityselinuxlabeldata/chardev.xml new file mode 100644 index 0000000000..64b6b5f068 --- /dev/null +++ b/tests/securityselinuxlabeldata/chardev.xml @@ -0,0 +1,47 @@ + + vm1 + c7b3edbd-edaf-9455-926a-d65c16db1800 + 219200 + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + system_u:object_r:svirt_image_t:s0:c41,c264 + + diff --git a/tests/securityselinuxlabeldata/disks.txt b/tests/securityselinuxlabeldata/disks.txt new file mode 100644 index 0000000000..2573d993b4 --- /dev/null +++ b/tests/securityselinuxlabeldata/disks.txt @@ -0,0 +1,5 @@ +/plain.raw;system_u:object_r:svirt_image_t:s0:c41,c264 +/shared.raw;system_u:object_r:svirt_image_t:s0 +/readonly.raw;system_u:object_r:virt_content_t:s0 +/nolabel.raw; +/altlabel.raw;system_u:object_r:svirt_image_custom_t:s0:c41,c264 diff --git a/tests/securityselinuxlabeldata/disks.xml b/tests/securityselinuxlabeldata/disks.xml new file mode 100644 index 0000000000..33e8763bfb --- /dev/null +++ b/tests/securityselinuxlabeldata/disks.xml @@ -0,0 +1,52 @@ + + vm1 + c7b3edbd-edaf-9455-926a-d65c16db1800 + 219200 + + hvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + system_u:object_r:svirt_image_t:s0:c41,c264 + + diff --git a/tests/securityselinuxlabeldata/kernel.txt b/tests/securityselinuxlabeldata/kernel.txt new file mode 100644 index 0000000000..87063fd047 --- /dev/null +++ b/tests/securityselinuxlabeldata/kernel.txt @@ -0,0 +1,2 @@ +/vmlinuz.raw;system_u:object_r:virt_content_t:s0 +/initrd.raw;system_u:object_r:virt_content_t:s0 diff --git a/tests/securityselinuxlabeldata/kernel.xml b/tests/securityselinuxlabeldata/kernel.xml new file mode 100644 index 0000000000..0fd551da49 --- /dev/null +++ b/tests/securityselinuxlabeldata/kernel.xml @@ -0,0 +1,20 @@ + + vm1 + c7b3edbd-edaf-9455-926a-d65c16db1800 + 219200 + + hvm + /vmlinuz.raw + /initrd.raw + + + + + + + + + + system_u:object_r:svirt_image_t:s0:c41,c264 + + diff --git a/tests/securityselinuxlabeltest.c b/tests/securityselinuxlabeltest.c new file mode 100644 index 0000000000..b108860f23 --- /dev/null +++ b/tests/securityselinuxlabeltest.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2011-2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * License along with this library; If not, see + * . + * + */ + + +#include + +#include +#include +#include +#include + +#include +#include + +#include "internal.h" +#include "testutils.h" +#include "testutilsqemu.h" +#include "qemu/qemu_domain.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" +#include "virutil.h" +#include "security/security_manager.h" + + +#define VIR_FROM_THIS VIR_FROM_NONE + +static virCapsPtr caps; + +static virSecurityManagerPtr mgr; + +typedef struct testSELinuxFile testSELinuxFile; + +struct testSELinuxFile { + char *file; + char *context; +}; + + +static int +testSELinuxMungePath(char **path) +{ + char *tmp; + + if (virAsprintf(&tmp, "%s/securityselinuxlabeldata%s", + abs_srcdir, *path) < 0) { + virReportOOMError(); + return -1; + } + + VIR_FREE(*path); + *path = tmp; + return 0; +} + +static int +testSELinuxLoadFileList(const char *testname, + testSELinuxFile **files, + size_t *nfiles) +{ + int ret = -1; + char *path = NULL; + FILE *fp = NULL; + + *files = NULL; + *nfiles = 0; + + if (virAsprintf(&path, "%s/securityselinuxlabeldata/%s.txt", + abs_srcdir, testname) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!(fp = fopen(path, "r"))) { + goto cleanup; + } + + while (!feof(fp)) { + char *line; + char *file, *context; + if (VIR_ALLOC_N(line, 1024) < 0) { + virReportOOMError(); + goto cleanup; + } + if (!fgets(line, 1024, fp)) { + if (!feof(fp)) + goto cleanup; + break; + } + + char *tmp = strchr(line, ';'); + *tmp = '\0'; + tmp++; + + if (virAsprintf(&file, "%s/securityselinuxlabeldata%s", abs_builddir, line) < 0) { + VIR_FREE(line); + virReportOOMError(); + goto cleanup; + } + if (*tmp != '\0' && *tmp != '\n') { + if (!(context = strdup(tmp))) { + VIR_FREE(line); + VIR_FREE(file); + virReportOOMError(); + goto cleanup; + } + + tmp = strchr(context, '\n'); + *tmp = '\0'; + } else { + context = NULL; + } + + if (VIR_EXPAND_N(*files, *nfiles, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + (*files)[(*nfiles)-1].file = file; + (*files)[(*nfiles)-1].context = context; + } + + ret = 0; + +cleanup: + VIR_FORCE_FCLOSE(fp); + VIR_FREE(path); + return ret; +} + + +static virDomainDefPtr +testSELinuxLoadDef(const char *testname) +{ + char *xmlfile = NULL; + char *xmlstr = NULL; + virDomainDefPtr def = NULL; + size_t i; + + if (virAsprintf(&xmlfile, "%s/securityselinuxlabeldata/%s.xml", + abs_srcdir, testname) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virFileReadAll(xmlfile, 1024*1024, &xmlstr) < 0) { + goto cleanup; + } + + if (!(def = virDomainDefParseString(caps, xmlstr, + QEMU_EXPECTED_VIRT_TYPES, + 0))) + goto cleanup; + + for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_FILE && + def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK) + continue; + + if (testSELinuxMungePath(&def->disks[i]->src) < 0) + goto cleanup; + } + + for (i = 0 ; i < def->nserials ; i++) { + if (def->serials[i]->source.type != VIR_DOMAIN_CHR_TYPE_FILE && + def->serials[i]->source.type != VIR_DOMAIN_CHR_TYPE_PIPE && + def->serials[i]->source.type != VIR_DOMAIN_CHR_TYPE_DEV && + def->serials[i]->source.type != VIR_DOMAIN_CHR_TYPE_UNIX) + continue; + + if (def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_UNIX) { + if (testSELinuxMungePath(&def->serials[i]->source.data.nix.path) < 0) + goto cleanup; + } else { + if (testSELinuxMungePath(&def->serials[i]->source.data.file.path) < 0) + goto cleanup; + } + } + + if (def->os.kernel && + testSELinuxMungePath(&def->os.kernel) < 0) + goto cleanup; + if (def->os.initrd && + testSELinuxMungePath(&def->os.initrd) < 0) + goto cleanup; + +cleanup: + VIR_FREE(xmlfile); + VIR_FREE(xmlstr); + return def; +} + + +static int +testSELinuxCreateDisks(testSELinuxFile *files, size_t nfiles) +{ + size_t i; + + if (virFileMakePath(abs_builddir "/securityselinuxlabeldata") < 0) + return -1; + + for (i = 0 ; i < nfiles ; i++) { + if (virFileTouch(files[i].file, 0600) < 0) + return -1; + } + return 0; +} + +static int +testSELinuxDeleteDisks(testSELinuxFile *files, size_t nfiles) +{ + size_t i; + + for (i = 0 ; i < nfiles ; i++) { + if (unlink(files[i].file) < 0) + return -1; + } + return 0; +} + +static int +testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles) +{ + size_t i; + security_context_t ctx; + + for (i = 0 ; i < nfiles ; i++) { + if (getfilecon(files[i].file, &ctx) < 0) { + if (errno == ENODATA) { + ctx = NULL; + } else { + virReportSystemError(errno, + "Cannot read label on %s", + files[i].file); + return -1; + } + } + if (!STREQ_NULLABLE(files[i].context, ctx)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "File %s context '%s' did not match epected '%s'", + files[i].file, ctx, files[i].context); + return -1; + } + } + return 0; +} + +static int +testSELinuxLabeling(const void *opaque) +{ + const char *testname = opaque; + int ret = -1; + testSELinuxFile *files = NULL; + size_t nfiles = 0; + size_t i; + virDomainDefPtr def = NULL; + + if (testSELinuxLoadFileList(testname, &files, &nfiles) < 0) + goto cleanup; + + if (testSELinuxCreateDisks(files, nfiles) < 0) + goto cleanup; + + if (!(def = testSELinuxLoadDef(testname))) + goto cleanup; + + if (virSecurityManagerSetAllLabel(mgr, def, NULL) < 0) + goto cleanup; + + if (testSELinuxCheckLabels(files, nfiles) < 0) + goto cleanup; + + ret = 0; + +cleanup: + if (testSELinuxDeleteDisks(files, nfiles) < 0) + goto cleanup; + + virDomainDefFree(def); + for (i = 0 ; i < nfiles; i++) { + VIR_FREE(files[i].file); + VIR_FREE(files[i].context); + } + VIR_FREE(files); + return ret; +} + + + +static int +mymain(void) +{ + int ret = 0; + + if (!(mgr = virSecurityManagerNew("selinux", "QEMU", false, true, false))) { + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_CONFIG_UNSUPPORTED) + exit(EXIT_AM_SKIP); + + fprintf(stderr, "Unable to initialize security driver: %s\n", + err->message); + exit(EXIT_FAILURE); + } + + if ((caps = testQemuCapsInit()) == NULL) + exit(EXIT_FAILURE); + +#define DO_TEST_LABELING(name) \ + if (virtTestRun("Labelling " # name, 1, testSELinuxLabeling, name) < 0) \ + ret = -1; \ + + setcon((security_context_t)"system_r:system_u:libvirtd_t:s0:c0.c1023"); + + DO_TEST_LABELING("disks"); + DO_TEST_LABELING("kernel"); + DO_TEST_LABELING("chardev"); + + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/libsecurityselinuxhelper.so")