security_selinux.c 31.1 KB
Newer Older
1
/*
2
 * Copyright (C) 2008-2010 Red Hat, Inc.
3 4 5 6 7 8 9 10
 *
 * 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.
 *
 * Authors:
 *     James Morris <jmorris@namei.org>
11
 *     Dan Walsh <dwalsh@redhat.com>
12 13 14 15 16 17 18 19 20
 *
 * SELinux security driver.
 */
#include <config.h>
#include <selinux/selinux.h>
#include <selinux/context.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
E
Eric Blake 已提交
21 22 23
#if HAVE_SELINUX_LABEL_H
# include <selinux/label.h>
#endif
24

25
#include "security_driver.h"
26 27 28 29
#include "security_selinux.h"
#include "virterror_internal.h"
#include "util.h"
#include "memory.h"
30
#include "logging.h"
31 32
#include "pci.h"
#include "hostusb.h"
33
#include "storage_file.h"
34
#include "files.h"
D
Daniel P. Berrange 已提交
35 36 37

#define VIR_FROM_THIS VIR_FROM_SECURITY

38
static char default_domain_context[1024];
39
static char default_content_context[1024];
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
static char default_image_context[1024];
#define SECURITY_SELINUX_VOID_DOI       "0"
#define SECURITY_SELINUX_NAME "selinux"

/* TODO
   The data struct of used mcs should be replaced with a better data structure in the future
*/

struct MCS {
    char *mcs;
    struct MCS *next;
};
static struct MCS *mcsList = NULL;

static int
mcsAdd(const char *mcs)
{
    struct MCS *ptr;

    for (ptr = mcsList; ptr; ptr = ptr->next) {
D
Daniel P. Berrange 已提交
60
        if (STREQ(ptr->mcs, mcs))
61 62
            return -1;
    }
D
Daniel P. Berrange 已提交
63 64
    if (VIR_ALLOC(ptr) < 0)
        return -1;
65 66 67 68 69 70 71 72 73 74 75 76 77
    ptr->mcs = strdup(mcs);
    ptr->next = mcsList;
    mcsList = ptr;
    return 0;
}

static int
mcsRemove(const char *mcs)
{
    struct MCS *prevptr = NULL;
    struct MCS *ptr = NULL;

    for (ptr = mcsList; ptr; ptr = ptr->next) {
D
Daniel P. Berrange 已提交
78
        if (STREQ(ptr->mcs, mcs)) {
79 80 81 82 83
            if (prevptr)
                prevptr->next = ptr->next;
            else {
                mcsList = ptr->next;
            }
84 85
            VIR_FREE(ptr->mcs);
            VIR_FREE(ptr);
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
            return 0;
        }
        prevptr = ptr;
    }
    return -1;
}

static char *
SELinuxGenNewContext(const char *oldcontext, const char *mcs)
{
    char *newcontext = NULL;
    char *scontext = strdup(oldcontext);
    if (!scontext) goto err;
    context_t con = context_new(scontext);
    if (!con) goto err;
    context_range_set(con, mcs);
    newcontext = strdup(context_str(con));
    context_free(con);
err:
    freecon(scontext);
    return (newcontext);
}

static int
110
SELinuxInitialize(void)
111 112 113 114 115 116
{
    char *ptr = NULL;
    int fd = 0;

    fd = open(selinux_virtual_domain_context_path(), O_RDONLY);
    if (fd < 0) {
117
        virReportSystemError(errno,
118 119
                             _("cannot open SELinux virtual domain context file '%s'"),
                             selinux_virtual_domain_context_path());
120 121 122 123
        return -1;
    }

    if (saferead(fd, default_domain_context, sizeof(default_domain_context)) < 0) {
124
        virReportSystemError(errno,
125 126
                             _("cannot read SELinux virtual domain context file %s"),
                             selinux_virtual_domain_context_path());
127
        VIR_FORCE_CLOSE(fd);
128 129
        return -1;
    }
130
    VIR_FORCE_CLOSE(fd);
131 132 133 134 135

    ptr = strchrnul(default_domain_context, '\n');
    *ptr = '\0';

    if ((fd = open(selinux_virtual_image_context_path(), O_RDONLY)) < 0) {
136
        virReportSystemError(errno,
137 138
                             _("cannot open SELinux virtual image context file %s"),
                             selinux_virtual_image_context_path());
139 140 141 142
        return -1;
    }

    if (saferead(fd, default_image_context, sizeof(default_image_context)) < 0) {
143
        virReportSystemError(errno,
144 145
                             _("cannot read SELinux virtual image context file %s"),
                             selinux_virtual_image_context_path());
146
        VIR_FORCE_CLOSE(fd);
147 148
        return -1;
    }
149
    VIR_FORCE_CLOSE(fd);
150 151

    ptr = strchrnul(default_image_context, '\n');
152 153 154 155 156 157 158
    if (*ptr == '\n') {
        *ptr = '\0';
        strcpy(default_content_context, ptr+1);
        ptr = strchrnul(default_content_context, '\n');
        if (*ptr == '\n')
            *ptr = '\0';
    }
159 160 161 162
    return 0;
}

