domain_audit.c 29.4 KB
Newer Older
1
/*
2
 * domain_audit.c: Domain audit management
3
 *
4
 * Copyright (C) 2006-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

26 27 28
#include <sys/stat.h>
#include <sys/types.h>

29
#include "domain_audit.h"
30
#include "viraudit.h"
31
#include "viruuid.h"
32
#include "virlog.h"
33
#include "viralloc.h"
34
#include "virstring.h"
35

36 37
VIR_LOG_INIT("conf.domain_audit");

38 39 40 41
/* 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 *
42
virDomainAuditGetRdev(const char *path)
43 44 45 46 47 48 49 50
{
    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);
51
        ignore_value(virAsprintfQuiet(&ret, "%02X:%02X", maj, min));
52 53 54 55 56
    }
    return ret;
}
#else
static char *
57
virDomainAuditGetRdev(const char *path ATTRIBUTE_UNUSED)
58 59 60 61 62
{
    return NULL;
}
#endif

63 64 65 66 67 68 69

static const char *
virDomainAuditChardevPath(virDomainChrSourceDefPtr chr)
{
    if (!chr)
        return NULL;

70
    switch ((virDomainChrType) chr->type) {
71 72 73 74
    case VIR_DOMAIN_CHR_TYPE_PTY:
    case VIR_DOMAIN_CHR_TYPE_DEV:
    case VIR_DOMAIN_CHR_TYPE_FILE:
    case VIR_DOMAIN_CHR_TYPE_PIPE:
75
    case VIR_DOMAIN_CHR_TYPE_NMDM:
76 77 78 79 80 81 82 83 84 85 86
        return chr->data.file.path;

    case VIR_DOMAIN_CHR_TYPE_UNIX:
        return chr->data.nix.path;

    case VIR_DOMAIN_CHR_TYPE_TCP:
    case VIR_DOMAIN_CHR_TYPE_UDP:
    case VIR_DOMAIN_CHR_TYPE_NULL:
    case VIR_DOMAIN_CHR_TYPE_VC:
    case VIR_DOMAIN_CHR_TYPE_STDIO:
    case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
87
    case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
88 89 90 91 92 93 94 95
    case VIR_DOMAIN_CHR_TYPE_LAST:
        return NULL;
    }

    return NULL;
}


96 97 98 99 100 101 102
static void
virDomainAuditGenericDev(virDomainObjPtr vm,
                         const char *type,
                         const char *oldsrcpath,
                         const char *newsrcpath,
                         const char *reason,
                         bool success)
103
{
104 105
    char *newdev = NULL;
    char *olddev = NULL;
106
    char uuidstr[VIR_UUID_STRING_BUFLEN];
107
    char *vmname = NULL;
108 109
    char *oldsrc = NULL;
    char *newsrc = NULL;
110
    const char *virt;
111

112 113
    /* if both new and old source aren't provided don't log anything */
    if (!newsrcpath && !oldsrcpath)
114
        return;
115 116 117 118 119 120 121 122 123 124 125

    if (virAsprintfQuiet(&newdev, "new-%s", type) < 0)
        goto no_memory;

    if (virAsprintfQuiet(&olddev, "old-%s", type) < 0)
        goto no_memory;

    virUUIDFormat(vm->def->uuid, uuidstr);

    if (!(vmname = virAuditEncode("vm", vm->def->name)))
        goto no_memory;
126

127
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
128 129
        VIR_WARN("Unexpected virt type %d while encoding audit message",
                 vm->def->virtType);
130 131 132
        virt = "?";
    }

133 134 135 136 137
    if (!(newsrc = virAuditEncode(newdev, VIR_AUDIT_STR(newsrcpath))))
        goto no_memory;

    if (!(oldsrc = virAuditEncode(olddev, VIR_AUDIT_STR(oldsrcpath))))
        goto no_memory;
138 139

    VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
140 141
              "virt=%s resrc=%s reason=%s %s uuid=%s %s %s",
              virt, type, reason, vmname, uuidstr, oldsrc, newsrc);
