security_selinux.c 30.8 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 164
SELinuxGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                        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 229
SELinuxReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                            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
SELinuxSecurityDriverOpen(virSecurityDriverPtr drv,
                          bool allowDiskFormatProbing)
275 276 277 278 279
{
    /*
     * Where will the DOI come from?  SELinux configuration, or qemu
     * configuration? For the moment, we'll just set it to "0".
     */
280
    virSecurityDriverSetDOI(drv, SECURITY_SELINUX_VOID_DOI);
281
    virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing);
282
    return SELinuxInitialize();
283 284 285
}

static int
286 287
SELinuxGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
288
                               virSecurityLabelPtr sec)
289 290 291 292
{
    security_context_t ctx;

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

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

    strcpy(sec->label, (char *) ctx);
309
    freecon(ctx);
310 311 312

    sec->enforcing = security_getenforce();
    if (sec->enforcing == -1) {
313
        virReportSystemError(errno, "%s",
314
                             _("error calling security_getenforce()"));
315 316 317 318 319 320 321
        return -1;
    }

    return 0;
}

static int
322
SELinuxSetFilecon(const char *path, char *tcon)
323
{
324
    security_context_t econ;
325

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

328
    if (setfilecon(path, tcon) < 0) {
329 330
        int setfilecon_errno = errno;

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

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

E
Eric Blake 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/* 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
}

379 380 381

/* This method shouldn't raise errors, since they'll overwrite
 * errors that the caller(s) are already dealing with */
382
static int
383
SELinuxRestoreSecurityFileLabel(const char *path)
384
{
385 386 387 388
    struct stat buf;
    security_context_t fcon = NULL;
    int rc = -1;
    char *newpath = NULL;
389
    char ebuf[1024];
390

391 392
    VIR_INFO("Restoring SELinux context on '%s'", path);

393
    if (virFileResolveLink(path, &newpath) < 0) {
394 395
        VIR_WARN("cannot resolve symlink %s: %s", path,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
396
        goto err;
397
    }
398

399
    if (stat(newpath, &buf) != 0) {
400 401
        VIR_WARN("cannot stat %s: %s", newpath,
                 virStrerror(errno, ebuf, sizeof(ebuf)));
D
Daniel P. Berrange 已提交
402
        goto err;
403
    }
D
Daniel P. Berrange 已提交
404

E
Eric Blake 已提交
405
    if (getContext(newpath, buf.st_mode, &fcon) < 0) {
406
        VIR_WARN("cannot lookup default selinux label for %s", newpath);
407
    } else {
408
        rc = SELinuxSetFilecon(newpath, fcon);
409
    }
410

411
err:
412
    freecon(fcon);
413 414
    VIR_FREE(newpath);
    return rc;
415 416
}

417
static int
418 419
SELinuxRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                    virDomainObjPtr vm,
420 421
                                    virDomainDiskDefPtr disk,
                                    int migrated)
422
{
423 424 425 426 427
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

428 429 430 431 432 433 434 435 436 437 438 439 440 441
    /* 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;

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    /* 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;
        }
    }

458
    return SELinuxRestoreSecurityFileLabel(disk->src);
459 460
}

461 462

static int
463 464
SELinuxRestoreSecurityImageLabel(virSecurityDriverPtr drv,
                                 virDomainObjPtr vm,
465 466
                                 virDomainDiskDefPtr disk)
{
467
    return SELinuxRestoreSecurityImageLabelInt(drv, vm, disk, 0);
468 469 470
}


471 472 473 474 475 476 477
static int
SELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk,
                            const char *path,
                            size_t depth,
                            void *opaque)
{
    const virSecurityLabelDefPtr secdef = opaque;
478
    int ret;
479 480 481

    if (depth == 0) {
        if (disk->shared) {
482
            ret = SELinuxSetFilecon(path, default_image_context);
483
        } else if (disk->readonly) {
484
            ret = SELinuxSetFilecon(path, default_content_context);
485
        } else if (secdef->imagelabel) {
486
            ret = SELinuxSetFilecon(path, secdef->imagelabel);
487
        } else {
488
            ret = 0;
489 490
        }
    } else {
491
        ret = SELinuxSetFilecon(path, default_content_context);
492
    }
493 494 495 496 497
    if (ret < 0 &&
        virStorageFileIsSharedFSType(path,
                                     VIR_STORAGE_FILE_SHFS_NFS) == 1)
       ret = 0;
    return ret;
498 499
}

500
static int
501
SELinuxSetSecurityImageLabel(virSecurityDriverPtr drv,
502
                             virDomainObjPtr vm,
503
                             virDomainDiskDefPtr disk)
504 505 506

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

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

512
    return virDomainDiskDefForeachPath(disk,
513
                                       allowDiskFormatProbing,
514
                                       true,
515 516
                                       SELinuxSetSecurityFileLabel,
                                       secdef);
517 518
}

519 520

static int
521
SELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
522 523 524 525 526
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

527
    return SELinuxSetFilecon(file, secdef->imagelabel);
528 529 530
}

static int
531
SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
532 533 534 535 536
                           const char *file, void *opaque)
{
    virDomainObjPtr vm = opaque;
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

537
    return SELinuxSetFilecon(file, secdef->imagelabel);
538 539 540
}

static int
541 542
SELinuxSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
543 544 545
                               virDomainHostdevDefPtr dev)

{
546
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
547 548
    int ret = -1;

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

552 553 554 555 556
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

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

560 561
        if (!usb)
            goto done;
562

563
        ret = usbDeviceFileIterate(usb, SELinuxSetSecurityUSBLabel, vm);
564
        usbFreeDevice(usb);
M
Mark McLoughlin 已提交
565
        break;
566 567 568
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
569
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
570 571 572 573 574 575 576
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

577
        ret = pciDeviceFileIterate(pci, SELinuxSetSecurityPCILabel, vm);
578
        pciFreeDevice(pci);
579 580 581 582 583 584 585 586 587 588 589 590 591

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

592

593
static int
594
SELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
595 596 597
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
598
    return SELinuxRestoreSecurityFileLabel(file);
599 600 601
}

static int
602
SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
603 604 605
                               const char *file,
                               void *opaque ATTRIBUTE_UNUSED)
{
606
    return SELinuxRestoreSecurityFileLabel(file);
607 608 609
}

static int
610 611
SELinuxRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                   virDomainObjPtr vm,
612 613 614
                                   virDomainHostdevDefPtr dev)

{
615
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
616 617
    int ret = -1;

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

621 622 623 624 625
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
626
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
627
                                      dev->source.subsys.u.usb.device);
628 629 630 631

        if (!usb)
            goto done;

632
        ret = usbDeviceFileIterate(usb, SELinuxRestoreSecurityUSBLabel, NULL);
633
        usbFreeDevice(usb);
634 635 636 637 638

        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
639
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
640 641 642 643 644 645 646
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

647
        ret = pciDeviceFileIterate(pci, SELinuxRestoreSecurityPCILabel, NULL);
648
        pciFreeDevice(pci);
649 650 651 652 653 654 655 656 657 658 659 660 661

        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    return ret;
}

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

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:
719 720 721
        if (SELinuxRestoreSecurityFileLabel(dev->data.file.path) < 0)
            goto done;
        ret = 0;
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
        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);
}


758
static int
759 760
SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm,
761
                               int migrated ATTRIBUTE_UNUSED)
762 763 764 765
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int i;
    int rc = 0;
766 767 768

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

769 770 771 772
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
773 774 775
        if (SELinuxRestoreSecurityHostdevLabel(drv,
                                               vm,
                                               vm->def->hostdevs[i]) < 0)
776
            rc = -1;
777
    }
778
    for (i = 0 ; i < vm->def->ndisks ; i++) {
779 780
        if (SELinuxRestoreSecurityImageLabelInt(drv,
                                                vm,
781 782
                                                vm->def->disks[i],
                                                migrated) < 0)
783 784
            rc = -1;
    }
785

786 787 788 789 790 791
    if (virDomainChrDefForeach(vm->def,
                               false,
                               SELinuxRestoreSecurityChardevCallback,
                               vm) < 0)
        rc = -1;

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

800 801 802 803
    return rc;
}

static int
804 805
SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                            virDomainObjPtr vm)
806 807 808
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

809 810
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC ||
        secdef->label == NULL)
811 812
        return 0;

813 814 815 816 817 818 819 820 821 822
    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);

823
    return 0;
824 825
}

826 827

static int
828 829
SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                          virDomainObjPtr vm,
830 831 832 833
                          const char *savefile)
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

837
    return SELinuxSetFilecon(savefile, secdef->imagelabel);
838 839 840 841
}


static int
842 843
SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                              virDomainObjPtr vm,
844 845
                              const char *savefile)
{
846 847 848 849 850
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

851
    return SELinuxRestoreSecurityFileLabel(savefile);
852 853 854
}


855
static int
856
SELinuxSecurityVerify(virDomainDefPtr def)
857 858 859 860
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (security_check_context(secdef->label) != 0) {
861
            virSecurityReportError(VIR_ERR_XML_ERROR,
862 863 864 865 866 867 868
                                   _("Invalid security label %s"), secdef->label);
            return -1;
        }
    }
    return 0;
}

869
static int
870
SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv,
871
                               virDomainObjPtr vm)
872 873 874 875
{
    /* TODO: verify DOI */
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

879
    if (!STREQ(drv->name, secdef->model)) {
880
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
881 882 883 884
                               _("security label driver mismatch: "
                                 "'%s' model configured for domain, but "
                                 "hypervisor driver is '%s'."),
                               secdef->model, drv->name);
885
        if (security_getenforce() == 1)
886
            return -1;
887 888 889
    }

    if (setexeccon(secdef->label) == -1) {
890
        virReportSystemError(errno,
891 892
                             _("unable to set security context '%s'"),
                             secdef->label);
893
        if (security_getenforce() == 1)
894
            return -1;
895 896
    }

897 898 899
    return 0;
}

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 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
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;
}

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012

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

    return SELinuxSetSecurityChardevLabel(vm, dev);
}


