security_apparmor.c 26.9 KB
Newer Older
J
Jamie Strandboge 已提交
1 2
/*
 * AppArmor security driver for libvirt
O
Osier Yang 已提交
3
 *
4
 * Copyright (C) 2011-2013 Red Hat, Inc.
5
 * Copyright (C) 2009-2010 Canonical Ltd.
J
Jamie Strandboge 已提交
6 7 8 9 10 11
 *
 * 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.
 *
O
Osier Yang 已提交
12 13 14 15 16 17
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19 20
 * <http://www.gnu.org/licenses/>.
 *
J
Jamie Strandboge 已提交
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 * 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"
41
#include "viralloc.h"
42
#include "virerror.h"
J
Jamie Strandboge 已提交
43
#include "datatypes.h"
44
#include "viruuid.h"
45
#include "virpci.h"
46
#include "virusb.h"
E
Eric Blake 已提交
47
#include "virfile.h"
48
#include "configmake.h"
49
#include "vircommand.h"
50
#include "virlog.h"
51
#include "virstring.h"
52
#include "virscsi.h"
J
Jamie Strandboge 已提交
53 54 55 56

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

59 60
/* Data structure to pass to *FileIterate so we have everything we need */
struct SDPDOP {
61
    virSecurityManagerPtr mgr;
62
    virDomainDefPtr def;
63 64
};

J
Jamie Strandboge 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/*
 * 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 */
80
    if (virAsprintf(&tmp, "%s ", str) == -1) {
81
        virReportOOMError();
J
Jamie Strandboge 已提交
82
        return rc;
83
    }
J
Jamie Strandboge 已提交
84 85 86 87 88

    if (check_enforcing != 0) {
        /* create string that is '<str> (enforce)\0' for accurate matching */
        if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
            VIR_FREE(tmp);
89
            virReportOOMError();
J
Jamie Strandboge 已提交
90 91 92 93 94
            return rc;
        }
    }

    if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
95
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
96 97
                             _("Failed to read AppArmor profiles list "
                             "\'%s\'"), APPARMOR_PROFILES_PATH);
98
        goto cleanup;
J
Jamie Strandboge 已提交
99 100 101 102 103 104 105 106 107 108
    }

    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);
109
  cleanup:
J
Jamie Strandboge 已提交
110
    VIR_FREE(tmp);
J
Jamie Strandboge 已提交
111
    VIR_FREE(etmp);
J
Jamie Strandboge 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

    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 已提交
129
    char *profile = NULL;
J
Jamie Strandboge 已提交
130 131 132 133 134
    char *content = NULL;
    char *tmp = NULL;
    int rc = -1;
    int len;

J
Jamie Strandboge 已提交
135
    if (virAsprintf(&profile, "%s/%s", APPARMOR_DIR "/libvirt", str) == -1) {
136
        virReportOOMError();
J
Jamie Strandboge 已提交
137 138 139
        return rc;
    }

J
Jamie Strandboge 已提交
140 141 142
    if (!virFileExists(profile))
        goto failed;

J
Jamie Strandboge 已提交
143
    if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
144
        virReportSystemError(errno,
J
Jamie Strandboge 已提交
145
                             _("Failed to read \'%s\'"), profile);
J
Jamie Strandboge 已提交
146
        goto failed;
J
Jamie Strandboge 已提交
147 148 149 150
    }

    /* create string that is ' <str> flags=(complain)\0' */
    if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
151
        virReportOOMError();
J
Jamie Strandboge 已提交
152
        goto failed;
J
Jamie Strandboge 已提交
153 154 155 156 157 158 159
    }

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

J
Jamie Strandboge 已提交
160
  failed:
J
Jamie Strandboge 已提交
161
    VIR_FREE(tmp);
J
Jamie Strandboge 已提交
162
    VIR_FREE(profile);
J
Jamie Strandboge 已提交
163 164 165 166 167 168 169 170 171
    VIR_FREE(content);

    return rc;
}

/*
 * load (add) a profile. Will create one if necessary
 */
