security_apparmor.c 21.7 KB
Newer Older
J
Jamie Strandboge 已提交
1 2 3

/*
 * AppArmor security driver for libvirt
4
 * Copyright (C) 2009-2010 Canonical Ltd.
J
Jamie Strandboge 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * Author:
 *   Jamie Strandboge <jamie@canonical.com>
 *   Based on security_selinux.c by James Morris <jmorris@namei.org>
 *
 * AppArmor security driver.
 */

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/apparmor.h>
#include <errno.h>
#include <unistd.h>
#include <wait.h>
#include <stdbool.h>

#include "internal.h"

#include "security_driver.h"
#include "security_apparmor.h"
#include "util.h"
#include "memory.h"
#include "virterror_internal.h"
#include "datatypes.h"
#include "uuid.h"
38 39
#include "pci.h"
#include "hostusb.h"
40
#include "files.h"
J
Jamie Strandboge 已提交
41 42 43 44 45 46

#define VIR_FROM_THIS VIR_FROM_SECURITY
#define SECURITY_APPARMOR_VOID_DOI      "0"
#define SECURITY_APPARMOR_NAME          "apparmor"
#define VIRT_AA_HELPER BINDIR "/virt-aa-helper"

47 48 49 50 51 52
/* Data structure to pass to *FileIterate so we have everything we need */
struct SDPDOP {
    virSecurityDriverPtr drv;
    virDomainObjPtr vm;
};

J
Jamie Strandboge 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/*
 * profile_status returns '-1' on error, '0' if loaded
 *
 * If check_enforcing is set to '1', then returns '-1' on error, '0' if
 * loaded in complain mode, and '1' if loaded in enforcing mode.
 */
static int
profile_status(const char *str, const int check_enforcing)
{
    char *content = NULL;
    char *tmp = NULL;
    char *etmp = NULL;
    int rc = -1;

    /* create string that is '<str> \0' for accurate matching */
68
    if (virAsprintf(&tmp, "%s ", str) == -1) {
69
        virReportOOMError();
J
Jamie Strandboge 已提交
70
        return rc;
71
    }
J
Jamie Strandboge 已提交
72 73 74 75 76

    if (check_enforcing != 0) {
        /* create string that is '<str> (enforce)\0' for accurate matching */
        if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
            VIR_FREE(tmp);
77
            virReportOOMError();
J
Jamie Strandboge 已提交
78 79 80 81 82
            return rc;
        }
    }

    if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
83
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
                             _("Failed to read AppArmor profiles list "
                             "\'%s\'"), APPARMOR_PROFILES_PATH);
        goto clean;
    }

    if (strstr(content, tmp) != NULL)
        rc = 0;
    if (check_enforcing != 0) {
        if (rc == 0 && strstr(content, etmp) != NULL)
            rc = 1;                 /* return '1' if loaded and enforcing */
    }

    VIR_FREE(content);
  clean:
    VIR_FREE(tmp);
J
Jamie Strandboge 已提交
99
    VIR_FREE(etmp);
J
Jamie Strandboge 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

    return rc;
}

static int
profile_loaded(const char *str)
{
    return profile_status(str, 0);
}

/*
 * profile_status_file returns '-1' on error, '0' if file on disk is in
 * complain mode and '1' if file on disk is in enforcing mode
 */
static int
profile_status_file(const char *str)
{
J
Jamie Strandboge 已提交
117
    char *profile = NULL;
J
Jamie Strandboge 已提交
118 119 120 121 122
    char *content = NULL;
    char *tmp = NULL;
    int rc = -1;
    int len;

J
Jamie Strandboge 已提交
123
    if (virAsprintf(&profile, "%s/%s", APPARMOR_DIR "/libvirt", str) == -1) {
124
        virReportOOMError();
J
Jamie Strandboge 已提交
125 126 127
        return rc;
    }

J
Jamie Strandboge 已提交
128 129 130
    if (!virFileExists(profile))
        goto failed;

J
Jamie Strandboge 已提交
131
    if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
132
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
133
                             _("Failed to read \'%s\'"), profile);
