security_apparmor.c 22.3 KB
Newer Older
J
Jamie Strandboge 已提交
1 2
/*
 * AppArmor security driver for libvirt
3
 * Copyright (C) 2009-2010 Canonical Ltd.
J
Jamie Strandboge 已提交
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
 *
 * 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"
35 36
#include "pci.h"
#include "hostusb.h"
37
#include "files.h"
38
#include "configmake.h"
J
Jamie Strandboge 已提交
39 40 41 42

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

45 46
/* Data structure to pass to *FileIterate so we have everything we need */
struct SDPDOP {
47
    virSecurityManagerPtr mgr;
48 49 50
    virDomainObjPtr vm;
};

J
Jamie Strandboge 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/*
 * 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 */
66
    if (virAsprintf(&tmp, "%s ", str) == -1) {
67
        virReportOOMError();
J
Jamie Strandboge 已提交
68
        return rc;
69
    }
J
Jamie Strandboge 已提交
70 71 72 73 74

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

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

    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 已提交
115
    char *profile = NULL;
J
Jamie Strandboge 已提交
116 117 118 119 120
    char *content = NULL;
    char *tmp = NULL;
    int rc = -1;
    int len;

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

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

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

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

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

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

    return rc;
}

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

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

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

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

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

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

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

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

  clean:
    VIR_FREE(xml);

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

    return rc;
}

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

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

    return rc;
}

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

    virUUIDFormat(vm->def->uuid, uuidstr);
    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
263
        virReportOOMError();
J
Jamie Strandboge 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277
        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;
278
    char *libvirt_daemon = NULL;
J
Jamie Strandboge 已提交
279

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

    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
287 288 289
        goto cleanup;

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

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

296 297 298 299
/* reload the profile, adding read/write file specified by fn if it is not
 * NULL.
 */
static int
300
reload_profile(virSecurityManagerPtr mgr,
301 302 303
               virDomainObjPtr vm,
               const char *fn,
               bool append)
304 305 306 307 308 309 310 311 312 313 314 315 316
{
    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) {
317
        if (load_profile(mgr, secdef->imagelabel, vm, fn, append) < 0) {
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
                                   _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                   secdef->imagelabel);
            goto clean;
        }
    }

    rc = 0;
  clean:
    VIR_FREE(profile_name);

    return rc;
}

333 334 335 336 337 338 339
static int
AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
                           const char *file, void *opaque)
{
    struct SDPDOP *ptr = opaque;
    virDomainObjPtr vm = ptr->vm;

340
    if (reload_profile(ptr->mgr, vm, file, true) < 0) {
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        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;

358
    if (reload_profile(ptr->mgr, vm, file, true) < 0) {
359 360 361 362 363 364 365 366 367 368
        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 已提交
369 370
/* Called on libvirtd startup to see if AppArmor is available */
static int
371
AppArmorSecurityManagerProbe(void)
J
Jamie Strandboge 已提交
372
{
J
Jamie Strandboge 已提交
373 374
    char *template = NULL;
    int rc = SECURITY_DRIVER_DISABLE;
J
Jamie Strandboge 已提交
375 376

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

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

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

J
Jamie Strandboge 已提交
393 394 395 396
  clean:
    VIR_FREE(template);

    return rc;
J
Jamie Strandboge 已提交
397 398 399 400 401 402
}

/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
 * currently not used.
 */
static int
403 404 405 406 407 408 409
AppArmorSecurityManagerOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return 0;
}

static int
AppArmorSecurityManagerClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
410 411 412 413
{
    return 0;
}

414 415 416 417 418 419 420 421 422 423 424 425 426
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 已提交
427 428 429 430 431 432
/* 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
433
AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
434
                         virDomainObjPtr vm)
J
Jamie Strandboge 已提交
435 436 437 438
{
    int rc = -1;
    char *profile_name = NULL;

439 440 441
    if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

J
Jamie Strandboge 已提交
442 443
    if ((vm->def->seclabel.label) ||
        (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
444
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
445 446 447 448 449
                               "%s",
                               _("security label already defined for VM"));
        return rc;
    }

450
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
451 452 453 454
        return rc;

    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
    if (!vm->def->seclabel.label) {
455
        virReportOOMError();
J
Jamie Strandboge 已提交
456 457 458 459 460 461 462
        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) {
463
        virReportOOMError();
J
Jamie Strandboge 已提交
464 465 466 467 468
        goto err;
    }

    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
    if (!vm->def->seclabel.model) {
469
        virReportOOMError();
J
Jamie Strandboge 已提交
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
        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;
}

487
static int
488
AppArmorSetSecurityAllLabel(virSecurityManagerPtr mgr,
489
                            virDomainObjPtr vm, const char *stdin_path)
490 491 492 493 494 495
{
    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) {
496
        if (load_profile(mgr, vm->def->seclabel.label, vm, stdin_path,
497
                         false) < 0) {
498
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
499 500 501 502 503 504 505 506 507
                                   _("cannot generate AppArmor profile "
                                   "\'%s\'"), vm->def->seclabel.label);
            return -1;
        }
    }

    return 0;
}

J
Jamie Strandboge 已提交
508 509 510 511
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
 * running.
 */
static int
512
AppArmorGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
513 514
                                virDomainObjPtr vm,
                                virSecurityLabelPtr sec)
J
Jamie Strandboge 已提交
515 516 517 518
{
    int rc = -1;
    char *profile_name = NULL;

519
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
520 521 522 523
        return rc;

    if (virStrcpy(sec->label, profile_name,
        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
524
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
525 526 527 528 529
                               "%s", _("error copying profile name"));
        goto clean;
    }

    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
530
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
                               "%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
546
AppArmorReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
547
                             virDomainObjPtr vm)
548 549 550 551 552 553 554 555 556 557 558 559
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;

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

    return 0;
}