static int
172
load_profile(virSecurityManagerPtr mgr,
173
             const char *profile,
174
             virDomainDefPtr def,
175 176
             const char *fn,
             bool append)
J
Jamie Strandboge 已提交
177
{
178
    int rc = -1;
J
Jamie Strandboge 已提交
179 180
    bool create = true;
    char *xml = NULL;
181
    virCommandPtr cmd = NULL;
182
    const char *probe = virSecurityManagerGetAllowDiskFormatProbing(mgr)
183
        ? "1" : "0";
J
Jamie Strandboge 已提交
184

185
    xml = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE);
J
Jamie Strandboge 已提交
186
    if (!xml)
187
        goto cleanup;
J
Jamie Strandboge 已提交
188 189 190 191

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

192 193 194 195 196 197 198 199 200
    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 已提交
201 202
    }

203 204
    virCommandSetInputBuffer(cmd, xml);
    rc = virCommandRun(cmd, NULL);
J
Jamie Strandboge 已提交
205

206
cleanup:
J
Jamie Strandboge 已提交
207
    VIR_FREE(xml);
208
    virCommandFree(cmd);
J
Jamie Strandboge 已提交
209 210 211 212 213 214 215 216 217 218 219 220

    return rc;
}

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

221
    if (virRun(argv, NULL) == 0)
J
Jamie Strandboge 已提交
222 223 224 225 226 227
        rc = 0;

    return rc;
}

static char *
228
get_profile_name(virDomainDefPtr def)
J
Jamie Strandboge 已提交
229 230 231 232
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];
    char *name = NULL;

233
    virUUIDFormat(def->uuid, uuidstr);
J
Jamie Strandboge 已提交
234
    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
235
        virReportOOMError();
J
Jamie Strandboge 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249
        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;
250
    char *libvirt_daemon = NULL;
J
Jamie Strandboge 已提交
251

252
    if (virFileResolveLink("/proc/self/exe", &libvirt_daemon) < 0) {
253 254
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("could not find libvirtd"));
J
Jamie Strandboge 已提交
255 256 257 258
        return rc;
    }

    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
259 260 261
        goto cleanup;

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

263 264 265
cleanup:
    VIR_FREE(libvirt_daemon);
    return rc;
J
Jamie Strandboge 已提交
266 267
}

268 269 270 271
/* reload the profile, adding read/write file specified by fn if it is not
 * NULL.
 */
static int
272
reload_profile(virSecurityManagerPtr mgr,
273
               virDomainDefPtr def,
274 275
               const char *fn,
               bool append)
276 277 278
{
    int rc = -1;
    char *profile_name = NULL;
279 280 281 282 283
    const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(
                                                def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return rc;
284

285
    if (secdef->norelabel)
286 287
        return 0;

288
    if ((profile_name = get_profile_name(def)) == NULL)
289 290 291 292
        return rc;

    /* Update the profile only if it is loaded */
    if (profile_loaded(secdef->imagelabel) >= 0) {
293
        if (load_profile(mgr, secdef->imagelabel, def, fn, append) < 0) {
294 295 296 297
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("cannot update AppArmor profile "
                             "\'%s\'"),
                           secdef->imagelabel);
298
            goto cleanup;
299 300 301 302
        }
    }

    rc = 0;
303
  cleanup:
304 305 306 307 308
    VIR_FREE(profile_name);

    return rc;
}

309
static int
310
AppArmorSetSecurityHostdevLabelHelper(const char *file, void *opaque)
311 312
{
    struct SDPDOP *ptr = opaque;
313
    virDomainDefPtr def = ptr->def;
314

315
    if (reload_profile(ptr->mgr, def, file, true) < 0) {
316 317 318 319 320 321
        const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(
                                                def, SECURITY_APPARMOR_NAME);
        if (!secdef) {
            virReportOOMError();
            return -1;
        }
322
        virReportError(VIR_ERR_INTERNAL_ERROR,
323
                       _("cannot update AppArmor profile \'%s\'"),
324
                       secdef->imagelabel);
325 326 327 328 329
        return -1;
    }
    return 0;
}

