2009-03-03 10:06:49 +00:00
|
|
|
/*
|
2010-05-14 14:50:27 -06:00
|
|
|
* Copyright (C) 2008-2010 Red Hat, Inc.
|
2009-03-03 10:06:49 +00:00
|
|
|
*
|
|
|
|
* 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>
|
2009-04-03 10:55:51 +00:00
|
|
|
* Dan Walsh <dwalsh@redhat.com>
|
2009-03-03 10:06:49 +00:00
|
|
|
*
|
|
|
|
* 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>
|
|
|
|
|
2009-09-15 19:06:37 +01:00
|
|
|
#include "security_driver.h"
|
2009-03-03 10:06:49 +00:00
|
|
|
#include "security_selinux.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "memory.h"
|
2009-07-03 10:26:37 +00:00
|
|
|
#include "logging.h"
|
2009-08-14 14:23:11 +01:00
|
|
|
#include "pci.h"
|
|
|
|
#include "hostusb.h"
|
2009-09-25 14:20:13 +01:00
|
|
|
#include "storage_file.h"
|
2009-03-03 15:18:24 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_SECURITY
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
static char default_domain_context[1024];
|
2009-07-03 10:26:37 +00:00
|
|
|
static char default_content_context[1024];
|
2009-03-03 10:06:49 +00:00
|
|
|
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) {
|
2009-03-03 15:18:24 +00:00
|
|
|
if (STREQ(ptr->mcs, mcs))
|
2009-03-03 10:06:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-03-03 15:18:24 +00:00
|
|
|
if (VIR_ALLOC(ptr) < 0)
|
|
|
|
return -1;
|
2009-03-03 10:06:49 +00:00
|
|
|
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) {
|
2009-03-03 15:18:24 +00:00
|
|
|
if (STREQ(ptr->mcs, mcs)) {
|
2009-03-03 10:06:49 +00:00
|
|
|
if (prevptr)
|
|
|
|
prevptr->next = ptr->next;
|
|
|
|
else {
|
|
|
|
mcsList = ptr->next;
|
|
|
|
}
|
2009-12-10 00:00:50 +01:00
|
|
|
VIR_FREE(ptr->mcs);
|
|
|
|
VIR_FREE(ptr);
|
2009-03-03 10:06:49 +00:00
|
|
|
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
|
2010-02-04 21:02:58 +01:00
|
|
|
SELinuxInitialize(void)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
char *ptr = NULL;
|
|
|
|
int fd = 0;
|
|
|
|
|
|
|
|
fd = open(selinux_virtual_domain_context_path(), O_RDONLY);
|
|
|
|
if (fd < 0) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("cannot open SELinux virtual domain context file '%s'"),
|
|
|
|
selinux_virtual_domain_context_path());
|
2009-03-03 10:06:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (saferead(fd, default_domain_context, sizeof(default_domain_context)) < 0) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("cannot read SELinux virtual domain context file %s"),
|
|
|
|
selinux_virtual_domain_context_path());
|
2009-03-03 10:06:49 +00:00
|
|
|
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) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("cannot open SELinux virtual image context file %s"),
|
|
|
|
selinux_virtual_image_context_path());
|
2009-03-03 10:06:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (saferead(fd, default_image_context, sizeof(default_image_context)) < 0) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("cannot read SELinux virtual image context file %s"),
|
|
|
|
selinux_virtual_image_context_path());
|
2009-03-03 10:06:49 +00:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
ptr = strchrnul(default_image_context, '\n');
|
2009-07-03 10:26:37 +00:00
|
|
|
if (*ptr == '\n') {
|
|
|
|
*ptr = '\0';
|
|
|
|
strcpy(default_content_context, ptr+1);
|
|
|
|
ptr = strchrnul(default_content_context, '\n');
|
|
|
|
if (*ptr == '\n')
|
|
|
|
*ptr = '\0';
|
|
|
|
}
|
2009-03-03 10:06:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
char mcs[1024];
|
|
|
|
char *scontext = NULL;
|
|
|
|
int c1 = 0;
|
|
|
|
int c2 = 0;
|
2009-04-03 14:10:17 +00:00
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2009-04-03 14:10:17 +00:00
|
|
|
if (vm->def->seclabel.label ||
|
|
|
|
vm->def->seclabel.model ||
|
|
|
|
vm->def->seclabel.imagelabel) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-04-03 14:10:17 +00:00
|
|
|
"%s", _("security label already defined for VM"));
|
2009-03-03 10:06:49 +00:00
|
|
|
return rc;
|
2009-03-03 15:18:24 +00:00
|
|
|
}
|
2009-03-03 10:06:49 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
c1 = virRandom(1024);
|
|
|
|
c2 = virRandom(1024);
|
|
|
|
|
|
|
|
if ( c1 == c2 ) {
|
|
|
|
sprintf(mcs, "s0:c%d", c1);
|
|
|
|
} else {
|
2009-03-03 15:18:24 +00:00
|
|
|
if ( c1 < c2 )
|
2009-03-03 10:06:49 +00:00
|
|
|
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);
|
2009-03-03 15:18:24 +00:00
|
|
|
if (! vm->def->seclabel.label) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-03-03 15:18:24 +00:00
|
|
|
_("cannot generate selinux context for %s"), mcs);
|
|
|
|
goto err;
|
|
|
|
}
|
2009-03-03 10:06:49 +00:00
|
|
|
vm->def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
|
2009-03-03 15:18:24 +00:00
|
|
|
if (! vm->def->seclabel.imagelabel) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-03-03 15:18:24 +00:00
|
|
|
_("cannot generate selinux context for %s"), mcs);
|
|
|
|
goto err;
|
|
|
|
}
|
2009-03-03 10:06:49 +00:00
|
|
|
vm->def->seclabel.model = strdup(SECURITY_SELINUX_NAME);
|
2009-04-03 14:10:17 +00:00
|
|
|
if (!vm->def->seclabel.model) {
|
2010-02-04 19:19:08 +01:00
|
|
|
virReportOOMError();
|
2009-03-03 15:18:24 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
goto done;
|
|
|
|
err:
|
2009-03-03 15:18:24 +00:00
|
|
|
VIR_FREE(vm->def->seclabel.label);
|
|
|
|
VIR_FREE(vm->def->seclabel.imagelabel);
|
|
|
|
VIR_FREE(vm->def->seclabel.model);
|
2009-03-03 10:06:49 +00:00
|
|
|
done:
|
2009-03-03 15:18:24 +00:00
|
|
|
VIR_FREE(scontext);
|
2009-03-03 10:06:49 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-06-12 11:38:50 +00:00
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm)
|
2009-06-12 11:38:50 +00:00
|
|
|
{
|
|
|
|
security_context_t pctx;
|
|
|
|
context_t ctx = NULL;
|
|
|
|
const char *mcs;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2009-06-12 11:38:50 +00:00
|
|
|
if (getpidcon(vm->pid, &pctx) == -1) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("unable to get PID %d security context"), vm->pid);
|
2009-06-12 11:38:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
static int
|
|
|
|
SELinuxSecurityDriverProbe(void)
|
|
|
|
{
|
|
|
|
return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:58:58 +01:00
|
|
|
SELinuxSecurityDriverOpen(virSecurityDriverPtr drv,
|
|
|
|
bool allowDiskFormatProbing)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Where will the DOI come from? SELinux configuration, or qemu
|
|
|
|
* configuration? For the moment, we'll just set it to "0".
|
|
|
|
*/
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityDriverSetDOI(drv, SECURITY_SELINUX_VOID_DOI);
|
2010-06-15 17:58:58 +01:00
|
|
|
virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing);
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxInitialize();
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
2010-01-11 11:04:40 +00:00
|
|
|
virSecurityLabelPtr sec)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
security_context_t ctx;
|
|
|
|
|
|
|
|
if (getpidcon(vm->pid, &ctx) == -1) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("unable to get PID %d security context"),
|
|
|
|
vm->pid);
|
2009-03-03 10:06:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("security label exceeds "
|
2009-10-27 12:20:22 -04:00
|
|
|
"maximum length: %d"),
|
2009-03-03 10:06:49 +00:00
|
|
|
VIR_SECURITY_LABEL_BUFLEN - 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(sec->label, (char *) ctx);
|
2009-12-10 00:00:50 +01:00
|
|
|
VIR_FREE(ctx);
|
2009-03-03 10:06:49 +00:00
|
|
|
|
|
|
|
sec->enforcing = security_getenforce();
|
|
|
|
if (sec->enforcing == -1) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno, "%s",
|
2009-08-28 18:44:43 +01:00
|
|
|
_("error calling security_getenforce()"));
|
2009-03-03 10:06:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-04 21:02:58 +01:00
|
|
|
SELinuxSetFilecon(const char *path, char *tcon)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
2009-07-03 10:27:46 +00:00
|
|
|
security_context_t econ;
|
2009-03-03 10:06:49 +00:00
|
|
|
|
2009-07-03 10:26:37 +00:00
|
|
|
VIR_INFO("Setting SELinux context on '%s' to '%s'", path, tcon);
|
|
|
|
|
2009-07-03 10:27:46 +00:00
|
|
|
if (setfilecon(path, tcon) < 0) {
|
2009-08-21 16:57:29 +02:00
|
|
|
int setfilecon_errno = errno;
|
|
|
|
|
2009-07-03 10:27:46 +00:00
|
|
|
if (getfilecon(path, &econ) >= 0) {
|
|
|
|
if (STREQ(tcon, econ)) {
|
|
|
|
freecon(econ);
|
|
|
|
/* It's alright, there's nothing to change anyway. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
freecon(econ);
|
|
|
|
}
|
2009-08-21 16:57:29 +02:00
|
|
|
|
|
|
|
/* if the error complaint is related to an image hosted on
|
2009-08-14 14:23:11 +01:00
|
|
|
* an nfs mount, or a usbfs/sysfs filesystem not supporting
|
|
|
|
* labelling, then just ignore it & hope for the best.
|
2009-09-22 11:42:06 +02:00
|
|
|
* The user hopefully set one of the necessary SELinux
|
2009-08-14 14:23:11 +01:00
|
|
|
* virt_use_{nfs,usb,pci} boolean tunables to allow it...
|
2009-08-21 16:57:29 +02:00
|
|
|
*/
|
|
|
|
if (setfilecon_errno != EOPNOTSUPP) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(setfilecon_errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("unable to set security context '%s' on '%s'"),
|
|
|
|
tcon, path);
|
2009-08-21 16:57:29 +02:00
|
|
|
if (security_getenforce() == 1)
|
|
|
|
return -1;
|
2009-08-28 18:44:43 +01:00
|
|
|
} else {
|
|
|
|
VIR_INFO("Setting security context '%s' on '%s' not supported",
|
|
|
|
tcon, path);
|
2009-08-21 16:57:29 +02:00
|
|
|
}
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-28 12:19:13 +01:00
|
|
|
|
|
|
|
/* This method shouldn't raise errors, since they'll overwrite
|
|
|
|
* errors that the caller(s) are already dealing with */
|
2009-03-03 10:06:49 +00:00
|
|
|
static int
|
2010-02-04 21:02:58 +01:00
|
|
|
SELinuxRestoreSecurityFileLabel(const char *path)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
2009-03-17 11:35:40 +00:00
|
|
|
struct stat buf;
|
|
|
|
security_context_t fcon = NULL;
|
|
|
|
int rc = -1;
|
|
|
|
char *newpath = NULL;
|
2010-05-28 12:19:13 +01:00
|
|
|
char ebuf[1024];
|
2009-07-15 12:45:13 +01:00
|
|
|
|
2009-08-28 18:44:43 +01:00
|
|
|
VIR_INFO("Restoring SELinux context on '%s'", path);
|
|
|
|
|
2010-05-14 14:50:27 -06:00
|
|
|
if (virFileResolveLink(path, &newpath) < 0) {
|
2010-05-28 12:19:13 +01:00
|
|
|
VIR_WARN("cannot resolve symlink %s: %s", path,
|
|
|
|
virStrerror(errno, ebuf, sizeof(ebuf)));
|
2009-04-01 10:26:22 +00:00
|
|
|
goto err;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
2009-03-17 11:35:40 +00:00
|
|
|
|
2010-04-30 13:35:15 +02:00
|
|
|
if (stat(newpath, &buf) != 0) {
|
2010-05-28 12:19:13 +01:00
|
|
|
VIR_WARN("cannot stat %s: %s", newpath,
|
|
|
|
virStrerror(errno, ebuf, sizeof(ebuf)));
|
2009-04-01 10:26:22 +00:00
|
|
|
goto err;
|
2010-04-30 13:35:15 +02:00
|
|
|
}
|
2009-04-01 10:26:22 +00:00
|
|
|
|
|
|
|
if (matchpathcon(newpath, buf.st_mode, &fcon) == 0) {
|
2010-02-04 21:02:58 +01:00
|
|
|
rc = SELinuxSetFilecon(newpath, fcon);
|
2010-04-30 13:35:15 +02:00
|
|
|
} else {
|
2010-05-28 12:19:13 +01:00
|
|
|
VIR_WARN("cannot lookup default selinux label for %s",
|
|
|
|
newpath);
|
2009-03-17 11:35:40 +00:00
|
|
|
}
|
2010-04-30 13:35:15 +02:00
|
|
|
|
2009-03-17 11:35:40 +00:00
|
|
|
err:
|
|
|
|
VIR_FREE(fcon);
|
|
|
|
VIR_FREE(newpath);
|
|
|
|
return rc;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
virDomainDiskDefPtr disk,
|
|
|
|
int migrated)
|
2009-08-14 14:23:11 +01:00
|
|
|
{
|
2010-01-13 14:03:04 +00:00
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
/* 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;
|
|
|
|
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxRestoreSecurityFileLabel(disk->src);
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxRestoreSecurityImageLabel(virSecurityDriverPtr drv,
|
|
|
|
virDomainObjPtr vm,
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
virDomainDiskDefPtr disk)
|
|
|
|
{
|
2010-06-15 17:44:19 +01:00
|
|
|
return SELinuxRestoreSecurityImageLabelInt(drv, vm, disk, 0);
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-15 16:40:47 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
static int
|
2010-06-15 17:58:58 +01:00
|
|
|
SELinuxSetSecurityImageLabel(virSecurityDriverPtr drv,
|
2010-06-15 17:44:19 +01:00
|
|
|
virDomainObjPtr vm,
|
2009-03-17 11:35:40 +00:00
|
|
|
virDomainDiskDefPtr disk)
|
2009-03-03 10:06:49 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
2010-06-15 17:58:58 +01:00
|
|
|
bool allowDiskFormatProbing = virSecurityDriverGetAllowDiskFormatProbing(drv);
|
2009-03-03 10:06:49 +00:00
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2010-06-15 16:40:47 +01:00
|
|
|
return virDomainDiskDefForeachPath(disk,
|
2010-06-15 17:58:58 +01:00
|
|
|
allowDiskFormatProbing,
|
2010-06-15 16:40:47 +01:00
|
|
|
false,
|
|
|
|
SELinuxSetSecurityFileLabel,
|
|
|
|
secdef);
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
|
|
|
|
static int
|
2010-02-10 09:55:39 +00:00
|
|
|
SELinuxSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
|
2009-08-14 14:23:11 +01:00
|
|
|
const char *file, void *opaque)
|
|
|
|
{
|
|
|
|
virDomainObjPtr vm = opaque;
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxSetFilecon(file, secdef->imagelabel);
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-10 09:55:39 +00:00
|
|
|
SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
|
2009-08-14 14:23:11 +01:00
|
|
|
const char *file, void *opaque)
|
|
|
|
{
|
|
|
|
virDomainObjPtr vm = opaque;
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxSetFilecon(file, secdef->imagelabel);
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
2009-08-14 14:23:11 +01:00
|
|
|
virDomainHostdevDefPtr dev)
|
|
|
|
|
|
|
|
{
|
2010-01-13 14:03:04 +00:00
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
2009-08-14 14:23:11 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (dev->source.subsys.type) {
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
2010-02-05 00:25:16 +01:00
|
|
|
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
|
2010-03-04 11:48:16 +00:00
|
|
|
dev->source.subsys.u.usb.device);
|
2009-08-14 14:23:11 +01:00
|
|
|
|
2010-01-11 11:40:46 -05:00
|
|
|
if (!usb)
|
|
|
|
goto done;
|
2009-08-14 14:23:11 +01:00
|
|
|
|
2010-02-10 09:55:39 +00:00
|
|
|
ret = usbDeviceFileIterate(usb, SELinuxSetSecurityUSBLabel, vm);
|
2010-02-05 00:25:16 +01:00
|
|
|
usbFreeDevice(usb);
|
2009-09-30 18:37:03 +01:00
|
|
|
break;
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
2010-02-05 00:16:34 +01:00
|
|
|
pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
|
2009-08-14 14:23:11 +01:00
|
|
|
dev->source.subsys.u.pci.bus,
|
|
|
|
dev->source.subsys.u.pci.slot,
|
|
|
|
dev->source.subsys.u.pci.function);
|
|
|
|
|
|
|
|
if (!pci)
|
|
|
|
goto done;
|
|
|
|
|
2010-02-10 09:55:39 +00:00
|
|
|
ret = pciDeviceFileIterate(pci, SELinuxSetSecurityPCILabel, vm);
|
2010-02-05 00:16:34 +01:00
|
|
|
pciFreeDevice(pci);
|
2009-08-14 14:23:11 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-11 12:07:00 +00:00
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
static int
|
2010-02-10 09:55:39 +00:00
|
|
|
SELinuxRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
|
2009-08-14 14:23:11 +01:00
|
|
|
const char *file,
|
|
|
|
void *opaque ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxRestoreSecurityFileLabel(file);
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-02-10 09:55:39 +00:00
|
|
|
SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
|
2009-08-14 14:23:11 +01:00
|
|
|
const char *file,
|
|
|
|
void *opaque ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxRestoreSecurityFileLabel(file);
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
2009-08-14 14:23:11 +01:00
|
|
|
virDomainHostdevDefPtr dev)
|
|
|
|
|
|
|
|
{
|
2010-01-13 14:03:04 +00:00
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
2009-08-14 14:23:11 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-14 14:23:11 +01:00
|
|
|
if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (dev->source.subsys.type) {
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
2010-02-05 00:25:16 +01:00
|
|
|
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
|
2010-03-04 11:48:16 +00:00
|
|
|
dev->source.subsys.u.usb.device);
|
2009-08-14 14:23:11 +01:00
|
|
|
|
|
|
|
if (!usb)
|
|
|
|
goto done;
|
|
|
|
|
2010-02-10 09:55:39 +00:00
|
|
|
ret = usbDeviceFileIterate(usb, SELinuxRestoreSecurityUSBLabel, NULL);
|
2010-02-05 00:25:16 +01:00
|
|
|
usbFreeDevice(usb);
|
2009-08-14 14:23:11 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
2010-02-05 00:16:34 +01:00
|
|
|
pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
|
2009-08-14 14:23:11 +01:00
|
|
|
dev->source.subsys.u.pci.bus,
|
|
|
|
dev->source.subsys.u.pci.slot,
|
|
|
|
dev->source.subsys.u.pci.function);
|
|
|
|
|
|
|
|
if (!pci)
|
|
|
|
goto done;
|
|
|
|
|
2010-02-10 09:55:39 +00:00
|
|
|
ret = pciDeviceFileIterate(pci, SELinuxRestoreSecurityPCILabel, NULL);
|
2010-02-05 00:16:34 +01:00
|
|
|
pciFreeDevice(pci);
|
2009-08-14 14:23:11 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-23 16:17:15 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
int migrated ATTRIBUTE_UNUSED)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
int i;
|
|
|
|
int rc = 0;
|
2009-08-28 18:44:43 +01:00
|
|
|
|
|
|
|
VIR_DEBUG("Restoring security label on %s", vm->def->name);
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0 ; i < vm->def->nhostdevs ; i++) {
|
2010-06-15 17:44:19 +01:00
|
|
|
if (SELinuxRestoreSecurityHostdevLabel(drv,
|
|
|
|
vm,
|
|
|
|
vm->def->hostdevs[i]) < 0)
|
2010-01-13 14:03:04 +00:00
|
|
|
rc = -1;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
2010-01-13 14:03:04 +00:00
|
|
|
for (i = 0 ; i < vm->def->ndisks ; i++) {
|
2010-06-15 17:44:19 +01:00
|
|
|
if (SELinuxRestoreSecurityImageLabelInt(drv,
|
|
|
|
vm,
|
Don't reset user/group/security label on shared filesystems during migrate
When QEMU runs with its disk on NFS, and as a non-root user, the
disk is chownd to that non-root user. When migration completes
the last step is shutting down the QEMU on the source host. THis
normally resets user/group/security label. This is bad when the
VM was just migrated because the file is still in use on the dest
host. It is thus neccessary to skip the reset step for any files
found to be on a shared filesystem
* src/libvirt_private.syms: Export virStorageFileIsSharedFS
* src/util/storage_file.c, src/util/storage_file.h: Add a new
method virStorageFileIsSharedFS() to determine if a file is
on a shared filesystem (NFS, GFS, OCFS2, etc)
* src/qemu/qemu_driver.c: Tell security driver not to reset
disk labels on migration completion
* src/qemu/qemu_security_dac.c, src/qemu/qemu_security_stacked.c,
src/security/security_selinux.c, src/security/security_driver.h,
src/security/security_apparmor.c: Add ability to skip disk
restore step for files on shared filesystems.
2010-05-13 11:49:22 -04:00
|
|
|
vm->def->disks[i],
|
|
|
|
migrated) < 0)
|
2010-01-13 14:03:04 +00:00
|
|
|
rc = -1;
|
|
|
|
}
|
2010-01-11 11:04:40 +00:00
|
|
|
|
2010-06-23 16:17:15 +01:00
|
|
|
if (virDomainChrDefForeach(vm->def,
|
|
|
|
false,
|
|
|
|
SELinuxRestoreSecurityChardevCallback,
|
|
|
|
vm) < 0)
|
|
|
|
rc = -1;
|
|
|
|
|
2010-03-12 13:38:39 -05:00
|
|
|
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;
|
|
|
|
|
2010-01-11 11:04:40 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm)
|
2010-01-11 11:04:40 +00:00
|
|
|
{
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
2010-03-22 10:45:36 -04:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC ||
|
|
|
|
secdef->label == NULL)
|
2010-01-11 11:04:40 +00:00
|
|
|
return 0;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
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);
|
|
|
|
|
2010-01-11 11:04:40 +00:00
|
|
|
return 0;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
2009-11-11 12:07:00 +00:00
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
2009-11-11 12:07:00 +00:00
|
|
|
const char *savefile)
|
|
|
|
{
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxSetFilecon(savefile, secdef->imagelabel);
|
2009-11-11 12:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm,
|
2009-11-11 12:07:00 +00:00
|
|
|
const char *savefile)
|
|
|
|
{
|
2010-01-13 14:03:04 +00:00
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
|
|
|
|
return 0;
|
|
|
|
|
2010-02-04 21:02:58 +01:00
|
|
|
return SELinuxRestoreSecurityFileLabel(savefile);
|
2009-11-11 12:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-03 10:55:51 +00:00
|
|
|
static int
|
2010-02-09 19:18:21 +00:00
|
|
|
SELinuxSecurityVerify(virDomainDefPtr def)
|
2009-04-03 10:55:51 +00:00
|
|
|
{
|
|
|
|
const virSecurityLabelDefPtr secdef = &def->seclabel;
|
|
|
|
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
|
|
|
|
if (security_check_context(secdef->label) != 0) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_XML_ERROR,
|
2009-04-03 10:55:51 +00:00
|
|
|
_("Invalid security label %s"), secdef->label);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
static int
|
2010-02-09 19:18:21 +00:00
|
|
|
SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv,
|
2010-01-11 11:04:40 +00:00
|
|
|
virDomainObjPtr vm)
|
2009-03-03 10:06:49 +00:00
|
|
|
{
|
|
|
|
/* TODO: verify DOI */
|
|
|
|
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
|
|
|
|
2010-01-13 14:03:04 +00:00
|
|
|
if (vm->def->seclabel.label == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
if (!STREQ(drv->name, secdef->model)) {
|
2010-02-09 19:18:21 +00:00
|
|
|
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("security label driver mismatch: "
|
|
|
|
"'%s' model configured for domain, but "
|
|
|
|
"hypervisor driver is '%s'."),
|
|
|
|
secdef->model, drv->name);
|
2009-03-03 10:06:49 +00:00
|
|
|
if (security_getenforce() == 1)
|
2009-08-28 18:44:43 +01:00
|
|
|
return -1;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (setexeccon(secdef->label) == -1) {
|
2010-02-04 21:02:58 +01:00
|
|
|
virReportSystemError(errno,
|
2009-08-28 18:44:43 +01:00
|
|
|
_("unable to set security context '%s'"),
|
|
|
|
secdef->label);
|
2009-03-03 10:06:49 +00:00
|
|
|
if (security_getenforce() == 1)
|
2009-08-28 18:44:43 +01:00
|
|
|
return -1;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
2010-01-11 11:04:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-27 16:44:47 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-06-23 16:17:15 +01:00
|
|
|
|
|
|
|
static int
|
|
|
|
SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
|
|
virDomainChrDefPtr dev,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
virDomainObjPtr vm = opaque;
|
|
|
|
|
|
|
|
return SELinuxSetSecurityChardevLabel(vm, dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-11 11:04:40 +00:00
|
|
|
static int
|
2010-06-15 17:44:19 +01:00
|
|
|
SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv,
|
|
|
|
virDomainObjPtr vm,
|
|
|
|
const char *stdin_path)
|
2010-01-11 11:04:40 +00:00
|
|
|
{
|
|
|
|
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;
|
2009-08-14 14:23:11 +01:00
|
|
|
}
|
2010-06-15 17:44:19 +01:00
|
|
|
if (SELinuxSetSecurityImageLabel(drv,
|
|
|
|
vm, vm->def->disks[i]) < 0)
|
2010-01-11 11:04:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (i = 0 ; i < vm->def->nhostdevs ; i++) {
|
2010-06-15 17:44:19 +01:00
|
|
|
if (SELinuxSetSecurityHostdevLabel(drv,
|
|
|
|
vm,
|
|
|
|
vm->def->hostdevs[i]) < 0)
|
2010-01-11 11:04:40 +00:00
|
|
|
return -1;
|
2009-03-03 10:06:49 +00:00
|
|
|
}
|
|
|
|
|
2010-06-23 16:17:15 +01:00
|
|
|
if (virDomainChrDefForeach(vm->def,
|
|
|
|
true,
|
|
|
|
SELinuxSetSecurityChardevCallback,
|
|
|
|
vm) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2010-03-12 13:38:39 -05:00
|
|
|
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;
|
|
|
|
|
2010-06-24 17:58:59 -04:00
|
|
|
if (stdin_path &&
|
|
|
|
SELinuxSetFilecon(stdin_path, default_content_context) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2009-03-03 10:06:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
virSecurityDriver virSELinuxSecurityDriver = {
|
|
|
|
.name = SECURITY_SELINUX_NAME,
|
|
|
|
.probe = SELinuxSecurityDriverProbe,
|
|
|
|
.open = SELinuxSecurityDriverOpen,
|
2009-04-03 10:55:51 +00:00
|
|
|
.domainSecurityVerify = SELinuxSecurityVerify,
|
2009-03-03 10:06:49 +00:00
|
|
|
.domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel,
|
2010-05-27 16:44:47 +01:00
|
|
|
.domainSetSecuritySocketLabel = SELinuxSetSecuritySocketLabel,
|
|
|
|
.domainClearSecuritySocketLabel = SELinuxClearSecuritySocketLabel,
|
2009-03-03 10:06:49 +00:00
|
|
|
.domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel,
|
|
|
|
.domainGenSecurityLabel = SELinuxGenSecurityLabel,
|
2009-06-12 11:38:50 +00:00
|
|
|
.domainReserveSecurityLabel = SELinuxReserveSecurityLabel,
|
2010-01-11 11:04:40 +00:00
|
|
|
.domainReleaseSecurityLabel = SELinuxReleaseSecurityLabel,
|
|
|
|
.domainGetSecurityProcessLabel = SELinuxGetSecurityProcessLabel,
|
|
|
|
.domainSetSecurityProcessLabel = SELinuxSetSecurityProcessLabel,
|
|
|
|
.domainRestoreSecurityAllLabel = SELinuxRestoreSecurityAllLabel,
|
|
|
|
.domainSetSecurityAllLabel = SELinuxSetSecurityAllLabel,
|
2009-08-14 14:23:11 +01:00
|
|
|
.domainSetSecurityHostdevLabel = SELinuxSetSecurityHostdevLabel,
|
|
|
|
.domainRestoreSecurityHostdevLabel = SELinuxRestoreSecurityHostdevLabel,
|
2009-11-11 12:07:00 +00:00
|
|
|
.domainSetSavedStateLabel = SELinuxSetSavedStateLabel,
|
|
|
|
.domainRestoreSavedStateLabel = SELinuxRestoreSavedStateLabel,
|
2009-03-03 10:06:49 +00:00
|
|
|
};
|