142

143
 cleanup:
144 145
    VIR_FREE(newdev);
    VIR_FREE(olddev);
146 147 148
    VIR_FREE(vmname);
    VIR_FREE(oldsrc);
    VIR_FREE(newsrc);
149 150 151 152 153 154 155 156
    return;

 no_memory:
    VIR_WARN("OOM while encoding audit message");
    goto cleanup;
}


157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
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",
                             virDomainAuditChardevPath(oldsrc),
                             virDomainAuditChardevPath(newsrc),
                             reason, success);
}


P
Peter Krempa 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
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,
                                     virDomainAuditChardevPath(&def->data.passthru),
                                     reason, success);
            break;

        case VIR_DOMAIN_SMARTCARD_TYPE_LAST:
            break;
        }
    }
}


225 226
void
virDomainAuditDisk(virDomainObjPtr vm,
227 228 229 230
                   virStorageSourcePtr oldDef,
                   virStorageSourcePtr newDef,
                   const char *reason,
                   bool success)
231
{
232 233 234 235 236 237 238 239 240 241
    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);
242 243 244
}


L
Luyao Huang 已提交
245
void
246
virDomainAuditRNG(virDomainObjPtr vm,
247
                  virDomainRNGDefPtr oldDef, virDomainRNGDefPtr newDef,
248 249 250 251 252 253
                  const char *reason, bool success)
{
    const char *newsrcpath = NULL;
    const char *oldsrcpath = NULL;

    if (newDef) {
254
        switch ((virDomainRNGBackend) newDef->backend) {
255
        case VIR_DOMAIN_RNG_BACKEND_RANDOM:
256
            newsrcpath = newDef->source.file;
257 258 259 260 261 262 263 264 265 266 267 268
            break;

        case VIR_DOMAIN_RNG_BACKEND_EGD:
            newsrcpath = virDomainAuditChardevPath(newDef->source.chardev);
            break;

        case VIR_DOMAIN_RNG_BACKEND_LAST:
            break;
        }
    }

    if (oldDef) {
269
        switch ((virDomainRNGBackend) oldDef->backend) {
270
        case VIR_DOMAIN_RNG_BACKEND_RANDOM:
271
            oldsrcpath = oldDef->source.file;
272 273 274 275 276 277 278 279 280 281 282
            break;

        case VIR_DOMAIN_RNG_BACKEND_EGD:
            oldsrcpath = virDomainAuditChardevPath(oldDef->source.chardev);
            break;

        case VIR_DOMAIN_RNG_BACKEND_LAST:
            break;
        }
    }

283
    virDomainAuditGenericDev(vm, "rng", oldsrcpath, newsrcpath, reason, success);
284 285 286
}


D
Daniel P. Berrange 已提交
287 288 289 290 291
void
virDomainAuditFS(virDomainObjPtr vm,
                 virDomainFSDefPtr oldDef, virDomainFSDefPtr newDef,
                 const char *reason, bool success)
{
292 293 294 295
    virDomainAuditGenericDev(vm, "fs",
                             oldDef ? oldDef->src : NULL,
                             newDef ? newDef->src : NULL,
                             reason, success);
D
Daniel P. Berrange 已提交
296 297 298
}


299
void
300 301 302
virDomainAuditNet(virDomainObjPtr vm,
                  virDomainNetDefPtr oldDef, virDomainNetDefPtr newDef,
                  const char *reason, bool success)
303 304 305 306 307
{
    char newMacstr[VIR_MAC_STRING_BUFLEN];
    char oldMacstr[VIR_MAC_STRING_BUFLEN];

    if (oldDef)
308
        virMacAddrFormat(&oldDef->mac, oldMacstr);
309

310
    if (newDef)
311
        virMacAddrFormat(&newDef->mac, newMacstr);
312

313 314 315 316
    virDomainAuditGenericDev(vm, "net",
                             oldDef ? oldMacstr : NULL,
                             newDef ? newMacstr : NULL,
                             reason, success);
317 318
}

