security_selinux.c 30.2 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 21
 *
 * 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>

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

#define VIR_FROM_THIS VIR_FROM_SECURITY

35
static char default_domain_context[1024];
36
static char default_content_context[1024];
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
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 已提交
57
        if (STREQ(ptr->mcs, mcs))
58 59
            return -1;
    }
D
Daniel P. Berrange 已提交
60 61
    if (VIR_ALLOC(ptr) < 0)
        return -1;
62 63 64 65 66 67 68 69 70 71 72 73 74
    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 已提交
75
        if (STREQ(ptr->mcs, mcs)) {
76 77 78 79 80
            if (prevptr)
                prevptr->next = ptr->next;
            else {
                mcsList = ptr->next;
            }
81 82
            VIR_FREE(ptr->mcs);
            VIR_FREE(ptr);
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            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
107
SELinuxInitialize(void)
108 109 110 111 112 113
{
    char *ptr = NULL;
    int fd = 0;

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

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

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

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

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

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

static int
160 161
SELinuxGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                        virDomainObjPtr vm)
162 163
{
    int rc = -1;
164
    char mcs[1024];
165 166 167
    char *scontext = NULL;
    int c1 = 0;
    int c2 = 0;
168

169 170
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;
171

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

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

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

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

212 213 214 215

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

224
static int
225 226
SELinuxReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                            virDomainObjPtr vm)
227 228 229 230 231
{
    security_context_t pctx;
    context_t ctx = NULL;
    const char *mcs;

232 233 234
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

235
    if (getpidcon(vm->pid, &pctx) == -1) {
236
        virReportSystemError(errno,
237
                             _("unable to get PID %d security context"), vm->pid);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
        return -1;
    }

    ctx = context_new(pctx);
    VIR_FREE(pctx);
    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;
}



263 264 265 266 267 268 269
static int
SELinuxSecurityDriverProbe(void)
{
    return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE;
}

static int
270 271
SELinuxSecurityDriverOpen(virSecurityDriverPtr drv,
                          bool allowDiskFormatProbing)
272 273 274 275 276
{
    /*
     * Where will the DOI come from?  SELinux configuration, or qemu
     * configuration? For the moment, we'll just set it to "0".
     */
277
    virSecurityDriverSetDOI(drv, SECURITY_SELINUX_VOID_DOI);
278
    virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing);
279
    return SELinuxInitialize();
280 281 282
}

static int
283 284
SELinuxGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
285
                               virSecurityLabelPtr sec)
286 287 288 289
{
    security_context_t ctx;

    if (getpidcon(vm->pid, &ctx) == -1) {
290
        virReportSystemError(errno,
291 292
                             _("unable to get PID %d security context"),
                             vm->pid);
293 294 295 296
        return -1;
    }

    if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) {
297
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
298
                               _("security label exceeds "
C
Cole Robinson 已提交
299
                                 "maximum length: %d"),
300 301 302 303 304
                               VIR_SECURITY_LABEL_BUFLEN - 1);
        return -1;
    }

    strcpy(sec->label, (char *) ctx);
305
    VIR_FREE(ctx);
306 307 308

    sec->enforcing = security_getenforce();
    if (sec->enforcing == -1) {
309
        virReportSystemError(errno, "%s",
310
                             _("error calling security_getenforce()"));
311 312 313 314 315 316 317
        return -1;
    }

    return 0;
}

static int
318
SELinuxSetFilecon(const char *path, char *tcon)
319
{
320
    security_context_t econ;
321

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

324
    if (setfilecon(path, tcon) < 0) {
325 326
        int setfilecon_errno = errno;

327 328 329 330 331 332 333 334
        if (getfilecon(path, &econ) >= 0) {
            if (STREQ(tcon, econ)) {
                freecon(econ);
                /* It's alright, there's nothing to change anyway. */
                return 0;
            }
            freecon(econ);
        }
335 336

        /* if the error complaint is related to an image hosted on
337 338
         * an nfs mount, or a usbfs/sysfs filesystem not supporting
         * labelling, then just ignore it & hope for the best.
339
         * The user hopefully set one of the necessary SELinux
340
         * virt_use_{nfs,usb,pci}  boolean tunables to allow it...
341 342
         */
        if (setfilecon_errno != EOPNOTSUPP) {
343
            virReportSystemError(setfilecon_errno,
344 345
                                 _("unable to set security context '%s' on '%s'"),
                                 tcon, path);
346 347
            if (security_getenforce() == 1)
                return -1;
348 349 350
        } else {
            VIR_INFO("Setting security context '%s' on '%s' not supported",
                     tcon, path);
351
        }
352 353 354 355
    }
    return 0;
}

