diff --git a/.gitignore b/.gitignore
index 17dcae4d72..5041ddf29e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -146,6 +146,7 @@
/tests/reconnect
/tests/secaatest
/tests/seclabeltest
+/tests/securityselinuxtest
/tests/sexpr2xmltest
/tests/shunloadtest
/tests/sockettest
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 60d322dfe6..e97a487ca3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -94,6 +94,10 @@ test_programs = virshtest sockettest \
virtimetest viruritest virkeyfiletest \
virauthconfigtest
+if WITH_SECDRIVER_SELINUX
+test_programs += securityselinuxtest
+endif
+
if WITH_DRIVER_MODULES
test_programs += virdrivermoduletest
endif
@@ -523,6 +527,28 @@ seclabeltest_SOURCES = \
seclabeltest.c
seclabeltest_LDADD = $(LDADDS)
+if WITH_SECDRIVER_SELINUX
+if WITH_TESTS
+noinst_LTLIBRARIES += libsecurityselinuxhelper.la
+else
+check_LTLIBRARIES += libsecurityselinuxhelper.la
+endif
+
+libsecurityselinuxhelper_la_SOURCES = \
+ securityselinuxhelper.c
+libsecurityselinuxhelper_la_CFLAGS = $(AM_CFLAGS)
+libsecurityselinuxhelper_la_LDFLAGS = -module -avoid-version \
+ -rpath /evil/libtool/hack/to/force/shared/lib/creation
+
+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
+endif
+
virbuftest_SOURCES = \
virbuftest.c testutils.h testutils.c
virbuftest_LDADD = $(LDADDS)
diff --git a/tests/securityselinuxhelper.c b/tests/securityselinuxhelper.c
new file mode 100644
index 0000000000..98472a6c6c
--- /dev/null
+++ b/tests/securityselinuxhelper.c
@@ -0,0 +1,67 @@
+/*
+ * 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
+/*
+ * The kernel policy will not allow us to arbitrarily change
+ * test process context. This helper is used as an LD_PRELOAD
+ * so that the libvirt code /thinks/ it is changing/reading
+ * the process context, where as in fact we're faking it all
+ */
+
+int getcon(security_context_t *context)
+{
+ if (getenv("FAKE_CONTEXT") == NULL) {
+ *context = NULL;
+ errno = EINVAL;
+ return -1;
+ }
+ if (!(*context = strdup(getenv("FAKE_CONTEXT"))))
+ return -1;
+ return 0;
+}
+
+int getpidcon(pid_t pid, security_context_t *context)
+{
+ if (pid != getpid()) {
+ *context = NULL;
+ errno = ESRCH;
+ return -1;
+ }
+ if (getenv("FAKE_CONTEXT") == NULL) {
+ *context = NULL;
+ errno = EINVAL;
+ return -1;
+ }
+ if (!(*context = strdup(getenv("FAKE_CONTEXT"))))
+ return -1;
+ return 0;
+}
+
+int setcon(security_context_t context)
+{
+ return setenv("FAKE_CONTEXT", context, 1);
+}
diff --git a/tests/securityselinuxtest.c b/tests/securityselinuxtest.c
new file mode 100644
index 0000000000..b3e4c89e9e
--- /dev/null
+++ b/tests/securityselinuxtest.c
@@ -0,0 +1,327 @@
+/*
+ * 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 "memory.h"
+#include "util.h"
+#include "logging.h"
+#include "virterror_internal.h"
+#include "security/security_manager.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+struct testSELinuxGenLabelData {
+ virSecurityManagerPtr mgr;
+
+ const char *pidcon;
+
+ bool dynamic;
+ const char *label;
+ const char *baselabel;
+
+ const char *user;
+ const char *role;
+ const char *imagerole;
+ const char *type;
+ const char *imagetype;
+
+ int sensMin;
+ int sensMax;
+ int catMin;
+ int catMax;
+};
+
+static virDomainDefPtr
+testBuildDomainDef(bool dynamic,
+ const char *label,
+ const char *baselabel)
+{
+ virDomainDefPtr def;
+ virSecurityLabelDefPtr secdef;
+
+ if (VIR_ALLOC(def) < 0)
+ goto no_memory;
+
+ if (VIR_ALLOC_N(def->seclabels, 1) < 0)
+ goto no_memory;
+
+ if (VIR_ALLOC(secdef) < 0)
+ goto no_memory;
+
+ def->seclabels[0] = secdef;
+ def->seclabels[0]->type = dynamic ? VIR_DOMAIN_SECLABEL_DYNAMIC : VIR_DOMAIN_SECLABEL_STATIC;
+
+ if (label &&
+ !(def->seclabels[0]->label = strdup(label)))
+ goto no_memory;
+
+ if (baselabel &&
+ !(def->seclabels[0]->baselabel = strdup(baselabel)))
+ goto no_memory;
+
+ return def;
+
+no_memory:
+ virReportOOMError();
+ virDomainDefFree(def);
+ return NULL;
+}
+
+
+static bool
+testSELinuxCheckCon(context_t con,
+ const char *user,
+ const char *role,
+ const char *type,
+ int sensMin,
+ int sensMax ATTRIBUTE_UNUSED,
+ int catMin,
+ int catMax)
+{
+ const char *range;
+ char *tmp;
+ int gotSens;
+ int gotCatOne;
+ int gotCatTwo;
+
+ if (STRNEQ(context_user_get(con), user)) {
+ fprintf(stderr, "Expect user %s got %s\n",
+ user, context_user_get(con));
+ return false;
+ }
+ if (STRNEQ(context_role_get(con), role)) {
+ fprintf(stderr, "Expect role %s got %s\n",
+ role, context_role_get(con));
+ return false;
+ }
+ if (STRNEQ(context_type_get(con), type)) {
+ fprintf(stderr, "Expect type %s got %s\n",
+ type, context_type_get(con));
+ return false;
+ }
+
+ range = context_range_get(con);
+ if (range[0] != 's') {
+ fprintf(stderr, "Malformed range %s, cannot find sensitivity\n",
+ range);
+ return false;
+ }
+ if (virStrToLong_i(range + 1, &tmp, 10, &gotSens) < 0 ||
+ !tmp) {
+ fprintf(stderr, "Malformed range %s, cannot parse sensitivity\n",
+ range + 1);
+ return false;
+ }
+ if (*tmp != ':') {
+ fprintf(stderr, "Malformed range %s, too many sensitivity values\n",
+ tmp);
+ return false;
+ }
+ tmp++;
+ if (*tmp != 'c') {
+ fprintf(stderr, "Malformed range %s, cannot find first category\n",
+ tmp);
+ return false;
+ }
+ tmp++;
+ if (virStrToLong_i(tmp, &tmp, 10, &gotCatOne) < 0) {
+ fprintf(stderr, "Malformed range %s, cannot parse category one\n",
+ tmp);
+ return false;
+ }
+ if (tmp && *tmp == ',')
+ tmp++;
+ if (tmp && *tmp == 'c') {
+ tmp++;
+ if (virStrToLong_i(tmp, &tmp, 10, &gotCatTwo) < 0) {
+ fprintf(stderr, "Malformed range %s, cannot parse category two\n",
+ tmp);
+ return false;
+ }
+ if (*tmp != '\0') {
+ fprintf(stderr, "Malformed range %s, junk after second category\n",
+ tmp);
+ return false;
+ }
+ if (gotCatOne == gotCatTwo) {
+ fprintf(stderr, "Saw category pair %d,%d where cats were equal\n",
+ gotCatOne, gotCatTwo);
+ return false;
+ }
+ } else {
+ gotCatTwo = gotCatOne;
+ }
+
+ if (gotSens != sensMin) {
+ fprintf(stderr, "Sensitivity %d is not equal to min %d\n",
+ gotSens, sensMin);
+ return false;
+ }
+ if (gotCatOne < catMin ||
+ gotCatOne > catMax) {
+ fprintf(stderr, "Category one %d is out of range %d-%d\n",
+ gotCatTwo, catMin, catMax);
+ return false;
+ }
+ if (gotCatTwo < catMin ||
+ gotCatTwo > catMax) {
+ fprintf(stderr, "Category two %d is out of range %d-%d\n",
+ gotCatTwo, catMin, catMax);
+ return false;
+ }
+
+ if (gotCatOne > gotCatTwo) {
+ fprintf(stderr, "Category one %d is greater than category two %d\n",
+ gotCatOne, gotCatTwo);
+ return false;
+ }
+
+ return true;
+}
+
+static int
+testSELinuxGenLabel(const void *opaque)
+{
+ const struct testSELinuxGenLabelData *data = opaque;
+ int ret = -1;
+ virDomainDefPtr def;
+ context_t con = NULL;
+ context_t imgcon = NULL;
+
+ if (setcon((security_context_t)data->pidcon) < 0) {
+ perror("Cannot set process security context");
+ return -1;
+ }
+
+ if (!(def = testBuildDomainDef(data->dynamic,
+ data->label,
+ data->baselabel)))
+ goto cleanup;
+
+ if (virSecurityManagerGenLabel(data->mgr, def) < 0) {
+ virErrorPtr err = virGetLastError();
+ fprintf(stderr, "Cannot generated label %s\n", err->message);
+ goto cleanup;
+ }
+
+ VIR_DEBUG("label=%s imagelabel=%s",
+ def->seclabels[0]->label, def->seclabels[0]->imagelabel);
+
+ if (!(con = context_new(def->seclabels[0]->label)))
+ goto cleanup;
+ if (!(imgcon = context_new(def->seclabels[0]->imagelabel)))
+ goto cleanup;
+
+ if (!testSELinuxCheckCon(con,
+ data->user, data->role, data->type,
+ data->sensMin, data->sensMax,
+ data->catMin, data->catMax))
+ goto cleanup;
+
+ if (!testSELinuxCheckCon(imgcon,
+ data->user, data->imagerole, data->imagetype,
+ data->sensMin, data->sensMax,
+ data->catMin, data->catMax))
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ context_free(con);
+ context_free(imgcon);
+ virDomainDefFree(def);
+ return ret;
+}
+
+
+
+static int
+mymain(void)
+{
+ int ret = 0;
+ virSecurityManagerPtr mgr;
+
+ 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);
+ }
+
+#define DO_TEST_GEN_LABEL(desc, pidcon, \
+ dynamic, label, baselabel, \
+ user, role, imageRole, \
+ type, imageType, \
+ sensMin, sensMax, catMin, catMax) \
+ do { \
+ struct testSELinuxGenLabelData data = { \
+ mgr, pidcon, dynamic, label, baselabel, \
+ user, role, imageRole, type, imageType, \
+ sensMin, sensMax, catMin, catMax \
+ }; \
+ if (virtTestRun("GenLabel " # desc, 1, testSELinuxGenLabel, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
+ DO_TEST_GEN_LABEL("dynamic unconfined, s0, c0.c1023",
+ "unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
+ true, NULL, NULL,
+ "unconfined_u", "unconfined_r", "object_r",
+ "svirt_t", "svirt_image_t",
+ 0, 0, 0, 1023);
+ DO_TEST_GEN_LABEL("dynamic virtd, s0, c0.c1023",
+ "system_u:system_r:virtd_t:s0-s0:c0.c1023",
+ true, NULL, NULL,
+ "system_u", "system_r", "object_r",
+ "svirt_t", "svirt_image_t",
+ 0, 0, 0, 1023);
+ DO_TEST_GEN_LABEL("dynamic virtd, s0, c0.c10",
+ "system_u:system_r:virtd_t:s0-s0:c0.c10",
+ true, NULL, NULL,
+ "system_u", "system_r", "object_r",
+ "svirt_t", "svirt_image_t",
+ 0, 0, 0, 10);
+ DO_TEST_GEN_LABEL("dynamic virtd, s2-s3, c0.c1023",
+ "system_u:system_r:virtd_t:s2-s3:c0.c1023",
+ true, NULL, NULL,
+ "system_u", "system_r", "object_r",
+ "svirt_t", "svirt_image_t",
+ 2, 3, 0, 1023);
+
+ return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/libsecurityselinuxhelper.so")
diff --git a/tests/testutils.h b/tests/testutils.h
index f372c23359..51aed9a896 100644
--- a/tests/testutils.h
+++ b/tests/testutils.h
@@ -70,4 +70,20 @@ int virtTestMain(int argc,
return virtTestMain(argc, argv, func); \
}
+# define VIRT_TEST_MAIN_PRELOAD(func, lib) \
+ int main(int argc, char **argv) { \
+ const char *preload = getenv("LD_PRELOAD"); \
+ if (preload == NULL || strstr(preload, lib) == NULL) { \
+ char *newenv; \
+ if (virAsprintf(&newenv, "%s%s%s", preload ? preload : "", \
+ preload ? ":" : "", lib) < 0) { \
+ perror("virAsprintf"); \
+ exit(EXIT_FAILURE); \
+ } \
+ setenv("LD_PRELOAD", newenv, 1); \
+ execv(argv[0], argv); \
+ } \
+ return virtTestMain(argc, argv, func); \
+ }
+
#endif /* __VIT_TEST_UTILS_H__ */