static int
163
SELinuxGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
164
                        virDomainObjPtr vm)
165 166
{
    int rc = -1;
167
    char mcs[1024];
168 169 170
    char *scontext = NULL;
    int c1 = 0;
    int c2 = 0;
171

172 173
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;
174

175 176 177
    if (vm->def->seclabel.label ||
        vm->def->seclabel.model ||
        vm->def->seclabel.imagelabel) {
178
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
179
                               "%s", _("security label already defined for VM"));
180
        return rc;
D
Daniel P. Berrange 已提交
181
    }
182 183 184 185 186 187

    do {
        c1 = virRandom(1024);
        c2 = virRandom(1024);

        if ( c1 == c2 ) {
E
Eric Blake 已提交
188
            snprintf(mcs, sizeof(mcs), "s0:c%d", c1);
189
        } else {
D
Daniel P. Berrange 已提交
190
            if ( c1 < c2 )
E
Eric Blake 已提交
191
                snprintf(mcs, sizeof(mcs), "s0:c%d,c%d", c1, c2);
192
            else
E
Eric Blake 已提交
193
                snprintf(mcs, sizeof(mcs), "s0:c%d,c%d", c2, c1);
194 195 196 197
        }
    } while(mcsAdd(mcs) == -1);

    vm->def->seclabel.label = SELinuxGenNewContext(default_domain_context, mcs);
D
Daniel P. Berrange 已提交
198
    if (! vm->def->seclabel.label)  {
199
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
D
Daniel P. Berrange 已提交
200 201 202
                               _("cannot generate selinux context for %s"), mcs);
        goto err;
    }
203
    vm->def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
D
Daniel P. Berrange 已提交
204
    if (! vm->def->seclabel.imagelabel)  {
205
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
D
Daniel P. Berrange 已提交
206 207 208
                               _("cannot generate selinux context for %s"), mcs);
        goto err;
    }
209
    vm->def->seclabel.model = strdup(SECURITY_SELINUX_NAME);
210
    if (!vm->def->seclabel.model) {
211
        virReportOOMError();
D
Daniel P. Berrange 已提交
212 213 214
        goto err;
    }

215 216 217 218

    rc = 0;
    goto done;
err:
D
Daniel P. Berrange 已提交
219 220 221
    VIR_FREE(vm->def->seclabel.label);
    VIR_FREE(vm->def->seclabel.imagelabel);
    VIR_FREE(vm->def->seclabel.model);
222
done:
D
Daniel P. Berrange 已提交
223
    VIR_FREE(scontext);
224 225 226
    return rc;
}

227
static int
228
SELinuxReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
229
                            virDomainObjPtr vm)
230 231 232 233 234
{
    security_context_t pctx;
    context_t ctx = NULL;
    const char *mcs;

235 236 237
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

238
    if (getpidcon(vm->pid, &pctx) == -1) {
239
        virReportSystemError(errno,
240
                             _("unable to get PID %d security context"), vm->pid);
241 242 243 244
        return -1;
    }

    ctx = context_new(pctx);
245
    freecon(pctx);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    if (!ctx)
        goto err;

    mcs = context_range_get(ctx);
    if (!mcs)
        goto err;

    mcsAdd(mcs);

    context_free(ctx);

    return 0;

err:
    context_free(ctx);
    return -1;
}



266 267 268 269 270 271 272
static int
SELinuxSecurityDriverProbe(void)
{
    return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE;
}

static int
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
SELinuxSecurityDriverOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return SELinuxInitialize();
}

static int
SELinuxSecurityDriverClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return 0;
}


