security_apparmor.c 22.5 KB
Newer Older
J
Jamie Strandboge 已提交
1 2
/*
 * AppArmor security driver for libvirt
3
 * Copyright (C) 2011 Red Hat, Inc.
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
 *
 * 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 "internal.h"

#include "security_apparmor.h"
#include "util.h"
#include "memory.h"
#include "virterror_internal.h"
#include "datatypes.h"
#include "uuid.h"
36 37
#include "pci.h"
#include "hostusb.h"
38
#include "files.h"
39
#include "configmake.h"
40
#include "command.h"
J
Jamie Strandboge 已提交
41 42 43 44

#define VIR_FROM_THIS VIR_FROM_SECURITY
#define SECURITY_APPARMOR_VOID_DOI      "0"
#define SECURITY_APPARMOR_NAME          "apparmor"
45
#define VIRT_AA_HELPER LIBEXECDIR "/virt-aa-helper"
J
Jamie Strandboge 已提交
46

47 48
/* Data structure to pass to *FileIterate so we have everything we need */
struct SDPDOP {
49
    virSecurityManagerPtr mgr;
50 51 52
    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(virSecurityManagerPtr mgr,
161 162 163 164
             const char *profile,
             virDomainObjPtr vm,
             const char *fn,
             bool append)
J
Jamie Strandboge 已提交
165
{
166
    int rc = -1;
J
Jamie Strandboge 已提交
167 168
    bool create = true;
    char *xml = NULL;
169
    virCommandPtr cmd;
170
    const char *probe = virSecurityManagerGetAllowDiskFormatProbing(mgr)
171
        ? "1" : "0";
J
Jamie Strandboge 已提交
172

173
    xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_SECURE);
J
Jamie Strandboge 已提交
174
    if (!xml)
J
Jamie Strandboge 已提交
175
        goto clean;
J
Jamie Strandboge 已提交
176 177 178 179

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

180 181 182 183 184 185 186 187 188
    cmd = virCommandNewArgList(VIRT_AA_HELPER, "-p", probe,
                               create ? "-c" : "-r",
                               "-u", profile, NULL);
    if (!create && fn) {
        if (append) {
            virCommandAddArgList(cmd, "-F", fn, NULL);
        } else {
            virCommandAddArgList(cmd, "-f", fn, NULL);
        }
J
Jamie Strandboge 已提交
189 190
    }

191 192
    virCommandSetInputBuffer(cmd, xml);
    rc = virCommandRun(cmd, NULL);
J
Jamie Strandboge 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

  clean:
    VIR_FREE(xml);

    return rc;
}

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

208
    if (virRun(argv, NULL) == 0)
J
Jamie Strandboge 已提交
209 210 211 212 213 214
        rc = 0;

    return rc;
}

static char *
215
get_profile_name(virDomainObjPtr vm)
J
Jamie Strandboge 已提交
216 217 218 219 220 221
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *name = NULL;

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
222
        virReportOOMError();
J
Jamie Strandboge 已提交
223 224 225 226 227 228 229 230 231 232 233 234 235 236
        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;
237
    char *libvirt_daemon = NULL;
J
Jamie Strandboge 已提交
238

239
    if (virFileResolveLink("/proc/self/exe", &libvirt_daemon) < 0) {
240
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
241 242 243 244 245
                               "%s", _("could not find libvirtd"));
        return rc;
    }

    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
246 247 248
        goto cleanup;

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

250 251 252
cleanup:
    VIR_FREE(libvirt_daemon);
    return rc;
J
Jamie Strandboge 已提交
253 254
}

255 256 257 258
/* reload the profile, adding read/write file specified by fn if it is not
 * NULL.
 */
static int
259
reload_profile(virSecurityManagerPtr mgr,
260 261 262
               virDomainObjPtr vm,
               const char *fn,
               bool append)
263 264 265 266 267 268 269 270 271 272 273 274 275
{
    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) {
276
        if (load_profile(mgr, secdef->imagelabel, vm, fn, append) < 0) {
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                   secdef->imagelabel);
            goto clean;
        }
    }

    rc = 0;
  clean:
    VIR_FREE(profile_name);

    return rc;
}

