2009-10-08 14:34:22 +00:00
|
|
|
/*
|
|
|
|
* virt-aa-helper: wrapper program used by AppArmor security driver.
|
2010-05-12 17:23:06 +00:00
|
|
|
*
|
2014-03-25 11:48:26 +00:00
|
|
|
* Copyright (C) 2010-2014 Red Hat, Inc.
|
2011-09-28 07:43:39 +00:00
|
|
|
* Copyright (C) 2009-2011 Canonical Ltd.
|
2009-10-08 14:34:22 +00:00
|
|
|
*
|
2012-07-27 09:39:53 +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.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-27 09:39:53 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-10-08 14:34:22 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2015-06-25 08:36:52 +00:00
|
|
|
#include "virlog.h"
|
2017-07-26 19:57:23 +00:00
|
|
|
#include "driver.h"
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
#include "security_driver.h"
|
|
|
|
#include "security_apparmor.h"
|
|
|
|
#include "domain_conf.h"
|
2012-12-13 18:13:21 +00:00
|
|
|
#include "virxml.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-12-12 17:04:51 +00:00
|
|
|
#include "virusb.h"
|
2020-02-24 23:24:14 +00:00
|
|
|
#include "virutil.h"
|
2012-12-13 14:52:25 +00:00
|
|
|
#include "virpci.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2010-11-16 14:54:17 +00:00
|
|
|
#include "configmake.h"
|
2012-01-27 10:14:21 +00:00
|
|
|
#include "virrandom.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2016-04-12 22:29:52 +00:00
|
|
|
#include "virgettext.h"
|
2017-09-20 14:59:06 +00:00
|
|
|
#include "virhostdev.h"
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2011-05-12 21:20:47 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_SECURITY
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static char *progname;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char uuid[PROFILE_NAME_SIZE]; /* UUID of vm */
|
|
|
|
bool dryrun; /* dry run */
|
|
|
|
char cmd; /* 'c' create
|
|
|
|
* 'a' add (load)
|
|
|
|
* 'r' replace
|
|
|
|
* 'R' remove */
|
|
|
|
char *files; /* list of files */
|
|
|
|
virDomainDefPtr def; /* VM definition */
|
|
|
|
virCapsPtr caps; /* VM capabilities */
|
2013-05-24 16:58:25 +00:00
|
|
|
virDomainXMLOptionPtr xmlopt; /* XML parser data */
|
2015-06-25 14:08:42 +00:00
|
|
|
char *virtType; /* type of hypervisor (eg qemu, xen, lxc) */
|
2015-06-25 11:01:40 +00:00
|
|
|
char *os; /* type of os (eg hvm, xen, exe) */
|
2012-12-19 11:45:31 +00:00
|
|
|
virArch arch; /* machine architecture */
|
2010-09-30 20:54:56 +00:00
|
|
|
char *newfile; /* newly added file */
|
|
|
|
bool append; /* append to .files instead of rewrite */
|
2009-10-08 14:34:22 +00:00
|
|
|
} vahControl;
|
|
|
|
|
|
|
|
static int
|
|
|
|
vahDeinit(vahControl * ctl)
|
|
|
|
{
|
|
|
|
if (ctl == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_FREE(ctl->def);
|
2013-02-01 12:26:18 +00:00
|
|
|
virObjectUnref(ctl->caps);
|
2013-03-31 18:03:42 +00:00
|
|
|
virObjectUnref(ctl->xmlopt);
|
2009-12-09 23:00:50 +00:00
|
|
|
VIR_FREE(ctl->files);
|
2015-06-25 14:08:42 +00:00
|
|
|
VIR_FREE(ctl->virtType);
|
2015-06-25 11:01:40 +00:00
|
|
|
VIR_FREE(ctl->os);
|
2010-09-30 20:54:56 +00:00
|
|
|
VIR_FREE(ctl->newfile);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
vah_usage(void)
|
|
|
|
{
|
2019-10-16 06:47:36 +00:00
|
|
|
printf(_("\n%s mode [options] [extra file] [< def.xml]\n\n"
|
|
|
|
" Modes:\n"
|
2010-11-17 20:31:04 +00:00
|
|
|
" -a | --add load profile\n"
|
|
|
|
" -c | --create create profile from template\n"
|
|
|
|
" -D | --delete unload and delete profile\n"
|
|
|
|
" -r | --replace reload profile\n"
|
|
|
|
" -R | --remove unload profile\n"
|
2019-10-16 06:47:36 +00:00
|
|
|
" Options:\n"
|
|
|
|
" -d | --dryrun dry run\n"
|
2010-11-17 20:31:04 +00:00
|
|
|
" -u | --uuid <uuid> uuid (profile name)\n"
|
2019-10-16 06:47:36 +00:00
|
|
|
" -h | --help this help\n"
|
|
|
|
" Extra File:\n"
|
|
|
|
" -f | --add-file <file> add file to a profile generated from XML\n"
|
|
|
|
" -F | --append-file <file> append file to an existing profile\n"
|
2010-11-17 20:31:04 +00:00
|
|
|
"\n"), progname);
|
|
|
|
|
|
|
|
puts(_("This command is intended to be used by libvirtd "
|
|
|
|
"and not used directly.\n"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_error(vahControl * ctl, int doexit, const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s: error: %s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (doexit) {
|
|
|
|
if (ctl != NULL)
|
|
|
|
vahDeinit(ctl);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_warning(const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s: warning: %s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_info(const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s:\n%s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* run an apparmor_parser command
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserCommand(const char *profile_name, const char cmd)
|
|
|
|
{
|
2011-04-03 09:21:17 +00:00
|
|
|
int result = -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
char flag[3];
|
2011-04-03 09:21:17 +00:00
|
|
|
char *profile;
|
2010-04-06 14:17:08 +00:00
|
|
|
int status;
|
|
|
|
int ret;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (strchr("arR", cmd) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid flag"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-11-13 13:53:42 +00:00
|
|
|
g_snprintf(flag, 3, "-%c", cmd);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
profile = g_strdup_printf("%s/%s", APPARMOR_DIR "/libvirt", profile_name);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (!virFileExists(profile)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("profile does not exist"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
} else {
|
|
|
|
const char * const argv[] = {
|
|
|
|
"/sbin/apparmor_parser", flag, profile, NULL
|
|
|
|
};
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) command = virCommandNewArgs(argv);
|
2019-08-19 07:02:10 +00:00
|
|
|
|
2019-08-19 07:05:58 +00:00
|
|
|
virCommandRawStatus(command);
|
2019-08-19 07:02:10 +00:00
|
|
|
if ((ret = virCommandRun(command, &status)) != 0 ||
|
2010-04-06 14:17:08 +00:00
|
|
|
(WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
|
|
|
|
if (ret != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to run apparmor_parser"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2010-05-12 17:23:06 +00:00
|
|
|
} else if (cmd == 'R' && WIFEXITED(status) &&
|
|
|
|
WEXITSTATUS(status) == 234) {
|
|
|
|
vah_warning(_("unable to unload already unloaded profile"));
|
2010-04-06 14:17:08 +00:00
|
|
|
} else {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("apparmor_parser exited with error"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2010-04-06 14:17:08 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
result = 0;
|
|
|
|
|
2014-03-25 06:57:22 +00:00
|
|
|
cleanup:
|
2011-04-03 09:21:17 +00:00
|
|
|
VIR_FREE(profile);
|
|
|
|
|
|
|
|
return result;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the dynamic files
|
|
|
|
*/
|
|
|
|
static int
|
2010-09-30 20:54:56 +00:00
|
|
|
update_include_file(const char *include_file, const char *included_files,
|
|
|
|
bool append)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
2011-05-07 11:13:41 +00:00
|
|
|
int plen, flen = 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
int fd;
|
|
|
|
char *pcontent = NULL;
|
2010-09-30 20:54:56 +00:00
|
|
|
char *existing = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
const char *warning =
|
|
|
|
"# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
|
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (virFileExists(include_file)) {
|
|
|
|
flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
|
|
|
|
if (flen < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
if (append && virFileExists(include_file))
|
|
|
|
pcontent = g_strdup_printf("%s%s", existing, included_files);
|
|
|
|
else
|
|
|
|
pcontent = g_strdup_printf("%s%s", warning, included_files);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
plen = strlen(pcontent);
|
|
|
|
if (plen > MAX_FILE_LEN) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid length for new profile"));
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* only update the disk profile if it is different */
|
2010-09-30 20:54:56 +00:00
|
|
|
if (flen > 0 && flen == plen && STREQLEN(existing, pcontent, plen)) {
|
|
|
|
rc = 0;
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* write the file */
|
|
|
|
if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to create include file"));
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to write to profile"));
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
if (VIR_CLOSE(fd) != 0) {
|
2010-11-17 20:31:04 +00:00
|
|
|
vah_error(NULL, 0, _("failed to close or write to profile"));
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
|
2014-03-25 06:57:22 +00:00
|
|
|
cleanup:
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(pcontent);
|
2010-09-30 20:54:56 +00:00
|
|
|
VIR_FREE(existing);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a profile based on a template
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
create_profile(const char *profile, const char *profile_name,
|
2014-03-03 10:26:42 +00:00
|
|
|
const char *profile_files, int virtType)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
2020-03-05 16:21:54 +00:00
|
|
|
g_autofree char *template = NULL;
|
|
|
|
g_autofree char *tcontent = NULL;
|
|
|
|
g_autofree char *pcontent = NULL;
|
|
|
|
g_autofree char *replace_name = NULL;
|
|
|
|
g_autofree char *replace_files = NULL;
|
2016-05-13 08:48:12 +00:00
|
|
|
char *tmp = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
|
|
|
|
const char *template_end = "\n}";
|
|
|
|
int tlen, plen;
|
|
|
|
int fd;
|
2014-10-28 20:42:34 +00:00
|
|
|
const char *driver_name = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (virFileExists(profile)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("profile exists"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2014-10-28 20:42:34 +00:00
|
|
|
switch (virtType) {
|
|
|
|
case VIR_DOMAIN_VIRT_QEMU:
|
|
|
|
case VIR_DOMAIN_VIRT_KQEMU:
|
|
|
|
case VIR_DOMAIN_VIRT_KVM:
|
|
|
|
driver_name = "qemu";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
driver_name = virDomainVirtTypeToString(virtType);
|
|
|
|
}
|
2014-07-15 09:02:50 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
template = g_strdup_printf("%s/TEMPLATE.%s", APPARMOR_DIR "/libvirt", driver_name);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (!virFileExists(template)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("template does not exist"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to read AppArmor template"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(tcontent, template_name) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("no replacement string in template"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(tcontent, template_end) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("no replacement string in template"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* '\nprofile <profile_name>\0' */
|
2019-10-22 13:26:14 +00:00
|
|
|
replace_name = g_strdup_printf("\nprofile %s", profile_name);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
/* '\n<profile_files>\n}\0' */
|
2019-10-22 13:26:14 +00:00
|
|
|
if (virtType != VIR_DOMAIN_VIRT_LXC)
|
|
|
|
replace_files = g_strdup_printf("\n%s\n}", profile_files);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2014-07-15 09:02:50 +00:00
|
|
|
plen = tlen + strlen(replace_name) - strlen(template_name) + 1;
|
2014-03-03 10:26:42 +00:00
|
|
|
|
|
|
|
if (virtType != VIR_DOMAIN_VIRT_LXC)
|
|
|
|
plen += strlen(replace_files) - strlen(template_end);
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (plen > MAX_FILE_LEN || plen < tlen) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid length for new profile"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2016-05-13 08:48:12 +00:00
|
|
|
if (!(pcontent = virStringReplace(tcontent, template_name, replace_name)))
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2016-05-13 08:48:12 +00:00
|
|
|
if (virtType != VIR_DOMAIN_VIRT_LXC) {
|
|
|
|
if (!(tmp = virStringReplace(pcontent, template_end, replace_files)))
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2016-05-13 08:48:12 +00:00
|
|
|
VIR_FREE(pcontent);
|
2019-10-16 11:45:15 +00:00
|
|
|
pcontent = g_steal_pointer(&tmp);
|
2016-05-13 08:48:12 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
/* write the file */
|
|
|
|
if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to create profile"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to write to profile"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
if (VIR_CLOSE(fd) != 0) {
|
2010-11-17 20:31:04 +00:00
|
|
|
vah_error(NULL, 0, _("failed to close or write to profile"));
|
2020-03-05 16:21:54 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 16:21:54 +00:00
|
|
|
return 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserLoad(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'a');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserRemove(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'R');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserReplace(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'r');
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
valid_uuid(const char *uuid)
|
|
|
|
{
|
|
|
|
unsigned char rawuuid[VIR_UUID_BUFLEN];
|
|
|
|
|
|
|
|
if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!STRPREFIX(uuid, AA_PREFIX))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
valid_name(const char *name)
|
|
|
|
{
|
|
|
|
/* just try to filter out any dangerous characters in the name that can be
|
|
|
|
* used to subvert the profile */
|
2017-10-26 13:22:10 +00:00
|
|
|
const char *bad = "/[]{}?^,\"*";
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2012-05-25 13:14:07 +00:00
|
|
|
if (strlen(name) == 0)
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (strcspn(name, bad) != strlen(name))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see if one of the strings in arr starts with str */
|
|
|
|
static int
|
|
|
|
array_starts_with(const char *str, const char * const *arr, const long size)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/security files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2009-10-08 14:34:22 +00:00
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
if (strlen(str) < strlen(arr[i]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (STRPREFIX(str, arr[i]))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow access to special files or restricted paths such as /bin, /sbin,
|
|
|
|
* /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
|
|
|
|
* access to system files which could be used to elevate privileges. This is a
|
|
|
|
* safety measure in case libvirtd is under a restrictive profile and is
|
|
|
|
* subverted and trying to escape confinement.
|
|
|
|
*
|
|
|
|
* Note that we cannot exclude block devices because they are valid devices.
|
|
|
|
* The TEMPLATE file can be adjusted to explicitly disallow these if needed.
|
|
|
|
*
|
|
|
|
* RETURN: -1 on error, 0 if ok, 1 if blocked
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
valid_path(const char *path, const bool readonly)
|
|
|
|
{
|
|
|
|
const char * const restricted[] = {
|
|
|
|
"/bin/",
|
|
|
|
"/etc/",
|
|
|
|
"/lib",
|
|
|
|
"/lost+found/",
|
|
|
|
"/proc/",
|
|
|
|
"/sbin/",
|
|
|
|
"/selinux/",
|
|
|
|
"/sys/",
|
|
|
|
"/usr/bin/",
|
|
|
|
"/usr/lib",
|
|
|
|
"/usr/sbin/",
|
|
|
|
"/usr/share/",
|
|
|
|
"/usr/local/bin/",
|
|
|
|
"/usr/local/etc/",
|
|
|
|
"/usr/local/lib",
|
|
|
|
"/usr/local/sbin/"
|
|
|
|
};
|
|
|
|
/* these paths are ok for readonly, but not read/write */
|
|
|
|
const char * const restricted_rw[] = {
|
|
|
|
"/boot/",
|
|
|
|
"/vmlinuz",
|
|
|
|
"/initrd",
|
2015-08-21 08:52:52 +00:00
|
|
|
"/initrd.img",
|
2019-10-09 18:21:24 +00:00
|
|
|
"/usr/share/edk2/",
|
2017-05-23 16:22:39 +00:00
|
|
|
"/usr/share/OVMF/", /* for OVMF images */
|
2017-05-23 16:22:41 +00:00
|
|
|
"/usr/share/ovmf/", /* for OVMF images */
|
|
|
|
"/usr/share/AAVMF/", /* for AAVMF images */
|
2017-09-15 16:04:21 +00:00
|
|
|
"/usr/share/qemu-efi/", /* for AAVMF images */
|
|
|
|
"/usr/share/qemu-efi-aarch64/" /* for AAVMF images */
|
2009-10-08 14:34:22 +00:00
|
|
|
};
|
2010-04-06 15:57:36 +00:00
|
|
|
/* override the above with these */
|
|
|
|
const char * const override[] = {
|
2019-05-15 11:35:32 +00:00
|
|
|
"/sys/devices/pci", /* for hostdev pci devices */
|
|
|
|
"/sys/kernel/config/target/vhost", /* for hostdev vhost_scsi devices */
|
|
|
|
"/etc/libvirt-sandbox/services/" /* for virt-sandbox service config */
|
2010-04-06 15:57:36 +00:00
|
|
|
};
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2019-10-15 11:55:26 +00:00
|
|
|
const int nropaths = G_N_ELEMENTS(restricted);
|
|
|
|
const int nrwpaths = G_N_ELEMENTS(restricted_rw);
|
|
|
|
const int nopaths = G_N_ELEMENTS(override);
|
virt-aa-helper: Improve valid_path
So, after some movement in virt-aa-helper, I've noticed the
virt-aa-helper-test failing. I've ran gdb (it took me a while to
realize how to do that) and this showed up immediately:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) bt
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x0000555555561a13 in array_starts_with (str=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", arr=0x7fffffffd160, size=-1540438016) at security/virt-aa-helper.c:525
#2 0x0000555555561d49 in valid_path (path=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", readonly=false) at security/virt-aa-helper.c:617
#3 0x0000555555562506 in vah_add_path (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw", recursive=false) at security/virt-aa-helper.c:823
#4 0x0000555555562693 in vah_add_file (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw") at security/virt-aa-helper.c:854
#5 0x0000555555562918 in add_file_path (disk=0x5555557d4440, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", depth=0, opaque=0x7fffffffd3e0) at security/virt-aa-helper.c:931
#6 0x00007ffff78f18b1 in virDomainDiskDefForeachPath (disk=0x5555557d4440, ignoreOpenFailure=true, iter=0x5555555628a6 <add_file_path>, opaque=0x7fffffffd3e0) at conf/domain_conf.c:23286
#7 0x0000555555562b5f in get_files (ctl=0x7fffffffd670) at security/virt-aa-helper.c:982
#8 0x0000555555564100 in vahParseArgv (ctl=0x7fffffffd670, argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1277
#9 0x00005555555643d6 in main (argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1332
So I've taken look at valid_path() because it is obviously
calling array_starts_with() with malformed @size. And here's the
result: there are two variables to hold the size of three arrays
and their value is recalculated before each call of
array_starts_with(). What if we just use three variables,
initialize them and do not touch them afterwards?
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-08-27 00:50:21 +00:00
|
|
|
|
2012-05-25 13:14:07 +00:00
|
|
|
if (path == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("bad pathname"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't allow double quotes, since we use them to quote the filename
|
|
|
|
* and this will confuse the apparmor parser.
|
|
|
|
*/
|
|
|
|
if (strchr(path, '"') != NULL)
|
|
|
|
return 1;
|
|
|
|
|
2009-11-13 14:22:20 +00:00
|
|
|
/* Require an absolute path */
|
|
|
|
if (STRNEQLEN(path, "/", 1))
|
|
|
|
return 1;
|
|
|
|
|
2015-07-01 08:15:29 +00:00
|
|
|
if (!virFileExists(path))
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("path does not exist, skipping file type checks"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2015-08-21 08:49:15 +00:00
|
|
|
/* overrides are always allowed */
|
virt-aa-helper: Improve valid_path
So, after some movement in virt-aa-helper, I've noticed the
virt-aa-helper-test failing. I've ran gdb (it took me a while to
realize how to do that) and this showed up immediately:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) bt
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x0000555555561a13 in array_starts_with (str=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", arr=0x7fffffffd160, size=-1540438016) at security/virt-aa-helper.c:525
#2 0x0000555555561d49 in valid_path (path=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", readonly=false) at security/virt-aa-helper.c:617
#3 0x0000555555562506 in vah_add_path (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw", recursive=false) at security/virt-aa-helper.c:823
#4 0x0000555555562693 in vah_add_file (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw") at security/virt-aa-helper.c:854
#5 0x0000555555562918 in add_file_path (disk=0x5555557d4440, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", depth=0, opaque=0x7fffffffd3e0) at security/virt-aa-helper.c:931
#6 0x00007ffff78f18b1 in virDomainDiskDefForeachPath (disk=0x5555557d4440, ignoreOpenFailure=true, iter=0x5555555628a6 <add_file_path>, opaque=0x7fffffffd3e0) at conf/domain_conf.c:23286
#7 0x0000555555562b5f in get_files (ctl=0x7fffffffd670) at security/virt-aa-helper.c:982
#8 0x0000555555564100 in vahParseArgv (ctl=0x7fffffffd670, argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1277
#9 0x00005555555643d6 in main (argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1332
So I've taken look at valid_path() because it is obviously
calling array_starts_with() with malformed @size. And here's the
result: there are two variables to hold the size of three arrays
and their value is recalculated before each call of
array_starts_with(). What if we just use three variables,
initialize them and do not touch them afterwards?
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-08-27 00:50:21 +00:00
|
|
|
if (array_starts_with(path, override, nopaths) == 0)
|
2015-08-21 08:49:15 +00:00
|
|
|
return 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2015-08-21 08:49:15 +00:00
|
|
|
/* allow read only paths upfront */
|
|
|
|
if (readonly) {
|
virt-aa-helper: Improve valid_path
So, after some movement in virt-aa-helper, I've noticed the
virt-aa-helper-test failing. I've ran gdb (it took me a while to
realize how to do that) and this showed up immediately:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) bt
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x0000555555561a13 in array_starts_with (str=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", arr=0x7fffffffd160, size=-1540438016) at security/virt-aa-helper.c:525
#2 0x0000555555561d49 in valid_path (path=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", readonly=false) at security/virt-aa-helper.c:617
#3 0x0000555555562506 in vah_add_path (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw", recursive=false) at security/virt-aa-helper.c:823
#4 0x0000555555562693 in vah_add_file (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw") at security/virt-aa-helper.c:854
#5 0x0000555555562918 in add_file_path (disk=0x5555557d4440, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", depth=0, opaque=0x7fffffffd3e0) at security/virt-aa-helper.c:931
#6 0x00007ffff78f18b1 in virDomainDiskDefForeachPath (disk=0x5555557d4440, ignoreOpenFailure=true, iter=0x5555555628a6 <add_file_path>, opaque=0x7fffffffd3e0) at conf/domain_conf.c:23286
#7 0x0000555555562b5f in get_files (ctl=0x7fffffffd670) at security/virt-aa-helper.c:982
#8 0x0000555555564100 in vahParseArgv (ctl=0x7fffffffd670, argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1277
#9 0x00005555555643d6 in main (argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1332
So I've taken look at valid_path() because it is obviously
calling array_starts_with() with malformed @size. And here's the
result: there are two variables to hold the size of three arrays
and their value is recalculated before each call of
array_starts_with(). What if we just use three variables,
initialize them and do not touch them afterwards?
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-08-27 00:50:21 +00:00
|
|
|
if (array_starts_with(path, restricted_rw, nrwpaths) == 0)
|
2015-08-21 08:49:15 +00:00
|
|
|
return 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2018-12-04 17:08:14 +00:00
|
|
|
/* disallow RW access to all paths in restricted and restriced_rw */
|
virt-aa-helper: Improve valid_path
So, after some movement in virt-aa-helper, I've noticed the
virt-aa-helper-test failing. I've ran gdb (it took me a while to
realize how to do that) and this showed up immediately:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) bt
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x0000555555561a13 in array_starts_with (str=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", arr=0x7fffffffd160, size=-1540438016) at security/virt-aa-helper.c:525
#2 0x0000555555561d49 in valid_path (path=0x5555557ce910 "/tmp/tmp.6nI2Fkv0KL/1.img", readonly=false) at security/virt-aa-helper.c:617
#3 0x0000555555562506 in vah_add_path (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw", recursive=false) at security/virt-aa-helper.c:823
#4 0x0000555555562693 in vah_add_file (buf=0x7fffffffd3e0, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", perms=0x555555581585 "rw") at security/virt-aa-helper.c:854
#5 0x0000555555562918 in add_file_path (disk=0x5555557d4440, path=0x5555557cb910 "/tmp/tmp.6nI2Fkv0KL/1.img", depth=0, opaque=0x7fffffffd3e0) at security/virt-aa-helper.c:931
#6 0x00007ffff78f18b1 in virDomainDiskDefForeachPath (disk=0x5555557d4440, ignoreOpenFailure=true, iter=0x5555555628a6 <add_file_path>, opaque=0x7fffffffd3e0) at conf/domain_conf.c:23286
#7 0x0000555555562b5f in get_files (ctl=0x7fffffffd670) at security/virt-aa-helper.c:982
#8 0x0000555555564100 in vahParseArgv (ctl=0x7fffffffd670, argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1277
#9 0x00005555555643d6 in main (argc=5, argv=0x7fffffffd7e8) at security/virt-aa-helper.c:1332
So I've taken look at valid_path() because it is obviously
calling array_starts_with() with malformed @size. And here's the
result: there are two variables to hold the size of three arrays
and their value is recalculated before each call of
array_starts_with(). What if we just use three variables,
initialize them and do not touch them afterwards?
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2015-08-27 00:50:21 +00:00
|
|
|
if ((array_starts_with(path, restricted, nropaths) == 0 ||
|
|
|
|
array_starts_with(path, restricted_rw, nrwpaths) == 0))
|
2015-08-21 08:49:15 +00:00
|
|
|
return 1;
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-11 21:59:50 +00:00
|
|
|
static int
|
|
|
|
verify_xpath_context(xmlXPathContextPtr ctxt)
|
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
|
|
|
|
if (!ctxt) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Invalid context"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2010-11-11 21:59:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check if have <name> */
|
|
|
|
if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Could not find <name>"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2010-11-11 21:59:50 +00:00
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
|
|
|
/* check if have <uuid> */
|
|
|
|
if (!(tmp = virXPathString("string(./uuid[1])", ctxt))) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Could not find <uuid>"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2010-11-11 21:59:50 +00:00
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
2020-01-06 21:57:44 +00:00
|
|
|
return 0;
|
2010-11-11 21:59:50 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 10:04:23 +00:00
|
|
|
/*
|
|
|
|
* Parse the xml we received to fill in the following:
|
2015-06-25 14:08:42 +00:00
|
|
|
* ctl->virtType
|
2015-06-25 11:01:40 +00:00
|
|
|
* ctl->os
|
2009-11-13 10:04:23 +00:00
|
|
|
* ctl->arch
|
|
|
|
*
|
|
|
|
* These are suitable for setting up a virCapsPtr
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
caps_mockup(vahControl * ctl, const char *xmlStr)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
xmlDocPtr xml = NULL;
|
|
|
|
xmlXPathContextPtr ctxt = NULL;
|
2012-12-10 22:28:09 +00:00
|
|
|
char *arch;
|
2009-11-13 10:04:23 +00:00
|
|
|
|
2011-09-14 14:17:57 +00:00
|
|
|
if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain_definition)"),
|
2011-09-08 13:25:01 +00:00
|
|
|
&ctxt))) {
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2017-08-14 12:31:52 +00:00
|
|
|
if (!virXMLNodeNameEqual(ctxt->node, "domain")) {
|
2011-11-25 12:23:03 +00:00
|
|
|
vah_error(NULL, 0, _("unexpected root element, expecting <domain>"));
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-11-11 21:59:50 +00:00
|
|
|
/* Quick sanity check for some required elements */
|
|
|
|
if (verify_xpath_context(ctxt) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2015-06-25 14:08:42 +00:00
|
|
|
ctl->virtType = virXPathString("string(./@type)", ctxt);
|
|
|
|
if (!ctl->virtType) {
|
|
|
|
vah_error(ctl, 0, _("domain type is not defined"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2015-06-25 11:01:40 +00:00
|
|
|
ctl->os = virXPathString("string(./os/type[1])", ctxt);
|
|
|
|
if (!ctl->os) {
|
2014-03-03 10:26:42 +00:00
|
|
|
vah_error(ctl, 0, _("os.type is not defined"));
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-12-10 22:28:09 +00:00
|
|
|
arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
|
|
|
|
if (!arch) {
|
|
|
|
ctl->arch = virArchFromHost();
|
|
|
|
} else {
|
|
|
|
ctl->arch = virArchFromString(arch);
|
|
|
|
VIR_FREE(arch);
|
2009-11-13 10:04:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
2014-03-25 06:57:22 +00:00
|
|
|
cleanup:
|
2012-10-17 09:23:12 +00:00
|
|
|
xmlFreeDoc(xml);
|
2009-11-13 10:04:23 +00:00
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-02-06 13:08:14 +00:00
|
|
|
virDomainDefParserConfig virAAHelperDomainDefParserConfig = {
|
|
|
|
.features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG |
|
|
|
|
VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN |
|
2019-01-18 20:57:32 +00:00
|
|
|
VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS |
|
|
|
|
VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING,
|
2018-02-06 13:08:14 +00:00
|
|
|
};
|
2011-11-07 10:17:28 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
|
|
|
get_definition(vahControl * ctl, const char *xmlStr)
|
|
|
|
{
|
2020-01-06 21:57:44 +00:00
|
|
|
int ostype, virtType;
|
2009-10-08 14:34:22 +00:00
|
|
|
virCapsGuestPtr guest; /* this is freed when caps is freed */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mock up some capabilities. We don't currently use these explicitly,
|
|
|
|
* but need them for virDomainDefParseString().
|
|
|
|
*/
|
2009-11-13 10:04:23 +00:00
|
|
|
if (caps_mockup(ctl, xmlStr) != 0)
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2014-07-14 12:56:13 +00:00
|
|
|
if ((ctl->caps = virCapabilitiesNew(ctl->arch, true, true)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2018-02-06 13:08:14 +00:00
|
|
|
if (!(ctl->xmlopt = virDomainXMLOptionNew(&virAAHelperDomainDefParserConfig,
|
|
|
|
NULL, NULL, NULL, NULL))) {
|
2013-03-05 15:17:24 +00:00
|
|
|
vah_error(ctl, 0, _("Failed to create XML config object"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2013-03-05 15:17:24 +00:00
|
|
|
}
|
|
|
|
|
2015-06-25 11:01:40 +00:00
|
|
|
if ((ostype = virDomainOSTypeFromString(ctl->os)) < 0) {
|
2015-04-17 22:09:16 +00:00
|
|
|
vah_error(ctl, 0, _("unknown OS type"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2015-04-17 22:09:16 +00:00
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if ((guest = virCapabilitiesAddGuest(ctl->caps,
|
2015-04-17 22:09:16 +00:00
|
|
|
ostype,
|
2009-11-13 10:04:23 +00:00
|
|
|
ctl->arch,
|
2009-10-08 14:34:22 +00:00
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2015-06-25 14:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((virtType = virDomainVirtTypeFromString(ctl->virtType)) < 0) {
|
|
|
|
vah_error(ctl, 0, _("unknown virtualization type"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2015-06-25 14:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest,
|
|
|
|
virtType,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL) == NULL) {
|
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2013-03-28 13:55:55 +00:00
|
|
|
ctl->def = virDomainDefParseString(xmlStr,
|
2019-11-27 12:29:21 +00:00
|
|
|
ctl->xmlopt, NULL,
|
2016-11-21 14:40:23 +00:00
|
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL |
|
2016-05-26 13:58:53 +00:00
|
|
|
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
|
2016-04-02 10:49:28 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (ctl->def == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not parse XML"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctl->def->name) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not find name in XML"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (valid_name(ctl->def->name) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("bad name"));
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 21:57:44 +00:00
|
|
|
return 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2016-01-18 10:22:32 +00:00
|
|
|
/**
|
|
|
|
* The permissions allowed are apparmor valid permissions and 'R'. 'R' stands for
|
|
|
|
* read with no explicit deny rule.
|
|
|
|
*/
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
2014-03-09 15:03:20 +00:00
|
|
|
vah_add_path(virBufferPtr buf, const char *path, const char *perms, bool recursive)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
int rc = -1;
|
|
|
|
bool readonly = true;
|
2016-01-18 10:22:32 +00:00
|
|
|
bool explicit_deny_rule = true;
|
|
|
|
char *sub = NULL;
|
2016-08-24 23:15:29 +00:00
|
|
|
char *perms_new = NULL;
|
2018-03-01 13:58:39 +00:00
|
|
|
char *pathdir = NULL;
|
|
|
|
char *pathtmp = NULL;
|
|
|
|
char *pathreal = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
return rc;
|
|
|
|
|
2009-11-13 14:22:20 +00:00
|
|
|
/* Skip files without an absolute path. Not having one confuses the
|
|
|
|
* apparmor parser and this also ensures things like tcp consoles don't
|
|
|
|
* get added to the profile.
|
|
|
|
*/
|
|
|
|
if (STRNEQLEN(path, "/", 1)) {
|
|
|
|
vah_warning(path);
|
2012-07-18 23:45:34 +00:00
|
|
|
vah_warning(_("skipped non-absolute path"));
|
2009-11-13 14:22:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-01 13:58:39 +00:00
|
|
|
/* files might be created by qemu later on and not exist right now.
|
|
|
|
* But realpath needs a valid path to work on, therefore:
|
|
|
|
* 1. walk the path to find longest valid path
|
|
|
|
* 2. get the realpath of that valid path
|
|
|
|
* 3. re-combine the realpath with the remaining suffix
|
|
|
|
* Note: A totally non existent path is used as-is
|
|
|
|
*/
|
2019-10-20 11:49:46 +00:00
|
|
|
pathdir = g_strdup(path);
|
2018-03-01 13:58:39 +00:00
|
|
|
while (!virFileExists(pathdir)) {
|
2019-12-20 16:48:02 +00:00
|
|
|
pathtmp = g_path_get_dirname(pathdir);
|
2018-03-01 13:58:39 +00:00
|
|
|
VIR_FREE(pathdir);
|
2019-10-16 11:45:15 +00:00
|
|
|
pathdir = g_steal_pointer(&pathtmp);
|
2018-03-01 13:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(pathdir) == 1) {
|
|
|
|
/* nothing of the path does exist yet */
|
2019-10-20 11:49:46 +00:00
|
|
|
tmp = g_strdup(path);
|
2018-03-01 13:58:39 +00:00
|
|
|
} else {
|
2019-10-20 11:49:46 +00:00
|
|
|
pathtmp = g_strdup(path + strlen(pathdir));
|
2018-03-01 13:58:39 +00:00
|
|
|
if ((pathreal = realpath(pathdir, NULL)) == NULL) {
|
|
|
|
vah_error(NULL, 0, pathdir);
|
|
|
|
vah_error(NULL, 0, _("could not find realpath"));
|
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
2019-10-22 13:26:14 +00:00
|
|
|
tmp = g_strdup_printf("%s%s", pathreal, pathtmp);
|
2014-09-03 19:39:21 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
perms_new = g_strdup(perms);
|
2016-08-24 23:15:29 +00:00
|
|
|
|
|
|
|
if (strchr(perms_new, 'w') != NULL) {
|
2009-10-08 14:34:22 +00:00
|
|
|
readonly = false;
|
2016-01-18 10:22:32 +00:00
|
|
|
explicit_deny_rule = false;
|
|
|
|
}
|
|
|
|
|
2016-08-24 23:15:29 +00:00
|
|
|
if ((sub = strchr(perms_new, 'R')) != NULL) {
|
2016-01-18 10:22:32 +00:00
|
|
|
/* Don't write the invalid R permission, replace it with 'r' */
|
|
|
|
sub[0] = 'r';
|
|
|
|
explicit_deny_rule = false;
|
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
rc = valid_path(tmp, readonly);
|
|
|
|
if (rc != 0) {
|
|
|
|
if (rc > 0) {
|
|
|
|
vah_error(NULL, 0, path);
|
2012-07-18 23:45:34 +00:00
|
|
|
vah_error(NULL, 0, _("skipped restricted file"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2015-06-24 07:56:04 +00:00
|
|
|
if (tmp[strlen(tmp) - 1] == '/')
|
|
|
|
tmp[strlen(tmp) - 1] = '\0';
|
|
|
|
|
2016-08-24 23:15:29 +00:00
|
|
|
virBufferAsprintf(buf, " \"%s%s\" %s,\n", tmp, recursive ? "/**" : "",
|
|
|
|
perms_new);
|
2016-01-18 10:22:32 +00:00
|
|
|
if (explicit_deny_rule) {
|
2013-05-07 10:28:50 +00:00
|
|
|
virBufferAddLit(buf, " # don't audit writes to readonly files\n");
|
2014-03-09 15:03:20 +00:00
|
|
|
virBufferAsprintf(buf, " deny \"%s%s\" w,\n", tmp, recursive ? "/**" : "");
|
|
|
|
}
|
|
|
|
if (recursive) {
|
|
|
|
/* allow reading (but not creating) the dir */
|
|
|
|
virBufferAsprintf(buf, " \"%s/\" r,\n", tmp);
|
2009-11-13 14:25:30 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2014-03-25 06:57:22 +00:00
|
|
|
cleanup:
|
2018-03-01 13:58:39 +00:00
|
|
|
VIR_FREE(pathdir);
|
|
|
|
VIR_FREE(pathtmp);
|
|
|
|
VIR_FREE(pathreal);
|
2016-08-24 23:15:29 +00:00
|
|
|
VIR_FREE(perms_new);
|
2009-12-09 23:00:50 +00:00
|
|
|
VIR_FREE(tmp);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-03-09 15:03:20 +00:00
|
|
|
static int
|
|
|
|
vah_add_file(virBufferPtr buf, const char *path, const char *perms)
|
|
|
|
{
|
|
|
|
return vah_add_path(buf, path, perms, false);
|
|
|
|
}
|
|
|
|
|
2011-09-28 07:43:39 +00:00
|
|
|
static int
|
|
|
|
vah_add_file_chardev(virBufferPtr buf,
|
|
|
|
const char *path,
|
|
|
|
const char *perms,
|
|
|
|
const int type)
|
|
|
|
{
|
|
|
|
char *pipe_in;
|
|
|
|
char *pipe_out;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
if (type == VIR_DOMAIN_CHR_TYPE_PIPE) {
|
|
|
|
/* add the pipe input */
|
2019-10-22 13:26:14 +00:00
|
|
|
pipe_in = g_strdup_printf("%s.in", path);
|
2011-09-28 07:43:39 +00:00
|
|
|
|
|
|
|
if (vah_add_file(buf, pipe_in, perms) != 0)
|
|
|
|
goto clean_pipe_in;
|
|
|
|
|
|
|
|
/* add the pipe output */
|
2019-10-22 13:26:14 +00:00
|
|
|
pipe_out = g_strdup_printf("%s.out", path);
|
2011-09-28 07:43:39 +00:00
|
|
|
|
|
|
|
if (vah_add_file(buf, pipe_out, perms) != 0)
|
|
|
|
goto clean_pipe_out;
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
clean_pipe_out:
|
|
|
|
VIR_FREE(pipe_out);
|
|
|
|
clean_pipe_in:
|
|
|
|
VIR_FREE(pipe_in);
|
|
|
|
} else {
|
|
|
|
/* add the file */
|
|
|
|
if (vah_add_file(buf, path, perms) != 0)
|
2020-01-06 21:57:44 +00:00
|
|
|
return -1;
|
2011-09-28 07:43:39 +00:00
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
file_iterate_hostdev_cb(virUSBDevicePtr dev G_GNUC_UNUSED,
|
2010-04-06 15:57:36 +00:00
|
|
|
const char *file, void *opaque)
|
|
|
|
{
|
|
|
|
virBufferPtr buf = opaque;
|
|
|
|
return vah_add_file(buf, file, "rw");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
file_iterate_pci_cb(virPCIDevicePtr dev G_GNUC_UNUSED,
|
2013-01-14 22:11:44 +00:00
|
|
|
const char *file, void *opaque)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
|
|
|
virBufferPtr buf = opaque;
|
|
|
|
return vah_add_file(buf, file, "rw");
|
|
|
|
}
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
static int
|
2019-10-08 15:53:21 +00:00
|
|
|
add_file_path(virStorageSourcePtr src,
|
2010-06-15 15:40:47 +00:00
|
|
|
size_t depth,
|
2019-10-08 15:50:55 +00:00
|
|
|
virBufferPtr buf)
|
2010-06-15 15:40:47 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2019-10-08 15:55:24 +00:00
|
|
|
/* execute the callback only for local storage */
|
|
|
|
if (!src->path || !virStorageSourceIsLocalStorage(src))
|
|
|
|
return 0;
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
if (depth == 0) {
|
2019-10-08 15:53:21 +00:00
|
|
|
if (src->readonly)
|
|
|
|
ret = vah_add_file(buf, src->path, "rk");
|
2010-06-15 15:40:47 +00:00
|
|
|
else
|
2019-10-08 15:53:21 +00:00
|
|
|
ret = vah_add_file(buf, src->path, "rwk");
|
2010-06-15 15:40:47 +00:00
|
|
|
} else {
|
2019-10-08 15:53:21 +00:00
|
|
|
ret = vah_add_file(buf, src->path, "rk");
|
2010-06-15 15:40:47 +00:00
|
|
|
}
|
|
|
|
|
2010-07-23 16:27:43 +00:00
|
|
|
if (ret != 0)
|
|
|
|
ret = -1;
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-10-08 15:40:30 +00:00
|
|
|
|
|
|
|
static int
|
2019-10-08 16:04:57 +00:00
|
|
|
storage_source_add_files(virStorageSourcePtr src,
|
2019-10-08 16:07:17 +00:00
|
|
|
virBufferPtr buf,
|
|
|
|
size_t depth)
|
2019-10-08 15:40:30 +00:00
|
|
|
{
|
|
|
|
virStorageSourcePtr tmp;
|
|
|
|
|
2019-10-08 16:04:57 +00:00
|
|
|
for (tmp = src; virStorageSourceIsBacking(tmp); tmp = tmp->backingStore) {
|
2019-10-08 15:55:24 +00:00
|
|
|
if (add_file_path(tmp, depth, buf) < 0)
|
|
|
|
return -1;
|
2019-10-08 15:40:30 +00:00
|
|
|
|
|
|
|
depth++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
|
|
|
get_files(vahControl * ctl)
|
|
|
|
{
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
int rc = -1;
|
Convert 'int i' to 'size_t i' in src/security files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2009-10-08 14:34:22 +00:00
|
|
|
char *uuid;
|
2019-10-17 10:48:10 +00:00
|
|
|
char *mem_path = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
2019-02-12 10:12:52 +00:00
|
|
|
bool needsVfio = false, needsvhost = false, needsgl = false;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
/* verify uuid is same as what we were given on the command line */
|
|
|
|
virUUIDFormat(ctl->def->uuid, uuidstr);
|
2019-10-22 13:26:14 +00:00
|
|
|
uuid = g_strdup_printf("%s%s", AA_PREFIX, uuidstr);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (STRNEQ(uuid, ctl->uuid)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("given uuid does not match XML uuid"));
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 19:57:23 +00:00
|
|
|
/* load the storage driver so that backing store can be accessed */
|
|
|
|
#ifdef WITH_STORAGE
|
2018-04-19 15:50:56 +00:00
|
|
|
virDriverLoadModule("storage", "storageRegister", false);
|
2017-07-26 19:57:23 +00:00
|
|
|
#endif
|
2017-07-18 07:43:41 +00:00
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
for (i = 0; i < ctl->def->ndisks; i++) {
|
2012-10-09 22:08:14 +00:00
|
|
|
virDomainDiskDefPtr disk = ctl->def->disks[i];
|
|
|
|
|
2014-04-18 12:49:54 +00:00
|
|
|
if (!virDomainDiskGetSource(disk))
|
2014-04-04 22:56:29 +00:00
|
|
|
continue;
|
2012-10-09 22:08:14 +00:00
|
|
|
/* XXX - if we knew the qemu user:group here we could send it in
|
|
|
|
* so that the open could be re-tried as that user:group.
|
|
|
|
*/
|
2018-06-04 07:00:47 +00:00
|
|
|
if (!virStorageSourceHasBacking(disk->src))
|
2018-06-04 08:25:17 +00:00
|
|
|
virStorageFileGetMetadata(disk->src, -1, -1, false);
|
2012-10-09 22:08:14 +00:00
|
|
|
|
2019-10-08 15:42:21 +00:00
|
|
|
/* XXX should handle open errors more careful than just ignoring them.
|
2012-01-12 18:24:45 +00:00
|
|
|
*/
|
2019-10-08 16:07:17 +00:00
|
|
|
if (storage_source_add_files(disk->src, &buf, 0) < 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2010-06-15 15:40:47 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ctl->def->nserials; i++)
|
2010-09-23 17:22:44 +00:00
|
|
|
if (ctl->def->serials[i] &&
|
2016-10-21 11:45:54 +00:00
|
|
|
(ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
|
|
|
|
ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->serials[i]->source->data.file.path &&
|
|
|
|
ctl->def->serials[i]->source->data.file.path[0] != '\0')
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->serials[i]->source->data.file.path,
|
2011-09-28 07:43:39 +00:00
|
|
|
"rw",
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->serials[i]->source->type) != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
for (i = 0; i < ctl->def->nconsoles; i++)
|
|
|
|
if (ctl->def->consoles[i] &&
|
2016-10-21 11:45:54 +00:00
|
|
|
(ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
|
|
|
|
ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->consoles[i]->source->data.file.path &&
|
|
|
|
ctl->def->consoles[i]->source->data.file.path[0] != '\0')
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
if (vah_add_file(&buf,
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->consoles[i]->source->data.file.path, "rw") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2013-05-21 07:21:21 +00:00
|
|
|
for (i = 0; i < ctl->def->nparallels; i++)
|
2010-09-23 17:22:44 +00:00
|
|
|
if (ctl->def->parallels[i] &&
|
2016-10-21 11:45:54 +00:00
|
|
|
(ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
|
|
|
|
ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->parallels[i]->source->data.file.path &&
|
|
|
|
ctl->def->parallels[i]->source->data.file.path[0] != '\0')
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->parallels[i]->source->data.file.path,
|
2011-09-28 07:43:39 +00:00
|
|
|
"rw",
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->parallels[i]->source->type) != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2010-09-23 17:22:44 +00:00
|
|
|
|
2013-05-21 07:21:21 +00:00
|
|
|
for (i = 0; i < ctl->def->nchannels; i++)
|
2010-09-23 17:22:44 +00:00
|
|
|
if (ctl->def->channels[i] &&
|
2016-10-21 11:45:54 +00:00
|
|
|
(ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
|
|
|
|
ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->channels[i]->source->data.file.path &&
|
|
|
|
ctl->def->channels[i]->source->data.file.path[0] != '\0')
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->channels[i]->source->data.file.path,
|
2011-09-28 07:43:39 +00:00
|
|
|
"rw",
|
2016-10-21 11:45:54 +00:00
|
|
|
ctl->def->channels[i]->source->type) != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2010-09-23 17:22:44 +00:00
|
|
|
|
2010-04-06 20:53:43 +00:00
|
|
|
if (ctl->def->os.kernel)
|
2009-10-08 14:34:22 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2010-04-06 20:53:43 +00:00
|
|
|
if (ctl->def->os.initrd)
|
2009-10-08 14:34:22 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2013-03-14 04:49:44 +00:00
|
|
|
|
|
|
|
if (ctl->def->os.dtb)
|
|
|
|
if (vah_add_file(&buf, ctl->def->os.dtb, "r") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2016-05-13 14:16:55 +00:00
|
|
|
if (ctl->def->os.slic_table)
|
|
|
|
if (vah_add_file(&buf, ctl->def->os.slic_table, "r") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2014-08-06 11:18:53 +00:00
|
|
|
if (ctl->def->os.loader && ctl->def->os.loader->path)
|
2017-08-17 08:55:43 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.loader->path, "rk") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2010-04-06 20:53:43 +00:00
|
|
|
|
2015-08-20 17:58:59 +00:00
|
|
|
if (ctl->def->os.loader && ctl->def->os.loader->nvram)
|
2017-08-17 08:55:43 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.loader->nvram, "rwk") != 0)
|
2015-08-20 17:58:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-12-11 20:20:30 +00:00
|
|
|
for (i = 0; i < ctl->def->ngraphics; i++) {
|
2016-06-08 08:35:37 +00:00
|
|
|
virDomainGraphicsDefPtr graphics = ctl->def->graphics[i];
|
|
|
|
size_t n;
|
2019-01-14 13:15:06 +00:00
|
|
|
const char *rendernode = virDomainGraphicsGetRenderNode(graphics);
|
|
|
|
|
|
|
|
if (rendernode) {
|
|
|
|
vah_add_file(&buf, rendernode, "rw");
|
2019-02-12 10:12:52 +00:00
|
|
|
needsgl = true;
|
2019-01-14 13:15:06 +00:00
|
|
|
} else {
|
|
|
|
if (virDomainGraphicsNeedsAutoRenderNode(graphics)) {
|
|
|
|
char *defaultRenderNode = virHostGetDRMRenderNode();
|
2019-02-12 10:12:52 +00:00
|
|
|
needsgl = true;
|
2019-01-14 13:15:06 +00:00
|
|
|
|
|
|
|
if (defaultRenderNode) {
|
|
|
|
vah_add_file(&buf, defaultRenderNode, "rw");
|
|
|
|
VIR_FREE(defaultRenderNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 08:35:37 +00:00
|
|
|
|
|
|
|
for (n = 0; n < graphics->nListens; n++) {
|
|
|
|
virDomainGraphicsListenDef listenObj = graphics->listens[n];
|
|
|
|
|
|
|
|
if (listenObj.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
|
|
|
|
listenObj.socket &&
|
|
|
|
vah_add_file(&buf, listenObj.socket, "rw"))
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-12-11 20:20:30 +00:00
|
|
|
}
|
|
|
|
|
2010-04-06 20:53:43 +00:00
|
|
|
if (ctl->def->ngraphics == 1 &&
|
|
|
|
ctl->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL)
|
|
|
|
if (vah_add_file(&buf, ctl->def->graphics[0]->data.sdl.xauth,
|
|
|
|
"r") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ctl->def->nhostdevs; i++)
|
|
|
|
if (ctl->def->hostdevs[i]) {
|
|
|
|
virDomainHostdevDefPtr dev = ctl->def->hostdevs[i];
|
2014-07-03 19:43:05 +00:00
|
|
|
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
|
2009-10-08 14:34:22 +00:00
|
|
|
switch (dev->source.subsys.type) {
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
2013-01-14 22:11:44 +00:00
|
|
|
virUSBDevicePtr usb =
|
2014-07-03 19:43:05 +00:00
|
|
|
virUSBDeviceNew(usbsrc->bus, usbsrc->device, NULL);
|
2010-01-11 16:40:46 +00:00
|
|
|
|
|
|
|
if (usb == NULL)
|
|
|
|
continue;
|
|
|
|
|
2017-09-20 14:59:06 +00:00
|
|
|
if (virHostdevFindUSBDevice(dev, true, &usb) < 0)
|
|
|
|
continue;
|
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
rc = virUSBDeviceFileIterate(usb, file_iterate_hostdev_cb, &buf);
|
|
|
|
virUSBDeviceFree(usb);
|
2010-01-11 16:40:46 +00:00
|
|
|
if (rc != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-06 15:57:36 +00:00
|
|
|
|
2018-11-22 12:55:02 +00:00
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
|
|
|
|
virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
|
|
|
|
switch ((virMediatedDeviceModelType) mdevsrc->model) {
|
|
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_PCI:
|
|
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_AP:
|
|
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_CCW:
|
|
|
|
needsVfio = true;
|
|
|
|
break;
|
|
|
|
case VIR_MDEV_MODEL_TYPE_LAST:
|
|
|
|
default:
|
|
|
|
virReportEnumRangeError(virMediatedDeviceModelType,
|
|
|
|
mdevsrc->model);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
2013-01-14 22:11:44 +00:00
|
|
|
virPCIDevicePtr pci = virPCIDeviceNew(
|
2013-03-18 19:56:12 +00:00
|
|
|
dev->source.subsys.u.pci.addr.domain,
|
|
|
|
dev->source.subsys.u.pci.addr.bus,
|
|
|
|
dev->source.subsys.u.pci.addr.slot,
|
|
|
|
dev->source.subsys.u.pci.addr.function);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2014-03-13 11:59:32 +00:00
|
|
|
virDomainHostdevSubsysPCIBackendType backend = dev->source.subsys.u.pci.backend;
|
2014-03-25 11:48:26 +00:00
|
|
|
if (backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO ||
|
|
|
|
backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) {
|
|
|
|
needsVfio = true;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (pci == NULL)
|
|
|
|
continue;
|
|
|
|
|
2013-01-14 22:11:44 +00:00
|
|
|
rc = virPCIDeviceFileIterate(pci, file_iterate_pci_cb, &buf);
|
|
|
|
virPCIDeviceFree(pci);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2010-04-06 15:57:36 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
default:
|
|
|
|
rc = 0;
|
|
|
|
break;
|
|
|
|
} /* switch */
|
|
|
|
}
|
|
|
|
|
2014-03-09 15:03:20 +00:00
|
|
|
for (i = 0; i < ctl->def->nfss; i++) {
|
|
|
|
if (ctl->def->fss[i] &&
|
|
|
|
ctl->def->fss[i]->type == VIR_DOMAIN_FS_TYPE_MOUNT &&
|
|
|
|
(ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH ||
|
|
|
|
ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) &&
|
2014-08-20 11:00:30 +00:00
|
|
|
ctl->def->fss[i]->src) {
|
2014-03-09 15:03:20 +00:00
|
|
|
virDomainFSDefPtr fs = ctl->def->fss[i];
|
|
|
|
|
2015-11-17 09:11:30 +00:00
|
|
|
/* We don't need to add deny rw rules for readonly mounts,
|
|
|
|
* this can only lead to troubles when mounting / readonly.
|
|
|
|
*/
|
2016-01-18 10:22:32 +00:00
|
|
|
if (vah_add_path(&buf, fs->src->path, fs->readonly ? "R" : "rw", true) != 0)
|
2014-03-09 15:03:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-20 10:11:13 +00:00
|
|
|
for (i = 0; i < ctl->def->ninputs; i++) {
|
|
|
|
if (ctl->def->inputs[i] &&
|
|
|
|
ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) {
|
|
|
|
if (vah_add_file(&buf, ctl->def->inputs[i]->source.evdev, "rw") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-01 08:15:29 +00:00
|
|
|
for (i = 0; i < ctl->def->nnets; i++) {
|
|
|
|
if (ctl->def->nets[i] &&
|
|
|
|
ctl->def->nets[i]->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER &&
|
|
|
|
ctl->def->nets[i]->data.vhostuser) {
|
|
|
|
virDomainChrSourceDefPtr vhu = ctl->def->nets[i]->data.vhostuser;
|
|
|
|
|
|
|
|
if (vah_add_file_chardev(&buf, vhu->data.nix.path, "rw",
|
|
|
|
vhu->type) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-20 10:13:54 +00:00
|
|
|
for (i = 0; i < ctl->def->nmems; i++) {
|
|
|
|
if (ctl->def->mems[i] &&
|
|
|
|
ctl->def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
|
|
|
|
if (vah_add_file(&buf, ctl->def->mems[i]->nvdimmPath, "rw") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 18:56:09 +00:00
|
|
|
for (i = 0; i < ctl->def->nsysinfo; i++) {
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
for (j = 0; j < ctl->def->sysinfo[i]->nfw_cfgs; j++) {
|
|
|
|
virSysinfoFWCfgDefPtr f = &ctl->def->sysinfo[i]->fw_cfgs[j];
|
|
|
|
|
|
|
|
if (f->file &&
|
|
|
|
vah_add_file(&buf, f->file, "r") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-17 10:48:10 +00:00
|
|
|
for (i = 0; i < ctl->def->nshmems; i++) {
|
|
|
|
virDomainShmemDef *shmem = ctl->def->shmems[i];
|
|
|
|
/* explicit server paths can be on any model to overwrites defaults.
|
|
|
|
* When the server path is enabled, use it - otherwise fallback to
|
|
|
|
* model dependent defaults. */
|
|
|
|
if (shmem->server.enabled &&
|
|
|
|
shmem->server.chr.data.nix.path) {
|
|
|
|
if (vah_add_file(&buf, shmem->server.chr.data.nix.path,
|
|
|
|
"rw") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
switch (shmem->model) {
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
|
|
|
|
/* until exposed, recreate qemuBuildShmemBackendMemProps */
|
|
|
|
mem_path = g_strdup_printf("/dev/shm/%s", shmem->name);
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL:
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM:
|
|
|
|
/* until exposed, recreate qemuDomainPrepareShmemChardev */
|
|
|
|
mem_path = g_strdup_printf("/var/lib/libvirt/shmem-%s-sock",
|
|
|
|
shmem->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mem_path != NULL) {
|
|
|
|
if (vah_add_file(&buf, mem_path, "rw") != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-19 03:33:46 +00:00
|
|
|
if (ctl->def->tpm) {
|
|
|
|
char *shortName = NULL;
|
|
|
|
const char *tpmpath = NULL;
|
|
|
|
|
2020-06-10 18:11:46 +00:00
|
|
|
if (ctl->def->tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR) {
|
2018-05-19 03:33:46 +00:00
|
|
|
shortName = virDomainDefGetShortName(ctl->def);
|
|
|
|
|
|
|
|
switch (ctl->def->tpm->version) {
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_1_2:
|
|
|
|
tpmpath = "tpm1.2";
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_2_0:
|
|
|
|
tpmpath = "tpm2";
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_DEFAULT:
|
|
|
|
case VIR_DOMAIN_TPM_VERSION_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unix socket for QEMU and swtpm to use */
|
|
|
|
virBufferAsprintf(&buf,
|
2019-08-20 15:05:12 +00:00
|
|
|
" \"%s/libvirt/qemu/swtpm/%s-swtpm.sock\" rw,\n",
|
|
|
|
RUNSTATEDIR, shortName);
|
2018-05-19 03:33:46 +00:00
|
|
|
/* Paths for swtpm to use: give it access to its state
|
|
|
|
* directory, log, and PID files.
|
|
|
|
*/
|
|
|
|
virBufferAsprintf(&buf,
|
2019-09-24 19:25:14 +00:00
|
|
|
" \"%s/lib/libvirt/swtpm/%s/%s/**\" rwk,\n",
|
2018-05-19 03:33:46 +00:00
|
|
|
LOCALSTATEDIR, uuidstr, tpmpath);
|
|
|
|
virBufferAsprintf(&buf,
|
2019-09-24 19:25:14 +00:00
|
|
|
" \"%s/log/swtpm/libvirt/qemu/%s-swtpm.log\" w,\n",
|
2018-05-19 03:33:46 +00:00
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
|
|
|
virBufferAsprintf(&buf,
|
2019-08-20 15:05:12 +00:00
|
|
|
" \"%s/libvirt/qemu/swtpm/%s-swtpm.pid\" rw,\n",
|
|
|
|
RUNSTATEDIR, shortName);
|
2018-05-19 03:33:46 +00:00
|
|
|
|
|
|
|
VIR_FREE(shortName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 17:11:41 +00:00
|
|
|
for (i = 0; i < ctl->def->nsmartcards; i++) {
|
|
|
|
virDomainSmartcardDefPtr sc = ctl->def->smartcards[i];
|
|
|
|
virDomainSmartcardType sc_type = sc->type;
|
|
|
|
char *sc_db = (char *)VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
|
|
|
|
if (sc->data.cert.database)
|
|
|
|
sc_db = sc->data.cert.database;
|
|
|
|
switch (sc_type) {
|
|
|
|
/*
|
|
|
|
* Note: At time of writing, to get this working, qemu seccomp sandbox has
|
|
|
|
* to be disabled or the host must be running QEMU with commit
|
|
|
|
* 9a1565a03b79d80b236bc7cc2dbce52a2ef3a1b8.
|
|
|
|
* It's possibly due to libcacard:vcard_emul_new_event_thread(), which calls
|
|
|
|
* PR_CreateThread(), which calls {g,s}etpriority(). And resourcecontrol seccomp
|
|
|
|
* filter forbids it (cf src/qemu/qemu_command.c which seems to always use
|
|
|
|
* resourcecontrol=deny).
|
|
|
|
*/
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
|
|
|
|
virBufferAddLit(&buf, " \"/etc/pki/nssdb/{,*}\" rk,\n");
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
|
|
|
|
virBufferAsprintf(&buf, " \"%s/{,*}\" rk,\n", sc_db);
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* Nothing to do for passthrough, as the smartcard
|
|
|
|
* access is done through TCP or Spice
|
|
|
|
*/
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-18 03:20:59 +00:00
|
|
|
if (ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
|
|
|
|
for (i = 0; i < ctl->def->nnets; i++) {
|
|
|
|
virDomainNetDefPtr net = ctl->def->nets[i];
|
2019-01-17 22:06:28 +00:00
|
|
|
if (net && virDomainNetGetModelString(net)) {
|
2014-06-18 03:20:59 +00:00
|
|
|
if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU)
|
|
|
|
continue;
|
2019-01-21 22:59:02 +00:00
|
|
|
if (!virDomainNetIsVirtioModel(net))
|
2014-06-18 03:20:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
needsvhost = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (needsvhost)
|
2017-09-20 14:59:09 +00:00
|
|
|
virBufferAddLit(&buf, " \"/dev/vhost-net\" rw,\n");
|
2014-06-18 03:20:59 +00:00
|
|
|
|
2014-03-25 11:48:26 +00:00
|
|
|
if (needsVfio) {
|
2017-09-20 14:59:09 +00:00
|
|
|
virBufferAddLit(&buf, " \"/dev/vfio/vfio\" rw,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/dev/vfio/[0-9]*\" rw,\n");
|
2014-03-25 11:48:26 +00:00
|
|
|
}
|
2019-02-12 10:12:52 +00:00
|
|
|
if (needsgl) {
|
|
|
|
/* if using gl all sorts of further dri related paths will be needed */
|
|
|
|
virBufferAddLit(&buf, " # DRI/Mesa/(e)GL config and driver paths\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/lib{,32,64}/dri/*.so*\" mr,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/lib/@{multiarch}/dri/*.so*\" mr,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/lib/fglrx/dri/*.so*\" mr,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/etc/drirc\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/share/drirc.d/{,*.conf}\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/etc/glvnd/egl_vendor.d/{,*}\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/share/glvnd/egl_vendor.d/{,*}\" r,\n");
|
2019-03-01 06:25:59 +00:00
|
|
|
virBufferAddLit(&buf, " \"/usr/share/egl/egl_external_platform.d/\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/usr/share/egl/egl_external_platform.d/*\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/proc/modules\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/proc/driver/nvidia/params\" r,\n");
|
|
|
|
virBufferAddLit(&buf, " \"/dev/nvidiactl\" rw,\n");
|
2019-02-12 10:12:52 +00:00
|
|
|
virBufferAddLit(&buf, " # Probe DRI device attributes\n");
|
|
|
|
virBufferAddLit(&buf, " \"/dev/dri/\" r,\n");
|
2019-03-05 12:38:38 +00:00
|
|
|
virBufferAddLit(&buf, " \"/sys/devices/**/{uevent,vendor,device,subsystem_vendor,subsystem_device}\" r,\n");
|
2019-02-12 10:12:52 +00:00
|
|
|
virBufferAddLit(&buf, " # dri libs will trigger that, but t is not requited and DAC would deny it anyway\n");
|
|
|
|
virBufferAddLit(&buf, " deny \"/var/lib/libvirt/.cache/\" w,\n");
|
|
|
|
}
|
2014-03-25 11:48:26 +00:00
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->newfile)
|
2017-10-24 14:54:59 +00:00
|
|
|
if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
ctl->files = virBufferContentAndReset(&buf);
|
|
|
|
|
2014-03-25 06:57:22 +00:00
|
|
|
cleanup:
|
2019-10-17 10:48:10 +00:00
|
|
|
VIR_FREE(mem_path);
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(uuid);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vahParseArgv(vahControl * ctl, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int arg, idx = 0;
|
|
|
|
struct option opt[] = {
|
|
|
|
{"add", 0, 0, 'a'},
|
|
|
|
{"create", 0, 0, 'c'},
|
|
|
|
{"dryrun", 0, 0, 'd'},
|
|
|
|
{"delete", 0, 0, 'D'},
|
|
|
|
{"add-file", 0, 0, 'f'},
|
2010-09-30 20:54:56 +00:00
|
|
|
{"append-file", 0, 0, 'F'},
|
2009-10-08 14:34:22 +00:00
|
|
|
{"help", 0, 0, 'h'},
|
|
|
|
{"replace", 0, 0, 'r'},
|
|
|
|
{"remove", 0, 0, 'R'},
|
|
|
|
{"uuid", 1, 0, 'u'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt,
|
2009-10-08 14:34:22 +00:00
|
|
|
&idx)) != -1) {
|
|
|
|
switch (arg) {
|
|
|
|
case 'a':
|
|
|
|
ctl->cmd = 'a';
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
ctl->cmd = 'c';
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
ctl->dryrun = true;
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
ctl->cmd = 'D';
|
|
|
|
break;
|
|
|
|
case 'f':
|
2010-09-30 20:54:56 +00:00
|
|
|
case 'F':
|
2019-10-20 11:49:46 +00:00
|
|
|
ctl->newfile = g_strdup(optarg);
|
2010-09-30 20:54:56 +00:00
|
|
|
ctl->append = arg == 'F';
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
vah_usage();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
ctl->cmd = 'r';
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
ctl->cmd = 'R';
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid UUID"));
|
2018-04-25 12:42:34 +00:00
|
|
|
if (virStrcpy((char *)ctl->uuid, optarg,
|
2018-07-20 07:50:37 +00:00
|
|
|
PROFILE_NAME_SIZE) < 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("error copying UUID"));
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
default:
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("unsupported option"));
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (strchr("acDrR", ctl->cmd) == NULL)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("bad command"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (valid_uuid(ctl->uuid) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid UUID"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (!ctl->cmd) {
|
|
|
|
vah_usage();
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctl->cmd == 'c' || ctl->cmd == 'r') {
|
|
|
|
char *xmlStr = NULL;
|
|
|
|
if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not read xml file"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
|
|
|
|
VIR_FREE(xmlStr);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not get VM definition"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
VIR_FREE(xmlStr);
|
|
|
|
|
|
|
|
if (get_files(ctl) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid VM definition"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* virt-aa-helper -c -u UUID < file.xml
|
|
|
|
* virt-aa-helper -r -u UUID [-f <file>] < file.xml
|
|
|
|
* virt-aa-helper -a -u UUID
|
|
|
|
* virt-aa-helper -R -u UUID
|
|
|
|
* virt-aa-helper -D -u UUID
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
vahControl _ctl, *ctl = &_ctl;
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
int rc = -1;
|
2011-04-03 09:21:17 +00:00
|
|
|
char *profile = NULL;
|
|
|
|
char *include_file = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2016-04-12 22:29:52 +00:00
|
|
|
if (virGettextInitialize() < 0 ||
|
2014-11-20 10:31:44 +00:00
|
|
|
virErrorInitialize() < 0) {
|
|
|
|
fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2019-08-29 10:52:08 +00:00
|
|
|
virFileActivateDirOverrideForProg(argv[0]);
|
2017-07-18 07:43:41 +00:00
|
|
|
|
2015-06-25 08:36:52 +00:00
|
|
|
/* Initialize the log system */
|
|
|
|
virLogSetFromEnv();
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
/* clear the environment */
|
|
|
|
environ = NULL;
|
2019-12-18 17:16:19 +00:00
|
|
|
if (g_setenv("PATH", "/sbin:/usr/sbin", TRUE) == FALSE)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not set PATH"));
|
|
|
|
|
2014-11-12 16:41:38 +00:00
|
|
|
/* ensure the traditional IFS setting */
|
2019-12-18 17:16:19 +00:00
|
|
|
if (g_setenv("IFS", " \t\n", TRUE) == FALSE)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not set IFS"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (!(progname = strrchr(argv[0], '/')))
|
|
|
|
progname = argv[0];
|
|
|
|
else
|
|
|
|
progname++;
|
|
|
|
|
|
|
|
memset(ctl, 0, sizeof(vahControl));
|
|
|
|
|
|
|
|
if (vahParseArgv(ctl, argc, argv) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not parse arguments"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
profile = g_strdup_printf("%s/%s", APPARMOR_DIR "/libvirt", ctl->uuid);
|
|
|
|
include_file = g_strdup_printf("%s/%s.files", APPARMOR_DIR "/libvirt", ctl->uuid);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2014-09-03 19:39:21 +00:00
|
|
|
if (ctl->cmd == 'a') {
|
2009-10-08 14:34:22 +00:00
|
|
|
rc = parserLoad(ctl->uuid);
|
2014-09-03 19:39:21 +00:00
|
|
|
} else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
|
2009-10-08 14:34:22 +00:00
|
|
|
rc = parserRemove(ctl->uuid);
|
|
|
|
if (ctl->cmd == 'D') {
|
|
|
|
unlink(include_file);
|
|
|
|
unlink(profile);
|
|
|
|
}
|
|
|
|
} else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
|
|
|
|
char *included_files = NULL;
|
|
|
|
|
2014-11-13 14:29:21 +00:00
|
|
|
if (ctl->cmd == 'c' && virFileExists(profile))
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("profile exists"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->append && ctl->newfile) {
|
2017-10-24 14:54:59 +00:00
|
|
|
if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2010-09-30 20:54:56 +00:00
|
|
|
} else {
|
2014-03-24 17:20:20 +00:00
|
|
|
if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU ||
|
|
|
|
ctl->def->virtType == VIR_DOMAIN_VIRT_KQEMU ||
|
|
|
|
ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
|
2014-03-03 10:26:42 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n",
|
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
2015-08-29 16:05:50 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/lib/libvirt/qemu/domain-%s/monitor.sock\" rw,\n",
|
2014-03-03 10:26:42 +00:00
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
2016-04-02 10:49:28 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/lib/libvirt/qemu/domain-%d-%.*s/*\" rw,\n",
|
|
|
|
LOCALSTATEDIR, ctl->def->id, 20, ctl->def->name);
|
2019-08-20 15:05:12 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/libvirt/**/%s.pid\" rwk,\n",
|
|
|
|
RUNSTATEDIR, ctl->def->name);
|
|
|
|
virBufferAsprintf(&buf, " \"%s/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
|
|
|
|
RUNSTATEDIR, ctl->def->name);
|
2014-03-03 10:26:42 +00:00
|
|
|
}
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->files)
|
2011-04-14 19:27:47 +00:00
|
|
|
virBufferAdd(&buf, ctl->files, -1);
|
2010-09-30 20:54:56 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
included_files = virBufferContentAndReset(&buf);
|
|
|
|
|
|
|
|
/* (re)create the include file using included_files */
|
|
|
|
if (ctl->dryrun) {
|
|
|
|
vah_info(include_file);
|
|
|
|
vah_info(included_files);
|
|
|
|
rc = 0;
|
2014-07-11 20:09:05 +00:00
|
|
|
} else if (ctl->def->virtType == VIR_DOMAIN_VIRT_LXC) {
|
|
|
|
rc = 0;
|
|
|
|
} else if ((rc = update_include_file(include_file,
|
2010-09-30 20:54:56 +00:00
|
|
|
included_files,
|
2014-07-11 20:09:05 +00:00
|
|
|
ctl->append)) != 0) {
|
2013-04-29 17:38:21 +00:00
|
|
|
goto cleanup;
|
2014-07-11 20:09:05 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* create the profile from TEMPLATE */
|
|
|
|
if (ctl->cmd == 'c') {
|
|
|
|
char *tmp = NULL;
|
2019-10-22 13:26:14 +00:00
|
|
|
tmp = g_strdup_printf(" #include <libvirt/%s.files>\n", ctl->uuid);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (ctl->dryrun) {
|
|
|
|
vah_info(profile);
|
|
|
|
vah_info(ctl->uuid);
|
|
|
|
vah_info(tmp);
|
|
|
|
rc = 0;
|
2014-03-03 10:26:42 +00:00
|
|
|
} else if ((rc = create_profile(profile, ctl->uuid, tmp,
|
|
|
|
ctl->def->virtType)) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not create profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
unlink(include_file);
|
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == 0 && !ctl->dryrun) {
|
|
|
|
if (ctl->cmd == 'c')
|
|
|
|
rc = parserLoad(ctl->uuid);
|
|
|
|
else
|
|
|
|
rc = parserReplace(ctl->uuid);
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
if (rc != 0) {
|
|
|
|
unlink(include_file);
|
|
|
|
if (ctl->cmd == 'c')
|
|
|
|
unlink(profile);
|
|
|
|
}
|
|
|
|
}
|
2013-04-29 17:38:21 +00:00
|
|
|
cleanup:
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(included_files);
|
|
|
|
}
|
|
|
|
|
|
|
|
vahDeinit(ctl);
|
2011-04-03 09:21:17 +00:00
|
|
|
|
|
|
|
VIR_FREE(profile);
|
|
|
|
VIR_FREE(include_file);
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
|
|
}
|