330 331 332 333 334 335 336
static int
AppArmorSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED,
                            const char *file, void *opaque)
{
    return AppArmorSetSecurityHostdevLabelHelper(file, opaque);
}

337
static int
338 339
AppArmorSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED,
                            const char *file, void *opaque)
340
{
341 342
    return AppArmorSetSecurityHostdevLabelHelper(file, opaque);
}
343

344 345 346 347 348
static int
AppArmorSetSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED,
                             const char *file, void *opaque)
{
    return AppArmorSetSecurityHostdevLabelHelper(file, opaque);
349 350
}

J
Jamie Strandboge 已提交
351 352
/* Called on libvirtd startup to see if AppArmor is available */
static int
353
AppArmorSecurityManagerProbe(const char *virtDriver)
J
Jamie Strandboge 已提交
354
{
J
Jamie Strandboge 已提交
355 356
    char *template = NULL;
    int rc = SECURITY_DRIVER_DISABLE;
J
Jamie Strandboge 已提交
357 358

    if (use_apparmor() < 0)
J
Jamie Strandboge 已提交
359
        return rc;
J
Jamie Strandboge 已提交
360

361 362 363
    if (virtDriver && STREQ(virtDriver, "LXC"))
        return rc;

J
Jamie Strandboge 已提交
364
    /* see if template file exists */
J
Jamie Strandboge 已提交
365 366
    if (virAsprintf(&template, "%s/TEMPLATE",
                               APPARMOR_DIR "/libvirt") == -1) {
367
        virReportOOMError();
J
Jamie Strandboge 已提交
368
        return rc;
J
Jamie Strandboge 已提交
369 370 371
    }

    if (!virFileExists(template)) {
372 373
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("template \'%s\' does not exist"), template);
374
        goto cleanup;
J
Jamie Strandboge 已提交
375
    }
J
Jamie Strandboge 已提交
376
    rc = SECURITY_DRIVER_ENABLE;
J
Jamie Strandboge 已提交
377

378
  cleanup:
J
Jamie Strandboge 已提交
379 380 381
    VIR_FREE(template);

    return rc;
J
Jamie Strandboge 已提交
382 383 384 385 386 387
}

/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
 * currently not used.
 */
static int
388 389 390 391 392 393 394
AppArmorSecurityManagerOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
{
    return 0;
}

static int
AppArmorSecurityManagerClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
395 396 397 398
{
    return 0;
}

399 400 401 402 403 404 405 406 407 408 409 410 411
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 已提交
412 413 414 415 416 417
/* 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
418
AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
419
                         virDomainDefPtr def)
J
Jamie Strandboge 已提交
420 421 422
{
    int rc = -1;
    char *profile_name = NULL;
423 424
    virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def,
                                                SECURITY_APPARMOR_NAME);
J
Jamie Strandboge 已提交
425

426 427 428 429
    if (!secdef)
        return -1;

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

432
    if (secdef->baselabel) {
433 434
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       "%s", _("Cannot set a base label with AppArmour"));
435 436 437
        return rc;
    }

438
    if (secdef->label || secdef->imagelabel) {
439 440 441
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s",
                       _("security label already defined for VM"));
J
Jamie Strandboge 已提交
442 443 444
        return rc;
    }

445
    if ((profile_name = get_profile_name(def)) == NULL)
J
Jamie Strandboge 已提交
446 447
        return rc;

448
    if (VIR_STRDUP(secdef->label, profile_name) < 0)
449
        goto cleanup;
J
Jamie Strandboge 已提交
450 451

    /* set imagelabel the same as label (but we won't use it) */
452
    if (VIR_STRDUP(secdef->imagelabel, profile_name) < 0)
J
Jamie Strandboge 已提交
453 454
        goto err;

455
    if (!secdef->model && VIR_STRDUP(secdef->model, SECURITY_APPARMOR_NAME) < 0)
J
Jamie Strandboge 已提交
456 457
        goto err;