292 293 294 295 296 297 298
static int
AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
                           const char *file, void *opaque)
{
    struct SDPDOP *ptr = opaque;
    virDomainObjPtr vm = ptr->vm;

299
    if (reload_profile(ptr->mgr, vm, file, true) < 0) {
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        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;

317
    if (reload_profile(ptr->mgr, vm, file, true) < 0) {
318 319 320 321 322 323 324 325 326 327
        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 已提交
328 329
/* Called on libvirtd startup to see if AppArmor is available */
static int
330
AppArmorSecurityManagerProbe(void)
J
Jamie Strandboge 已提交
331
{
J
Jamie Strandboge 已提交
332 333
    char *template = NULL;
    int rc = SECURITY_DRIVER_DISABLE;
J
Jamie Strandboge 已提交
334 335

    if (use_apparmor() < 0)
J
Jamie Strandboge 已提交
336
        return rc;
J
Jamie Strandboge 已提交
337 338

    /* see if template file exists */
J
Jamie Strandboge 已提交
339 340
    if (virAsprintf(&template, "%s/TEMPLATE",
                               APPARMOR_DIR "/libvirt") == -1) {
341
        virReportOOMError();
J
Jamie Strandboge 已提交
342
        return rc;
J
Jamie Strandboge 已提交
343 344 345
    }

    if (!virFileExists(template)) {
346
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
347
                               _("template \'%s\' does not exist"), template);
J
Jamie Strandboge 已提交
348
        goto clean;
J
Jamie Strandboge 已提交
349
    }
J
Jamie Strandboge 已提交
350
    rc = SECURITY_DRIVER_ENABLE;
J
Jamie Strandboge 已提交
351

J
Jamie Strandboge 已提交
352 353 354 355
  clean:
    VIR_FREE(template);

    return rc;
J
Jamie Strandboge 已提交
356 357 358 359 360 361
}

/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
 * currently not used.
 */
static int
362 363 364 365 366 367 368
AppArmorSecurityManagerOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return 0;
}

static int
AppArmorSecurityManagerClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
369 370 371 372
{
    return 0;
}

373 374 375 376 377 378 379 380 381 382 383 384 385
static const char *
AppArmorSecurityManagerGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return SECURITY_APPARMOR_NAME;
}

static const char *
AppArmorSecurityManagerGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return SECURITY_APPARMOR_VOID_DOI;
}


J
Jamie Strandboge 已提交
386 387 388 389 390 391
/* 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
392
AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
393
                         virDomainObjPtr vm)
J
Jamie Strandboge 已提交
394 395 396 397
{
    int rc = -1;
    char *profile_name = NULL;

398 399 400
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

401 402 403 404 405 406
    if (vm->def->seclabel.baselabel) {
        virSecurityReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               "%s", _("Cannot set a base label with AppArmour"));
        return rc;
    }

J
Jamie Strandboge 已提交
407 408
    if ((vm->def->seclabel.label) ||
        (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
409
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
410 411 412 413 414
                               "%s",
                               _("security label already defined for VM"));
        return rc;
    }

415
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
416 417 418 419
        return rc;

    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
    if (!vm->def->seclabel.label) {
420
        virReportOOMError();
J
Jamie Strandboge 已提交
421 422 423 424 425 426 427
        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) {
428
        virReportOOMError();
J
Jamie Strandboge 已提交
429 430 431 432 433
        goto err;
    }

    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
    if (!vm->def->seclabel.model) {
434
        virReportOOMError();
J
Jamie Strandboge 已提交
435 436 437
        goto err;
    }

438 439 440 441 442 443 444 445
    /* Now that we have a label, load the profile into the kernel. */
    if (load_profile(mgr, vm->def->seclabel.label, vm, NULL, false) < 0) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               _("cannot load AppArmor profile "
                               "\'%s\'"), vm->def->seclabel.label);
        goto err;
    }

J
Jamie Strandboge 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459
    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;
}