1013
static int
1014 1015 1016
SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv,
                           virDomainObjPtr vm,
                           const char *stdin_path)
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
{
    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;
1030
        }
1031 1032
        if (SELinuxSetSecurityImageLabel(drv,
                                         vm, vm->def->disks[i]) < 0)
1033 1034 1035
            return -1;
    }
    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
1036 1037 1038
        if (SELinuxSetSecurityHostdevLabel(drv,
                                           vm,
                                           vm->def->hostdevs[i]) < 0)
1039
            return -1;
1040 1041
    }

1042 1043 1044 1045 1046 1047
    if (virDomainChrDefForeach(vm->def,
                               true,
                               SELinuxSetSecurityChardevCallback,
                               vm) < 0)
        return -1;

1048 1049 1050 1051 1052 1053 1054 1055
    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;

1056 1057 1058 1059 1060 1061
    if (stdin_path) {
        if (SELinuxSetFilecon(stdin_path, default_content_context) < 0 &&
            virStorageFileIsSharedFSType(stdin_path,
                                         VIR_STORAGE_FILE_SHFS_NFS) != 1)
            return -1;
    }
1062

1063 1064 1065 1066 1067 1068 1069
    return 0;
}

virSecurityDriver virSELinuxSecurityDriver = {
    .name                       = SECURITY_SELINUX_NAME,
    .probe                      = SELinuxSecurityDriverProbe,
    .open                       = SELinuxSecurityDriverOpen,
1070
    .domainSecurityVerify       = SELinuxSecurityVerify,
1071
    .domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel,
1072 1073
    .domainSetSecuritySocketLabel     = SELinuxSetSecuritySocketLabel,
    .domainClearSecuritySocketLabel     = SELinuxClearSecuritySocketLabel,
1074 1075
    .domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel,
    .domainGenSecurityLabel     = SELinuxGenSecurityLabel,
1076
    .domainReserveSecurityLabel     = SELinuxReserveSecurityLabel,
1077 1078 1079 1080 1081
    .domainReleaseSecurityLabel     = SELinuxReleaseSecurityLabel,
    .domainGetSecurityProcessLabel     = SELinuxGetSecurityProcessLabel,
    .domainSetSecurityProcessLabel     = SELinuxSetSecurityProcessLabel,
    .domainRestoreSecurityAllLabel = SELinuxRestoreSecurityAllLabel,
    .domainSetSecurityAllLabel     = SELinuxSetSecurityAllLabel,
1082 1083
    .domainSetSecurityHostdevLabel = SELinuxSetSecurityHostdevLabel,
    .domainRestoreSecurityHostdevLabel = SELinuxRestoreSecurityHostdevLabel,
1084 1085
    .domainSetSavedStateLabel = SELinuxSetSavedStateLabel,
    .domainRestoreSavedStateLabel = SELinuxRestoreSavedStateLabel,
1086
};