J
Jamie Strandboge 已提交
134
        goto failed;
J
Jamie Strandboge 已提交
135 136 137 138
    }

    /* create string that is ' <str> flags=(complain)\0' */
    if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
139
        virReportOOMError();
J
Jamie Strandboge 已提交
140
        goto failed;
J
Jamie Strandboge 已提交
141 142 143 144 145 146 147
    }

    if (strstr(content, tmp) != NULL)
        rc = 0;
    else
        rc = 1;

J
Jamie Strandboge 已提交
148
  failed:
J
Jamie Strandboge 已提交
149
    VIR_FREE(tmp);
J
Jamie Strandboge 已提交
150
    VIR_FREE(profile);
J
Jamie Strandboge 已提交
151 152 153 154 155 156 157 158 159
    VIR_FREE(content);

    return rc;
}

/*
 * load (add) a profile. Will create one if necessary
 */
static int
160
load_profile(virSecurityDriverPtr drv,
161 162 163 164
             const char *profile,
             virDomainObjPtr vm,
             const char *fn,
             bool append)
J
Jamie Strandboge 已提交
165 166 167 168 169 170
{
    int rc = -1, status, ret;
    bool create = true;
    char *xml = NULL;
    int pipefd[2];
    pid_t child;
171 172
    const char *probe = virSecurityDriverGetAllowDiskFormatProbing(drv)
        ? "1" : "0";
J
Jamie Strandboge 已提交
173 174

    if (pipe(pipefd) < -1) {
175
        virReportSystemError(errno, "%s", _("unable to create pipe"));
J
Jamie Strandboge 已提交
176 177 178
        return rc;
    }

179
    xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_SECURE);
J
Jamie Strandboge 已提交
180
    if (!xml)
J
Jamie Strandboge 已提交
181
        goto clean;
J
Jamie Strandboge 已提交
182 183 184 185 186 187

    if (profile_status_file(profile) >= 0)
        create = false;

    if (create) {
        const char *const argv[] = {
188
            VIRT_AA_HELPER, "-p", probe, "-c", "-u", profile, NULL
J
Jamie Strandboge 已提交
189
        };
190
        ret = virExec(argv, NULL, NULL, &child,
191
                      pipefd[0], NULL, NULL, VIR_EXEC_NONE);
192 193 194 195 196 197
    } else if (fn && append) {
        const char *const argv[] = {
            VIRT_AA_HELPER, "-p", probe, "-r", "-u", profile, "-F", fn, NULL
        };
        ret = virExec(argv, NULL, NULL, &child,
                      pipefd[0], NULL, NULL, VIR_EXEC_NONE);
198
    } else if (fn) {
J
Jamie Strandboge 已提交
199
        const char *const argv[] = {
200
            VIRT_AA_HELPER, "-p", probe, "-r", "-u", profile, "-f", fn, NULL
J
Jamie Strandboge 已提交
201
        };
202
        ret = virExec(argv, NULL, NULL, &child,
203
                      pipefd[0], NULL, NULL, VIR_EXEC_NONE);
J
Jamie Strandboge 已提交
204 205
    } else {
        const char *const argv[] = {
206
            VIRT_AA_HELPER, "-p", probe, "-r", "-u", profile, NULL
J
Jamie Strandboge 已提交
207
        };
208
        ret = virExec(argv, NULL, NULL, &child,
209
                      pipefd[0], NULL, NULL, VIR_EXEC_NONE);
J
Jamie Strandboge 已提交
210 211 212 213 214 215
    }
    if (ret < 0)
        goto clean;

    /* parent continues here */
    if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
216
        virReportSystemError(errno, "%s", _("unable to write to pipe"));
J
Jamie Strandboge 已提交
217 218
        goto clean;
    }
219
    VIR_FORCE_CLOSE(pipefd[1]);