319
/**
320
 * virDomainAuditNetDevice:
W
Wang Rui 已提交
321 322
 * @vmDef: the definition of the VM
 * @netDef: details of network device that fd will be tied to
323 324
 * @device: device being opened (such as /dev/vhost-net,
 * /dev/net/tun, /dev/tanN). Note that merely opening a device
325
 * does not mean that virDomain owns it; a followup virDomainAuditNet
326 327 328 329 330 331
 * 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
332 333
virDomainAuditNetDevice(virDomainDefPtr vmDef, virDomainNetDefPtr netDef,
                        const char *device, bool success)
334 335 336 337
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char macstr[VIR_MAC_STRING_BUFLEN];
    char *vmname;
338
    char *dev_name = NULL;
339
    char *rdev;
340
    const char *virt;
341 342

    virUUIDFormat(vmDef->uuid, uuidstr);
343
    virMacAddrFormat(&netDef->mac, macstr);
344
    rdev = virDomainAuditGetRdev(device);
345 346

    if (!(vmname = virAuditEncode("vm", vmDef->name)) ||
347
        !(dev_name = virAuditEncode("path", device))) {
348
        VIR_WARN("OOM while encoding audit message");
349 350 351
        goto cleanup;
    }

352 353 354 355 356
    if (!(virt = virDomainVirtTypeToString(vmDef->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vmDef->virtType);
        virt = "?";
    }

357
    VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
358
              "virt=%s resrc=net reason=open %s uuid=%s net=%s %s rdev=%s",
359
              virt, vmname, uuidstr, macstr, dev_name, VIR_AUDIT_STR(rdev));
360

361
 cleanup:
362
    VIR_FREE(vmname);
363
    VIR_FREE(dev_name);
364 365
    VIR_FREE(rdev);
}
366

367
/**
368
 * virDomainAuditHostdev:
369 370
 * @vm: domain making a change in pass-through host device
 * @hostdev: device being attached or removed
371
 * @reason: one of "start", "attach", or "detach"
372 373 374 375 376
 * @success: true if the device passthrough operation succeeded
 *
 * Log an audit message about an attempted device passthrough change.
 */
void
377 378
virDomainAuditHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev,
                      const char *reason, bool success)
379 380 381
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
382 383
    char *address = NULL;
    char *device = NULL;
384
    const char *virt;
385
    virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
386
    virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
387
    virDomainHostdevSubsysSCSIPtr scsisrc = &hostdev->source.subsys.u.scsi;
388 389 390

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
391
        VIR_WARN("OOM while encoding audit message");
392 393 394
        return;
    }

395 396 397 398 399
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

400 401 402 403
    switch (hostdev->mode) {
    case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
        switch (hostdev->source.subsys.type) {
        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
404
            if (virAsprintfQuiet(&address, "%.4x:%.2x:%.2x.%.1x",
405 406 407 408
                                 pcisrc->addr.domain,
                                 pcisrc->addr.bus,
                                 pcisrc->addr.slot,
                                 pcisrc->addr.function) < 0) {
409 410 411 412 413
                VIR_WARN("OOM while encoding audit message");
                goto cleanup;
            }
            break;
        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
414
            if (virAsprintfQuiet(&address, "%.3d.%.3d",
415
                                 usbsrc->bus, usbsrc->device) < 0) {
416 417
                VIR_WARN("OOM while encoding audit message");
                goto cleanup;
H
Han Cheng 已提交
418 419
            }
            break;
420
        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: {
421 422 423 424 425
            if (scsisrc->protocol ==
                VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
                /* Follow virDomainAuditDisk && virDomainAuditGenericDev
                 * and don't audit the networked device.
                 */
H
Han Cheng 已提交
426
                goto cleanup;
427 428 429 430 431 432 433 434 435 436
            } else {
                virDomainHostdevSubsysSCSIHostPtr scsihostsrc =
                    &scsisrc->u.host;
                if (virAsprintfQuiet(&address, "%s:%d:%d:%d",
                                     scsihostsrc->adapter, scsihostsrc->bus,
                                     scsihostsrc->target,
                                     scsihostsrc->unit) < 0) {
                    VIR_WARN("OOM while encoding audit message");
                    goto cleanup;
                }
437 438
            }
            break;
439
        }
440 441 442 443 444 445 446
        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)))) {
447
            VIR_WARN("OOM while encoding audit message");
448 449
            goto cleanup;
        }
