SELinux security driver for sVirt support (James Morris, Dan Walsh & Daniel Berrange)

This commit is contained in:
Daniel P. Berrange 2009-03-03 10:06:49 +00:00
parent aa2c97263d
commit 41ed6eb327
7 changed files with 461 additions and 0 deletions

View File

@ -1,3 +1,13 @@
Tue Mar 3 10:01:13 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
SELinux security driver for sVirt support (James Morris, Dan Walsh & Daniel
Berrange)
* configure.in: Check for selinux_virtual_domain_context_path() and
selinux_virtual_image_context_path() methods in libselinux.so
* po/POTFILES.in: add src/security_selinux.c
* src/Makefile.am, src/security.c, src/security_selinux.c,
src/security_selinux.h: Add SELinux impl of security driver API
Tue Mar 3 09:55:13 GMT 2009 Daniel P. Berrange <berrange@redhat.com> Tue Mar 3 09:55:13 GMT 2009 Daniel P. Berrange <berrange@redhat.com>
virsh additions for sVirt support (James Morris & Dan Walsh) virsh additions for sVirt support (James Morris & Dan Walsh)

View File

@ -640,6 +640,45 @@ AM_CONDITIONAL([HAVE_SELINUX], [test "$with_selinux" != "no"])
AC_SUBST([SELINUX_CFLAGS]) AC_SUBST([SELINUX_CFLAGS])
AC_SUBST([SELINUX_LIBS]) AC_SUBST([SELINUX_LIBS])
AC_ARG_WITH([secdriver-selinux],
[ --with-secdriver-selinux use SELinux security driver],
[],
[with_secdriver_selinux=check])
if test "$with_selinux" != "yes" ; then
if test "$with_secdriver_selinux" = "check" ; then
with_secdriver_selinux=no
else
AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])
fi
else
old_cflags="$CFLAGS"
old_libs="$LIBS"
CFLAGS="$CFLAGS $SELINUX_CFLAGS"
LIBS="$CFLAGS $SELINUX_LIBS"
fail=0
AC_CHECK_FUNC([selinux_virtual_domain_context_path], [], [fail=1])
AC_CHECK_FUNC([selinux_virtual_image_context_path], [], [fail=1])
CFLAGS="$old_cflags"
LIBS="$old_libs"
if test "$fail" = "1" ; then
if test "$with_secdriver_selinux" = "check" ; then
with_secdriver_selinux=no
else
AC_MSG_ERROR([You must install the SELinux development package in order to compile libvirt])
fi
else
with_secdriver_selinux=yes
AC_DEFINE_UNQUOTED([WITH_SECDRIVER_SELINUX], 1, [whether SELinux security driver is available])
fi
fi
AM_CONDITIONAL([WITH_SECDRIVER_SELINUX], [test "$with_secdriver_selinux" != "no"])
dnl NUMA lib dnl NUMA lib
AC_ARG_WITH([numactl], AC_ARG_WITH([numactl],
[ --with-numactl use numactl for host topology info], [ --with-numactl use numactl for host topology info],
@ -1320,6 +1359,10 @@ AC_MSG_NOTICE([ LVM: $with_storage_lvm])
AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi]) AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi])
AC_MSG_NOTICE([ Disk: $with_storage_disk]) AC_MSG_NOTICE([ Disk: $with_storage_disk])
AC_MSG_NOTICE([]) AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Driver Loadable Modules]) AC_MSG_NOTICE([Driver Loadable Modules])
AC_MSG_NOTICE([]) AC_MSG_NOTICE([])
if test "$with_driver_modules" != "no" ; then if test "$with_driver_modules" != "no" ; then

View File

@ -24,6 +24,7 @@ src/qemu_conf.c
src/qemu_driver.c src/qemu_driver.c
src/remote_internal.c src/remote_internal.c
src/security.c src/security.c
src/security_selinux.c
src/storage_backend.c src/storage_backend.c
src/storage_backend_disk.c src/storage_backend_disk.c
src/storage_backend_fs.c src/storage_backend_fs.c

View File