static const char *SELinuxSecurityGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return SECURITY_SELINUX_NAME;
}

static const char *SELinuxSecurityGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
291 292 293 294 295
{
    /*
     * Where will the DOI come from?  SELinux configuration, or qemu
     * configuration? For the moment, we'll just set it to "0".
     */
296
    return SECURITY_SELINUX_VOID_DOI;
297 298 299
}

static int
300
SELinuxGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
301
                               virDomainObjPtr vm,
302
                               virSecurityLabelPtr sec)
303 304 305 306
{
    security_context_t ctx;

    if (getpidcon(vm->pid, &ctx) == -1) {
307
        virReportSystemError(errno,
308 309
                             _("unable to get PID %d security context"),
                             vm->pid);
310 311 312 313
        return -1;
    }

    if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) {
314
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
315
                               _("security label exceeds "
C
Cole Robinson 已提交
316
                                 "maximum length: %d"),
317
                               VIR_SECURITY_LABEL_BUFLEN - 1);
318
        freecon(ctx);
319 320 321 322
        return -1;
    }

    strcpy(sec->label, (char *) ctx);
323
    freecon(ctx);
324 325 326

    sec->enforcing = security_getenforce();
    if (sec->enforcing == -1) {
327
        virReportSystemError(errno, "%s",
328
                             _("error calling security_getenforce()"));
329 330 331 332 333 334 335
        return -1;
    }

    return 0;
}

static int
336
SELinuxSetFilecon(const char *path, char *tcon)
337
{
338
    security_context_t econ;
339

340 341
    VIR_INFO("Setting SELinux context on '%s' to '%s'", path, tcon);

342
    if (setfilecon(path, tcon) < 0) {
343 344
        int setfilecon_errno = errno;

345 346 347 348 349 350 351 352
        if (getfilecon(path, &econ) >= 0) {
            if (STREQ(tcon, econ)) {
                freecon(econ);
                /* It's alright, there's nothing to change anyway. */
                return 0;
            }
            freecon(econ);
        }
353 354

        /* if the error complaint is related to an image hosted on
355 356
         * an nfs mount, or a usbfs/sysfs filesystem not supporting
         * labelling, then just ignore it & hope for the best.
357
         * The user hopefully set one of the necessary SELinux
358
         * virt_use_{nfs,usb,pci}  boolean tunables to allow it...
359 360
         */
        if (setfilecon_errno != EOPNOTSUPP) {
361
            virReportSystemError(setfilecon_errno,
362 363
                                 _("unable to set security context '%s' on '%s'"),
                                 tcon, path);
364 365
            if (security_getenforce() == 1)
                return -1;
366 367 368
        } else {
            VIR_INFO("Setting security context '%s' on '%s' not supported",
                     tcon, path);
369
        }
370 371 372 373
    }
    return 0;
}

E
Eric Blake 已提交
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
/* Set fcon to the appropriate label for path and mode, or return -1.  */
static int
getContext(const char *newpath, mode_t mode, security_context_t *fcon)
{
#if HAVE_SELINUX_LABEL_H
    struct selabel_handle *handle = selabel_open(SELABEL_CTX_FILE, NULL, 0);
    int ret;

    if (handle == NULL)
        return -1;

    ret = selabel_lookup(handle, fcon, newpath, mode);
    selabel_close(handle);
    return ret;
#else
    return matchpathcon(newpath, mode, fcon);
#endif
}

393 394 395

/* This method shouldn't raise errors, since they'll overwrite
 * errors that the caller(s) are already dealing with */