450 451 452 453 454 455

        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);
456
        break;
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

    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);
487 488 489 490
            goto cleanup;
        }
        break;

491 492 493
    default:
        VIR_WARN("Unexpected hostdev mode while encoding audit message: %d",
                 hostdev->mode);
494 495 496
        goto cleanup;
    }

497
 cleanup:
498 499 500 501 502 503
    VIR_FREE(vmname);
    VIR_FREE(device);
    VIR_FREE(address);
}


504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
/**
 * 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;
519 520
    char *address = NULL;
    char *device = NULL;
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
    const char *virt;

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
        VIR_WARN("OOM while encoding audit message");
        return;
    }

    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

    switch (redirdev->bus) {
    case VIR_DOMAIN_REDIRDEV_BUS_USB:
536
        if (VIR_STRDUP_QUIET(address, "USB redirdev") < 0) {
537 538 539
            VIR_WARN("OOM while encoding audit message");
            goto cleanup;
        }
540
        break;
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    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);

558
 cleanup:
559 560 561 562 563 564
    VIR_FREE(vmname);
    VIR_FREE(device);
    VIR_FREE(address);
}


565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
/**
 * virDomainAuditTPM:
 * @vm: domain making a change in pass-through host device
 * @tpm: TPM 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.
 */
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;

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
        VIR_WARN("OOM while encoding audit message");
        return;
    }

    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

    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=dev reason=%s %s uuid=%s %s",
                  virt, reason, vmname, uuidstr, device);
        break;
    default:
        break;
    }

611
 cleanup:
612 613 614 615 616
    VIR_FREE(vmname);
    VIR_FREE(device);
}


617
/**
618
 * virDomainAuditCgroup:
619 620 621
 * @vm: domain making the cgroups ACL change
 * @cgroup: cgroup that manages the devices
 * @reason: either "allow" or "deny"
622 623
 * @extra: additional details, in the form "all",
 * "major category=xyz maj=nn", or "path path=xyz dev=nn:mm" (the
624 625
 * latter two are generated by virDomainAuditCgroupMajor and
 * virDomainAuditCgroupPath).
626 627 628 629
 * @success: true if the cgroup operation succeeded
 *
 * Log an audit message about an attempted cgroup device ACL change.
 */
630
void
631 632
virDomainAuditCgroup(virDomainObjPtr vm, virCgroupPtr cgroup,
                     const char *reason, const char *extra, bool success)
633 634 635
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
636 637
    char *controller = NULL;
    char *detail;
638
    const char *virt;
639 640 641

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
642
        VIR_WARN("OOM while encoding audit message");
643 644
        return;
    }
645

646 647 648 649 650
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

E
Eric Blake 已提交
651 652 653
    ignore_value(virCgroupPathOfController(cgroup,
                                           VIR_CGROUP_CONTROLLER_DEVICES,
                                           NULL, &controller));
654 655
    detail = virAuditEncode("cgroup", VIR_AUDIT_STR(controller));

656
    VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
657 658
              "virt=%s resrc=cgroup reason=%s %s uuid=%s %s class=%s",
              virt, reason, vmname, uuidstr,
659
              detail ? detail : "cgroup=?", extra);
660 661

    VIR_FREE(vmname);
662 663
    VIR_FREE(controller);
    VIR_FREE(detail);
664 665 666
}