J
Jamie Strandboge 已提交
220 221 222 223 224 225 226
    rc = 0;

  rewait:
    if (waitpid(child, &status, 0) != child) {
        if (errno == EINTR)
            goto rewait;

227
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
228 229 230 231 232 233 234 235 236
                               _("Unexpected exit status from virt-aa-helper "
                               "%d pid %lu"),
                               WEXITSTATUS(status), (unsigned long)child);
        rc = -1;
    }

  clean:
    VIR_FREE(xml);

237 238
    VIR_FORCE_CLOSE(pipefd[0]);
    VIR_FORCE_CLOSE(pipefd[1]);
J
Jamie Strandboge 已提交
239 240 241 242 243 244 245 246 247 248 249 250

    return rc;
}

static int
remove_profile(const char *profile)
{
    int rc = -1;
    const char * const argv[] = {
        VIRT_AA_HELPER, "-R", "-u", profile, NULL
    };

251
    if (virRun(argv, NULL) == 0)
J
Jamie Strandboge 已提交
252 253 254 255 256 257
        rc = 0;

    return rc;
}

static char *
258
get_profile_name(virDomainObjPtr vm)
J
Jamie Strandboge 已提交
259 260 261 262 263 264
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *name = NULL;

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
265
        virReportOOMError();
J
Jamie Strandboge 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279
        return NULL;
    }

    return name;
}

/* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
 * mode and 1 if enforcing. This is required because at present you cannot
 * aa_change_profile() from a process that is unconfined.
 */
static int
use_apparmor(void)
{
    int rc = -1;
280
    char *libvirt_daemon = NULL;
J
Jamie Strandboge 已提交
281

282
    if (virFileResolveLink("/proc/self/exe", &libvirt_daemon) < 0) {
283
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
284 285 286 287 288
                               "%s", _("could not find libvirtd"));
        return rc;
    }

    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
289 290 291
        goto cleanup;

    rc = profile_status(libvirt_daemon, 1);
J
Jamie Strandboge 已提交
292

293 294 295
cleanup:
    VIR_FREE(libvirt_daemon);
    return rc;
J
Jamie Strandboge 已提交
296 297
}

298 299 300 301
/* reload the profile, adding read/write file specified by fn if it is not
 * NULL.
 */
static int
302
reload_profile(virSecurityDriverPtr drv,
303 304 305
               virDomainObjPtr vm,
               const char *fn,
               bool append)
306 307 308 309 310 311 312 313 314 315 316 317 318
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    if ((profile_name = get_profile_name(vm)) == NULL)
        return rc;

    /* Update the profile only if it is loaded */
    if (profile_loaded(secdef->imagelabel) >= 0) {
319
        if (load_profile(drv, secdef->imagelabel, vm, fn, append) < 0) {
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                   secdef->imagelabel);
            goto clean;
        }
    }

    rc = 0;
  clean:
    VIR_FREE(profile_name);

    return rc;
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
static int
AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
                           const char *file, void *opaque)
{
    struct SDPDOP *ptr = opaque;
    virDomainObjPtr vm = ptr->vm;

    if (reload_profile(ptr->drv, vm, file, true) < 0) {
        const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("cannot update AppArmor profile "
                                 "\'%s\'"),
                               secdef->imagelabel);
        return -1;
    }
    return 0;
}

static int
AppArmorSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
                           const char *file, void *opaque)
{
    struct SDPDOP *ptr = opaque;
    virDomainObjPtr vm = ptr->vm;

    if (reload_profile(ptr->drv, vm, file, true) < 0) {
        const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("cannot update AppArmor profile "
                                 "\'%s\'"),
                               secdef->imagelabel);
        return -1;
    }
    return 0;
}

