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__ */