396
static int
397
SELinuxRestoreSecurityFileLabel(const char *path)
398
{
399 400 401 402
    struct stat buf;
    security_context_t fcon = NULL;
    int rc = -1;
    char *newpath = NULL;
403
    char ebuf[1024];
404

405 406
    VIR_INFO("Restoring SELinux context on '%s'", path);

407
    if (virFileResolveLink(path, &newpath) < 0) {
408 409
        VIR_WARN("cannot resolve symlink %s: %s", path,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
410
        goto err;
411
    }
412

413
    if (stat(newpath, &buf) != 0) {
414 415
        VIR_WARN("cannot stat %s: %s", newpath,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
416
        goto err;
417
    }
D
Daniel P. Berrange 已提交
418

E
Eric Blake 已提交
419
    if (getContext(newpath, buf.st_mode, &fcon) < 0) {
420
        VIR_WARN("cannot lookup default selinux label for %s", newpath);
421
    } else {
422
        rc = SELinuxSetFilecon(newpath, fcon);
423
    }
424

425
err:
426
    freecon(fcon);
427 428
    VIR_FREE(newpath);
    return rc;
429 430
}

431
static int
432
SELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
433
                                    virDomainObjPtr vm,
434 435
                                    virDomainDiskDefPtr disk,
                                    int migrated)
436
{
437 438 439 440 441
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

442 443 444 445 446 447 448 449 450 451 452
    /* Don't restore labels on readoly/shared disks, because
     * other VMs may still be accessing these
     * Alternatively we could iterate over all running
     * domains and try to figure out if it is in use, but
     * this would not work for clustered filesystems, since
     * we can't see running VMs using the file on other nodes
     * Safest bet is thus to skip the restore step.
     */
    if (disk->readonly || disk->shared)
        return 0;

453
    if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
454 455
        return 0;

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    /* If we have a shared FS & doing migrated, we must not
     * change ownership, because that kills access on the
     * destination host which is sub-optimal for the guest
     * VM's I/O attempts :-)
     */
    if (migrated) {
        int rc = virStorageFileIsSharedFS(disk->src);
        if (rc < 0)
            return -1;
        if (rc == 1) {
            VIR_DEBUG("Skipping image label restore on %s because FS is shared",
                      disk->src);
            return 0;
        }
    }

472
    return SELinuxRestoreSecurityFileLabel(disk->src);
473 474
}

475 476

static int
477
SELinuxRestoreSecurityImageLabel(virSecurityManagerPtr mgr,
478
                                 virDomainObjPtr vm,
479 480
                                 virDomainDiskDefPtr disk)
{
481
    return SELinuxRestoreSecurityImageLabelInt(mgr, vm, disk, 0);
482 483 484
}


485 486 487 488 489 490 491
static int
SELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk,
                            const char *path,
                            size_t depth,
                            void *opaque)
{
    const virSecurityLabelDefPtr secdef = opaque;
492
    int ret;
493 494 495

    if (depth == 0) {
        if (disk->shared) {
496
            ret = SELinuxSetFilecon(path, default_image_context);
497
        } else if (disk->readonly) {
498
            ret = SELinuxSetFilecon(path, default_content_context);
499
        } else if (secdef->imagelabel) {
500
            ret = SELinuxSetFilecon(path, secdef->imagelabel);
501
        } else {
502
            ret = 0;
503 504
        }
    } else {
505
        ret = SELinuxSetFilecon(path, default_content_context);
506
    }
507 508 509 510 511
    if (ret < 0 &&
        virStorageFileIsSharedFSType(path,
                                     VIR_STORAGE_FILE_SHFS_NFS) == 1)
       ret = 0;
    return ret;
512 513
}

514
static int
515
SELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr,
516
                             virDomainObjPtr vm,
517
                             virDomainDiskDefPtr disk)
518 519 520

{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
521
    bool allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr);
522

523 524 525
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

526
    return virDomainDiskDefForeachPath(disk,
527
                                       allowDiskFormatProbing,
528
                                       true,
529 530
                                       SELinuxSetSecurityFileLabel,
                                       secdef);
531 532
}

533 534

static int
535
SELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
536 537 538 539 540
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

541
    return SELinuxSetFilecon(file, secdef->imagelabel);
542 543 544
}

static int
545
SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
546 547 548 549 550
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

551
    return SELinuxSetFilecon(file, secdef->imagelabel);
552 553 554
}

static int
555
SELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
556
                               virDomainObjPtr vm,
557 558 559
                               virDomainHostdevDefPtr dev)

{
560
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
561 562
    int ret = -1;

563 564 565
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

566 567 568 569 570
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
571
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
572
                                      dev->source.subsys.u.usb.device);
573

574 575
        if (!usb)
            goto done;
576

577
        ret = usbDeviceFileIterate(usb, SELinuxSetSecurityUSBLabel, vm);
578
        usbFreeDevice(usb);
M
Mark McLoughlin 已提交
579
        break;
580 581 582
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
583
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
584 585 586 587 588 589 590
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

591
        ret = pciDeviceFileIterate(pci, SELinuxSetSecurityPCILabel, vm);