J
Jamie Strandboge 已提交
371 372 373 374
/* Called on libvirtd startup to see if AppArmor is available */
static int
AppArmorSecurityDriverProbe(void)
{
J
Jamie Strandboge 已提交
375 376
    char *template = NULL;
    int rc = SECURITY_DRIVER_DISABLE;
J
Jamie Strandboge 已提交
377 378

    if (use_apparmor() < 0)
J
Jamie Strandboge 已提交
379
        return rc;
J
Jamie Strandboge 已提交
380 381

    /* see if template file exists */
J
Jamie Strandboge 已提交
382 383
    if (virAsprintf(&template, "%s/TEMPLATE",
                               APPARMOR_DIR "/libvirt") == -1) {
384
        virReportOOMError();
J
Jamie Strandboge 已提交
385
        return rc;
J
Jamie Strandboge 已提交
386 387 388
    }

    if (!virFileExists(template)) {
389
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
390
                               _("template \'%s\' does not exist"), template);
J
Jamie Strandboge 已提交
391
        goto clean;
J
Jamie Strandboge 已提交
392
    }
J
Jamie Strandboge 已提交
393
    rc = SECURITY_DRIVER_ENABLE;
J
Jamie Strandboge 已提交
394

J
Jamie Strandboge 已提交
395 396 397 398
  clean:
    VIR_FREE(template);

    return rc;
J
Jamie Strandboge 已提交
399 400 401 402 403 404
}

/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
 * currently not used.
 */
static int
405 406
AppArmorSecurityDriverOpen(virSecurityDriverPtr drv,
                           bool allowDiskFormatProbing)
J
Jamie Strandboge 已提交
407
{
408
    virSecurityDriverSetDOI(drv, SECURITY_APPARMOR_VOID_DOI);
409
    virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing);
J
Jamie Strandboge 已提交
410 411 412 413 414 415 416 417 418
    return 0;
}

/* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and
 * use a profile based on the UUID, otherwise create one based on a template.
 * Keep in mind that this is called on 'start' with RestoreSecurityLabel being
 * called on shutdown.
*/
static int
419 420
AppArmorGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                         virDomainObjPtr vm)
J
Jamie Strandboge 已提交
421 422 423 424
{
    int rc = -1;
    char *profile_name = NULL;

425 426 427
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

J
Jamie Strandboge 已提交
428 429
    if ((vm->def->seclabel.label) ||
        (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
430
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
431 432 433 434 435
                               "%s",
                               _("security label already defined for VM"));
        return rc;
    }

436
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
437 438 439 440
        return rc;

    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
    if (!vm->def->seclabel.label) {
441
        virReportOOMError();
J
Jamie Strandboge 已提交
442 443 444 445 446 447 448
        goto clean;
    }

    /* set imagelabel the same as label (but we won't use it) */
    vm->def->seclabel.imagelabel = strndup(profile_name,
                                           strlen(profile_name));
    if (!vm->def->seclabel.imagelabel) {
449
        virReportOOMError();
J
Jamie Strandboge 已提交
450 451 452 453 454
        goto err;
    }

    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
    if (!vm->def->seclabel.model) {
455
        virReportOOMError();
J
Jamie Strandboge 已提交
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
        goto err;
    }

    rc = 0;
    goto clean;

  err:
    VIR_FREE(vm->def->seclabel.label);
    VIR_FREE(vm->def->seclabel.imagelabel);
    VIR_FREE(vm->def->seclabel.model);

  clean:
    VIR_FREE(profile_name);

    return rc;
}

473
static int
474 475
AppArmorSetSecurityAllLabel(virSecurityDriverPtr drv,
                            virDomainObjPtr vm, const char *stdin_path)
476 477 478 479 480 481
{
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

    /* if the profile is not already loaded, then load one */
    if (profile_loaded(vm->def->seclabel.label) < 0) {
482 483
        if (load_profile(drv, vm->def->seclabel.label, vm, stdin_path,
                         false) < 0) {
484
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
485 486 487 488 489 490 491 492 493
                                   _("cannot generate AppArmor profile "
                                   "\'%s\'"), vm->def->seclabel.label);
            return -1;
        }
    }

    return 0;
}

J
Jamie Strandboge 已提交
494 495 496 497
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
 * running.
 */