460
static int
461
AppArmorSetSecurityAllLabel(virSecurityManagerPtr mgr,
462
                            virDomainObjPtr vm, const char *stdin_path)
463 464 465 466
{
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

467 468 469 470
    /* Reload the profile if stdin_path is specified. Note that
       GenSecurityLabel() will have already been run. */
    if (stdin_path)
        return reload_profile(mgr, vm, stdin_path, true);
471 472 473 474

    return 0;
}

J
Jamie Strandboge 已提交
475 476 477 478
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
 * running.
 */
static int
479
AppArmorGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
480 481
                                virDomainObjPtr vm,
                                virSecurityLabelPtr sec)
J
Jamie Strandboge 已提交
482 483 484 485
{
    int rc = -1;
    char *profile_name = NULL;

486
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
487 488 489 490
        return rc;

    if (virStrcpy(sec->label, profile_name,
        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
491
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
492 493 494 495 496
                               "%s", _("error copying profile name"));
        goto clean;
    }

    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
497
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
                               "%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
513
AppArmorReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
514
                             virDomainObjPtr vm)
515 516 517 518 519 520 521 522 523 524 525 526
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

    return 0;
}


static int
527
AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
528
                                virDomainObjPtr vm,
529
                                int migrated ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
530 531 532 533
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = 0;

534
    if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
J
Jamie Strandboge 已提交
535
        if ((rc = remove_profile(secdef->label)) != 0) {
536
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
537 538 539 540 541 542 543
                                   _("could not remove profile for \'%s\'"),
                                   secdef->label);
        }
    }
    return rc;
}

544
/* Called via virCommand hook. Output goes to
545
 * LOCALSTATEDIR/log/libvirt/qemu/<vm name>.log
J
Jamie Strandboge 已提交
546 547
 */
static int
548
AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm)
J
Jamie Strandboge 已提交
549 550 551 552 553
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

554
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
555 556
        return rc;

557
    if (STRNEQ(virSecurityManagerGetModel(mgr), secdef->model)) {
558
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
559 560 561
                               _("security label driver mismatch: "
                               "\'%s\' model configured for domain, but "
                               "hypervisor driver is \'%s\'."),
562
                               secdef->model, virSecurityManagerGetModel(mgr));
J
Jamie Strandboge 已提交
563 564 565 566 567
        if (use_apparmor() > 0)
            goto clean;
    }

    if (aa_change_profile(profile_name) < 0) {
568
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR, "%s",
J
Jamie Strandboge 已提交
569 570 571 572 573 574 575 576 577 578 579
                               _("error calling aa_change_profile()"));
        goto clean;
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

580 581 582 583 584 585 586 587 588 589 590 591 592 593
static int
AppArmorSetSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                               virDomainObjPtr vm ATTRIBUTE_UNUSED)
{
    return 0;
}

static int
AppArmorClearSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                                 virDomainObjPtr vm ATTRIBUTE_UNUSED)
{
    return 0;
}

J
Jamie Strandboge 已提交
594 595 596

/* Called when hotplugging */
static int
597
AppArmorRestoreSecurityImageLabel(virSecurityManagerPtr mgr,
598
                                  virDomainObjPtr vm,
J
Jamie Strandboge 已提交
599 600
                                  virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
601
    return reload_profile(mgr, vm, NULL, false);
J
Jamie Strandboge 已提交
602 603 604 605
}

/* Called when hotplugging */
static int
606
AppArmorSetSecurityImageLabel(virSecurityManagerPtr mgr,
607
                              virDomainObjPtr vm, virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
608 609 610 611 612
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name;

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

616
    if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
J
Jamie Strandboge 已提交
617 618 619 620 621
        return 0;

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

627
        if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
628 629 630 631
            return rc;

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

  clean:
    VIR_FREE(profile_name);

    return rc;
}

static int
651 652
AppArmorSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                       virDomainDefPtr def)
J
Jamie Strandboge 已提交
653 654 655 656 657
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;

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

static int
668
AppArmorReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
669
                             virDomainObjPtr vm ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