@ -170,6 +170,9 @@ STORAGE_HELPER_DISK_SOURCES = \
SECURITY_DRIVER_SOURCES = \ SECURITY_DRIVER_SOURCES = \
security.h security.c security.h security.c
SECURITY_DRIVER_SELINUX_SOURCES = \
security_selinux.h security_selinux.c
NODE_DEVICE_DRIVER_SOURCES = \ NODE_DEVICE_DRIVER_SOURCES = \
node_device.c node_device.h node_device.c node_device.h
@ -387,6 +390,9 @@ endif
libvirt_driver_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES) libvirt_driver_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES)
noinst_LTLIBRARIES += libvirt_driver_security.la noinst_LTLIBRARIES += libvirt_driver_security.la
libvirt_la_LIBADD += libvirt_driver_security.la libvirt_la_LIBADD += libvirt_driver_security.la
if WITH_SECDRIVER_SELINUX
libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
endif
# Add all conditional sources just in case... # Add all conditional sources just in case...
EXTRA_DIST += \ EXTRA_DIST += \

View File

@ -16,8 +16,14 @@
#include "virterror_internal.h" #include "virterror_internal.h"
#include "security.h" #include "security.h"
#ifdef WITH_SECDRIVER_SELINUX
#include "security_selinux.h"
#endif
static virSecurityDriverPtr security_drivers[] = { static virSecurityDriverPtr security_drivers[] = {
#ifdef WITH_SECDRIVER_SELINUX
&virSELinuxSecurityDriver,
#endif
NULL NULL
}; };