/**
667
 * virDomainAuditCgroupMajor:
668 669 670 671 672
 * @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
673
 * @perms: string containing "r", "w", and/or "m" as appropriate
674 675 676 677 678
 * @success: true if the cgroup operation succeeded
 *
 * Log an audit message about an attempted cgroup device ACL change.
 */
void
679 680 681
virDomainAuditCgroupMajor(virDomainObjPtr vm, virCgroupPtr cgroup,
                          const char *reason, int maj, const char *name,
                          const char *perms, bool success)
682 683 684
{
    char *extra;

685 686
    if (virAsprintfQuiet(&extra, "major category=%s maj=%02X acl=%s",
                         name, maj, perms) < 0) {
687
        VIR_WARN("OOM while encoding audit message");
688 689 690
        return;
    }

691
    virDomainAuditCgroup(vm, cgroup, reason, extra, success);
692 693 694 695 696

    VIR_FREE(extra);
}

/**
697
 * virDomainAuditCgroupPath:
698 699 700 701
 * @vm: domain making the cgroups ACL change
 * @cgroup: cgroup that manages the devices
 * @reason: either "allow" or "deny"
 * @path: the device being adjusted
702
 * @perms: string containing "r", "w", and/or "m" as appropriate
703 704 705 706 707 708
 * @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
709 710 711
virDomainAuditCgroupPath(virDomainObjPtr vm, virCgroupPtr cgroup,
                         const char *reason, const char *path, const char *perms,
                         int rc)
712 713 714
{
    char *detail;
    char *rdev;
715
    char *extra = NULL;
716 717 718 719 720

    /* Nothing to audit for regular files.  */
    if (rc > 0)
        return;

721
    rdev = virDomainAuditGetRdev(path);
722 723

    if (!(detail = virAuditEncode("path", path)) ||
724 725
        virAsprintfQuiet(&extra, "path %s rdev=%s acl=%s",
                         detail, VIR_AUDIT_STR(rdev), perms) < 0) {
726
        VIR_WARN("OOM while encoding audit message");
727 728 729
        goto cleanup;
    }

730
    virDomainAuditCgroup(vm, cgroup, reason, extra, rc == 0);
731

732
 cleanup:
733
    VIR_FREE(extra);
734
    VIR_FREE(detail);
735
    VIR_FREE(rdev);
736 737
}

