mirror of https://gitee.com/openkylin/libvirt.git
sVirt AppArmor security driver
* configure.in: look for AppArmor and devel * src/security/security_apparmor.[ch] src/security/security_driver.c src/Makefile.am: add and plug the new driver * src/security/virt-aa-helper.c: new binary which is used exclusively by the AppArmor security driver to manipulate AppArmor. * po/POTFILES.in: registers the new files * tests/Makefile.am tests/secaatest.c tests/virt-aa-helper-test: tests for virt-aa-helper and the security driver, secaatest.c is identical to seclabeltest.c except it initializes the 'apparmor' driver instead of 'selinux'
This commit is contained in:
parent
f5c65fa192
commit
bbaecd6a8f
83
configure.in
83
configure.in
|
@ -799,6 +799,83 @@ fi
|
|||
AM_CONDITIONAL([WITH_SECDRIVER_SELINUX], [test "$with_secdriver_selinux" != "no"])
|
||||
|
||||
|
||||
dnl AppArmor
|
||||
AC_ARG_WITH([apparmor],
|
||||
[ --with-apparmor use AppArmor to manage security],
|
||||
[],
|
||||
[with_apparmor=check])
|
||||
|
||||
APPARMOR_CFLAGS=
|
||||
APPARMOR_LIBS=
|
||||
if test "$with_apparmor" != "no"; then
|
||||
old_cflags="$CFLAGS"
|
||||
old_libs="$LIBS"
|
||||
if test "$with_apparmor" = "check"; then
|
||||
AC_CHECK_HEADER([sys/apparmor.h],[],[with_apparmor=no])
|
||||
AC_CHECK_LIB([apparmor], [aa_change_profile],[],[with_apparmor=no])
|
||||
AC_CHECK_LIB([apparmor], [aa_change_hat],[],[with_apparmor=no])
|
||||
if test "$with_apparmor" != "no"; then
|
||||
with_apparmor="yes"
|
||||
fi
|
||||
else
|
||||
fail=0
|
||||
AC_CHECK_HEADER([sys/apparmor.h],[],[fail=1])
|
||||
AC_CHECK_LIB([apparmor], [aa_change_profile],[],[fail=1])
|
||||
AC_CHECK_LIB([apparmor], [aa_change_hat],[],[fail=1])
|
||||
test $fail = 1 &&
|
||||
AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
|
||||
fi
|
||||
CFLAGS="$old_cflags"
|
||||
LIBS="$old_libs"
|
||||
fi
|
||||
if test "$with_apparmor" = "yes"; then
|
||||
APPARMOR_LIBS="-lapparmor"
|
||||
AC_DEFINE_UNQUOTED([HAVE_APPARMOR], 1, [whether AppArmor is available for security])
|
||||
AC_DEFINE_UNQUOTED([APPARMOR_DIR], "/etc/apparmor.d", [path to apparmor directory])
|
||||
AC_DEFINE_UNQUOTED([APPARMOR_PROFILES_PATH], "/sys/kernel/security/apparmor/profiles", [path to kernel profiles])
|
||||
fi
|
||||
AM_CONDITIONAL([HAVE_APPARMOR], [test "$with_apparmor" != "no"])
|
||||
AC_SUBST([APPARMOR_CFLAGS])
|
||||
AC_SUBST([APPARMOR_LIBS])
|
||||
|
||||
|
||||
AC_ARG_WITH([secdriver-apparmor],
|
||||
[ --with-secdriver-apparmor use AppArmor security driver],
|
||||
[],
|
||||
[with_secdriver_apparmor=check])
|
||||
|
||||
if test "$with_apparmor" != "yes" ; then
|
||||
if test "$with_secdriver_apparmor" = "check" ; then
|
||||
with_secdriver_apparmor=no
|
||||
else
|
||||
AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
|
||||
fi
|
||||
else
|
||||
old_cflags="$CFLAGS"
|
||||
old_libs="$LIBS"
|
||||
CFLAGS="$CFLAGS $APPARMOR_CFLAGS"
|
||||
LIBS="$CFLAGS $APPARMOR_LIBS"
|
||||
|
||||
fail=0
|
||||
AC_CHECK_FUNC([change_hat], [], [fail=1])
|
||||
AC_CHECK_FUNC([aa_change_profile], [], [fail=1])
|
||||
CFLAGS="$old_cflags"
|
||||
LIBS="$old_libs"
|
||||
|
||||
if test "$fail" = "1" ; then
|
||||
if test "$with_secdriver_apparmor" = "check" ; then
|
||||
with_secdriver_apparmor=no
|
||||
else
|
||||
AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
|
||||
fi
|
||||
else
|
||||
with_secdriver_apparmor=yes
|
||||
AC_DEFINE_UNQUOTED([WITH_SECDRIVER_APPARMOR], 1, [whether AppArmor security driver is available])
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL([WITH_SECDRIVER_APPARMOR], [test "$with_secdriver_apparmor" != "no"])
|
||||
|
||||
|
||||
|
||||
dnl NUMA lib
|
||||
AC_ARG_WITH([numactl],
|
||||
|
@ -1745,6 +1822,7 @@ AC_MSG_NOTICE([])
|
|||
AC_MSG_NOTICE([Security Drivers])
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux])
|
||||
AC_MSG_NOTICE([ AppArmor: $with_secdriver_apparmor])
|
||||
AC_MSG_NOTICE([])
|
||||
AC_MSG_NOTICE([Driver Loadable Modules])
|
||||
AC_MSG_NOTICE([])
|
||||
|
@ -1792,6 +1870,11 @@ AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS $SELINUX_LIBS])
|
|||
else
|
||||
AC_MSG_NOTICE([ selinux: no])
|
||||
fi
|
||||
if test "$with_apparmor" = "yes" ; then
|
||||
AC_MSG_NOTICE([ apparmor: $APPARMOR_CFLAGS $APPARMOR_LIBS])
|
||||
else
|
||||
AC_MSG_NOTICE([ apparmor: no])
|
||||
fi
|
||||
if test "$with_numactl" = "yes" ; then
|
||||
AC_MSG_NOTICE([ numactl: $NUMACTL_CFLAGS $NUMACTL_LIBS])
|
||||
else
|
||||
|
|
|
@ -30,7 +30,9 @@ src/qemu/qemu_monitor_text.c
|
|||
src/remote/remote_driver.c
|
||||
src/secret/secret_driver.c
|
||||
src/security/security_driver.c
|
||||
src/security/security_apparmor.c
|
||||
src/security/security_selinux.c
|
||||
src/security/virt-aa-helper.c
|
||||
src/storage/storage_backend.c
|
||||
src/storage/storage_backend_disk.c
|
||||
src/storage/storage_backend_fs.c
|
||||
|
|
|
@ -153,6 +153,9 @@ LXC_CONTROLLER_SOURCES = \
|
|||
lxc/lxc_controller.c \
|
||||
lxc/veth.c lxc/veth.h
|
||||
|
||||
SECURITY_DRIVER_APPARMOR_HELPER_SOURCES = \
|
||||
security/virt-aa-helper.c
|
||||
|
||||
PHYP_DRIVER_SOURCES = \
|
||||
phyp/phyp_driver.c phyp/phyp_driver.h
|
||||
|
||||
|
@ -238,6 +241,9 @@ SECURITY_DRIVER_SOURCES = \
|
|||
SECURITY_DRIVER_SELINUX_SOURCES = \
|
||||
security/security_selinux.h security/security_selinux.c
|
||||
|
||||
SECURITY_DRIVER_APPARMOR_SOURCES = \
|
||||
security/security_apparmor.h security/security_apparmor.c
|
||||
|
||||
|
||||
NODE_DEVICE_DRIVER_SOURCES = \
|
||||
node_device/node_device_driver.c node_device/node_device_driver.h
|
||||
|
@ -641,9 +647,15 @@ noinst_LTLIBRARIES += libvirt_driver_security.la
|
|||
libvirt_la_LIBADD += libvirt_driver_security.la
|
||||
libvirt_driver_security_la_CFLAGS = \
|
||||
-I@top_srcdir@/src/conf
|
||||
libvirt_driver_security_la_LDFLAGS =
|
||||
if WITH_SECDRIVER_SELINUX
|
||||
libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
|
||||
endif
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_APPARMOR_SOURCES)
|
||||
libvirt_driver_security_la_CFLAGS += $(APPARMOR_CFLAGS)
|
||||
libvirt_driver_security_la_LDFLAGS += $(APPARMOR_LIBS)
|
||||
endif
|
||||
|
||||
# Add all conditional sources just in case...
|
||||
EXTRA_DIST += \
|
||||
|
@ -671,6 +683,7 @@ EXTRA_DIST += \
|
|||
$(NODE_DEVICE_DRIVER_HAL_SOURCES) \
|
||||
$(NODE_DEVICE_DRIVER_DEVKIT_SOURCES) \
|
||||
$(SECURITY_DRIVER_SELINUX_SOURCES) \
|
||||
$(SECURITY_DRIVER_APPARMOR_SOURCES) \
|
||||
$(SECRET_DRIVER_SOURCES) \
|
||||
$(VBOX_DRIVER_EXTRA_DIST)
|
||||
|
||||
|
@ -795,6 +808,26 @@ endif
|
|||
endif
|
||||
EXTRA_DIST += $(LXC_CONTROLLER_SOURCES)
|
||||
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
if WITH_LIBVIRTD
|
||||
libexec_PROGRAMS += virt-aa-helper
|
||||
|
||||
virt_aa_helper_SOURCES = $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
|
||||
|
||||
virt_aa_helper_LDFLAGS = $(WARN_CFLAGS)
|
||||
virt_aa_helper_LDADD = \
|
||||
$(WARN_CFLAGS) \
|
||||
$(LIBXML_LIBS) \
|
||||
@top_srcdir@/gnulib/lib/libgnu.la \
|
||||
@top_srcdir@/src/libvirt_conf.la \
|
||||
@top_srcdir@/src/libvirt_util.la
|
||||
virt_aa_helper_CFLAGS = \
|
||||
-I@top_srcdir@/src/conf \
|
||||
-I@top_srcdir@/src/security
|
||||
endif
|
||||
endif
|
||||
EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
|
||||
|
||||
install-data-local:
|
||||
$(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt"
|
||||
$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/images"
|
||||
|
|
|
@ -0,0 +1,607 @@
|
|||
|
||||
/*
|
||||
* AppArmor security driver for libvirt
|
||||
* Copyright (C) 2009 Canonical Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author:
|
||||
* Jamie Strandboge <jamie@canonical.com>
|
||||
* Based on security_selinux.c by James Morris <jmorris@namei.org>
|
||||
*
|
||||
* AppArmor security driver.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/apparmor.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include "security_driver.h"
|
||||
#include "security_apparmor.h"
|
||||
#include "util.h"
|
||||
#include "memory.h"
|
||||
#include "virterror_internal.h"
|
||||
#include "datatypes.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_SECURITY
|
||||
#define SECURITY_APPARMOR_VOID_DOI "0"
|
||||
#define SECURITY_APPARMOR_NAME "apparmor"
|
||||
#define VIRT_AA_HELPER BINDIR "/virt-aa-helper"
|
||||
|
||||
/*
|
||||
* profile_status returns '-1' on error, '0' if loaded
|
||||
*
|
||||
* If check_enforcing is set to '1', then returns '-1' on error, '0' if
|
||||
* loaded in complain mode, and '1' if loaded in enforcing mode.
|
||||
*/
|
||||
static int
|
||||
profile_status(const char *str, const int check_enforcing)
|
||||
{
|
||||
char *content = NULL;
|
||||
char *tmp = NULL;
|
||||
char *etmp = NULL;
|
||||
int rc = -1;
|
||||
|
||||
/* create string that is '<str> \0' for accurate matching */
|
||||
if (virAsprintf(&tmp, "%s ", str) == -1)
|
||||
return rc;
|
||||
|
||||
if (check_enforcing != 0) {
|
||||
/* create string that is '<str> (enforce)\0' for accurate matching */
|
||||
if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
|
||||
VIR_FREE(tmp);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
|
||||
virReportSystemError(NULL, errno,
|
||||
_("Failed to read AppArmor profiles list "
|
||||
"\'%s\'"), APPARMOR_PROFILES_PATH);
|
||||
if (check_enforcing != 0)
|
||||
VIR_FREE(etmp);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (strstr(content, tmp) != NULL)
|
||||
rc = 0;
|
||||
if (check_enforcing != 0) {
|
||||
if (rc == 0 && strstr(content, etmp) != NULL)
|
||||
rc = 1; /* return '1' if loaded and enforcing */
|
||||
VIR_FREE(etmp);
|
||||
}
|
||||
|
||||
VIR_FREE(content);
|
||||
clean:
|
||||
VIR_FREE(tmp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
profile_loaded(const char *str)
|
||||
{
|
||||
return profile_status(str, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* profile_status_file returns '-1' on error, '0' if file on disk is in
|
||||
* complain mode and '1' if file on disk is in enforcing mode
|
||||
*/
|
||||
static int
|
||||
profile_status_file(const char *str)
|
||||
{
|
||||
char profile[PATH_MAX];
|
||||
char *content = NULL;
|
||||
char *tmp = NULL;
|
||||
int rc = -1;
|
||||
int len;
|
||||
|
||||
if (snprintf(profile, PATH_MAX, "%s/%s", APPARMOR_DIR "/libvirt", str)
|
||||
> PATH_MAX - 1) {
|
||||
virSecurityReportError(NULL, VIR_ERR_ERROR,
|
||||
"%s", _("profile name exceeds maximum length"));
|
||||
}
|
||||
|
||||
if (!virFileExists(profile)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
|
||||
virReportSystemError(NULL, errno,
|
||||
_("Failed to read \'%s\'"), profile);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* create string that is ' <str> flags=(complain)\0' */
|
||||
if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
|
||||
virReportOOMError(NULL);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (strstr(content, tmp) != NULL)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
|
||||
VIR_FREE(tmp);
|
||||
clean:
|
||||
VIR_FREE(content);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* load (add) a profile. Will create one if necessary
|
||||
*/
|
||||
static int
|
||||
load_profile(virConnectPtr conn, const char *profile, virDomainObjPtr vm,
|
||||
virDomainDiskDefPtr disk)
|
||||
{
|
||||
int rc = -1, status, ret;
|
||||
bool create = true;
|
||||
char *xml = NULL;
|
||||
int pipefd[2];
|
||||
pid_t child;
|
||||
|
||||
if (pipe(pipefd) < -1) {
|
||||
virReportSystemError(conn, errno, "%s", _("unable to create pipe"));
|
||||
return rc;
|
||||
}
|
||||
|
||||
xml = virDomainDefFormat(conn, vm->def, VIR_DOMAIN_XML_SECURE);
|
||||
if (!xml)
|
||||
goto failed;
|
||||
|
||||
if (profile_status_file(profile) >= 0)
|
||||
create = false;
|
||||
|
||||
if (create) {
|
||||
const char *const argv[] = {
|
||||
VIRT_AA_HELPER, "-c", "-u", profile, NULL
|
||||
};
|
||||
ret = virExec(conn, argv, NULL, NULL, &child,
|
||||
pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
|
||||
} else if (disk && disk->src) {
|
||||
const char *const argv[] = {
|
||||
VIRT_AA_HELPER, "-r", "-u", profile, "-f", disk->src, NULL
|
||||
};
|
||||
ret = virExec(conn, argv, NULL, NULL, &child,
|
||||
pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
|
||||
} else {
|
||||
const char *const argv[] = {
|
||||
VIRT_AA_HELPER, "-r", "-u", profile, NULL
|
||||
};
|
||||
ret = virExec(conn, argv, NULL, NULL, &child,
|
||||
pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto clean;
|
||||
|
||||
/* parent continues here */
|
||||
if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
|
||||
virReportSystemError(conn, errno, "%s", _("unable to write to pipe"));
|
||||
goto clean;
|
||||
}
|
||||
close(pipefd[1]);
|
||||
rc = 0;
|
||||
|
||||
rewait:
|
||||
if (waitpid(child, &status, 0) != child) {
|
||||
if (errno == EINTR)
|
||||
goto rewait;
|
||||
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("Unexpected exit status from virt-aa-helper "
|
||||
"%d pid %lu"),
|
||||
WEXITSTATUS(status), (unsigned long)child);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
clean:
|
||||
VIR_FREE(xml);
|
||||
|
||||
failed:
|
||||
if (pipefd[0] > 0)
|
||||
close(pipefd[0]);
|
||||
if (pipefd[1] > 0)
|
||||
close(pipefd[1]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
remove_profile(const char *profile)
|
||||
{
|
||||
int rc = -1;
|
||||
const char * const argv[] = {
|
||||
VIRT_AA_HELPER, "-R", "-u", profile, NULL
|
||||
};
|
||||
|
||||
if (virRun(NULL, argv, NULL) == 0)
|
||||
rc = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_profile_name(virConnectPtr conn, virDomainObjPtr vm)
|
||||
{
|
||||
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
||||
char *name = NULL;
|
||||
|
||||
virUUIDFormat(vm->def->uuid, uuidstr);
|
||||
if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
|
||||
virReportOOMError(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
|
||||
* mode and 1 if enforcing. This is required because at present you cannot
|
||||
* aa_change_profile() from a process that is unconfined.
|
||||
*/
|
||||
static int
|
||||
use_apparmor(void)
|
||||
{
|
||||
char libvirt_daemon[PATH_MAX];
|
||||
int rc = -1;
|
||||
ssize_t len = 0;
|
||||
|
||||
if ((len = readlink("/proc/self/exe", libvirt_daemon,
|
||||
PATH_MAX - 1)) < 0) {
|
||||
virSecurityReportError(NULL, VIR_ERR_ERROR,
|
||||
"%s", _("could not find libvirtd"));
|
||||
return rc;
|
||||
}
|
||||
libvirt_daemon[len] = '\0';
|
||||
|
||||
if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
|
||||
return rc;
|
||||
|
||||
return profile_status(libvirt_daemon, 1);
|
||||
}
|
||||
|
||||
/* Called on libvirtd startup to see if AppArmor is available */
|
||||
static int
|
||||
AppArmorSecurityDriverProbe(void)
|
||||
{
|
||||
char template[PATH_MAX];
|
||||
|
||||
if (use_apparmor() < 0)
|
||||
return SECURITY_DRIVER_DISABLE;
|
||||
|
||||
/* see if template file exists */
|
||||
if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
|
||||
APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
|
||||
virSecurityReportError(NULL, VIR_ERR_ERROR,
|
||||
"%s", _("template too large"));
|
||||
return SECURITY_DRIVER_DISABLE;
|
||||
}
|
||||
|
||||
if (!virFileExists(template)) {
|
||||
virSecurityReportError(NULL, VIR_ERR_ERROR,
|
||||
_("template \'%s\' does not exist"), template);
|
||||
return SECURITY_DRIVER_DISABLE;
|
||||
}
|
||||
|
||||
return SECURITY_DRIVER_ENABLE;
|
||||
}
|
||||
|
||||
/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
|
||||
* currently not used.
|
||||
*/
|
||||
static int
|
||||
AppArmorSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
|
||||
{
|
||||
virSecurityDriverSetDOI(conn, drv, SECURITY_APPARMOR_VOID_DOI);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and
|
||||
* use a profile based on the UUID, otherwise create one based on a template.
|
||||
* Keep in mind that this is called on 'start' with RestoreSecurityLabel being
|
||||
* called on shutdown.
|
||||
*/
|
||||
static int
|
||||
AppArmorGenSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
|
||||
{
|
||||
int rc = -1;
|
||||
char *profile_name = NULL;
|
||||
|
||||
if ((vm->def->seclabel.label) ||
|
||||
(vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
"%s",
|
||||
_("security label already defined for VM"));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((profile_name = get_profile_name(conn, vm)) == NULL)
|
||||
return rc;
|
||||
|
||||
/* if the profile is not already loaded, then load one */
|
||||
if (profile_loaded(profile_name) < 0) {
|
||||
if (load_profile(conn, profile_name, vm, NULL) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("cannot generate AppArmor profile "
|
||||
"\'%s\'"), profile_name);
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
|
||||
if (!vm->def->seclabel.label) {
|
||||
virReportOOMError(NULL);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* set imagelabel the same as label (but we won't use it) */
|
||||
vm->def->seclabel.imagelabel = strndup(profile_name,
|
||||
strlen(profile_name));
|
||||
if (!vm->def->seclabel.imagelabel) {
|
||||
virReportOOMError(NULL);
|
||||
goto err;
|
||||
}
|
||||
|
||||
vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
|
||||
if (!vm->def->seclabel.model) {
|
||||
virReportOOMError(conn);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
goto clean;
|
||||
|
||||
err:
|
||||
remove_profile(profile_name);
|
||||
VIR_FREE(vm->def->seclabel.label);
|
||||
VIR_FREE(vm->def->seclabel.imagelabel);
|
||||
VIR_FREE(vm->def->seclabel.model);
|
||||
|
||||
clean:
|
||||
VIR_FREE(profile_name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
|
||||
* running.
|
||||
*/
|
||||
static int
|
||||
AppArmorGetSecurityLabel(virConnectPtr conn,
|
||||
virDomainObjPtr vm, virSecurityLabelPtr sec)
|
||||
{
|
||||
int rc = -1;
|
||||
char *profile_name = NULL;
|
||||
|
||||
if ((profile_name = get_profile_name(conn, vm)) == NULL)
|
||||
return rc;
|
||||
|
||||
if (virStrcpy(sec->label, profile_name,
|
||||
VIR_SECURITY_LABEL_BUFLEN) == NULL) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
"%s", _("error copying profile name"));
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
"%s", _("error calling profile_status()"));
|
||||
goto clean;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
clean:
|
||||
VIR_FREE(profile_name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
|
||||
* more details. Currently called via qemudShutdownVMDaemon.
|
||||
*/
|
||||
static int
|
||||
AppArmorRestoreSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
|
||||
{
|
||||
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
||||
int rc = 0;
|
||||
|
||||
if (secdef->imagelabel) {
|
||||
if ((rc = remove_profile(secdef->label)) != 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("could not remove profile for \'%s\'"),
|
||||
secdef->label);
|
||||
}
|
||||
VIR_FREE(secdef->model);
|
||||
VIR_FREE(secdef->label);
|
||||
VIR_FREE(secdef->imagelabel);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called via virExecWithHook. Output goes to
|
||||
* LOCAL_STATE_DIR/log/libvirt/qemu/<vm name>.log
|
||||
*/
|
||||
static int
|
||||
AppArmorSetSecurityLabel(virConnectPtr conn,
|
||||
virSecurityDriverPtr drv, virDomainObjPtr vm)
|
||||
{
|
||||
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
||||
int rc = -1;
|
||||
char *profile_name = NULL;
|
||||
|
||||
if ((profile_name = get_profile_name(conn, vm)) == NULL)
|
||||
return rc;
|
||||
|
||||
if (STRNEQ(drv->name, secdef->model)) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("security label driver mismatch: "
|
||||
"\'%s\' model configured for domain, but "
|
||||
"hypervisor driver is \'%s\'."),
|
||||
secdef->model, drv->name);
|
||||
if (use_apparmor() > 0)
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (aa_change_profile(profile_name) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("error calling aa_change_profile()"));
|
||||
goto clean;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
clean:
|
||||
VIR_FREE(profile_name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Called when hotplugging */
|
||||
static int
|
||||
AppArmorRestoreSecurityImageLabel(virConnectPtr conn,
|
||||
virDomainObjPtr vm,
|
||||
virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
|
||||
{
|
||||
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
||||
int rc = -1;
|
||||
char *profile_name = NULL;
|
||||
|
||||
if (secdef->imagelabel) {
|
||||
if ((profile_name = get_profile_name(conn, vm)) == NULL)
|
||||
return rc;
|
||||
|
||||
/* Update the profile only if it is loaded */
|
||||
if (profile_loaded(secdef->imagelabel) >= 0) {
|
||||
if (load_profile(conn, secdef->imagelabel, vm, NULL) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("cannot update AppArmor profile "
|
||||
"\'%s\'"),
|
||||
secdef->imagelabel);
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
clean:
|
||||
VIR_FREE(profile_name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called when hotplugging */
|
||||
static int
|
||||
AppArmorSetSecurityImageLabel(virConnectPtr conn,
|
||||
virDomainObjPtr vm, virDomainDiskDefPtr disk)
|
||||
{
|
||||
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
|
||||
int rc = -1;
|
||||
char *profile_name;
|
||||
|
||||
if (!disk->src)
|
||||
return 0;
|
||||
|
||||
if (secdef->imagelabel) {
|
||||
/* if the device doesn't exist, error out */
|
||||
if (!virFileExists(disk->src)) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("\'%s\' does not exist"), disk->src);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((profile_name = get_profile_name(conn, vm)) == NULL)
|
||||
return rc;
|
||||
|
||||
/* update the profile only if it is loaded */
|
||||
if (profile_loaded(secdef->imagelabel) >= 0) {
|
||||
if (load_profile(conn, secdef->imagelabel, vm, disk) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_ERROR,
|
||||
_("cannot update AppArmor profile "
|
||||
"\'%s\'"),
|
||||
secdef->imagelabel);
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
clean:
|
||||
VIR_FREE(profile_name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
AppArmorSecurityVerify(virConnectPtr conn, virDomainDefPtr def)
|
||||
{
|
||||
const virSecurityLabelDefPtr secdef = &def->seclabel;
|
||||
|
||||
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
|
||||
if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
|
||||
virSecurityReportError(conn, VIR_ERR_XML_ERROR,
|
||||
_("Invalid security label \'%s\'"),
|
||||
secdef->label);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
AppArmorReserveSecurityLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virDomainObjPtr vm ATTRIBUTE_UNUSED)
|
||||
{
|
||||
/* NOOP. Nothing to reserve with AppArmor */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
AppArmorSetSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virDomainObjPtr vm ATTRIBUTE_UNUSED,
|
||||
virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
|
||||
|
||||
{
|
||||
/* TODO: call load_profile with an update vm->def */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
AppArmorRestoreSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
|
||||
|
||||
{
|
||||
/* TODO: call load_profile (needs virDomainObjPtr vm) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
virSecurityDriver virAppArmorSecurityDriver = {
|
||||
.name = SECURITY_APPARMOR_NAME,
|
||||
.probe = AppArmorSecurityDriverProbe,
|
||||
.open = AppArmorSecurityDriverOpen,
|
||||
.domainSecurityVerify = AppArmorSecurityVerify,
|
||||
.domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
|
||||
.domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
|
||||
.domainGenSecurityLabel = AppArmorGenSecurityLabel,
|
||||
.domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
|
||||
.domainGetSecurityLabel = AppArmorGetSecurityLabel,
|
||||
.domainRestoreSecurityLabel = AppArmorRestoreSecurityLabel,
|
||||
.domainSetSecurityLabel = AppArmorSetSecurityLabel,
|
||||
.domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
|
||||
.domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) 2009 Canonical Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author:
|
||||
* Jamie Strandboge <jamie@canonical.com>
|
||||
*
|
||||
*/
|
||||
#ifndef __VIR_SECURITY_APPARMOR_H__
|
||||
#define __VIR_SECURITY_APPARMOR_H__
|
||||
|
||||
extern virSecurityDriver virAppArmorSecurityDriver;
|
||||
|
||||
#define AA_PREFIX "libvirt-"
|
||||
#define PROFILE_NAME_SIZE 8 + VIR_UUID_STRING_BUFLEN /* AA_PREFIX + uuid */
|
||||
#define MAX_FILE_LEN (1024*1024*10) /* 10MB limit for sanity check */
|
||||
|
||||
#endif /* __VIR_SECURITY_APPARMOR_H__ */
|
|
@ -20,9 +20,16 @@
|
|||
#include "security_selinux.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SECDRIVER_APPARMOR
|
||||
#include "security_apparmor.h"
|
||||
#endif
|
||||
|
||||
static virSecurityDriverPtr security_drivers[] = {
|
||||
#ifdef WITH_SECDRIVER_SELINUX
|
||||
&virSELinuxSecurityDriver,
|
||||
#endif
|
||||
#ifdef WITH_SECDRIVER_APPARMOR
|
||||
&virAppArmorSecurityDriver,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,6 +16,7 @@ INCLUDES = \
|
|||
$(GNUTLS_CFLAGS) \
|
||||
$(SASL_CFLAGS) \
|
||||
$(SELINUX_CFLAGS) \
|
||||
$(APPARMOR_CFLAGS) \
|
||||
-DGETTEXT_PACKAGE=\"$(PACKAGE)\" \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(WARN_CFLAGS)
|
||||
|
@ -31,6 +32,7 @@ LDADDS = \
|
|||
$(GNUTLS_LIBS) \
|
||||
$(SASL_LIBS) \
|
||||
$(SELINUX_LIBS) \
|
||||
$(APPARMOR_LIBS) \
|
||||
$(WARN_CFLAGS) \
|
||||
../src/libvirt_test.la \
|
||||
../gnulib/lib/libgnu.la \
|
||||
|
@ -84,6 +86,10 @@ if WITH_SECDRIVER_SELINUX
|
|||
noinst_PROGRAMS += seclabeltest
|
||||
endif
|
||||
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
noinst_PROGRAMS += secaatest
|
||||
endif
|
||||
|
||||
if WITH_CIL
|
||||
noinst_PROGRAMS += object-locking
|
||||
endif
|
||||
|
@ -119,6 +125,9 @@ test_scripts += \
|
|||
virsh-synopsis
|
||||
endif
|
||||
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
test_scripts += virt-aa-helper-test
|
||||
endif
|
||||
EXTRA_DIST += $(test_scripts)
|
||||
|
||||
TESTS = virshtest \
|
||||
|
@ -149,6 +158,10 @@ if WITH_SECDRIVER_SELINUX
|
|||
TESTS += seclabeltest
|
||||
endif
|
||||
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
TESTS += secaatest
|
||||
endif
|
||||
|
||||
if WITH_LIBVIRTD
|
||||
noinst_PROGRAMS += eventtest
|
||||
TESTS += eventtest
|
||||
|
@ -285,6 +298,14 @@ else
|
|||
EXTRA_DIST += seclabeltest.c
|
||||
endif
|
||||
|
||||
if WITH_SECDRIVER_APPARMOR
|
||||
secaatest_SOURCES = \
|
||||
secaatest.c
|
||||
secaatest_LDADD = ../src/libvirt_driver_security.la $(LDADDS)
|
||||
else
|
||||
EXTRA_DIST += secaatest.c
|
||||
endif
|
||||
|
||||
qparamtest_SOURCES = \
|
||||
qparamtest.c testutils.h testutils.c
|
||||
qparamtest_LDADD = $(LDADDS)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#include <config.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "security/security_driver.h"
|
||||
|
||||
int
|
||||
main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int ret;
|
||||
|
||||
const char *doi, *model;
|
||||
virSecurityDriverPtr security_drv;
|
||||
|
||||
ret = virSecurityDriverStartup (&security_drv, "apparmor");
|
||||
if (ret == -1)
|
||||
{
|
||||
fprintf (stderr, "Failed to start security driver");
|
||||
exit (-1);
|
||||
}
|
||||
/* No security driver wanted to be enabled: just return */
|
||||
if (ret == -2)
|
||||
return 0;
|
||||
|
||||
model = virSecurityDriverGetModel (security_drv);
|
||||
if (!model)
|
||||
{
|
||||
fprintf (stderr, "Failed to copy secModel model: %s",
|
||||
strerror (errno));
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
doi = virSecurityDriverGetDOI (security_drv);
|
||||
if (!doi)
|
||||
{
|
||||
fprintf (stderr, "Failed to copy secModel DOI: %s",
|
||||
strerror (errno));
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
test_hostdev="no"
|
||||
if [ "$1" = "test_hostdev" ]; then
|
||||
test_hostdev="yes"
|
||||
shift
|
||||
fi
|
||||
|
||||
output="/dev/null"
|
||||
use_valgrind=""
|
||||
ld_library_path="../src/.libs/"
|
||||
if [ ! -z "$1" ] && [ "$1" = "-d" ]; then
|
||||
output="/dev/stdout"
|
||||
shift
|
||||
fi
|
||||
|
||||
exe="../src/virt-aa-helper"
|
||||
if [ ! -z "$1" ]; then
|
||||
if [ "$1" = "-v" ]; then
|
||||
use_valgrind="yes"
|
||||
shift
|
||||
fi
|
||||
if [ -n "$1" ]; then
|
||||
exe="$1"
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$exe" ]; then
|
||||
echo "Could not find '$exe'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "testing `basename $exe`" >$output
|
||||
if [ "$use_valgrind" = "yes" ]; then
|
||||
exe="valgrind --error-exitcode=2 --track-origins=yes $exe"
|
||||
fi
|
||||
|
||||
extra_args="--dryrun"
|
||||
errors=0
|
||||
|
||||
tmpdir=`mktemp -d`
|
||||
trap "rm -rf $tmpdir" EXIT HUP INT QUIT TERM
|
||||
|
||||
template_xml="$tmpdir/template.xml"
|
||||
test_xml="$tmpdir/test.xml"
|
||||
|
||||
uuid="00000000-0000-0000-0000-0123456789ab"
|
||||
disk1="$tmpdir/1.img"
|
||||
disk2="$tmpdir/2.img"
|
||||
relative_disk1="$tmpdir/./../`basename $tmpdir`//./1.img"
|
||||
nonexistent="$tmpdir/nonexistant.img"
|
||||
bad_disk="/etc/passwd"
|
||||
valid_uuid="libvirt-$uuid"
|
||||
nonexistent_uuid="libvirt-00000000-0000-0000-0000-000000000001"
|
||||
|
||||
cat > "$template_xml" <<EOM
|
||||
<domain type='kvm'>
|
||||
<name>virt-aa-helper-test</name>
|
||||
<uuid>###UUID###</uuid>
|
||||
<memory>524288</memory>
|
||||
<currentMemory>524288</currentMemory>
|
||||
<vcpu>1</vcpu>
|
||||
<os>
|
||||
<type arch='x86_64' machine='pc'>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
</features>
|
||||
<clock offset='utc'/>
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>restart</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
<devices>
|
||||
<emulator>/usr/bin/kvm</emulator>
|
||||
<disk type='file' device='disk'>
|
||||
<source file='###DISK###'/>
|
||||
<target dev='hda' bus='ide'/>
|
||||
</disk>
|
||||
<interface type='network'>
|
||||
<mac address='52:54:00:50:4b:26'/>
|
||||
<source network='default'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
<input type='tablet' bus='usb'/>
|
||||
<input type='mouse' bus='ps2'/>
|
||||
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'/>
|
||||
<video>
|
||||
<model type='cirrus' vram='9216' heads='1'/>
|
||||
</video>
|
||||
</devices>
|
||||
</domain>
|
||||
EOM
|
||||
|
||||
touch "$disk1" "$disk2"
|
||||
|
||||
testme() {
|
||||
expected="$1"
|
||||
outstr="$2"
|
||||
args="$3"
|
||||
input=""
|
||||
|
||||
if [ -n "$4" ]; then
|
||||
input="$4"
|
||||
if [ ! -e "$input" ]; then
|
||||
echo "FAIL: could not find $input" >$output
|
||||
echo "FAIL: could not find $input"
|
||||
echo " '$extra_args $args': "
|
||||
errors=$(($errors + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -n " $outstr: " >$output
|
||||
echo -n " '$extra_args $args" >$output
|
||||
if [ -n "$input" ]; then
|
||||
echo -n " < $input" >$output
|
||||
fi
|
||||
echo "': " >$output
|
||||
set +e
|
||||
if [ -n "$input" ]; then
|
||||
LD_LIBRARY_PATH="$ld_library_path" $exe $extra_args $args < $input >$output 2>&1
|
||||
else
|
||||
LD_LIBRARY_PATH="$ld_library_path" $exe $extra_args $args >$output 2>&1
|
||||
fi
|
||||
rc="$?"
|
||||
set -e
|
||||
if [ "$rc" = "$expected" ]; then
|
||||
echo "pass" >$output
|
||||
else
|
||||
echo "FAIL: exited with '$rc'" >$output
|
||||
echo "FAIL: exited with '$rc'"
|
||||
echo -n " $outstr: "
|
||||
echo " '$extra_args $args': "
|
||||
errors=$(($errors + 1))
|
||||
#exit $rc
|
||||
fi
|
||||
}
|
||||
|
||||
# Expected failures
|
||||
echo "Expected failures:" >$output
|
||||
testme "1" "invalid arg" "-z"
|
||||
testme "1" "invalid case" "-A"
|
||||
testme "1" "not enough args" "-c"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
|
||||
testme "1" "no -u with -c" "-c" "$test_xml"
|
||||
testme "1" "bad uuid (bad digit)" "-c -u libvirt-00000000-0000-0000-0000-00000000000g" "$test_xml"
|
||||
testme "1" "bad uuid (too long)" "-c -u ${valid_uuid}abcdef" "$test_xml"
|
||||
testme "1" "bad uuid (too short)" "-c -u libvirt-00000000-0000-0000-0000-0123456789a" "$test_xml"
|
||||
testme "1" "non-matching uuid" "-c -u libvirt-00000000-0000-0000-0000-00000000000a" "$test_xml"
|
||||
testme "1" "missing uuid" "-c -u" "$test_xml"
|
||||
testme "1" "no -u with -R" "-R"
|
||||
testme "1" "non-existent uuid" "-R -u $nonexistent_uuid"
|
||||
testme "1" "no -u with -r" "-r"
|
||||
testme "1" "old '-n' option" "-c -n foo -u $valid_uuid" "$test_xml"
|
||||
testme "1" "invalid bits" "-c -b 15 -u $valid_uuid" "$test_xml"
|
||||
testme "1" "invalid bits2" "-c -b a -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$bad_disk,g" > "$test_xml"
|
||||
testme "1" "bad disk" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$bad_disk,g" | sed "s,</devices>,<disk type='file' device='disk'><source file='$disk2'<target dev='hda' bus='ide'/></disk></devices>,g" > "$test_xml"
|
||||
testme "1" "bad disk2" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<devices>,g" > "$test_xml"
|
||||
testme "1" "malformed xml" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,/boot/initrd,g" > "$test_xml"
|
||||
testme "1" "disk in /boot" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,/boot/initrd,g" > "$test_xml"
|
||||
testme "1" "-r with invalid -f" "-r -u $valid_uuid -f $bad_disk" "$test_xml"
|
||||
|
||||
|
||||
echo "Expected pass:" >$output
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
|
||||
testme "0" "create" "-c -u $valid_uuid" "$test_xml"
|
||||
testme "0" "create with bits (32)" "-c -b 32 -u $valid_uuid" "$test_xml"
|
||||
testme "0" "create with bits (64)" "-c -b 64 -u $valid_uuid" "$test_xml"
|
||||
testme "0" "create with hvm" "-c -H hvm -u $valid_uuid" "$test_xml"
|
||||
testme "0" "create with hvm and bits" "-c -H hvm --bits 32 -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><disk type='file' device='disk'><source file='$disk2'/><target dev='hdb' bus='ide'/></disk>,g" > "$test_xml"
|
||||
testme "0" "create multiple disks" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###',${disk1}'/><readonly,g" > "$test_xml"
|
||||
testme "0" "create (readonly)" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
if [ "$test_hostdev" = "yes" ]; then
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><hostdev mode='subsystem' type='usb'><source><address bus='002' device='004'/></source></hostdev>,g" > "$test_xml"
|
||||
testme "0" "create hostdev (USB)" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><hostdev mode='subsystem' type='pci'><source><address bus='0x00' slot='0x00' function='0x0'/></source></hostdev>,g" > "$test_xml"
|
||||
testme "0" "create hostdev (PCI)" "-c -u $valid_uuid" "$test_xml"
|
||||
fi
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$nonexistent,g" > "$test_xml"
|
||||
testme "0" "create (non-existent disk)" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$relative_disk1,g" > "$test_xml"
|
||||
testme "0" "create (relative path)" "-c -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk2,g" > "$test_xml"
|
||||
testme "0" "replace" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$nonexistent,g" > "$test_xml"
|
||||
testme "0" "replace (non-existent disk)" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
|
||||
testme "0" "replace (adding disk)" "-r -u $valid_uuid -f $disk2" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
|
||||
testme "0" "replace (adding non-existent disk)" "-r -u $valid_uuid -f $nonexistent" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<disk type='block' device='cdrom'><target dev='hdc' bus='ide'/><readonly/></disk></devices>,g" > "$test_xml"
|
||||
testme "0" "disk (empty cdrom)" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<serial type='file'><source path='$tmpdir/serial.log'/><target port='0'/></serial></devices>,g" > "$test_xml"
|
||||
testme "0" "serial" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<serial type='pty'><target port='0'/></serial></devices>,g" > "$test_xml"
|
||||
testme "0" "serial (pty)" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<console type='file'><source path='$tmpdir/console.log'/><target port='0'/></console></devices>,g" > "$test_xml"
|
||||
touch "$tmpdir/console.log"
|
||||
testme "0" "console" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<console type='pty'><target port='0'/></console></devices>,g" > "$test_xml"
|
||||
testme "0" "console (pty)" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>$tmpdir/kernel</kernel></os>,g" > "$test_xml"
|
||||
touch "$tmpdir/kernel"
|
||||
testme "0" "kernel" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>$tmpdir/initrd</initrd></os>,g" > "$test_xml"
|
||||
touch "$tmpdir/initrd"
|
||||
testme "0" "initrd" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>/boot/kernel</kernel></os>,g" > "$test_xml"
|
||||
testme "0" "kernel in /boot" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/boot/initrd</initrd></os>,g" > "$test_xml"
|
||||
testme "0" "initrd in /boot" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>/vmlinuz</kernel></os>,g" > "$test_xml"
|
||||
testme "0" "kernel is /vmlinuz" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/initrd/ramdisk</initrd></os>,g" > "$test_xml"
|
||||
testme "0" "initrd is /initrd/ramdisk" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/initrd.img</initrd></os>,g" > "$test_xml"
|
||||
testme "0" "initrd is /initrd.img" "-r -u $valid_uuid" "$test_xml"
|
||||
|
||||
testme "0" "help" "-h"
|
||||
|
||||
echo "" >$output
|
||||
if [ "$errors" != "0" ]; then
|
||||
echo "FAIL: $errors error(s)" >$output
|
||||
exit 1
|
||||
fi
|
||||
echo PASS >$output
|
Loading…
Reference in New Issue