mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-31 10:23:09 +00:00
03c532cf97
All our supported Linux distros now have this header. It has never existed on FreeBSD / macOS / Mingw. Reviewed-by: Pavel Hrdina <phrdina@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
972 lines
30 KiB
C
972 lines
30 KiB
C
/*
|
|
* domain_audit.c: Domain audit management
|
|
*
|
|
* Copyright (C) 2006-2014 Red Hat, Inc.
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
*
|
|
* 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
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef __linux__
|
|
# include <sys/sysmacros.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include "domain_audit.h"
|
|
#include "viraudit.h"
|
|
#include "viruuid.h"
|
|
#include "virlog.h"
|
|
#include "viralloc.h"
|
|
#include "virstring.h"
|
|
|
|
VIR_LOG_INIT("conf.domain_audit");
|
|
|
|
/* Return nn:mm in hex for block and character devices, and NULL
|
|
* for other file types, stat failure, or allocation failure. */
|
|
#if defined major && defined minor
|
|
static char *
|
|
virDomainAuditGetRdev(const char *path)
|
|
{
|
|
char *ret = NULL;
|
|
struct stat sb;
|
|
|
|
if (stat(path, &sb) == 0 &&
|
|
(S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode))) {
|
|
int maj = major(sb.st_rdev);
|
|
int min = minor(sb.st_rdev);
|
|
ret = g_strdup_printf("%02X:%02X", maj, min);
|
|
}
|
|
return ret;
|
|
}
|
|
#else
|
|
static char *
|
|
virDomainAuditGetRdev(const char *path G_GNUC_UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
static const char *
|
|
virDomainAuditGetVirtType(virDomainDefPtr def)
|
|
{
|
|
const char *virt;
|
|
|
|
if (!(virt = virDomainVirtTypeToString(def->virtType))) {
|
|
VIR_WARN("Unexpected virt type %d while encoding audit message",
|
|
def->virtType);
|
|
virt = "?";
|
|
}
|
|
|
|
return virt;
|
|
}
|
|
|
|
|
|
static void
|
|
virDomainAuditGenericDev(virDomainObjPtr vm,
|
|
const char *type,
|
|
const char *oldsrcpath,
|
|
const char *newsrcpath,
|
|
const char *reason,
|
|
bool success)
|
|
{
|
|
g_autofree char *newdev = NULL;
|
|
g_autofree char *olddev = NULL;
|
|
g_autofree char *vmname = NULL;
|
|
g_autofree char *oldsrc = NULL;
|
|
g_autofree char *newsrc = NULL;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
/* if both new and old source aren't provided don't log anything */
|
|
if (!newsrcpath && !oldsrcpath)
|
|
return;
|
|
|
|
newdev = g_strdup_printf("new-%s", type);
|
|
olddev = g_strdup_printf("old-%s", type);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name)))
|
|
return;
|
|
|
|
if (!(newsrc = virAuditEncode(newdev, VIR_AUDIT_STR(newsrcpath))))
|
|
return;
|
|
|
|
if (!(oldsrc = virAuditEncode(olddev, VIR_AUDIT_STR(oldsrcpath))))
|
|
return;
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=%s reason=%s %s uuid=%s %s %s",
|
|
virt, type, reason, vmname, uuidstr, oldsrc, newsrc);
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditChardev(virDomainObjPtr vm,
|
|
virDomainChrDefPtr oldDef,
|
|
virDomainChrDefPtr newDef,
|
|
const char *reason,
|
|
bool success)
|
|
{
|
|
virDomainChrSourceDefPtr oldsrc = NULL;
|
|
virDomainChrSourceDefPtr newsrc = NULL;
|
|
|
|
if (oldDef)
|
|
oldsrc = oldDef->source;
|
|
|
|
if (newDef)
|
|
newsrc = newDef->source;
|
|
|
|
virDomainAuditGenericDev(vm, "chardev",
|
|
virDomainChrSourceDefGetPath(oldsrc),
|
|
virDomainChrSourceDefGetPath(newsrc),
|
|
reason, success);
|
|
}
|
|
|
|
|
|
static void
|
|
virDomainAuditSmartcard(virDomainObjPtr vm,
|
|
virDomainSmartcardDefPtr def,
|
|
const char *reason,
|
|
bool success)
|
|
{
|
|
const char *database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
|
|
size_t i;
|
|
|
|
if (def) {
|
|
switch ((virDomainSmartcardType) def->type) {
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
|
|
virDomainAuditGenericDev(vm, "smartcard",
|
|
NULL, "nss-smartcard-device",
|
|
reason, success);
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
|
|
for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) {
|
|
virDomainAuditGenericDev(vm, "smartcard", NULL,
|
|
def->data.cert.file[i],
|
|
reason, success);
|
|
}
|
|
|
|
if (def->data.cert.database)
|
|
database = def->data.cert.database;
|
|
|
|
virDomainAuditGenericDev(vm, "smartcard",
|
|
NULL, database,
|
|
reason, success);
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
|
|
virDomainAuditGenericDev(vm, "smartcard", NULL,
|
|
virDomainChrSourceDefGetPath(def->data.passthru),
|
|
reason, success);
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_LAST:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditDisk(virDomainObjPtr vm,
|
|
virStorageSourcePtr oldDef,
|
|
virStorageSourcePtr newDef,
|
|
const char *reason,
|
|
bool success)
|
|
{
|
|
const char *oldsrc = NULL;
|
|
const char *newsrc = NULL;
|
|
|
|
if (oldDef && virStorageSourceIsLocalStorage(oldDef))
|
|
oldsrc = oldDef->path;
|
|
|
|
if (newDef && virStorageSourceIsLocalStorage(newDef))
|
|
newsrc = newDef->path;
|
|
|
|
virDomainAuditGenericDev(vm, "disk", oldsrc, newsrc, reason, success);
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditRNG(virDomainObjPtr vm,
|
|
virDomainRNGDefPtr oldDef, virDomainRNGDefPtr newDef,
|
|
const char *reason, bool success)
|
|
{
|
|
const char *newsrcpath = NULL;
|
|
const char *oldsrcpath = NULL;
|
|
|
|
if (newDef) {
|
|
switch ((virDomainRNGBackend) newDef->backend) {
|
|
case VIR_DOMAIN_RNG_BACKEND_RANDOM:
|
|
newsrcpath = newDef->source.file;
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_EGD:
|
|
newsrcpath = virDomainChrSourceDefGetPath(newDef->source.chardev);
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_BUILTIN:
|
|
case VIR_DOMAIN_RNG_BACKEND_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (oldDef) {
|
|
switch ((virDomainRNGBackend) oldDef->backend) {
|
|
case VIR_DOMAIN_RNG_BACKEND_RANDOM:
|
|
oldsrcpath = oldDef->source.file;
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_EGD:
|
|
oldsrcpath = virDomainChrSourceDefGetPath(oldDef->source.chardev);
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_BUILTIN:
|
|
case VIR_DOMAIN_RNG_BACKEND_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
virDomainAuditGenericDev(vm, "rng", oldsrcpath, newsrcpath, reason, success);
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditFS(virDomainObjPtr vm,
|
|
virDomainFSDefPtr oldDef, virDomainFSDefPtr newDef,
|
|
const char *reason, bool success)
|
|
{
|
|
virDomainAuditGenericDev(vm, "fs",
|
|
oldDef ? oldDef->src->path : NULL,
|
|
newDef ? newDef->src->path : NULL,
|
|
reason, success);
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditNet(virDomainObjPtr vm,
|
|
virDomainNetDefPtr oldDef, virDomainNetDefPtr newDef,
|
|
const char *reason, bool success)
|
|
{
|
|
char newMacstr[VIR_MAC_STRING_BUFLEN];
|
|
char oldMacstr[VIR_MAC_STRING_BUFLEN];
|
|
|
|
if (oldDef)
|
|
virMacAddrFormat(&oldDef->mac, oldMacstr);
|
|
|
|
if (newDef)
|
|
virMacAddrFormat(&newDef->mac, newMacstr);
|
|
|
|
virDomainAuditGenericDev(vm, "net",
|
|
oldDef ? oldMacstr : NULL,
|
|
newDef ? newMacstr : NULL,
|
|
reason, success);
|
|
}
|
|
|
|
/**
|
|
* virDomainAuditNetDevice:
|
|
* @vmDef: the definition of the VM
|
|
* @netDef: details of network device that fd will be tied to
|
|
* @device: device being opened (such as /dev/vhost-net,
|
|
* /dev/net/tun, /dev/tanN). Note that merely opening a device
|
|
* does not mean that virDomain owns it; a followup virDomainAuditNet
|
|
* shows whether the fd was passed on.
|
|
* @success: true if the device was opened
|
|
*
|
|
* Log an audit message about an attempted network device open.
|
|
*/
|
|
void
|
|
virDomainAuditNetDevice(virDomainDefPtr vmDef, virDomainNetDefPtr netDef,
|
|
const char *device, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char macstr[VIR_MAC_STRING_BUFLEN];
|
|
char *vmname;
|
|
char *dev_name = NULL;
|
|
char *rdev;
|
|
const char *virt = virDomainAuditGetVirtType(vmDef);
|
|
|
|
virUUIDFormat(vmDef->uuid, uuidstr);
|
|
virMacAddrFormat(&netDef->mac, macstr);
|
|
rdev = virDomainAuditGetRdev(device);
|
|
|
|
if (!(vmname = virAuditEncode("vm", vmDef->name)) ||
|
|
!(dev_name = virAuditEncode("path", device))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=net reason=open %s uuid=%s net=%s %s rdev=%s",
|
|
virt, vmname, uuidstr, macstr, dev_name, VIR_AUDIT_STR(rdev));
|
|
|
|
cleanup:
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(dev_name);
|
|
VIR_FREE(rdev);
|
|
}
|
|
|
|
/**
|
|
* virDomainAuditHostdev:
|
|
* @vm: domain making a change in pass-through host device
|
|
* @hostdev: device being attached or removed
|
|
* @reason: one of "start", "attach", or "detach"
|
|
* @success: true if the device passthrough operation succeeded
|
|
*
|
|
* Log an audit message about an attempted device passthrough change.
|
|
*/
|
|
void
|
|
virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
char *address = NULL;
|
|
char *device = NULL;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
|
|
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
|
|
virDomainHostdevSubsysSCSIPtr scsisrc = &hostdev->source.subsys.u.scsi;
|
|
virDomainHostdevSubsysSCSIVHostPtr hostsrc = &hostdev->source.subsys.u.scsi_host;
|
|
virDomainHostdevSubsysMediatedDevPtr mdevsrc = &hostdev->source.subsys.u.mdev;
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
switch ((virDomainHostdevMode) hostdev->mode) {
|
|
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
|
|
switch ((virDomainHostdevSubsysType) hostdev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
|
address = g_strdup_printf(VIR_PCI_DEVICE_ADDRESS_FMT,
|
|
pcisrc->addr.domain,
|
|
pcisrc->addr.bus,
|
|
pcisrc->addr.slot,
|
|
pcisrc->addr.function);
|
|
break;
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
|
|
address = g_strdup_printf("%.3d.%.3d", usbsrc->bus, usbsrc->device);
|
|
break;
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: {
|
|
if (scsisrc->protocol ==
|
|
VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
|
|
/* Follow virDomainAuditDisk && virDomainAuditGenericDev
|
|
* and don't audit the networked device.
|
|
*/
|
|
goto cleanup;
|
|
} else {
|
|
virDomainHostdevSubsysSCSIHostPtr scsihostsrc =
|
|
&scsisrc->u.host;
|
|
address = g_strdup_printf("%s:%u:%u:%llu",
|
|
scsihostsrc->adapter, scsihostsrc->bus,
|
|
scsihostsrc->target,
|
|
scsihostsrc->unit);
|
|
}
|
|
break;
|
|
}
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
|
|
address = g_strdup(hostsrc->wwpn);
|
|
break;
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
|
|
address = g_strdup(mdevsrc->uuidstr);
|
|
break;
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
|
|
default:
|
|
VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
|
|
hostdev->source.subsys.type);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s",
|
|
virt, reason, vmname, uuidstr,
|
|
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type),
|
|
device);
|
|
break;
|
|
|
|
case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
|
|
switch (hostdev->source.caps.type) {
|
|
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
|
|
if (!(device = virAuditEncode("disk",
|
|
VIR_AUDIT_STR(hostdev->source.caps.u.storage.block)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=hostdev reason=%s %s uuid=%s %s",
|
|
virt, reason, vmname, uuidstr, device);
|
|
break;
|
|
|
|
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
|
|
if (!(device = virAuditEncode("chardev",
|
|
VIR_AUDIT_STR(hostdev->source.caps.u.misc.chardev)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=hostdev reason=%s %s uuid=%s %s",
|
|
virt, reason, vmname, uuidstr, device);
|
|
break;
|
|
|
|
default:
|
|
VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
|
|
hostdev->source.caps.type);
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_HOSTDEV_MODE_LAST:
|
|
default:
|
|
VIR_WARN("Unexpected hostdev mode while encoding audit message: %d",
|
|
hostdev->mode);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(device);
|
|
VIR_FREE(address);
|
|
}
|
|
|
|
|
|
/**
|
|
* virDomainAuditRedirdev:
|
|
* @vm: domain making a change in pass-through host device
|
|
* @redirdev: device being attached or removed
|
|
* @reason: one of "start", "attach", or "detach"
|
|
* @success: true if the device passthrough operation succeeded
|
|
*
|
|
* Log an audit message about an attempted device passthrough change.
|
|
*/
|
|
void
|
|
virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
char *address = NULL;
|
|
char *device = NULL;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
switch (redirdev->bus) {
|
|
case VIR_DOMAIN_REDIRDEV_BUS_USB:
|
|
address = g_strdup("USB redirdev");
|
|
break;
|
|
default:
|
|
VIR_WARN("Unexpected redirdev bus while encoding audit message: %d",
|
|
redirdev->bus);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s",
|
|
virt, reason, vmname, uuidstr,
|
|
virDomainRedirdevBusTypeToString(redirdev->bus),
|
|
device);
|
|
|
|
cleanup:
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(device);
|
|
VIR_FREE(address);
|
|
}
|
|
|
|
|
|
/**
|
|
* virDomainAuditTPM:
|
|
* @vm: domain making a change in pass-through host device or emulator
|
|
* @tpm: TPM device being attached or removed
|
|
* @reason: one of "start", "attach", or "detach"
|
|
* @success: true if the device operation succeeded
|
|
*
|
|
* Log an audit message about an attempted device passthrough or emulator
|
|
* change.
|
|
*/
|
|
static void
|
|
virDomainAuditTPM(virDomainObjPtr vm, virDomainTPMDefPtr tpm,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
char *path = NULL;
|
|
char *device = NULL;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
switch (tpm->type) {
|
|
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
|
|
path = tpm->data.passthrough.source.data.file.path;
|
|
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(path)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=tpm reason=%s %s uuid=%s %s",
|
|
virt, reason, vmname, uuidstr, device);
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_EMULATOR:
|
|
path = tpm->data.emulator.source.data.nix.path;
|
|
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(path)))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=tpm-emulator reason=%s %s uuid=%s %s",
|
|
virt, reason, vmname, uuidstr, device);
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_LAST:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(device);
|
|
}
|
|
|
|
|
|
/**
|
|
* virDomainAuditCgroup:
|
|
* @vm: domain making the cgroups ACL change
|
|
* @cgroup: cgroup that manages the devices
|
|
* @reason: either "allow" or "deny"
|
|
* @extra: additional details, in the form "all",
|
|
* "major category=xyz maj=nn", or "path path=xyz dev=nn:mm" (the
|
|
* latter two are generated by virDomainAuditCgroupMajor and
|
|
* virDomainAuditCgroupPath).
|
|
* @success: true if the cgroup operation succeeded
|
|
*
|
|
* Log an audit message about an attempted cgroup device ACL change.
|
|
*/
|
|
void
|
|
virDomainAuditCgroup(virDomainObjPtr vm, virCgroupPtr cgroup,
|
|
const char *reason, const char *extra, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
char *controller = NULL;
|
|
char *detail;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
ignore_value(virCgroupPathOfController(cgroup,
|
|
VIR_CGROUP_CONTROLLER_DEVICES,
|
|
NULL, &controller));
|
|
detail = virAuditEncode("cgroup", VIR_AUDIT_STR(controller));
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=cgroup reason=%s %s uuid=%s %s class=%s",
|
|
virt, reason, vmname, uuidstr,
|
|
detail ? detail : "cgroup=?", extra);
|
|
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(controller);
|
|
VIR_FREE(detail);
|
|
}
|
|
|
|
/**
|
|
* virDomainAuditCgroupMajor:
|
|
* @vm: domain making the cgroups ACL change
|
|
* @cgroup: cgroup that manages the devices
|
|
* @reason: either "allow" or "deny"
|
|
* @maj: the major number of the device category
|
|
* @name: a textual name for that device category, alphabetic only
|
|
* @perms: string containing "r", "w", and/or "m" as appropriate
|
|
* @success: true if the cgroup operation succeeded
|
|
*
|
|
* Log an audit message about an attempted cgroup device ACL change.
|
|
*/
|
|
void
|
|
virDomainAuditCgroupMajor(virDomainObjPtr vm, virCgroupPtr cgroup,
|
|
const char *reason, int maj, const char *name,
|
|
const char *perms, bool success)
|
|
{
|
|
char *extra;
|
|
|
|
extra = g_strdup_printf("major category=%s maj=%02X acl=%s",
|
|
name, maj, perms);
|
|
|
|
virDomainAuditCgroup(vm, cgroup, reason, extra, success);
|
|
|
|
VIR_FREE(extra);
|
|
}
|
|
|
|
/**
|
|
* virDomainAuditCgroupPath:
|
|
* @vm: domain making the cgroups ACL change
|
|
* @cgroup: cgroup that manages the devices
|
|
* @reason: either "allow" or "deny"
|
|
* @path: the device being adjusted
|
|
* @perms: string containing "r", "w", and/or "m" as appropriate
|
|
* @rc: > 0 if not a device, 0 if success, < 0 if failure
|
|
*
|
|
* Log an audit message about an attempted cgroup device ACL change to
|
|
* a specific device.
|
|
*/
|
|
void
|
|
virDomainAuditCgroupPath(virDomainObjPtr vm, virCgroupPtr cgroup,
|
|
const char *reason, const char *path, const char *perms,
|
|
int rc)
|
|
{
|
|
char *detail;
|
|
char *rdev;
|
|
char *extra = NULL;
|
|
|
|
/* Nothing to audit for regular files. */
|
|
if (rc > 0)
|
|
return;
|
|
|
|
rdev = virDomainAuditGetRdev(path);
|
|
|
|
if (!(detail = virAuditEncode("path", path))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
extra = g_strdup_printf("path %s rdev=%s acl=%s",
|
|
detail, VIR_AUDIT_STR(rdev), perms);
|
|
|
|
virDomainAuditCgroup(vm, cgroup, reason, extra, rc == 0);
|
|
|
|
cleanup:
|
|
VIR_FREE(extra);
|
|
VIR_FREE(detail);
|
|
VIR_FREE(rdev);
|
|
}
|
|
|
|
/**
|
|
* virDomainAuditResource:
|
|
* @vm: domain making an integer resource change
|
|
* @resource: name of the resource: "mem" or "vcpu"
|
|
* @oldval: the old value of the resource
|
|
* @newval: the new value of the resource
|
|
* @reason: either "start" or "update"
|
|
* @success: true if the resource change succeeded
|
|
*
|
|
* Log an audit message about an attempted resource change.
|
|
*/
|
|
static void
|
|
virDomainAuditResource(virDomainObjPtr vm, const char *resource,
|
|
unsigned long long oldval, unsigned long long newval,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=%s reason=%s %s uuid=%s old-%s=%lld new-%s=%lld",
|
|
virt, resource, reason, vmname, uuidstr,
|
|
resource, oldval, resource, newval);
|
|
|
|
VIR_FREE(vmname);
|
|
}
|
|
|
|
void
|
|
virDomainAuditMemory(virDomainObjPtr vm,
|
|
unsigned long long oldmem, unsigned long long newmem,
|
|
const char *reason, bool success)
|
|
{
|
|
return virDomainAuditResource(vm, "mem", oldmem, newmem, reason, success);
|
|
}
|
|
|
|
void
|
|
virDomainAuditVcpu(virDomainObjPtr vm,
|
|
unsigned int oldvcpu, unsigned int newvcpu,
|
|
const char *reason, bool success)
|
|
{
|
|
return virDomainAuditResource(vm, "vcpu", oldvcpu, newvcpu, reason, success);
|
|
}
|
|
|
|
void
|
|
virDomainAuditIOThread(virDomainObjPtr vm,
|
|
unsigned int oldiothread, unsigned int newiothread,
|
|
const char *reason, bool success)
|
|
{
|
|
return virDomainAuditResource(vm, "iothread", oldiothread, newiothread,
|
|
reason, success);
|
|
}
|
|
|
|
static void
|
|
virDomainAuditLifecycle(virDomainObjPtr vm, const char *op,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, success,
|
|
"virt=%s op=%s reason=%s %s uuid=%s vm-pid=%lld",
|
|
virt, op, reason, vmname, uuidstr, (long long)vm->pid);
|
|
|
|
VIR_FREE(vmname);
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < vm->def->ndisks; i++)
|
|
virDomainAuditDisk(vm, NULL, vm->def->disks[i]->src, "start", true);
|
|
|
|
for (i = 0; i < vm->def->nfss; i++) {
|
|
virDomainFSDefPtr fs = vm->def->fss[i];
|
|
virDomainAuditFS(vm, NULL, fs, "start", true);
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nnets; i++) {
|
|
virDomainNetDefPtr net = vm->def->nets[i];
|
|
virDomainAuditNet(vm, NULL, net, "start", true);
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nhostdevs; i++) {
|
|
virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i];
|
|
virDomainAuditHostdev(vm, hostdev, "start", true);
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nredirdevs; i++) {
|
|
virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i];
|
|
virDomainAuditRedirdev(vm, redirdev, "start", true);
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nserials; i++)
|
|
virDomainAuditChardev(vm, NULL, vm->def->serials[i], "start", true);
|
|
|
|
for (i = 0; i < vm->def->nparallels; i++)
|
|
virDomainAuditChardev(vm, NULL, vm->def->parallels[i], "start", true);
|
|
|
|
for (i = 0; i < vm->def->nchannels; i++)
|
|
virDomainAuditChardev(vm, NULL, vm->def->channels[i], "start", true);
|
|
|
|
for (i = 0; i < vm->def->nconsoles; i++) {
|
|
if (i == 0 &&
|
|
(vm->def->consoles[i]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL ||
|
|
vm->def->consoles[i]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) &&
|
|
vm->def->os.type == VIR_DOMAIN_OSTYPE_HVM)
|
|
continue;
|
|
|
|
virDomainAuditChardev(vm, NULL, vm->def->consoles[i], "start", true);
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nsmartcards; i++)
|
|
virDomainAuditSmartcard(vm, vm->def->smartcards[i], "start", true);
|
|
|
|
for (i = 0; i < vm->def->nrngs; i++)
|
|
virDomainAuditRNG(vm, NULL, vm->def->rngs[i], "start", true);
|
|
|
|
if (vm->def->tpm)
|
|
virDomainAuditTPM(vm, vm->def->tpm, "start", true);
|
|
|
|
for (i = 0; i < vm->def->nshmems; i++)
|
|
virDomainAuditShmem(vm, vm->def->shmems[i], "start", true);
|
|
|
|
for (i = 0; i < vm->def->ninputs; i++)
|
|
virDomainAuditInput(vm, vm->def->inputs[i], "start", true);
|
|
|
|
virDomainAuditMemory(vm, 0, virDomainDefGetMemoryTotal(vm->def),
|
|
"start", true);
|
|
virDomainAuditVcpu(vm, 0, virDomainDefGetVcpus(vm->def), "start", true);
|
|
if (vm->def->niothreadids)
|
|
virDomainAuditIOThread(vm, 0, vm->def->niothreadids, "start", true);
|
|
|
|
virDomainAuditLifecycle(vm, "start", reason, success);
|
|
}
|
|
|
|
void
|
|
virDomainAuditInit(virDomainObjPtr vm,
|
|
pid_t initpid,
|
|
ino_t pidns)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, true,
|
|
"virt=%s op=init %s uuid=%s vm-pid=%lld init-pid=%lld pid-ns=%lld",
|
|
virt, vmname, uuidstr, (long long)vm->pid, (long long)initpid,
|
|
(long long)pidns);
|
|
|
|
VIR_FREE(vmname);
|
|
}
|
|
|
|
void
|
|
virDomainAuditStop(virDomainObjPtr vm, const char *reason)
|
|
{
|
|
virDomainAuditLifecycle(vm, "stop", reason, true);
|
|
}
|
|
|
|
void
|
|
virDomainAuditSecurityLabel(virDomainObjPtr vm, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname;
|
|
size_t i;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < vm->def->nseclabels; i++) {
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success,
|
|
"virt=%s %s uuid=%s vm-ctx=%s img-ctx=%s model=%s",
|
|
virt, vmname, uuidstr,
|
|
VIR_AUDIT_STR(vm->def->seclabels[i]->label),
|
|
VIR_AUDIT_STR(vm->def->seclabels[i]->imagelabel),
|
|
VIR_AUDIT_STR(vm->def->seclabels[i]->model));
|
|
}
|
|
|
|
VIR_FREE(vmname);
|
|
}
|
|
|
|
void
|
|
virDomainAuditShmem(virDomainObjPtr vm,
|
|
virDomainShmemDefPtr def,
|
|
const char *reason, bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
char *vmname = virAuditEncode("vm", vm->def->name);
|
|
const char *srcpath = virDomainChrSourceDefGetPath(&def->server.chr);
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
char *shmpath = NULL;
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
|
|
if (!vmname) {
|
|
VIR_WARN("OOM while encoding audit message");
|
|
goto cleanup;
|
|
}
|
|
|
|
shmpath = g_strdup_printf("/dev/shm/%s", def->name);
|
|
|
|
if (!virt) {
|
|
VIR_WARN("Unexpected virt type %d while encoding audit message",
|
|
vm->def->virtType);
|
|
virt = "?";
|
|
}
|
|
|
|
if (def->server.enabled) {
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=ivshmem-socket reason=%s %s uuid=%s path=%s",
|
|
virt, reason, vmname, uuidstr, VIR_AUDIT_STR(srcpath));
|
|
} else {
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=shmem reason=%s %s uuid=%s size=%llu path=%s",
|
|
virt, reason, vmname, uuidstr, def->size, VIR_AUDIT_STR(shmpath));
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FREE(vmname);
|
|
VIR_FREE(shmpath);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
virDomainAuditInput(virDomainObjPtr vm,
|
|
virDomainInputDefPtr input,
|
|
const char *reason,
|
|
bool success)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
g_autofree char *vmname = NULL;
|
|
const char *virt = virDomainAuditGetVirtType(vm->def);
|
|
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
|
|
if (!(vmname = virAuditEncode("vm", vm->def->name)))
|
|
return;
|
|
|
|
switch ((virDomainInputType) input->type) {
|
|
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
|
|
case VIR_DOMAIN_INPUT_TYPE_TABLET:
|
|
case VIR_DOMAIN_INPUT_TYPE_KBD:
|
|
break;
|
|
|
|
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
|
|
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
|
"virt=%s resrc=evdev reason=%s %s uuid=%s path=%s",
|
|
virt, reason, vmname, uuidstr, VIR_AUDIT_STR(input->source.evdev));
|
|
break;
|
|
|
|
case VIR_DOMAIN_INPUT_TYPE_LAST:
|
|
break;
|
|
}
|
|
}
|