738
/**
739
 * virDomainAuditResource:
740 741 742 743 744 745 746 747 748 749
 * @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
750 751 752
virDomainAuditResource(virDomainObjPtr vm, const char *resource,
                       unsigned long long oldval, unsigned long long newval,
                       const char *reason, bool success)
753 754 755
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
756
    const char *virt;
757 758 759

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
760
        VIR_WARN("OOM while encoding audit message");
761 762 763
        return;
    }

764 765 766 767 768
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

769
    VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
770 771
              "virt=%s resrc=%s reason=%s %s uuid=%s old-%s=%lld new-%s=%lld",
              virt, resource, reason, vmname, uuidstr,
772 773 774 775 776 777
              resource, oldval, resource, newval);

    VIR_FREE(vmname);
}

void
778 779 780
virDomainAuditMemory(virDomainObjPtr vm,
                     unsigned long long oldmem, unsigned long long newmem,
                     const char *reason, bool success)
781
{
782
    return virDomainAuditResource(vm, "mem", oldmem, newmem, reason, success);
783 784 785
}

void
786 787 788
virDomainAuditVcpu(virDomainObjPtr vm,
                   unsigned int oldvcpu, unsigned int newvcpu,
                   const char *reason, bool success)
789
{
790
    return virDomainAuditResource(vm, "vcpu", oldvcpu, newvcpu, reason, success);
791 792
}

793 794 795 796 797 798 799 800 801
void
virDomainAuditIOThread(virDomainObjPtr vm,
                       unsigned int oldiothread, unsigned int newiothread,
                       const char *reason, bool success)
{
    return virDomainAuditResource(vm, "iothread", oldiothread, newiothread,
                                  reason, success);
}

802
static void
803 804
virDomainAuditLifecycle(virDomainObjPtr vm, const char *op,
                        const char *reason, bool success)
805 806 807
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
808
    const char *virt;
809 810 811 812

    virUUIDFormat(vm->def->uuid, uuidstr);

    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
813
        VIR_WARN("OOM while encoding audit message");
814 815 816
        return;
    }

817 818 819 820 821
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

822
    VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, success,
823 824
              "virt=%s op=%s reason=%s %s uuid=%s vm-pid=%lld",
              virt, op, reason, vmname, uuidstr, (long long)vm->pid);
825 826 827 828 829

    VIR_FREE(vmname);
}


830
void
831
virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success)
832
{
833
    size_t i;
834

835 836
    for (i = 0; i < vm->def->ndisks; i++)
        virDomainAuditDisk(vm, NULL, vm->def->disks[i]->src, "start", true);
837

838
    for (i = 0; i < vm->def->nfss; i++) {
D
Daniel P. Berrange 已提交
839 840 841 842
        virDomainFSDefPtr fs = vm->def->fss[i];
        virDomainAuditFS(vm, NULL, fs, "start", true);
    }

843
    for (i = 0; i < vm->def->nnets; i++) {
844
        virDomainNetDefPtr net = vm->def->nets[i];
845
        virDomainAuditNet(vm, NULL, net, "start", true);
846 847
    }

848
    for (i = 0; i < vm->def->nhostdevs; i++) {
849
        virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i];
850
        virDomainAuditHostdev(vm, hostdev, "start", true);
851 852
    }

853
    for (i = 0; i < vm->def->nredirdevs; i++) {
854 855 856 857
        virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i];
        virDomainAuditRedirdev(vm, redirdev, "start", true);
    }

858 859 860 861 862 863 864 865 866 867 868 869 870
    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) &&
871
             vm->def->os.type == VIR_DOMAIN_OSTYPE_HVM)
872 873 874 875 876
            continue;

        virDomainAuditChardev(vm, NULL, vm->def->consoles[i], "start", true);
    }

P
Peter Krempa 已提交
877 878 879
    for (i = 0; i < vm->def->nsmartcards; i++)
        virDomainAuditSmartcard(vm, vm->def->smartcards[i], "start", true);

880 881
    for (i = 0; i < vm->def->nrngs; i++)
        virDomainAuditRNG(vm, NULL, vm->def->rngs[i], "start", true);
882

883 884 885
    if (vm->def->tpm)
        virDomainAuditTPM(vm, vm->def->tpm, "start", true);

886 887
    virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true);
    virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true);
888 889
    if (vm->def->iothreads)
        virDomainAuditIOThread(vm, 0, vm->def->iothreads, "start", true);
890

891
    virDomainAuditLifecycle(vm, "start", reason, success);
892 893
}

894 895
void
virDomainAuditInit(virDomainObjPtr vm,
896 897
                   pid_t initpid,
                   ino_t pidns)
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
    const char *virt;

    virUUIDFormat(vm->def->uuid, uuidstr);

    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
        VIR_WARN("OOM while encoding audit message");
        return;
    }

    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

    VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, true,
916 917 918
              "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);
919 920 921

    VIR_FREE(vmname);
}
922

923
void
924
virDomainAuditStop(virDomainObjPtr vm, const char *reason)
925
{
926
    virDomainAuditLifecycle(vm, "stop", reason, true);
927 928
}

929
void
930
virDomainAuditSecurityLabel(virDomainObjPtr vm, bool success)
931 932 933
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *vmname;
934
    const char *virt;
935
    size_t i;
936 937 938

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (!(vmname = virAuditEncode("vm", vm->def->name))) {
939
        VIR_WARN("OOM while encoding audit message");
940 941 942
        return;
    }

943 944 945 946 947
    if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
        VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
        virt = "?";
    }

948 949 950 951 952 953 954 955
    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));
    }
956 957 958

    VIR_FREE(vmname);
}