458
    /* Now that we have a label, load the profile into the kernel. */
459
    if (load_profile(mgr, secdef->label, def, NULL, false) < 0) {
460 461
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot load AppArmor profile "
462
                       "\'%s\'"), secdef->label);
463 464 465
        goto err;
    }

J
Jamie Strandboge 已提交
466
    rc = 0;
467
    goto cleanup;
J
Jamie Strandboge 已提交
468 469

  err:
470 471 472
    VIR_FREE(secdef->label);
    VIR_FREE(secdef->imagelabel);
    VIR_FREE(secdef->model);
J
Jamie Strandboge 已提交
473

474
  cleanup:
J
Jamie Strandboge 已提交
475 476 477 478 479
    VIR_FREE(profile_name);

    return rc;
}

480
static int
481
AppArmorSetSecurityAllLabel(virSecurityManagerPtr mgr,
482
                            virDomainDefPtr def, const char *stdin_path)
483
{
484 485 486 487 488 489
    virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def,
                                                    SECURITY_APPARMOR_NAME);
    if (!secdef)
        return -1;

    if (secdef->norelabel)
490 491
        return 0;

492 493 494
    /* Reload the profile if stdin_path is specified. Note that
       GenSecurityLabel() will have already been run. */
    if (stdin_path)
495
        return reload_profile(mgr, def, stdin_path, true);
496 497 498 499

    return 0;
}

J
Jamie Strandboge 已提交
500 501 502 503
/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
 * running.
 */
static int
504
AppArmorGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
505
                                virDomainDefPtr def,
506
                                pid_t pid ATTRIBUTE_UNUSED,
507
                                virSecurityLabelPtr sec)
J
Jamie Strandboge 已提交
508 509 510 511
{
    int rc = -1;
    char *profile_name = NULL;

512
    if ((profile_name = get_profile_name(def)) == NULL)
J
Jamie Strandboge 已提交
513 514 515 516
        return rc;

    if (virStrcpy(sec->label, profile_name,
        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
517 518
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("error copying profile name"));
519
        goto cleanup;
J
Jamie Strandboge 已提交
520 521 522
    }

    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
523 524
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("error calling profile_status()"));
525
        goto cleanup;
J
Jamie Strandboge 已提交
526 527 528
    }
    rc = 0;

529
  cleanup:
J
Jamie Strandboge 已提交
530 531 532 533 534 535 536 537 538
    VIR_FREE(profile_name);

    return rc;
}

/* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
 * more details. Currently called via qemudShutdownVMDaemon.
 */
static int
539
AppArmorReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
540
                             virDomainDefPtr def)
541
{
542 543 544 545
    const virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def,
                                                        SECURITY_APPARMOR_NAME);
    if (!secdef)
        return -1;
546 547 548 549 550 551 552 553 554 555

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

    return 0;
}


static int
556
AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
557
                                virDomainDefPtr def,
558
                                int migrated ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
559 560
{
    int rc = 0;
561 562 563 564 565
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
J
Jamie Strandboge 已提交
566

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

577
/* Called via virCommand hook. Output goes to
578
 * LOCALSTATEDIR/log/libvirt/qemu/<vm name>.log
J
Jamie Strandboge 已提交
579 580
 */
static int
581 582
AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                                virDomainDefPtr def)
J
Jamie Strandboge 已提交
583 584 585
{
    int rc = -1;
    char *profile_name = NULL;
586 587 588 589 590
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
J
Jamie Strandboge 已提交
591

592
    if ((profile_name = get_profile_name(def)) == NULL)
J
Jamie Strandboge 已提交
593 594
        return rc;

595
    if (STRNEQ(SECURITY_APPARMOR_NAME, secdef->model)) {
596 597 598 599
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("security label driver mismatch: "
                         "\'%s\' model configured for domain, but "
                         "hypervisor driver is \'%s\'."),
600
                       secdef->model, SECURITY_APPARMOR_NAME);
J
Jamie Strandboge 已提交
601
        if (use_apparmor() > 0)
602
            goto cleanup;
J
Jamie Strandboge 已提交
603 604 605
    }

    if (aa_change_profile(profile_name) < 0) {
606 607
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("error calling aa_change_profile()"));
608
        goto cleanup;
