securityselinuxlabeltest.c 9.7 KB
Newer Older
1
/*
2
 * Copyright (C) 2011-2014 Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * 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
 * <http://www.gnu.org/licenses/>.
 *
 */


#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <selinux/selinux.h>
#include <selinux/context.h>
31
#include <attr/xattr.h>
32 33 34 35 36 37 38 39 40 41

#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 "security/security_manager.h"
42
#include "virstring.h"
43 44 45

#define VIR_FROM_THIS VIR_FROM_NONE

46 47
VIR_LOG_INIT("tests.securityselinuxlabeltest");

48
static virCapsPtr caps;
49
static virQEMUDriver driver;
50 51 52 53 54 55 56 57 58 59

static virSecurityManagerPtr mgr;

typedef struct testSELinuxFile testSELinuxFile;

struct testSELinuxFile {
    char *file;
    char *context;
};

60 61 62 63 64 65 66 67
static int
testUserXattrEnabled(void)
{
    int ret = -1;
    ssize_t len;
    const char *con_value = "system_u:object_r:svirt_image_t:s0:c41,c264";
    char *path = NULL;
    if (virAsprintf(&path, "%s/securityselinuxlabeldata/testxattr",
68
                    abs_builddir) < 0)
69 70
        goto cleanup;

71 72
    if (virFileMakePath(abs_builddir "/securityselinuxlabeldata") < 0 ||
        virFileTouch(path, 0600) < 0)
73 74 75 76 77 78 79 80 81 82 83 84 85 86
        goto cleanup;

    len = setxattr(path, "user.libvirt.selinux", con_value,
                   strlen(con_value), 0);
    if (len < 0) {
        if (errno == EOPNOTSUPP)
            ret = 0;
        goto cleanup;
    }

    ret = 1;

 cleanup:
    unlink(path);
87
    rmdir(abs_builddir "/securityselinuxlabeldata");
88 89 90
    VIR_FREE(path);
    return ret;
}
91 92 93 94 95 96 97

static int
testSELinuxMungePath(char **path)
{
    char *tmp;

    if (virAsprintf(&tmp, "%s/securityselinuxlabeldata%s",
98
                    abs_builddir, *path) < 0)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
        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;
114
    char *line = NULL;
115 116 117 118 119

    *files = NULL;
    *nfiles = 0;

    if (virAsprintf(&path, "%s/securityselinuxlabeldata/%s.txt",
120
                    abs_srcdir, testname) < 0)
121 122
        goto cleanup;

123
    if (!(fp = fopen(path, "r")))
124 125
        goto cleanup;

126
    if (VIR_ALLOC_N(line, 1024) < 0)
127 128
        goto cleanup;

129
    while (!feof(fp)) {
130
        char *file = NULL, *context = NULL, *tmp;
131 132 133 134 135 136
        if (!fgets(line, 1024, fp)) {
            if (!feof(fp))
                goto cleanup;
            break;
        }

137 138 139 140 141 142 143
        tmp = strchr(line, ';');
        if (!tmp) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "unexpected format for line '%s'",
                           line);
            goto cleanup;
        }
144 145 146
        *tmp = '\0';
        tmp++;

147
        if (virAsprintf(&file, "%s/securityselinuxlabeldata%s",
148
                        abs_builddir, line) < 0)
149 150
            goto cleanup;
        if (*tmp != '\0' && *tmp != '\n') {
151
            if (VIR_STRDUP(context, tmp) < 0) {
152 153 154 155 156
                VIR_FREE(file);
                goto cleanup;
            }

            tmp = strchr(context, '\n');
157 158
            if (tmp)
                *tmp = '\0';
159 160
        }

161 162 163
        if (VIR_EXPAND_N(*files, *nfiles, 1) < 0) {
            VIR_FREE(file);
            VIR_FREE(context);
164
            goto cleanup;
165
        }
166 167 168 169 170 171 172

        (*files)[(*nfiles)-1].file = file;
        (*files)[(*nfiles)-1].context = context;
    }

    ret = 0;

173
 cleanup:
174 175
    VIR_FORCE_FCLOSE(fp);
    VIR_FREE(path);
176
    VIR_FREE(line);
177 178 179 180 181 182 183 184 185 186 187 188
    return ret;
}


