security_apparmor.c 17.0 KB
Newer Older
J
Jamie Strandboge 已提交
1 2 3 4 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

/*
 * AppArmor security driver for libvirt
 * Copyright (C) 2009 Canonical Ltd.
 *
 * 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"

#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"

/*
 * 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 */
59
    if (virAsprintf(&tmp, "%s ", str) == -1) {
60
        virReportOOMError();
J
Jamie Strandboge 已提交
61
        return rc;
62
    }
J
Jamie Strandboge 已提交
63 64 65 66 67

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

    if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
74
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
                             _("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 已提交
90
    VIR_FREE(etmp);
J
Jamie Strandboge 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

    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 已提交
108
    char *profile = NULL;
J
Jamie Strandboge 已提交
109 110 111 112 113
    char *content = NULL;
    char *tmp = NULL;
    int rc = -1;
    int len;

J
Jamie Strandboge 已提交
114
    if (virAsprintf(&profile, "%s/%s", APPARMOR_DIR "/libvirt", str) == -1) {
115
        virReportOOMError();
J
Jamie Strandboge 已提交
116 117 118
        return rc;
    }

J
Jamie Strandboge 已提交
119 120 121
    if (!virFileExists(profile))
        goto failed;

J
Jamie Strandboge 已提交
122
    if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
123
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
124
                             _("Failed to read \'%s\'"), profile);
J
Jamie Strandboge 已提交
125
        goto failed;
J
Jamie Strandboge 已提交
126 127 128 129
    }

    /* create string that is ' <str> flags=(complain)\0' */
    if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
130
        virReportOOMError();
J
Jamie Strandboge 已提交
131
        goto failed;
J
Jamie Strandboge 已提交
132 133 134 135 136 137 138
    }

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

J
Jamie Strandboge 已提交
139
  failed:
J
Jamie Strandboge 已提交
140
    VIR_FREE(tmp);
J
Jamie Strandboge 已提交
141
    VIR_FREE(profile);
J
Jamie Strandboge 已提交
142 143 144 145 146 147 148 149 150
    VIR_FREE(content);

    return rc;
}

/*
 * load (add) a profile. Will create one if necessary
 */
static int
151
load_profile(const char *profile, virDomainObjPtr vm,
J
Jamie Strandboge 已提交
152 153 154 155 156 157 158 159 160
             virDomainDiskDefPtr disk)
{
    int rc = -1, status, ret;
    bool create = true;
    char *xml = NULL;
    int pipefd[2];
    pid_t child;

    if (pipe(pipefd) < -1) {
161
        virReportSystemError(errno, "%s", _("unable to create pipe"));
J
Jamie Strandboge 已提交
162 163 164
        return rc;
    }

165
    xml = virDomainDefFormat(vm->def, VIR_DOMAIN_XML_SECURE);
J
Jamie Strandboge 已提交
166
    if (!xml)
J
Jamie Strandboge 已提交
167
        goto clean;
J
Jamie Strandboge 已提交
168 169 170 171 172 173 174 175

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

    if (create) {
        const char *const argv[] = {
            VIRT_AA_HELPER, "-c", "-u", profile, NULL
        };
176
        ret = virExec(argv, NULL, NULL, &child,
J
Jamie Strandboge 已提交
177 178 179 180 181
                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
    } else if (disk && disk->src) {
        const char *const argv[] = {
            VIRT_AA_HELPER, "-r", "-u", profile, "-f", disk->src, NULL
        };
182
        ret = virExec(argv, NULL, NULL, &child,
J
Jamie Strandboge 已提交
183 184 185 186 187
                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
    } else {
        const char *const argv[] = {
            VIRT_AA_HELPER, "-r", "-u", profile, NULL
        };
188
        ret = virExec(argv, NULL, NULL, &child,
J
Jamie Strandboge 已提交
189 190 191 192 193 194 195
                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
    }
    if (ret < 0)
        goto clean;

    /* parent continues here */
    if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
196
        virReportSystemError(errno, "%s", _("unable to write to pipe"));
J
Jamie Strandboge 已提交
197 198 199 200 201 202 203 204 205 206
        goto clean;
    }
    close(pipefd[1]);
    rc = 0;

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

207
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
                               _("Unexpected exit status from virt-aa-helper "
                               "%d pid %lu"),
                               WEXITSTATUS(status), (unsigned long)child);
        rc = -1;
    }

  clean:
    VIR_FREE(xml);

    if (pipefd[0] > 0)
        close(pipefd[0]);
    if (pipefd[1] > 0)
        close(pipefd[1]);

    return rc;
}

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

233
    if (virRun(argv, NULL) == 0)
J
Jamie Strandboge 已提交
234 235 236 237 238 239
        rc = 0;

    return rc;
}