592
        pciFreeDevice(pci);
593 594 595 596 597 598 599 600 601 602 603 604 605

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

606

607
static int
608
SELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
609 610 611
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
612
    return SELinuxRestoreSecurityFileLabel(file);
613 614 615
}

static int
616
SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
617 618 619
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
620
    return SELinuxRestoreSecurityFileLabel(file);
621 622 623
}

static int
624
SELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
625
                                   virDomainObjPtr vm,
626 627 628
                                   virDomainHostdevDefPtr dev)

{
629
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
630 631
    int ret = -1;

632 633 634
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

635 636 637 638 639
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
640
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
641
                                      dev->source.subsys.u.usb.device);
642 643 644 645

        if (!usb)
            goto done;

646
        ret = usbDeviceFileIterate(usb, SELinuxRestoreSecurityUSBLabel, NULL);
647
        usbFreeDevice(usb);
648 649 650 651 652

        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
653
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
654 655 656 657 658 659 660
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

661
        ret = pciDeviceFileIterate(pci, SELinuxRestoreSecurityPCILabel, NULL);
662
        pciFreeDevice(pci);
663 664 665 666 667 668 669 670 671 672 673 674 675

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732

static int
SELinuxSetSecurityChardevLabel(virDomainObjPtr vm,
                               virDomainChrDefPtr dev)

{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    char *in = NULL, *out = NULL;
    int ret = -1;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    switch (dev->type) {
    case VIR_DOMAIN_CHR_TYPE_DEV:
    case VIR_DOMAIN_CHR_TYPE_FILE:
        ret = SELinuxSetFilecon(dev->data.file.path, secdef->imagelabel);
        break;

    case VIR_DOMAIN_CHR_TYPE_PIPE:
        if ((virAsprintf(&in, "%s.in", dev->data.file.path) < 0) ||
            (virAsprintf(&out, "%s.out", dev->data.file.path) < 0)) {
            virReportOOMError();
            goto done;
        }
        if ((SELinuxSetFilecon(in, secdef->imagelabel) < 0) ||
            (SELinuxSetFilecon(out, secdef->imagelabel) < 0))
            goto done;
        ret = 0;
        break;

    default:
        ret = 0;
        break;
    }

done:
    VIR_FREE(in);
    VIR_FREE(out);
    return ret;
}

static int
SELinuxRestoreSecurityChardevLabel(virDomainObjPtr vm,
                                   virDomainChrDefPtr dev)

{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    char *in = NULL, *out = NULL;
    int ret = -1;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    switch (dev->type) {
    case VIR_DOMAIN_CHR_TYPE_DEV:
    case VIR_DOMAIN_CHR_TYPE_FILE:
733 734 735
        if (SELinuxRestoreSecurityFileLabel(dev->data.file.path) < 0)
            goto done;
        ret = 0;
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
        break;
    case VIR_DOMAIN_CHR_TYPE_PIPE:
        if ((virAsprintf(&out, "%s.out", dev->data.file.path) < 0) ||
            (virAsprintf(&in, "%s.in", dev->data.file.path) < 0)) {
            virReportOOMError();
            goto done;
        }
        if ((SELinuxRestoreSecurityFileLabel(out) < 0) ||
            (SELinuxRestoreSecurityFileLabel(in) < 0))
            goto done;
        ret = 0;
        break;

    default:
        ret = 0;
        break;
    }

done:
    VIR_FREE(in);
    VIR_FREE(out);
    return ret;
}


static int
SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
                                      virDomainChrDefPtr dev,
                                      void *opaque)
{
    virDomainObjPtr vm = opaque;

    return SELinuxRestoreSecurityChardevLabel(vm, dev);
}


772
static int
773
SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
774
                               virDomainObjPtr vm,
775
                               int migrated ATTRIBUTE_UNUSED)
776 777 778 779
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int i;
    int rc = 0;
780 781 782

    VIR_DEBUG("Restoring security label on %s", vm->def->name);

783 784 785 786
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
787
        if (SELinuxRestoreSecurityHostdevLabel(mgr,
788 789
                                               vm,
                                               vm->def->hostdevs[i]) < 0)
790
            rc = -1;
791
    }
792
    for (i = 0 ; i < vm->def->ndisks ; i++) {
793
        if (SELinuxRestoreSecurityImageLabelInt(mgr,
794
                                                vm,
795 796
                                                vm->def->disks[i],
                                                migrated) < 0)
797 798
            rc = -1;
    }