static int
560
AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
561
                                virDomainObjPtr vm,
562
                                int migrated ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
563 564 565 566
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = 0;

567
    if (secdef->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
J
Jamie Strandboge 已提交
568
        if ((rc = remove_profile(secdef->label)) != 0) {
569
            virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
570 571 572 573 574 575 576 577
                                   _("could not remove profile for \'%s\'"),
                                   secdef->label);
        }
    }
    return rc;
}

/* Called via virExecWithHook. Output goes to
578
 * LOCALSTATEDIR/log/libvirt/qemu/<vm name>.log
J
Jamie Strandboge 已提交
579 580
 */
static int
581
AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm)
J
Jamie Strandboge 已提交
582 583 584 585 586
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name = NULL;

587
    if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
588 589
        return rc;

590
    if (STRNEQ(virSecurityManagerGetModel(mgr), secdef->model)) {
591
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
592 593 594
                               _("security label driver mismatch: "
                               "\'%s\' model configured for domain, but "
                               "hypervisor driver is \'%s\'."),
595
                               secdef->model, virSecurityManagerGetModel(mgr));
J
Jamie Strandboge 已提交
596 597 598 599 600
        if (use_apparmor() > 0)
            goto clean;
    }

    if (aa_change_profile(profile_name) < 0) {
601
        virSecurityReportError(VIR_ERR_INTERNAL_ERROR, "%s",
J
Jamie Strandboge 已提交
602 603 604 605 606 607 608 609 610 611 612
                               _("error calling aa_change_profile()"));
        goto clean;
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

613 614 615 616 617 618 619 620 621 622 623 624 625 626
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 已提交
627 628 629

/* Called when hotplugging */
static int
630
AppArmorRestoreSecurityImageLabel(virSecurityManagerPtr mgr,
631
                                  virDomainObjPtr vm,
J
Jamie Strandboge 已提交
632 633
                                  virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
634
    return reload_profile(mgr, vm, NULL, false);
J
Jamie Strandboge 已提交
635 636 637 638
}

/* Called when hotplugging */
static int
639
AppArmorSetSecurityImageLabel(virSecurityManagerPtr mgr,
640
                              virDomainObjPtr vm, virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
641 642 643 644 645
{
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    int rc = -1;
    char *profile_name;

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

649
    if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
J
Jamie Strandboge 已提交
650 651 652 653 654
        return 0;

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

660
        if ((profile_name = get_profile_name(vm)) == NULL)
J
Jamie Strandboge 已提交
661 662 663 664
            return rc;

        /* update the profile only if it is loaded */
        if (profile_loaded(secdef->imagelabel) >= 0) {
665
            if (load_profile(mgr, secdef->imagelabel, vm, disk->src,
666
                             false) < 0) {
667
                virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
J
Jamie Strandboge 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
                                     _("cannot update AppArmor profile "
                                     "\'%s\'"),
                                     secdef->imagelabel);
                goto clean;
            }
        }
    }
    rc = 0;

  clean:
    VIR_FREE(profile_name);

    return rc;
}

static int
684 685
AppArmorSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                       virDomainDefPtr def)
J
Jamie Strandboge 已提交
686 687 688 689 690
{
    const virSecurityLabelDefPtr secdef = &def->seclabel;

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
691
            virSecurityReportError(VIR_ERR_XML_ERROR,
J
Jamie Strandboge 已提交
692 693 694 695 696 697 698 699 700
                                   _("Invalid security label \'%s\'"),
                                   secdef->label);
            return -1;
        }
    }
    return 0;
}

static int
701
AppArmorReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
702
                             virDomainObjPtr vm ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
703 704 705 706 707 708
{
    /* NOOP. Nothing to reserve with AppArmor */
    return 0;
}

static int
709
AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr,
710
                                virDomainObjPtr vm,
711
                                virDomainHostdevDefPtr dev)
J
Jamie Strandboge 已提交
712 713

{
714
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
715 716
    struct SDPDOP *ptr;
    int ret = -1;
717 718 719 720

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

721 722 723 724 725 726 727 728
    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;
729
    ptr->mgr = mgr;
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
    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 已提交
767 768
}

769

J
Jamie Strandboge 已提交
770
static int
771
AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr,
772
                                    virDomainObjPtr vm,
J
Jamie Strandboge 已提交
773 774 775
                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)

{
776 777 778 779
    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
        return 0;

780
    return reload_profile(mgr, vm, NULL, false);
J
Jamie Strandboge 已提交
781 782
}

783
static int
784
AppArmorSetSavedStateLabel(virSecurityManagerPtr mgr,
785 786
                           virDomainObjPtr vm,
                           const char *savefile)
787
{
788
    return reload_profile(mgr, vm, savefile, true);
789 790 791 792
}


static int
793
AppArmorRestoreSavedStateLabel(virSecurityManagerPtr mgr,
794
                               virDomainObjPtr vm,
795 796
                               const char *savefile ATTRIBUTE_UNUSED)
{
797
    return reload_profile(mgr, vm, NULL, false);
798 799
}

800 801 802 803 804 805 806 807
static int
AppArmorSetFDLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                   virDomainObjPtr vm ATTRIBUTE_UNUSED,
                   int fd ATTRIBUTE_UNUSED)
{
    return 0;
}

J
Jamie Strandboge 已提交
808
virSecurityDriver virAppArmorSecurityDriver = {
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
    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,
841 842

    AppArmorSetFDLabel,
J
Jamie Strandboge 已提交
843
};