J
Jamie Strandboge 已提交
609 610 611
    }
    rc = 0;

612
  cleanup:
J
Jamie Strandboge 已提交
613 614 615 616 617
    VIR_FREE(profile_name);

    return rc;
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
/* Called directly by API user prior to virCommandRun().
 * virCommandRun() will then call aa_change_profile() (if a
 * cmd->appArmorProfile has been set) *after forking the child
 * process*.
 */
static int
AppArmorSetSecurityChildProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                                     virDomainDefPtr def,
                                     virCommandPtr cmd)
{
    int rc = -1;
    char *profile_name = NULL;
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        goto cleanup;

    if (STRNEQ(SECURITY_APPARMOR_NAME, secdef->model)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("security label driver mismatch: "
                         "\'%s\' model configured for domain, but "
                         "hypervisor driver is \'%s\'."),
                       secdef->model, SECURITY_APPARMOR_NAME);
        if (use_apparmor() > 0)
            goto cleanup;
    }

    if ((profile_name = get_profile_name(def)) == NULL)
        goto cleanup;

    virCommandSetAppArmorProfile(cmd, profile_name);
    rc = 0;

  cleanup:
    VIR_FREE(profile_name);
    return rc;
}

657
static int
658
AppArmorSetSecurityDaemonSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
659
                                     virDomainDefPtr vm ATTRIBUTE_UNUSED)
660 661 662 663
{
    return 0;
}

664 665
static int
AppArmorSetSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
666
                               virDomainDefPtr def ATTRIBUTE_UNUSED)
667 668 669 670
{
    return 0;
}

671 672
static int
AppArmorClearSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
673
                                 virDomainDefPtr def ATTRIBUTE_UNUSED)
674 675 676 677
{
    return 0;
}

J
Jamie Strandboge 已提交
678 679 680

/* Called when hotplugging */
static int
681
AppArmorRestoreSecurityImageLabel(virSecurityManagerPtr mgr,
682 683
                                  virDomainDefPtr def,
                                  virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
684
{
685 686 687
    if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
        return 0;

688
    return reload_profile(mgr, def, NULL, false);
J
Jamie Strandboge 已提交
689 690 691 692
}

/* Called when hotplugging */
static int
693
AppArmorSetSecurityImageLabel(virSecurityManagerPtr mgr,
694
                              virDomainDefPtr def, virDomainDiskDefPtr disk)
J
Jamie Strandboge 已提交
695 696 697
{
    int rc = -1;
    char *profile_name;
698 699 700 701 702
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
J
Jamie Strandboge 已提交
703

704
    if (secdef->norelabel)
705 706
        return 0;

707
    if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
J
Jamie Strandboge 已提交
708 709 710 711 712
        return 0;

    if (secdef->imagelabel) {
        /* if the device doesn't exist, error out */
        if (!virFileExists(disk->src)) {
713 714
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("\'%s\' does not exist"), disk->src);
J
Jamie Strandboge 已提交
715 716 717
            return rc;
        }

718
        if ((profile_name = get_profile_name(def)) == NULL)
J
Jamie Strandboge 已提交
719 720 721 722
            return rc;

        /* update the profile only if it is loaded */
        if (profile_loaded(secdef->imagelabel) >= 0) {
723
            if (load_profile(mgr, secdef->imagelabel, def, disk->src,
724
                             false) < 0) {
725 726 727 728
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("cannot update AppArmor profile "
                                 "\'%s\'"),
                               secdef->imagelabel);
729
                goto cleanup;
J
Jamie Strandboge 已提交
730 731 732 733 734
            }
        }
    }
    rc = 0;

735
  cleanup:
J
Jamie Strandboge 已提交
736 737 738 739 740 741
    VIR_FREE(profile_name);

    return rc;
}