377
src/security_selinux.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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>
*
* 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>
#include "security.h"
#include "security_selinux.h"
#include "virterror_internal.h"
#include "util.h"
#include "memory.h"
static char default_domain_context[1024];
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) {
if (STREQ(ptr->mcs, mcs) == 0)
return -1;
}
ptr = malloc(sizeof(struct MCS));
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) {
if (STREQ(ptr->mcs, mcs) == 0) {
if (prevptr)
prevptr->next = ptr->next;
else {
mcsList = ptr->next;
}
free(ptr->mcs);
free(ptr);
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
SELinuxInitialize(virConnectPtr conn)
{
char *ptr = NULL;
int fd = 0;
char ebuf[1024];
virRandomInitialize(time(NULL) ^ getpid());
fd = open(selinux_virtual_domain_context_path(), O_RDONLY);
if (fd < 0) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: cannot open SELinux virtual domain context file %s: %s"),
__func__,selinux_virtual_domain_context_path(),
virStrerror(errno, ebuf, sizeof ebuf));
return -1;
}
if (saferead(fd, default_domain_context, sizeof(default_domain_context)) < 0) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: cannot read SELinux virtual domain context file %s: %s"),
__func__,selinux_virtual_domain_context_path(),
virStrerror(errno, ebuf, sizeof ebuf));
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) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: cannot open SELinux virtual image context file %s: %s"),
__func__,selinux_virtual_image_context_path(),
virStrerror(errno, ebuf, sizeof ebuf));
return -1;
}
if (saferead(fd, default_image_context, sizeof(default_image_context)) < 0) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: cannot read SELinux virtual image context file %s: %s"),
__func__,selinux_virtual_image_context_path(),
virStrerror(errno, ebuf, sizeof ebuf));
close(fd);
return -1;
}
close(fd);
ptr = strchrnul(default_image_context, '\n');
*ptr = '\0';
return 0;
}
static int
SELinuxGenSecurityLabel(virDomainObjPtr vm)
{
int rc = -1;
char mcs[1024];
char *scontext = NULL;
int c1 = 0;
int c2 = 0;
if ( ( vm->def->seclabel.label ) ||
( vm->def->seclabel.model ) ||
( vm->def->seclabel.imagelabel ))
return rc;
do {
c1 = virRandom(1024);
c2 = virRandom(1024);
if ( c1 == c2 ) {
sprintf(mcs, "s0:c%d", c1);
} else {
if ( c1 == c2 )
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);
if (! vm->def->seclabel.label) goto err;
vm->def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
if (! vm->def->seclabel.imagelabel) goto err;
vm->def->seclabel.model = strdup(SECURITY_SELINUX_NAME);
if (! vm->def->seclabel.model) goto err;
rc = 0;
goto done;
err:
free(vm->def->seclabel.label); vm->def->seclabel.label = NULL;
free(vm->def->seclabel.imagelabel); vm->def->seclabel.imagelabel = NULL;
free(vm->def->seclabel.model); vm->def->seclabel.model = NULL;
done:
free(scontext);
return rc;
}
static int
SELinuxSecurityDriverProbe(void)
{
return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE;
}
static int
SELinuxSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
{
/*
* Where will the DOI come from? SELinux configuration, or qemu
* configuration? For the moment, we'll just set it to "0".
*/
virSecurityDriverSetDOI(conn, drv, SECURITY_SELINUX_VOID_DOI);
return SELinuxInitialize(conn);
}
static int
SELinuxGetSecurityLabel(virConnectPtr conn,
virDomainObjPtr vm,
virSecurityLabelPtr sec)
{
security_context_t ctx;
if (getpidcon(vm->pid, &ctx) == -1) {
char ebuf[1024];
virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
"getpidcon(): %s"), __func__,
virStrerror(errno, ebuf, sizeof ebuf));
return -1;
}
if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: security label exceeds "
"maximum lenth: %d"), __func__,
VIR_SECURITY_LABEL_BUFLEN - 1);
return -1;
}
strcpy(sec->label, (char *) ctx);
free(ctx);
sec->enforcing = security_getenforce();
if (sec->enforcing == -1) {
char ebuf[1024];
virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
"security_getenforce(): %s"), __func__,
virStrerror(errno, ebuf, sizeof ebuf));
return -1;
}
return 0;
}
static int
SELinuxSetFilecon(virConnectPtr conn, char *path, char *tcon)
{
char ebuf[1024];
if(setfilecon(path, tcon) < 0) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: unable to set security context "
"'\%s\' on %s: %s."), __func__,
tcon,
path,
virStrerror(errno, ebuf, sizeof ebuf));
if (security_getenforce() == 1)
return -1;
}
return 0;
}
static int
SELinuxRestoreSecurityImageLabel(virConnectPtr conn,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
{
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
if (secdef->imagelabel) {
return SELinuxSetFilecon(conn, dev->data.disk->src, default_image_context);
}
return 0;
}
static int
SELinuxSetSecurityImageLabel(virConnectPtr conn,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev)
{
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
if (secdef->imagelabel) {
return SELinuxSetFilecon(conn, dev->data.disk->src, secdef->imagelabel);
}
return 0;
}
static int
SELinuxRestoreSecurityLabel(virConnectPtr conn,
virDomainObjPtr vm)
{
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
int i;
int rc = 0;
if (secdef->imagelabel) {
for (i = 0 ; i < vm->def->ndisks ; i++) {
if (SELinuxSetFilecon(conn, vm->def->disks[i]->src, default_image_context) < 0)
rc = -1;
}
VIR_FREE(secdef->model);
VIR_FREE(secdef->label);
context_t con = context_new(secdef->imagelabel);
if (con) {
mcsRemove(context_range_get(con));
context_free(con);
}
VIR_FREE(secdef->imagelabel);
}
return rc;
}
static int
SELinuxSetSecurityLabel(virConnectPtr conn,
virSecurityDriverPtr drv,
virDomainObjPtr vm)
{
/* TODO: verify DOI */
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
int i;
char ebuf[1024];
if (!STREQ(drv->name, secdef->model)) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: security label driver mismatch: "
"\'%s\' model configured for domain, but "
"hypervisor driver is \'%s\'."),
__func__, secdef->model, drv->name);
if (security_getenforce() == 1)
return -1;
}
if (setexeccon(secdef->label) == -1) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: unable to set security context "
"'\%s\': %s."), __func__, secdef->label,
virStrerror(errno, ebuf, sizeof ebuf));
if (security_getenforce() == 1)
return -1;
}
if (secdef->imagelabel) {
for (i = 0 ; i < vm->def->ndisks ; i++) {
if(setfilecon(vm->def->disks[i]->src, secdef->imagelabel) < 0) {
virSecurityReportError(conn, VIR_ERR_ERROR,
_("%s: unable to set security context "
"'\%s\' on %s: %s."), __func__,
secdef->imagelabel,
vm->def->disks[i]->src,
virStrerror(errno, ebuf, sizeof ebuf));
if (security_getenforce() == 1)
return -1;
}
}
}
return 0;
}
virSecurityDriver virSELinuxSecurityDriver = {
.name = SECURITY_SELINUX_NAME,
.probe = SELinuxSecurityDriverProbe,
.open = SELinuxSecurityDriverOpen,
.domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel,
.domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel,
.domainGenSecurityLabel = SELinuxGenSecurityLabel,
.domainGetSecurityLabel = SELinuxGetSecurityLabel,
.domainRestoreSecurityLabel = SELinuxRestoreSecurityLabel,
.domainSetSecurityLabel = SELinuxSetSecurityLabel,
};

18
src/security_selinux.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* 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>
*
*/
#ifndef __VIR_SECURITY_SELINUX_H__
#define __VIR_SECURITY_SELINUX_H__
extern virSecurityDriver virSELinuxSecurityDriver;
#endif /* __VIR_SECURITY_SELINUX_H__ */