static char *
240
get_profile_name(virDomainObjPtr vm)
J
Jamie Strandboge 已提交
241 242 243 244 245 246
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *name = NULL;

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
247
        virReportOOMError();
J
Jamie Strandboge 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261
        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;
262
    char *libvirt_daemon = NULL;
J
Jamie Strandboge 已提交
263

264
    if (virFileResolveLink("/proc/self/exe", &libvirt_daemon) < 0) {
265
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
266 267 268 269 270
                               "%s", _("could not find libvirtd"));
        return rc;
    }

    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
271 272 273
        goto cleanup;

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

275 276 277
cleanup:
    VIR_FREE(libvirt_daemon);
    return rc;
J
Jamie Strandboge 已提交
278 279 280 281 282 283
}

/* Called on libvirtd startup to see if AppArmor is available */
static int
AppArmorSecurityDriverProbe(void)
{
J
Jamie Strandboge 已提交
284 285
    char *template = NULL;
    int rc = SECURITY_DRIVER_DISABLE;
J
Jamie Strandboge 已提交
286 287

    if (use_apparmor() < 0)
J
Jamie Strandboge 已提交
288
        return rc;
J
Jamie Strandboge 已提交
289 290

    /* see if template file exists */
J
Jamie Strandboge 已提交
291 292
    if (virAsprintf(&template, "%s/TEMPLATE",
                               APPARMOR_DIR "/libvirt") == -1) {
293
        virReportOOMError();
J
Jamie Strandboge 已提交
294
        return rc;
J
Jamie Strandboge 已提交
295 296 297
    }

    if (!virFileExists(template)) {
298
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
299
                               _("template \'%s\' does not exist"), template);
J
Jamie Strandboge 已提交
300
        goto clean;
J
Jamie Strandboge 已提交
301
    }
J
Jamie Strandboge 已提交
302
    rc = SECURITY_DRIVER_ENABLE;
J
Jamie Strandboge 已提交
303

J
Jamie Strandboge 已提交
304 305 306 307
  clean:
    VIR_FREE(template);

    return rc;
J
Jamie Strandboge 已提交
308 309 310 311 312 313
}

/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
 * currently not used.
 */
static int
314
AppArmorSecurityDriverOpen(virSecurityDriverPtr drv)
J
Jamie Strandboge 已提交
315
{
316
    virSecurityDriverSetDOI(drv, SECURITY_APPARMOR_VOID_DOI);
J
Jamie Strandboge 已提交
317 318 319 320 321 322 323 324 325
    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
326
AppArmorGenSecurityLabel(virDomainObjPtr vm)
J
Jamie Strandboge 已提交
327 328 329 330
{
    int rc = -1;
    char *profile_name = NULL;

331 332 333
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

J
Jamie Strandboge 已提交
334 335
    if ((vm->def->seclabel.label) ||
        (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
336
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
337 338 339 340 341
                               "%s",
                               _("security label already defined for VM"));
        return rc;
    }

342
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
343 344 345 346
        return rc;

    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
    if (!vm->def->seclabel.label) {
347
        virReportOOMError();
J
Jamie Strandboge 已提交
348 349 350 351 352 353 354
        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) {
355
        virReportOOMError();
J
Jamie Strandboge 已提交
356 357 358 359 360
        goto err;
    }

    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
    if (!vm->def->seclabel.model) {
361
        virReportOOMError();
J
Jamie Strandboge 已提交
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
        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;
}

379
static int
380
AppArmorSetSecurityAllLabel(virDomainObjPtr vm)
381 382 383 384 385 386
{
    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) {
387 388
        if (load_profile(vm->def->seclabel.label, vm, NULL) < 0) {
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
389 390 391 392 393 394 395 396 397
                                   _("cannot generate AppArmor profile "
                                   "\'%s\'"), vm->def->seclabel.label);
            return -1;
        }
    }

    return 0;
}

J
Jamie Strandboge 已提交
398 399 400 401
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
 * running.
 */
static int
402
AppArmorGetSecurityProcessLabel(virDomainObjPtr vm, virSecurityLabelPtr sec)
J
Jamie Strandboge 已提交
403 404 405 406
{
    int rc = -1;
    char *profile_name = NULL;

407
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
408 409 410 411
        return rc;

    if (virStrcpy(sec->label, profile_name,
        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
412
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
413 414 415 416 417
                               "%s", _("error copying profile name"));
        goto clean;
    }

    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
418
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
                               "%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
434
AppArmorReleaseSecurityLabel(virDomainObjPtr vm)
435 436 437 438 439 440 441 442 443 444 445 446
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

    return 0;
}


