/* * qemu_audit.c: QEMU audit management * * Copyright (C) 2006-2011 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Daniel P. Berrange */ #include #include #include #include "qemu_audit.h" #include "virtaudit.h" #include "uuid.h" #include "logging.h" #include "memory.h" /* 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 * qemuAuditGetRdev(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); virAsprintf(&ret, "%02X:%02X", maj, min); } return ret; } #else static char * qemuAuditGetRdev(const char *path ATTRIBUTE_UNUSED) { return NULL; } #endif void qemuDomainDiskAudit(virDomainObjPtr vm, virDomainDiskDefPtr oldDef, virDomainDiskDefPtr newDef, const char *reason, bool success) { char uuidstr[VIR_UUID_STRING_BUFLEN]; char *vmname; char *oldsrc = NULL; char *newsrc = NULL; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } if (!(oldsrc = virAuditEncode("old-disk", oldDef && oldDef->src ? oldDef->src : "?"))) { VIR_WARN0("OOM while encoding audit message"); goto cleanup; } if (!(newsrc = virAuditEncode("new-disk", newDef && newDef->src ? newDef->src : "?"))) { VIR_WARN0("OOM while encoding audit message"); goto cleanup; } VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "resrc=disk reason=%s %s uuid=%s %s %s", reason, vmname, uuidstr, oldsrc, newsrc); cleanup: VIR_FREE(vmname); VIR_FREE(oldsrc); VIR_FREE(newsrc); } void qemuDomainNetAudit(virDomainObjPtr vm, virDomainNetDefPtr oldDef, virDomainNetDefPtr newDef, const char *reason, bool success) { char uuidstr[VIR_UUID_STRING_BUFLEN]; char newMacstr[VIR_MAC_STRING_BUFLEN]; char oldMacstr[VIR_MAC_STRING_BUFLEN]; char *vmname; virUUIDFormat(vm->def->uuid, uuidstr); if (oldDef) virFormatMacAddr(oldDef->mac, oldMacstr); if (newDef) virFormatMacAddr(newDef->mac, newMacstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "resrc=net reason=%s %s uuid=%s old-net='%s' new-net='%s'", reason, vmname, uuidstr, oldDef ? oldMacstr : "?", newDef ? newMacstr : "?"); VIR_FREE(vmname); } /** * qemuDomainHostdevAudit: * @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 qemuDomainHostdevAudit(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev, const char *reason, bool success) { char uuidstr[VIR_UUID_STRING_BUFLEN]; char *vmname; char *address; char *device; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } switch (hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (virAsprintf(&address, "%.4x:%.2x:%.2x.%.1x", hostdev->source.subsys.u.pci.domain, hostdev->source.subsys.u.pci.bus, hostdev->source.subsys.u.pci.slot, hostdev->source.subsys.u.pci.function) < 0) { VIR_WARN0("OOM while encoding audit message"); goto cleanup; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (virAsprintf(&address, "%.3d.%.3d", hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device) < 0) { VIR_WARN0("OOM while encoding audit message"); goto cleanup; } break; 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_WARN0("OOM while encoding audit message"); goto cleanup; } VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "resrc=dev reason=%s %s uuid=%s bus=%s %s", reason, vmname, uuidstr, virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type), device); cleanup: VIR_FREE(vmname); VIR_FREE(device); VIR_FREE(address); } /** * qemuAuditCgroup: * @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 qemuAuditCgroupMajor and * qemuAuditCgroupPath). * @success: true if the cgroup operation succeeded * * Log an audit message about an attempted cgroup device ACL change. */ void qemuAuditCgroup(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; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } virCgroupPathOfController(cgroup, VIR_CGROUP_CONTROLLER_DEVICES, NULL, &controller); detail = virAuditEncode("cgroup", VIR_AUDIT_STR(controller)); VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "resrc=cgroup reason=%s %s uuid=%s %s class=%s", reason, vmname, uuidstr, detail ? detail : "cgroup=?", extra); VIR_FREE(vmname); VIR_FREE(controller); VIR_FREE(detail); } /** * qemuAuditCgroupMajor: * @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 * @success: true if the cgroup operation succeeded * * Log an audit message about an attempted cgroup device ACL change. */ void qemuAuditCgroupMajor(virDomainObjPtr vm, virCgroupPtr cgroup, const char *reason, int maj, const char *name, bool success) { char *extra; if (virAsprintf(&extra, "major category=%s maj=%02X", name, maj) < 0) { VIR_WARN0("OOM while encoding audit message"); return; } qemuAuditCgroup(vm, cgroup, reason, extra, success); VIR_FREE(extra); } /** * qemuAuditCgroupPath: * @vm: domain making the cgroups ACL change * @cgroup: cgroup that manages the devices * @reason: either "allow" or "deny" * @path: the device being adjusted * @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 qemuAuditCgroupPath(virDomainObjPtr vm, virCgroupPtr cgroup, const char *reason, const char *path, int rc) { char *detail; char *rdev; char *extra; /* Nothing to audit for regular files. */ if (rc > 0) return; rdev = qemuAuditGetRdev(path); if (!(detail = virAuditEncode("path", path)) || virAsprintf(&extra, "path path=%s rdev=%s", path, VIR_AUDIT_STR(rdev)) < 0) { VIR_WARN0("OOM while encoding audit message"); goto cleanup; } qemuAuditCgroup(vm, cgroup, reason, extra, rc == 0); cleanup: VIR_FREE(extra); VIR_FREE(detail); VIR_FREE(rdev); } /** * qemuDomainResourceAudit: * @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 qemuDomainResourceAudit(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; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, "resrc=%s reason=%s %s uuid=%s old-%s=%lld new-%s=%lld", resource, reason, vmname, uuidstr, resource, oldval, resource, newval); VIR_FREE(vmname); } void qemuDomainMemoryAudit(virDomainObjPtr vm, unsigned long long oldmem, unsigned long long newmem, const char *reason, bool success) { return qemuDomainResourceAudit(vm, "mem", oldmem, newmem, reason, success); } void qemuDomainVcpuAudit(virDomainObjPtr vm, unsigned int oldvcpu, unsigned int newvcpu, const char *reason, bool success) { return qemuDomainResourceAudit(vm, "vcpu", oldvcpu, newvcpu, reason, success); } static void qemuDomainLifecycleAudit(virDomainObjPtr vm, const char *op, const char *reason, bool success) { char uuidstr[VIR_UUID_STRING_BUFLEN]; char *vmname; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, success, "op=%s reason=%s %s uuid=%s", op, reason, vmname, uuidstr); VIR_FREE(vmname); } void qemuDomainStartAudit(virDomainObjPtr vm, const char *reason, bool success) { int i; for (i = 0 ; i < vm->def->ndisks ; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; if (disk->src) /* Skips CDROM without media initially inserted */ qemuDomainDiskAudit(vm, NULL, disk, "start", true); } for (i = 0 ; i < vm->def->nnets ; i++) { virDomainNetDefPtr net = vm->def->nets[i]; qemuDomainNetAudit(vm, NULL, net, "start", true); } for (i = 0 ; i < vm->def->nhostdevs ; i++) { virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i]; qemuDomainHostdevAudit(vm, hostdev, "start", true); } qemuDomainMemoryAudit(vm, 0, vm->def->mem.cur_balloon, "start", true); qemuDomainVcpuAudit(vm, 0, vm->def->vcpus, "start", true); qemuDomainLifecycleAudit(vm, "start", reason, success); } void qemuDomainStopAudit(virDomainObjPtr vm, const char *reason) { qemuDomainLifecycleAudit(vm, "stop", reason, true); } void qemuDomainSecurityLabelAudit(virDomainObjPtr vm, bool success) { char uuidstr[VIR_UUID_STRING_BUFLEN]; char *vmname; virUUIDFormat(vm->def->uuid, uuidstr); if (!(vmname = virAuditEncode("vm", vm->def->name))) { VIR_WARN0("OOM while encoding audit message"); return; } VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success, "%s uuid=%s vm-ctx=%s img-ctx=%s", vmname, uuidstr, VIR_AUDIT_STR(vm->def->seclabel.label), VIR_AUDIT_STR(vm->def->seclabel.imagelabel)); VIR_FREE(vmname); }