799

800 801 802 803 804 805
    if (virDomainChrDefForeach(vm->def,
                               false,
                               SELinuxRestoreSecurityChardevCallback,
                               vm) < 0)
        rc = -1;

806 807 808 809 810 811 812 813
    if (vm->def->os.kernel &&
        SELinuxRestoreSecurityFileLabel(vm->def->os.kernel) < 0)
        rc = -1;

    if (vm->def->os.initrd &&
        SELinuxRestoreSecurityFileLabel(vm->def->os.initrd) < 0)
        rc = -1;

814 815 816 817
    return rc;
}

static int
818
SELinuxReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
819
                            virDomainObjPtr vm)
820 821 822
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

823 824
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC ||
        secdef->label == NULL)
825 826
        return 0;

827 828 829 830 831 832 833 834 835 836
    context_t con = context_new(secdef->label);
    if (con) {
        mcsRemove(context_range_get(con));
        context_free(con);
    }

    VIR_FREE(secdef->model);
    VIR_FREE(secdef->label);
    VIR_FREE(secdef->imagelabel);

837
    return 0;
838 839
}

840 841

static int
842
SELinuxSetSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
843
                          virDomainObjPtr vm,
844 845 846 847
                          const char *savefile)
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

848 849 850
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

851
    return SELinuxSetFilecon(savefile, secdef->imagelabel);
852 853 854 855
}


static int
856
SELinuxRestoreSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
857
                              virDomainObjPtr vm,
858 859
                              const char *savefile)
{
860 861 862 863 864
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

865
    return SELinuxRestoreSecurityFileLabel(savefile);
866 867 868
}


869
static int
870 871
SELinuxSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                      virDomainDefPtr def)
872 873
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;
874 875 876 877 878 879 880 881 882
    if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, virSecurityManagerGetModel(mgr));
        return -1;
    }

883 884
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (security_check_context(secdef->label) != 0) {
885
            virSecurityReportError(VIR_ERR_XML_ERROR,
886 887 888 889 890 891 892
                                   _("Invalid security label %s"), secdef->label);
            return -1;
        }
    }
    return 0;
}

893
static int
894
SELinuxSetSecurityProcessLabel(virSecurityManagerPtr mgr,
895
                               virDomainObjPtr vm)
896 897 898 899
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

900 901 902
    if (vm->def->seclabel.label == NULL)
        return 0;

903
    if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) {
904
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
905 906 907
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
908
                               secdef->model, virSecurityManagerGetModel(mgr));
909
        if (security_getenforce() == 1)
910
            return -1;
911 912 913
    }

    if (setexeccon(secdef->label) == -1) {
914
        virReportSystemError(errno,
915 916
                             _("unable to set security context '%s'"),
                             secdef->label);
917
        if (security_getenforce() == 1)
918
            return -1;
919 920
    }

921 922 923
    return 0;
}

924
static int
925
SELinuxSetSecuritySocketLabel(virSecurityManagerPtr mgr,
926 927 928 929 930 931 932 933 934 935 936 937
                               virDomainObjPtr vm)
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    context_t execcon = NULL;
    context_t proccon = NULL;
    security_context_t scon = NULL;
    int rc = -1;

    if (vm->def->seclabel.label == NULL)
        return 0;

938
    if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) {
939 940 941 942
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
943
                               secdef->model, virSecurityManagerGetModel(mgr));
944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995
        goto done;
    }

    if ( !(execcon = context_new(secdef->label)) ) {
        virReportSystemError(errno,
                             _("unable to allocate socket security context '%s'"),
                             secdef->label);
        goto done;
    }

    if (getcon(&scon) == -1) {
        virReportSystemError(errno,
                             _("unable to get current process context '%s'"),
                             secdef->label);
        goto done;
    }

    if ( !(proccon = context_new(scon)) ) {
        virReportSystemError(errno,
                             _("unable to set socket security context '%s'"),
                             secdef->label);
        goto done;
    }

    if (context_range_set(proccon, context_range_get(execcon)) == -1) {
        virReportSystemError(errno,
                             _("unable to set socket security context range '%s'"),
                             secdef->label);
        goto done;
    }

    VIR_DEBUG("Setting VM %s socket context %s",
              vm->def->name, context_str(proccon));
    if (setsockcreatecon(context_str(proccon)) == -1) {
        virReportSystemError(errno,
                             _("unable to set socket security context '%s'"),
                             context_str(proccon));
        goto done;
    }

    rc = 0;