static int
742 743
AppArmorSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                       virDomainDefPtr def)
J
Jamie Strandboge 已提交
744
{
745 746 747 748 749
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
J
Jamie Strandboge 已提交
750 751 752

    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
        if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
753 754 755
            virReportError(VIR_ERR_XML_ERROR,
                           _("Invalid security label \'%s\'"),
                           secdef->label);
J
Jamie Strandboge 已提交
756 757 758 759 760 761 762
            return -1;
        }
    }
    return 0;
}

static int
763
AppArmorReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
764 765
                             virDomainDefPtr def ATTRIBUTE_UNUSED,
                             pid_t pid ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
766 767 768 769 770 771
{
    /* NOOP. Nothing to reserve with AppArmor */
    return 0;
}

static int
772
AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr,
773
                                virDomainDefPtr def,
774 775
                                virDomainHostdevDefPtr dev,
                                const char *vroot)
J
Jamie Strandboge 已提交
776
{
777 778
    struct SDPDOP *ptr;
    int ret = -1;
779 780 781 782 783
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
784

785
    if (secdef->norelabel)
786 787
        return 0;

788 789 790 791 792 793 794 795
    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;
796
    ptr->mgr = mgr;
797
    ptr->def = def;
798 799 800

    switch (dev->source.subsys.type) {
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
801 802 803 804
        virUSBDevicePtr usb =
            virUSBDeviceNew(dev->source.subsys.u.usb.bus,
                            dev->source.subsys.u.usb.device,
                            vroot);
805 806 807 808

        if (!usb)
            goto done;

809 810
        ret = virUSBDeviceFileIterate(usb, AppArmorSetSecurityUSBLabel, ptr);
        virUSBDeviceFree(usb);
811 812 813 814
        break;
    }

    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
815
        virPCIDevicePtr pci =
816 817 818 819
            virPCIDeviceNew(dev->source.subsys.u.pci.addr.domain,
                            dev->source.subsys.u.pci.addr.bus,
                            dev->source.subsys.u.pci.addr.slot,
                            dev->source.subsys.u.pci.addr.function);
820 821 822 823

        if (!pci)
            goto done;

824
        if (dev->source.subsys.u.pci.backend
825
            == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
826
            char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
827

828 829
            if (!vfioGroupDev) {
                virPCIDeviceFree(pci);
830
                goto done;
831
            }
832 833 834 835 836
            ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
            VIR_FREE(vfioGroupDev);
        } else {
            ret = virPCIDeviceFileIterate(pci, AppArmorSetSecurityPCILabel, ptr);
        }
837
        virPCIDeviceFree(pci);
838 839 840
        break;
    }

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: {
        virSCSIDevicePtr scsi =
            virSCSIDeviceNew(dev->source.subsys.u.scsi.adapter,
                             dev->source.subsys.u.scsi.bus,
                             dev->source.subsys.u.scsi.target,
                             dev->source.subsys.u.scsi.unit,
                             dev->readonly);

         if (!scsi)
             goto done;

        ret = virSCSIDeviceFileIterate(scsi, AppArmorSetSecuritySCSILabel, ptr);
        virSCSIDeviceFree(scsi);

        break;
    }

858 859 860 861 862 863 864 865
    default:
        ret = 0;
        break;
    }

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

868

J
Jamie Strandboge 已提交
869
static int
870
AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr,
871
                                    virDomainDefPtr def,
872 873
                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED,
                                    const char *vroot ATTRIBUTE_UNUSED)
J
Jamie Strandboge 已提交
874 875

{
876 877 878 879 880 881
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;

882
    if (secdef->norelabel)
883 884
        return 0;

885
    return reload_profile(mgr, def, NULL, false);
J
Jamie Strandboge 已提交
886 887
}

888
static int
889
AppArmorSetSavedStateLabel(virSecurityManagerPtr mgr,
890
                           virDomainDefPtr def,
891
                           const char *savefile)
892
{
893
    return reload_profile(mgr, def, savefile, true);
894 895 896 897
}