static int
498 499 500
AppArmorGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                virDomainObjPtr vm,
                                virSecurityLabelPtr sec)
J
Jamie Strandboge 已提交
501 502 503 504
{
    int rc = -1;
    char *profile_name = NULL;

505
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
506 507 508 509
        return rc;

    if (virStrcpy(sec->label, profile_name,
        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
510
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
511 512 513 514 515
                               "%s", _("error copying profile name"));
        goto clean;
    }

    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
516
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
                               "%s", _("error calling profile_status()"));
        goto clean;
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

/* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
 * more details. Currently called via qemudShutdownVMDaemon.
 */
static int
532 533
AppArmorReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                             virDomainObjPtr vm)
534 535 536 537 538 539 540 541 542 543 544 545
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    VIR_FREE(secdef->model);
    VIR_FREE(secdef->label);
    VIR_FREE(secdef->imagelabel);

    return 0;
}


static int
546 547
AppArmorRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                                virDomainObjPtr vm,
548
                                int migrated ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
549 550 551 552
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = 0;

553
    if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
J
Jamie Strandboge 已提交
554
        if ((rc = remove_profile(secdef->label)) != 0) {
555
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
556 557 558 559 560 561 562 563 564 565 566
                                   _("could not remove profile for \'%s\'"),
                                   secdef->label);
        }
    }
    return rc;
}

/* Called via virExecWithHook. Output goes to
 * LOCAL_STATE_DIR/log/libvirt/qemu/<vm name>.log
 */
static int
567
AppArmorSetSecurityProcessLabel(virSecurityDriverPtr drv, virDomainObjPtr vm)
J
Jamie Strandboge 已提交
568 569 570 571 572
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

573
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
574 575 576
        return rc;

    if (STRNEQ(drv->name, secdef->model)) {
577
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
578 579 580 581 582 583 584 585 586
                               _("security label driver mismatch: "
                               "\'%s\' model configured for domain, but "
                               "hypervisor driver is \'%s\'."),
                               secdef->model, drv->name);
        if (use_apparmor() > 0)
            goto clean;
    }

    if (aa_change_profile(profile_name) < 0) {
587
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR, "%s",
J
Jamie Strandboge 已提交
588 589 590 591 592 593 594 595 596 597 598 599 600 601
                               _("error calling aa_change_profile()"));
        goto clean;
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}


/* Called when hotplugging */
static int
602 603
AppArmorRestoreSecurityImageLabel(virSecurityDriverPtr drv,
                                  virDomainObjPtr vm,
J
Jamie Strandboge 已提交
604 605
                                  virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
606
    return reload_profile(drv, vm, NULL, false);
J
Jamie Strandboge 已提交
607 608 609 610
}

/* Called when hotplugging */
static int
611 612
AppArmorSetSecurityImageLabel(virSecurityDriverPtr drv,
                              virDomainObjPtr vm, virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
613 614 615 616 617
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name;

618 619 620
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

J
Jamie Strandboge 已提交
621 622 623 624 625 626
    if (!disk->src)
        return 0;

    if (secdef->imagelabel) {
        /* if the device doesn't exist, error out */
        if (!virFileExists(disk->src)) {
627
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
628 629 630 631
                                   _("\'%s\' does not exist"), disk->src);
            return rc;
        }

632
        if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
633 634 635 636
            return rc;

        /* update the profile only if it is loaded */
        if (profile_loaded(secdef->imagelabel) >= 0) {
637 638
            if (load_profile(drv, secdef->imagelabel, vm, disk->src,
                             false) < 0) {
639
                virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
                                     _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                     secdef->imagelabel);
                goto clean;
            }
        }
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

static int
656
AppArmorSecurityVerify(virDomainDefPtr def)
J
Jamie Strandboge 已提交
657 658 659 660 661
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
662
            virSecurityReportError(VIR_ERR_XML_ERROR,
J
Jamie Strandboge 已提交
663 664 665 666 667 668 669 670 671
                                   _("Invalid security label \'%s\'"),
                                   secdef->label);
            return -1;
        }
    }
    return 0;
}