static virDomainDefPtr
testSELinuxLoadDef(const char *testname)
{
    char *xmlfile = NULL;
    virDomainDefPtr def = NULL;
    size_t i;

    if (virAsprintf(&xmlfile, "%s/securityselinuxlabeldata/%s.xml",
189
                    abs_srcdir, testname) < 0)
190 191
        goto cleanup;

192
    if (!(def = virDomainDefParseFile(xmlfile, caps, driver.xmlopt, 0)))
193 194
        goto cleanup;

195
    for (i = 0; i < def->ndisks; i++) {
196 197
        if (def->disks[i]->src->type != VIR_STORAGE_TYPE_FILE &&
            def->disks[i]->src->type != VIR_STORAGE_TYPE_BLOCK)
198 199
            continue;

200
        if (testSELinuxMungePath(&def->disks[i]->src->path) < 0)
201 202 203
            goto cleanup;
    }

204
    for (i = 0; i < def->nserials; i++) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
        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;

227
 cleanup:
228 229 230 231 232 233 234 235 236 237
    VIR_FREE(xmlfile);
    return def;
}


static int
testSELinuxCreateDisks(testSELinuxFile *files, size_t nfiles)
{
    size_t i;

238
    if (virFileMakePath(abs_builddir "/securityselinuxlabeldata/nfs") < 0)
239 240
        return -1;

241
    for (i = 0; i < nfiles; i++) {
242 243 244 245 246 247 248 249 250 251 252
        if (virFileTouch(files[i].file, 0600) < 0)
            return -1;
    }
    return 0;
}

static int
testSELinuxDeleteDisks(testSELinuxFile *files, size_t nfiles)
{
    size_t i;

253
    for (i = 0; i < nfiles; i++) {
254 255 256
        if (unlink(files[i].file) < 0)
            return -1;
    }
257 258 259 260
    if (rmdir(abs_builddir "/securityselinuxlabeldata/nfs") < 0)
        return -1;
    /* Ignore failure to remove non-empty directory with in-tree build */
    rmdir(abs_builddir "/securityselinuxlabeldata");
261 262 263 264 265 266 267 268 269
    return 0;
}

static int
testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles)
{
    size_t i;
    security_context_t ctx;

270
    for (i = 0; i < nfiles; i++) {
271
        ctx = NULL;
272 273
        if (getfilecon(files[i].file, &ctx) < 0) {
            if (errno == ENODATA) {
274 275 276 277
                /* nothing to do */
            } else if (errno == EOPNOTSUPP) {
                if (VIR_STRDUP(ctx, "EOPNOTSUPP") < 0)
                    return -1;
278 279 280 281 282 283 284 285 286 287 288
            } 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);
289
            VIR_FREE(ctx);
290 291
            return -1;
        }
292
        VIR_FREE(ctx);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    }
    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;

324
 cleanup:
325
    if (testSELinuxDeleteDisks(files, nfiles) < 0)
326
        VIR_WARN("unable to fully clean up");
327 328

    virDomainDefFree(def);
329
    for (i = 0; i < nfiles; i++) {
330 331 332 333
        VIR_FREE(files[i].file);
        VIR_FREE(files[i].context);
    }
    VIR_FREE(files);
334
    if (ret < 0) {
335
        virErrorPtr err = virGetLastError();
336
        VIR_TEST_VERBOSE("%s\n", err ? err->message : "<unknown>");
337
    }
338 339 340 341 342 343 344 345 346
    return ret;
}



static int
mymain(void)
{
    int ret = 0;
347 348 349 350 351 352
    int rc = testUserXattrEnabled();

    if (rc < 0)
        return EXIT_FAILURE;
    if (!rc)
        return EXIT_AM_SKIP;
353

354 355 356
    if (!(mgr = virSecurityManagerNew("selinux", "QEMU",
                                      VIR_SECURITY_MANAGER_DEFAULT_CONFINED |
                                      VIR_SECURITY_MANAGER_PRIVILEGED))) {
357
        virErrorPtr err = virGetLastError();
358
        VIR_TEST_VERBOSE("Unable to initialize security driver: %s\n",
359
                err->message);
360
        return EXIT_FAILURE;
361 362 363
    }

    if ((caps = testQemuCapsInit()) == NULL)
364
        return EXIT_FAILURE;
365

366
    if (qemuTestDriverInit(&driver) < 0)
367 368
        return EXIT_FAILURE;

369 370 371
#define DO_TEST_LABELING(name)                                           \
    if (virtTestRun("Labelling " # name, testSELinuxLabeling, name) < 0) \
        ret = -1;
372 373 374 375 376 377

    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");
378
    DO_TEST_LABELING("nfs");
379

380 381
    qemuTestDriverFree(&driver);

382 383 384 385
    return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/libsecurityselinuxhelper.so")