670 671 672 673 674 675
{
    /* NOOP. Nothing to reserve with AppArmor */
    return 0;
}

static int
676
AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr,
677
                                virDomainObjPtr vm,
678
                                virDomainHostdevDefPtr dev)
J
Jamie Strandboge 已提交
679 680

{
681
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
682 683
    struct SDPDOP *ptr;
    int ret = -1;
684 685 686 687

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

688 689 690 691 692 693 694 695
    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;
696
    ptr->mgr = mgr;
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
    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 已提交
734 735
}

736

J
Jamie Strandboge 已提交
737
static int
738
AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr,
739
                                    virDomainObjPtr vm,
J
Jamie Strandboge 已提交
740 741 742
                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)

{
743 744 745 746
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

747
    return reload_profile(mgr, vm, NULL, false);
J
Jamie Strandboge 已提交
748 749
}

750
static int
751
AppArmorSetSavedStateLabel(virSecurityManagerPtr mgr,
752 753
                           virDomainObjPtr vm,
                           const char *savefile)
754
{
755
    return reload_profile(mgr, vm, savefile, true);
756 757 758 759
}


static int
760
AppArmorRestoreSavedStateLabel(virSecurityManagerPtr mgr,
761
                               virDomainObjPtr vm,
762 763
                               const char *savefile ATTRIBUTE_UNUSED)
{
764
    return reload_profile(mgr, vm, NULL, false);
765 766
}

767
static int
768 769 770
AppArmorSetImageFDLabel(virSecurityManagerPtr mgr,
                        virDomainObjPtr vm,
                        int fd)
771
{
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
    int rc = -1;
    char *proc = NULL;
    char *fd_path = NULL;

    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    if (secdef->imagelabel == NULL)
        return 0;

    if (virAsprintf(&proc, "/proc/self/fd/%d", fd) == -1) {
        virReportOOMError();
        return rc;
    }

    if (virFileResolveLink(proc, &fd_path) < 0) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               "%s", _("could not find path for descriptor"));
        return rc;
    }

    return reload_profile(mgr, vm, fd_path, true);
793 794
}

795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
static int
AppArmorSetProcessFDLabel(virSecurityManagerPtr mgr,
                          virDomainObjPtr vm,
                          int fd)
{
    int rc = -1;
    char *proc = NULL;
    char *fd_path = NULL;

    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

    if (secdef->imagelabel == NULL)
        return 0;

    if (virAsprintf(&proc, "/proc/self/fd/%d", fd) == -1) {
        virReportOOMError();
        return rc;
    }

    if (virFileResolveLink(proc, &fd_path) < 0) {
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                               "%s", _("could not find path for descriptor"));
        return rc;
    }

    return reload_profile(mgr, vm, fd_path, true);
}

J
Jamie Strandboge 已提交
823
virSecurityDriver virAppArmorSecurityDriver = {
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
    0,
    SECURITY_APPARMOR_NAME,
    AppArmorSecurityManagerProbe,
    AppArmorSecurityManagerOpen,
    AppArmorSecurityManagerClose,

    AppArmorSecurityManagerGetModel,
    AppArmorSecurityManagerGetDOI,

    AppArmorSecurityVerify,

    AppArmorSetSecurityImageLabel,
    AppArmorRestoreSecurityImageLabel,

    AppArmorSetSecuritySocketLabel,
    AppArmorClearSecuritySocketLabel,

    AppArmorGenSecurityLabel,
    AppArmorReserveSecurityLabel,
    AppArmorReleaseSecurityLabel,

    AppArmorGetSecurityProcessLabel,
    AppArmorSetSecurityProcessLabel,

    AppArmorSetSecurityAllLabel,
    AppArmorRestoreSecurityAllLabel,

    AppArmorSetSecurityHostdevLabel,
    AppArmorRestoreSecurityHostdevLabel,

    AppArmorSetSavedStateLabel,
    AppArmorRestoreSavedStateLabel,
856

857
    AppArmorSetImageFDLabel,
858
    AppArmorSetProcessFDLabel,
J
Jamie Strandboge 已提交
859
};