mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 02:51:16 +00:00
SELinux security driver for sVirt support (James Morris, Dan Walsh & Daniel Berrange)
This commit is contained in:
parent
aa2c97263d
commit
41ed6eb327
10
ChangeLog
10
ChangeLog
@ -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>
|
||||
|
||||
virsh additions for sVirt support (James Morris & Dan Walsh)
|
||||
|
43
configure.in
43
configure.in
@ -640,6 +640,45 @@ AM_CONDITIONAL([HAVE_SELINUX], [test "$with_selinux" != "no"])
|
||||
AC_SUBST([SELINUX_CFLAGS])
|
||||
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
|
||||
AC_ARG_WITH([numactl],
|
||||
[ --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([ Disk: $with_storage_disk])
|
||||
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([])
|
||||
if test "$with_driver_modules" != "no" ; then
|
||||
|
@ -24,6 +24,7 @@ src/qemu_conf.c
|
||||
src/qemu_driver.c
|
||||
src/remote_internal.c
|
||||
src/security.c
|
||||
src/security_selinux.c
|
||||
src/storage_backend.c
|
||||
src/storage_backend_disk.c
|
||||
src/storage_backend_fs.c
|
||||
|
@ -170,6 +170,9 @@ STORAGE_HELPER_DISK_SOURCES = \
|
||||
SECURITY_DRIVER_SOURCES = \
|
||||
security.h security.c
|
||||
|
||||
SECURITY_DRIVER_SELINUX_SOURCES = \
|
||||
security_selinux.h security_selinux.c
|
||||
|
||||
|
||||
NODE_DEVICE_DRIVER_SOURCES = \
|
||||
node_device.c node_device.h
|
||||
@ -387,6 +390,9 @@ endif
|
||||
libvirt_driver_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES)
|
||||
noinst_LTLIBRARIES += 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...
|
||||
EXTRA_DIST += \
|
||||
|
@ -16,8 +16,14 @@
|
||||
#include "virterror_internal.h"
|
||||
|
||||
#include "security.h"
|
||||
#ifdef WITH_SECDRIVER_SELINUX
|
||||
#include "security_selinux.h"
|
||||
#endif
|
||||
|
||||
static virSecurityDriverPtr security_drivers[] = {
|
||||
#ifdef WITH_SECDRIVER_SELINUX
|
||||
&virSELinuxSecurityDriver,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
|
377
src/security_selinux.c
Normal file
377
src/security_selinux.c
Normal 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
18
src/security_selinux.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user