done:

    if (security_getenforce() != 1)
        rc = 0;
    if (execcon) context_free(execcon);
    if (proccon) context_free(proccon);
    freecon(scon);
    return rc;
}

static int
996
SELinuxClearSecuritySocketLabel(virSecurityManagerPtr mgr,
997 998 999 1000 1001 1002 1003 1004
                                virDomainObjPtr vm)
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    if (vm->def->seclabel.label == NULL)
        return 0;

1005
    if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) {
1006 1007 1008 1009
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
1010
                               secdef->model, virSecurityManagerGetModel(mgr));
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
        if (security_getenforce() == 1)
            return -1;
    }

    if (setsockcreatecon(NULL) == -1) {
        virReportSystemError(errno,
                             _("unable to clear socket security context '%s'"),
                             secdef->label);
        if (security_getenforce() == 1)
            return -1;
    }
    return 0;
}

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036

static int
SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
                                  virDomainChrDefPtr dev,
                                  void *opaque)
{
    virDomainObjPtr vm = opaque;

    return SELinuxSetSecurityChardevLabel(vm, dev);
}


1037
static int
1038
SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr,
1039 1040
                           virDomainObjPtr vm,
                           const char *stdin_path)
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int i;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    for (i = 0 ; i < vm->def->ndisks ; i++) {
        /* XXX fixme - we need to recursively label the entire tree :-( */
        if (vm->def->disks[i]->type == VIR_DOMAIN_DISK_TYPE_DIR) {
            VIR_WARN("Unable to relabel directory tree %s for disk %s",
                     vm->def->disks[i]->src, vm->def->disks[i]->dst);
            continue;
1054
        }
1055
        if (SELinuxSetSecurityImageLabel(mgr,
1056
                                         vm, vm->def->disks[i]) < 0)
1057 1058 1059
            return -1;
    }
    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
1060
        if (SELinuxSetSecurityHostdevLabel(mgr,
1061 1062
                                           vm,
                                           vm->def->hostdevs[i]) < 0)
1063
            return -1;
1064 1065
    }

1066 1067 1068 1069 1070 1071
    if (virDomainChrDefForeach(vm->def,
                               true,
                               SELinuxSetSecurityChardevCallback,
                               vm) < 0)
        return -1;

1072 1073 1074 1075 1076 1077 1078 1079
    if (vm->def->os.kernel &&
        SELinuxSetFilecon(vm->def->os.kernel, default_content_context) < 0)
        return -1;

    if (vm->def->os.initrd &&
        SELinuxSetFilecon(vm->def->os.initrd, default_content_context) < 0)
        return -1;

1080 1081 1082 1083 1084 1085
    if (stdin_path) {
        if (SELinuxSetFilecon(stdin_path, default_content_context) < 0 &&
            virStorageFileIsSharedFSType(stdin_path,
                                         VIR_STORAGE_FILE_SHFS_NFS) != 1)
            return -1;
    }
1086

1087 1088 1089
    return 0;
}

1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
virSecurityDriver virSecurityDriverSELinux = {
    0,
    SECURITY_SELINUX_NAME,
    SELinuxSecurityDriverProbe,
    SELinuxSecurityDriverOpen,
    SELinuxSecurityDriverClose,

    SELinuxSecurityGetModel,
    SELinuxSecurityGetDOI,

    SELinuxSecurityVerify,

    SELinuxSetSecurityImageLabel,
    SELinuxRestoreSecurityImageLabel,

    SELinuxSetSecuritySocketLabel,
    SELinuxClearSecuritySocketLabel,

    SELinuxGenSecurityLabel,
    SELinuxReserveSecurityLabel,
    SELinuxReleaseSecurityLabel,

    SELinuxGetSecurityProcessLabel,
    SELinuxSetSecurityProcessLabel,

    SELinuxSetSecurityAllLabel,
    SELinuxRestoreSecurityAllLabel,

    SELinuxSetSecurityHostdevLabel,
    SELinuxRestoreSecurityHostdevLabel,

    SELinuxSetSavedStateLabel,
    SELinuxRestoreSavedStateLabel,
1123
};