security_selinux.c 29.9 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"
D
Daniel P. Berrange 已提交
31 32 33

#define VIR_FROM_THIS VIR_FROM_SECURITY

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

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

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

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

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

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

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

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

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

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

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

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

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

211 212 213 214

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

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

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

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



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

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

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

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

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

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

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

    return 0;
}

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

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

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

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

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

355 356 357

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

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

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

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

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

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

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

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

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

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

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

438 439

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


448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
static int
SELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk,
                            const char *path,
                            size_t depth,
                            void *opaque)
{
    const virSecurityLabelDefPtr secdef = opaque;

    if (depth == 0) {
        if (disk->shared) {
            return SELinuxSetFilecon(path, default_image_context);
        } else if (disk->readonly) {
            return SELinuxSetFilecon(path, default_content_context);
        } else if (secdef->imagelabel) {
            return SELinuxSetFilecon(path, secdef->imagelabel);
        } else {
            return 0;
        }
    } else {
        return SELinuxSetFilecon(path, default_content_context);
    }
}

471
static int
472
SELinuxSetSecurityImageLabel(virSecurityDriverPtr drv,
473
                             virDomainObjPtr vm,
474
                             virDomainDiskDefPtr disk)
475 476 477

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

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

483
    return virDomainDiskDefForeachPath(disk,
484
                                       allowDiskFormatProbing,
485
                                       true,
486 487
                                       SELinuxSetSecurityFileLabel,
                                       secdef);
488 489
}

490 491

static int
492
SELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
493 494 495 496 497
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

498
    return SELinuxSetFilecon(file, secdef->imagelabel);
499 500 501
}

static int
502
SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
503 504 505 506 507
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

508
    return SELinuxSetFilecon(file, secdef->imagelabel);
509 510 511
}

static int
512 513
SELinuxSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
514 515 516
                               virDomainHostdevDefPtr dev)

{
517
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
518 519
    int ret = -1;

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

523 524 525 526 527
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

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

531 532
        if (!usb)
            goto done;
533

534
        ret = usbDeviceFileIterate(usb, SELinuxSetSecurityUSBLabel, vm);
535
        usbFreeDevice(usb);
M
Mark McLoughlin 已提交
536
        break;
537 538 539
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
540
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
541 542 543 544 545 546 547
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

548
        ret = pciDeviceFileIterate(pci, SELinuxSetSecurityPCILabel, vm);
549
        pciFreeDevice(pci);
550 551 552 553 554 555 556 557 558 559 560 561 562

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

563

564
static int
565
SELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
566 567 568
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
569
    return SELinuxRestoreSecurityFileLabel(file);
570 571 572
}

static int
573
SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
574 575 576
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
577
    return SELinuxRestoreSecurityFileLabel(file);
578 579 580
}

static int
581 582
SELinuxRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                   virDomainObjPtr vm,
583 584 585
                                   virDomainHostdevDefPtr dev)

{
586
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
587 588
    int ret = -1;

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

592 593 594 595 596
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
597
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
598
                                      dev->source.subsys.u.usb.device);
599 600 601 602

        if (!usb)
            goto done;

603
        ret = usbDeviceFileIterate(usb, SELinuxRestoreSecurityUSBLabel, NULL);
604
        usbFreeDevice(usb);
605 606 607 608 609

        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
610
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
611 612 613 614 615 616 617
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

618
        ret = pciDeviceFileIterate(pci, SELinuxRestoreSecurityPCILabel, NULL);
619
        pciFreeDevice(pci);
620 621 622 623 624 625 626 627 628 629 630 631 632

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

633 634 635 636 637 638 639 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

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);
}


728
static int
729 730
SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
731
                               int migrated ATTRIBUTE_UNUSED)
732 733 734 735
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int i;
    int rc = 0;
736 737 738

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

739 740 741 742
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
743 744 745
        if (SELinuxRestoreSecurityHostdevLabel(drv,
                                               vm,
                                               vm->def->hostdevs[i]) < 0)
746
            rc = -1;
747
    }
748
    for (i = 0 ; i < vm->def->ndisks ; i++) {
749 750
        if (SELinuxRestoreSecurityImageLabelInt(drv,
                                                vm,
751 752
                                                vm->def->disks[i],
                                                migrated) < 0)
753 754
            rc = -1;
    }
755

756 757 758 759 760 761
    if (virDomainChrDefForeach(vm->def,
                               false,
                               SELinuxRestoreSecurityChardevCallback,
                               vm) < 0)
        rc = -1;

762 763 764 765 766 767 768 769
    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;

770 771 772 773
    return rc;
}

static int
774 775
SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                            virDomainObjPtr vm)
776 777 778
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

779 780
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC ||
        secdef->label == NULL)
781 782
        return 0;

783 784 785 786 787 788 789 790 791 792
    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);

793
    return 0;
794 795
}

796 797

static int
798 799
SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                          virDomainObjPtr vm,
800 801 802 803
                          const char *savefile)
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

807
    return SELinuxSetFilecon(savefile, secdef->imagelabel);
808 809 810 811
}


static int
812 813
SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                              virDomainObjPtr vm,
814 815
                              const char *savefile)
{
816 817 818 819 820
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

821
    return SELinuxRestoreSecurityFileLabel(savefile);
822 823 824
}


825
static int
826
SELinuxSecurityVerify(virDomainDefPtr def)
827 828 829 830
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (security_check_context(secdef->label) != 0) {
831
            virSecurityReportError(VIR_ERR_XML_ERROR,
832 833 834 835 836 837 838
                                   _("Invalid security label %s"), secdef->label);
            return -1;
        }
    }
    return 0;
}

839
static int
840
SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv,
841
                               virDomainObjPtr vm)
842 843 844 845
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

849
    if (!STREQ(drv->name, secdef->model)) {
850
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
851 852 853 854
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, drv->name);
855
        if (security_getenforce() == 1)
856
            return -1;
857 858 859
    }

    if (setexeccon(secdef->label) == -1) {
860
        virReportSystemError(errno,
861 862
                             _("unable to set security context '%s'"),
                             secdef->label);
863
        if (security_getenforce() == 1)
864
            return -1;
865 866
    }

867 868 869
    return 0;
}

870 871 872 873 874 875 876 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
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;
}

971 972 973 974 975 976 977 978 979 980 981 982

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

    return SELinuxSetSecurityChardevLabel(vm, dev);
}


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

1012 1013 1014 1015 1016 1017
    if (virDomainChrDefForeach(vm->def,
                               true,
                               SELinuxSetSecurityChardevCallback,
                               vm) < 0)
        return -1;

1018 1019 1020 1021 1022 1023 1024 1025
    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;

1026 1027 1028 1029 1030 1031
    if (stdin_path) {
        if (SELinuxSetFilecon(stdin_path, default_content_context) < 0 &&
            virStorageFileIsSharedFSType(stdin_path,
                                         VIR_STORAGE_FILE_SHFS_NFS) != 1)
            return -1;
    }
1032

1033 1034 1035 1036 1037 1038 1039
    return 0;
}

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