static int
898
AppArmorRestoreSavedStateLabel(virSecurityManagerPtr mgr,
899
                               virDomainDefPtr def,
900 901
                               const char *savefile ATTRIBUTE_UNUSED)
{
902
    return reload_profile(mgr, def, NULL, false);
903 904
}

905
static int
906 907 908
AppArmorSetFDLabel(virSecurityManagerPtr mgr,
                   virDomainDefPtr def,
                   int fd)
909
{
910 911 912 913
    int rc = -1;
    char *proc = NULL;
    char *fd_path = NULL;

914 915 916 917 918
    const virSecurityLabelDefPtr secdef =
        virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME);

    if (!secdef)
        return -1;
919 920 921 922 923 924 925 926 927 928

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

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

    if (virFileResolveLink(proc, &fd_path) < 0) {
929 930 931
        /* it's a deleted file, presumably.  Ignore? */
        VIR_WARN("could not find path for descriptor %s, skipping", proc);
        return 0;
932 933
    }

934
    return reload_profile(mgr, def, fd_path, true);
935 936
}

937 938 939 940 941 942
static char *
AppArmorGetMountOptions(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
                        virDomainDefPtr vm ATTRIBUTE_UNUSED)
{
    char *opts;

943
    ignore_value(VIR_STRDUP(opts, ""));
944 945 946 947
    return opts;
}


J
Jamie Strandboge 已提交
948
virSecurityDriver virAppArmorSecurityDriver = {
949 950 951 952 953
    .privateDataLen                     = 0,
    .name                               = SECURITY_APPARMOR_NAME,
    .probe                              = AppArmorSecurityManagerProbe,
    .open                               = AppArmorSecurityManagerOpen,
    .close                              = AppArmorSecurityManagerClose,
954

955 956
    .getModel                           = AppArmorSecurityManagerGetModel,
    .getDOI                             = AppArmorSecurityManagerGetDOI,
957

958
    .domainSecurityVerify               = AppArmorSecurityVerify,
959

960 961
    .domainSetSecurityImageLabel        = AppArmorSetSecurityImageLabel,
    .domainRestoreSecurityImageLabel    = AppArmorRestoreSecurityImageLabel,
962

963 964 965
    .domainSetSecurityDaemonSocketLabel = AppArmorSetSecurityDaemonSocketLabel,
    .domainSetSecuritySocketLabel       = AppArmorSetSecuritySocketLabel,
    .domainClearSecuritySocketLabel     = AppArmorClearSecuritySocketLabel,
966

967 968 969
    .domainGenSecurityLabel             = AppArmorGenSecurityLabel,
    .domainReserveSecurityLabel         = AppArmorReserveSecurityLabel,
    .domainReleaseSecurityLabel         = AppArmorReleaseSecurityLabel,
970

971 972
    .domainGetSecurityProcessLabel      = AppArmorGetSecurityProcessLabel,
    .domainSetSecurityProcessLabel      = AppArmorSetSecurityProcessLabel,
973
    .domainSetSecurityChildProcessLabel = AppArmorSetSecurityChildProcessLabel,
974

975 976
    .domainSetSecurityAllLabel          = AppArmorSetSecurityAllLabel,
    .domainRestoreSecurityAllLabel      = AppArmorRestoreSecurityAllLabel,
977

978 979
    .domainSetSecurityHostdevLabel      = AppArmorSetSecurityHostdevLabel,
    .domainRestoreSecurityHostdevLabel  = AppArmorRestoreSecurityHostdevLabel,
980

981 982
    .domainSetSavedStateLabel           = AppArmorSetSavedStateLabel,
    .domainRestoreSavedStateLabel       = AppArmorRestoreSavedStateLabel,
983

984 985
    .domainSetSecurityImageFDLabel      = AppArmorSetFDLabel,
    .domainSetSecurityTapFDLabel        = AppArmorSetFDLabel,
986 987

    .domainGetSecurityMountOptions      = AppArmorGetMountOptions,
J
Jamie Strandboge 已提交
988
};