356 357 358

/* This method shouldn't raise errors, since they'll overwrite
 * errors that the caller(s) are already dealing with */
359
static int
360
SELinuxRestoreSecurityFileLabel(const char *path)
361
{
362 363 364 365
    struct stat buf;
    security_context_t fcon = NULL;
    int rc = -1;
    char *newpath = NULL;
366
    char ebuf[1024];
367

368 369
    VIR_INFO("Restoring SELinux context on '%s'", path);

370
    if (virFileResolveLink(path, &newpath) < 0) {
371 372
        VIR_WARN("cannot resolve symlink %s: %s", path,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
373
        goto err;
374
    }
375

376
    if (stat(newpath, &buf) != 0) {
377 378
        VIR_WARN("cannot stat %s: %s", newpath,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
379
        goto err;
380
    }
D
Daniel P. Berrange 已提交
381 382

    if (matchpathcon(newpath, buf.st_mode, &fcon) == 0)  {
383
        rc = SELinuxSetFilecon(newpath, fcon);
384
    } else {
385 386
        VIR_WARN("cannot lookup default selinux label for %s",
                 newpath);
387
    }
388

389 390 391 392
err:
    VIR_FREE(fcon);
    VIR_FREE(newpath);
    return rc;
393 394
}

395
static int
396 397
SELinuxRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                    virDomainObjPtr vm,
398 399
                                    virDomainDiskDefPtr disk,
                                    int migrated)
400
{
401 402 403 404 405
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

406 407 408 409 410 411 412 413 414 415 416 417 418 419
    /* 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;

    if (!disk->src)
        return 0;

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
    /* 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;
        }
    }

436
    return SELinuxRestoreSecurityFileLabel(disk->src);
437 438
}

439 440

static int
441 442
SELinuxRestoreSecurityImageLabel(virSecurityDriverPtr drv,
                                 virDomainObjPtr vm,
443 444
                                 virDomainDiskDefPtr disk)
{
445
    return SELinuxRestoreSecurityImageLabelInt(drv, vm, disk, 0);
446 447 448
}


449 450 451 452 453 454 455
static int
SELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk,
                            const char *path,
                            size_t depth,
                            void *opaque)
{
    const virSecurityLabelDefPtr secdef = opaque;
456
    int ret;
457 458 459

    if (depth == 0) {
        if (disk->shared) {
460
            ret = SELinuxSetFilecon(path, default_image_context);
461
        } else if (disk->readonly) {
462
            ret = SELinuxSetFilecon(path, default_content_context);
463
        } else if (secdef->imagelabel) {
464
            ret = SELinuxSetFilecon(path, secdef->imagelabel);
465
        } else {
466
            ret = 0;
467 468
        }
    } else {
469
        ret = SELinuxSetFilecon(path, default_content_context);
470
    }
471 472 473 474 475
    if (ret < 0 &&
        virStorageFileIsSharedFSType(path,
                                     VIR_STORAGE_FILE_SHFS_NFS) == 1)
       ret = 0;
    return ret;
476 477
}

478
static int
479
SELinuxSetSecurityImageLabel(virSecurityDriverPtr drv,
480
                             virDomainObjPtr vm,
481
                             virDomainDiskDefPtr disk)
482 483 484

{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
485
    bool allowDiskFormatProbing = virSecurityDriverGetAllowDiskFormatProbing(drv);
486

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

490
    return virDomainDiskDefForeachPath(disk,
491
                                       allowDiskFormatProbing,
492
                                       true,
493 494
                                       SELinuxSetSecurityFileLabel,
                                       secdef);
495 496
}

497 498

static int
499
SELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
500 501 502 503 504
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

505
    return SELinuxSetFilecon(file, secdef->imagelabel);
506 507 508
}

static int
509
SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
510 511 512 513 514
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

515
    return SELinuxSetFilecon(file, secdef->imagelabel);
516 517 518
}

static int
519 520
SELinuxSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
521 522 523
                               virDomainHostdevDefPtr dev)

{
524
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
525 526
    int ret = -1;

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

530 531 532 533 534
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

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

538 539
        if (!usb)
            goto done;
540

541
        ret = usbDeviceFileIterate(usb, SELinuxSetSecurityUSBLabel, vm);
542
        usbFreeDevice(usb);
M
Mark McLoughlin 已提交
543
        break;
544 545 546
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
547
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
548 549 550 551 552 553 554
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

555
        ret = pciDeviceFileIterate(pci, SELinuxSetSecurityPCILabel, vm);
556
        pciFreeDevice(pci);
557 558 559 560 561 562 563 564 565 566 567 568 569

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

570

571
static int
572
SELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
573 574 575
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
576
    return SELinuxRestoreSecurityFileLabel(file);
577 578 579
}

static int
580
SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
581 582 583
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
584
    return SELinuxRestoreSecurityFileLabel(file);
585 586 587
}

static int
588 589
SELinuxRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                   virDomainObjPtr vm,
590 591 592
                                   virDomainHostdevDefPtr dev)

{
593
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
594 595
    int ret = -1;

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

599 600 601 602 603
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
604
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
605
                                      dev->source.subsys.u.usb.device);
606 607 608 609

        if (!usb)
            goto done;

610
        ret = usbDeviceFileIterate(usb, SELinuxRestoreSecurityUSBLabel, NULL);
611
        usbFreeDevice(usb);
612 613 614 615 616

        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
617
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
618 619 620 621 622 623 624
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

625
        ret = pciDeviceFileIterate(pci, SELinuxRestoreSecurityPCILabel, NULL);
626
        pciFreeDevice(pci);
627 628 629 630 631 632 633 634 635 636 637 638 639

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 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 733 734

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:
        ret = SELinuxSetFilecon(dev->data.file.path, secdef->imagelabel);
        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);
}


735
static int
736 737
SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
738
                               int migrated ATTRIBUTE_UNUSED)
739 740 741 742
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int i;
    int rc = 0;
743 744 745

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

746 747 748 749
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
750 751 752
        if (SELinuxRestoreSecurityHostdevLabel(drv,
                                               vm,
                                               vm->def->hostdevs[i]) < 0)
753
            rc = -1;
754
    }
755
    for (i = 0 ; i < vm->def->ndisks ; i++) {
756 757
        if (SELinuxRestoreSecurityImageLabelInt(drv,
                                                vm,
758 759
                                                vm->def->disks[i],
                                                migrated) < 0)
760 761
            rc = -1;
    }
762

763 764 765 766 767 768
    if (virDomainChrDefForeach(vm->def,
                               false,
                               SELinuxRestoreSecurityChardevCallback,
                               vm) < 0)
        rc = -1;

769 770 771 772 773 774 775 776
    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;

777 778 779 780
    return rc;
}

static int
781 782
SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                            virDomainObjPtr vm)
783 784 785
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

786 787
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC ||
        secdef->label == NULL)
788 789
        return 0;

790 791 792 793 794 795 796 797 798 799
    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);

800
    return 0;
801 802
}

803 804

static int
805 806
SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                          virDomainObjPtr vm,
807 808 809 810
                          const char *savefile)
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

814
    return SELinuxSetFilecon(savefile, secdef->imagelabel);
815 816 817 818
}


static int
819 820
SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                              virDomainObjPtr vm,
821 822
                              const char *savefile)
{
823 824 825 826 827
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

828
    return SELinuxRestoreSecurityFileLabel(savefile);
829 830 831
}


832
static int
833
SELinuxSecurityVerify(virDomainDefPtr def)
834 835 836 837
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (security_check_context(secdef->label) != 0) {
838
            virSecurityReportError(VIR_ERR_XML_ERROR,
839 840 841 842 843 844 845
                                   _("Invalid security label %s"), secdef->label);
            return -1;
        }
    }
    return 0;
}

846
static int
847
SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv,
848
                               virDomainObjPtr vm)
849 850 851 852
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

856
    if (!STREQ(drv->name, secdef->model)) {
857
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
858 859 860 861
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, drv->name);
862
        if (security_getenforce() == 1)
863
            return -1;
864 865 866
    }

    if (setexeccon(secdef->label) == -1) {
867
        virReportSystemError(errno,
868 869
                             _("unable to set security context '%s'"),
                             secdef->label);
870
        if (security_getenforce() == 1)
871
            return -1;
872 873
    }

874 875 876
    return 0;
}

877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 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
static int
SELinuxSetSecuritySocketLabel(virSecurityDriverPtr drv,
                               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;

    if (!STREQ(drv->name, secdef->model)) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, drv->name);
        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
SELinuxClearSecuritySocketLabel(virSecurityDriverPtr drv,
                                virDomainObjPtr vm)
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

    if (!STREQ(drv->name, secdef->model)) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, drv->name);
        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;
}

978 979 980 981 982 983 984 985 986 987 988 989

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

    return SELinuxSetSecurityChardevLabel(vm, dev);
}


990
static int
991 992 993
SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv,
                           virDomainObjPtr vm,
                           const char *stdin_path)
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
{
    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;
1007
        }
1008 1009
        if (SELinuxSetSecurityImageLabel(drv,
                                         vm, vm->def->disks[i]) < 0)
1010 1011 1012
            return -1;
    }
    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
1013 1014 1015
        if (SELinuxSetSecurityHostdevLabel(drv,
                                           vm,
                                           vm->def->hostdevs[i]) < 0)
1016
            return -1;
1017 1018
    }

1019 1020 1021 1022 1023 1024
    if (virDomainChrDefForeach(vm->def,
                               true,
                               SELinuxSetSecurityChardevCallback,
                               vm) < 0)
        return -1;

1025 1026 1027 1028 1029 1030 1031 1032
    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;

1033 1034 1035 1036 1037 1038
    if (stdin_path) {
        if (SELinuxSetFilecon(stdin_path, default_content_context) < 0 &&
            virStorageFileIsSharedFSType(stdin_path,
                                         VIR_STORAGE_FILE_SHFS_NFS) != 1)
            return -1;
    }
1039

1040 1041 1042 1043 1044 1045 1046
    return 0;
}

virSecurityDriver virSELinuxSecurityDriver = {
    .name                       = SECURITY_SELINUX_NAME,
    .probe                      = SELinuxSecurityDriverProbe,
    .open                       = SELinuxSecurityDriverOpen,
1047
    .domainSecurityVerify       = SELinuxSecurityVerify,
1048
    .domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel,
1049 1050
    .domainSetSecuritySocketLabel     = SELinuxSetSecuritySocketLabel,
    .domainClearSecuritySocketLabel     = SELinuxClearSecuritySocketLabel,
1051 1052
    .domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel,
    .domainGenSecurityLabel     = SELinuxGenSecurityLabel,
1053
    .domainReserveSecurityLabel     = SELinuxReserveSecurityLabel,
1054 1055 1056 1057 1058
    .domainReleaseSecurityLabel     = SELinuxReleaseSecurityLabel,
    .domainGetSecurityProcessLabel     = SELinuxGetSecurityProcessLabel,
    .domainSetSecurityProcessLabel     = SELinuxSetSecurityProcessLabel,
    .domainRestoreSecurityAllLabel = SELinuxRestoreSecurityAllLabel,
    .domainSetSecurityAllLabel     = SELinuxSetSecurityAllLabel,
1059 1060
    .domainSetSecurityHostdevLabel = SELinuxSetSecurityHostdevLabel,
    .domainRestoreSecurityHostdevLabel = SELinuxRestoreSecurityHostdevLabel,
1061 1062
    .domainSetSavedStateLabel = SELinuxSetSavedStateLabel,
    .domainRestoreSavedStateLabel = SELinuxRestoreSavedStateLabel,
1063
};