static int
672 673
AppArmorReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
                             virDomainObjPtr vm ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
674 675 676 677 678 679
{
    /* NOOP. Nothing to reserve with AppArmor */
    return 0;
}

static int
680
AppArmorSetSecurityHostdevLabel(virSecurityDriverPtr drv,
681
                                virDomainObjPtr vm,
682
                                virDomainHostdevDefPtr dev)
J
Jamie Strandboge 已提交
683 684

{
685
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
686 687
    struct SDPDOP *ptr;
    int ret = -1;
688 689 690 691

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
    if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
        return 0;

    if (profile_loaded(secdef->imagelabel) < 0)
        return 0;

    if (VIR_ALLOC(ptr) < 0)
        return -1;
    ptr->drv = drv;
    ptr->vm = vm;

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
        usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
                                      dev->source.subsys.u.usb.device);

        if (!usb)
            goto done;

        ret = usbDeviceFileIterate(usb, AppArmorSetSecurityUSBLabel, ptr);
        usbFreeDevice(usb);
        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
        pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain,
                                      dev->source.subsys.u.pci.bus,
                                      dev->source.subsys.u.pci.slot,
                                      dev->source.subsys.u.pci.function);

        if (!pci)
            goto done;

        ret = pciDeviceFileIterate(pci, AppArmorSetSecurityPCILabel, ptr);
        pciFreeDevice(pci);
        break;
    }

    default:
        ret = 0;
        break;
    }

done:
    VIR_FREE(ptr);
    return ret;
J
Jamie Strandboge 已提交
738 739
}

740

J
Jamie Strandboge 已提交
741
static int
742
AppArmorRestoreSecurityHostdevLabel(virSecurityDriverPtr drv,
743
                                    virDomainObjPtr vm,
J
Jamie Strandboge 已提交
744 745 746
                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)

{
747 748 749 750
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

751
    return reload_profile(drv, vm, NULL, false);
J
Jamie Strandboge 已提交
752 753
}

754
static int
755 756 757
AppArmorSetSavedStateLabel(virSecurityDriverPtr drv,
                           virDomainObjPtr vm,
                           const char *savefile)
758
{
759
    return reload_profile(drv, vm, savefile, true);
760 761 762 763
}


static int
764 765
AppArmorRestoreSavedStateLabel(virSecurityDriverPtr drv,
                               virDomainObjPtr vm,
766 767
                               const char *savefile ATTRIBUTE_UNUSED)
{
768
    return reload_profile(drv, vm, NULL, false);
769 770
}

J
Jamie Strandboge 已提交
771 772 773 774 775 776 777 778 779
virSecurityDriver virAppArmorSecurityDriver = {
    .name = SECURITY_APPARMOR_NAME,
    .probe = AppArmorSecurityDriverProbe,
    .open = AppArmorSecurityDriverOpen,
    .domainSecurityVerify = AppArmorSecurityVerify,
    .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
    .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
    .domainGenSecurityLabel = AppArmorGenSecurityLabel,
    .domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
780 781 782 783 784
    .domainReleaseSecurityLabel = AppArmorReleaseSecurityLabel,
    .domainGetSecurityProcessLabel = AppArmorGetSecurityProcessLabel,
    .domainSetSecurityProcessLabel = AppArmorSetSecurityProcessLabel,
    .domainRestoreSecurityAllLabel = AppArmorRestoreSecurityAllLabel,
    .domainSetSecurityAllLabel = AppArmorSetSecurityAllLabel,
J
Jamie Strandboge 已提交
785 786
    .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
    .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,
787 788
    .domainSetSavedStateLabel = AppArmorSetSavedStateLabel,
    .domainRestoreSavedStateLabel = AppArmorRestoreSavedStateLabel,
J
Jamie Strandboge 已提交
789
};