static int
447
AppArmorRestoreSecurityAllLabel(virDomainObjPtr vm)
J
Jamie Strandboge 已提交
448 449 450 451
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = 0;

452
    if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
J
Jamie Strandboge 已提交
453
        if ((rc = remove_profile(secdef->label)) != 0) {
454
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
455 456 457 458 459 460 461 462 463 464 465
                                   _("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
466
AppArmorSetSecurityProcessLabel(virSecurityDriverPtr drv, virDomainObjPtr vm)
J
Jamie Strandboge 已提交
467 468 469 470 471
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

472
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
473 474 475
        return rc;

    if (STRNEQ(drv->name, secdef->model)) {
476
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
477 478 479 480 481 482 483 484 485
                               _("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) {
486
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR, "%s",
J
Jamie Strandboge 已提交
487 488 489 490 491 492 493 494 495 496 497 498 499 500
                               _("error calling aa_change_profile()"));
        goto clean;
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}


/* Called when hotplugging */
static int
501
AppArmorRestoreSecurityImageLabel(virDomainObjPtr vm,
J
Jamie Strandboge 已提交
502 503 504 505 506 507
                                  virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

508 509
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;
J
Jamie Strandboge 已提交
510

511
    if ((profile_name = get_profile_name(vm)) == NULL)
512 513 514 515
        return rc;

    /* Update the profile only if it is loaded */
    if (profile_loaded(secdef->imagelabel) >= 0) {
516 517
        if (load_profile(secdef->imagelabel, vm, NULL) < 0) {
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
518 519 520 521
                                   _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                   secdef->imagelabel);
            goto clean;
J
Jamie Strandboge 已提交
522 523
        }
    }
524

J
Jamie Strandboge 已提交
525 526 527 528 529 530 531 532 533
    rc = 0;
  clean:
    VIR_FREE(profile_name);

    return rc;
}

/* Called when hotplugging */
static int
534
AppArmorSetSecurityImageLabel(virDomainObjPtr vm, virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
535 536 537 538 539
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name;

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

J
Jamie Strandboge 已提交
543 544 545 546 547 548
    if (!disk->src)
        return 0;

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

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

        /* update the profile only if it is loaded */
        if (profile_loaded(secdef->imagelabel) >= 0) {
559 560
            if (load_profile(secdef->imagelabel, vm, disk) < 0) {
                virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
                                     _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                     secdef->imagelabel);
                goto clean;
            }
        }
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

static int
577
AppArmorSecurityVerify(virDomainDefPtr def)
J
Jamie Strandboge 已提交
578 579 580 581 582
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
583
            virSecurityReportError(VIR_ERR_XML_ERROR,
J
Jamie Strandboge 已提交
584 585 586 587 588 589 590 591 592
                                   _("Invalid security label \'%s\'"),
                                   secdef->label);
            return -1;
        }
    }
    return 0;
}

static int
593
AppArmorReserveSecurityLabel(virDomainObjPtr vm ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
594 595 596 597 598 599
{
    /* NOOP. Nothing to reserve with AppArmor */
    return 0;
}

static int
600
AppArmorSetSecurityHostdevLabel(virDomainObjPtr vm,
J
Jamie Strandboge 已提交
601 602 603
                                virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)

{
604 605 606 607 608
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

J
Jamie Strandboge 已提交
609 610 611 612 613
    /* TODO: call load_profile with an update vm->def */
    return 0;
}

static int
614
AppArmorRestoreSecurityHostdevLabel(virDomainObjPtr vm,
J
Jamie Strandboge 已提交
615 616 617
                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)

{
618 619 620 621
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

J
Jamie Strandboge 已提交
622 623 624 625 626 627 628 629 630 631 632 633 634
    /* TODO: call load_profile (needs virDomainObjPtr vm) */
    return 0;
}

virSecurityDriver virAppArmorSecurityDriver = {
    .name = SECURITY_APPARMOR_NAME,
    .probe = AppArmorSecurityDriverProbe,
    .open = AppArmorSecurityDriverOpen,
    .domainSecurityVerify = AppArmorSecurityVerify,
    .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
    .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
    .domainGenSecurityLabel = AppArmorGenSecurityLabel,
    .domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
635 636 637 638 639
    .domainReleaseSecurityLabel = AppArmorReleaseSecurityLabel,
    .domainGetSecurityProcessLabel = AppArmorGetSecurityProcessLabel,
    .domainSetSecurityProcessLabel = AppArmorSetSecurityProcessLabel,
    .domainRestoreSecurityAllLabel = AppArmorRestoreSecurityAllLabel,
    .domainSetSecurityAllLabel = AppArmorSetSecurityAllLabel,
J
Jamie Strandboge 已提交
640 641 642
    .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
    .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,
};