From nobody Tue Jan 21 07:31:54 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1736263953246753.7469472958841; Tue, 7 Jan 2025 07:32:33 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 5A45E11AF; Tue, 7 Jan 2025 10:32:32 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 3D849DC6; Tue, 7 Jan 2025 10:25:26 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 93FEBD10; Tue, 7 Jan 2025 10:25:22 -0500 (EST) Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 5594DD10 for ; Tue, 7 Jan 2025 10:24:29 -0500 (EST) Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 76070402EF for ; Tue, 7 Jan 2025 15:24:27 +0000 (UTC) Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2166e907b5eso208133295ad.3 for ; Tue, 07 Jan 2025 07:24:27 -0800 (PST) Received: from georgia.. ([2001:1284:f502:1965:7bc6:bb4:7636:7bb1]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-842b85f00f9sm31048120a12.43.2025.01.07.07.24.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jan 2025 07:24:24 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=canonical.com; s=20210705; t=1736263467; bh=i9094lAFRdFNmZjDrnZtVnhysg9PiGGCt7D7OGi4rEo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Mi6YfI/zM44DMakSj9aD3VFO4NNxail2I69bQgfiD4dvTHIRcIKObU5ywn4/ommVE jqQMDpbBj+4gXlQkOubk0qlLxO7oaLUWkr7wGEPYcTjJGKRUo3SAGXGvXzAxNEZNzZ wwGZEJqDo2d6l9HH0EFdVNQo4wGHQ/dZcwjU24Hy7IqqjkEYdyezK35On9mb89b1gQ hczadh4BWOkd4l1mBqZWAH8xUjpRUgTK1smOd+xnsQYONf1tKmvhsofDbRTvyzNeQH B/py9foePmSK9fRknuvmDMMHuwvLZBq3RWgs4UuN8TCe14+iGlTCKxbDce/bMY2xnD CbV1sF1c6SbRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736263466; x=1736868266; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=i9094lAFRdFNmZjDrnZtVnhysg9PiGGCt7D7OGi4rEo=; b=h4Bv0ocZ5t1qQCMXe3VZNfvkn21Di2qCNcmszTvDDY4xSMb7vhhZCxhcqxKujGRl5J bkxrk7umq/JKtlp/aUl4N+y2N+L7Eeqvm9gg+1NWWMh3zLtr3OKOA+T74TYEh2Iegnx6 7ReCxIqz0JWJhtQaOPf51/H1ofUauaA8lCmBlfxNoIlqm7C0nqu0IBPr3twmiuKnJO1Y QCyNGqJal3Matol0i1c7SUb6Qn1c1EBVkF/zm7/9AqqF2CsQ0JjJqvJAyVuI+iHPZcKL TAxPFauAyQo60SM0obvPwT72jRdQvHWFD5NZMkPpVtU9dEc7Hw3XXAoD7WYigxDHU/kE Y9zg== X-Gm-Message-State: AOJu0Yz0+f8HWk7y/symqsbQGy9PwztlOu2xkkjunlGyThkUjRZuuyBC 6f+9GZfnfZUIiJ2/WHhqi+6m4tHB9WptqXNRhSi4wheQnxqzPuU6TGXLSwSsCno8ac6VvMWJG7v WQWssfscWsjJRQiYZcOaeLmx+PvJ9/2xJJcyDdptVAQ8H0o1aZsEWetwu1G27vWcANlCqnjnGTL kCUqY= X-Gm-Gg: ASbGncuJTAKOddQ+bHTGtx+FOMJJwShT6c2o4Vr5zQEkazdxlYr3Gu4A2ZJQQ7yQPU2 tXG/FmKt4+IZjN/Q3v/lnpu3+CnOHKx4t8laoB0JROxZ5tyjIiivnqCxQJs7lTEatt1T2F935Py jjEU7gwHdQCBQ5MqC3Iv0fpUAfajhMn11vaLIQhRDW0o1k+QO09JQWxNBISNZ704Ai8kHNhiEeF S6opg1IBfWjinC7tPje8alt+a6tAD4UfpAGvmxg4TdS4NhOC7z7ZHLIUO9xIQDQlZjV X-Received: by 2002:a17:903:2281:b0:216:386e:dbf with SMTP id d9443c01a7336-219e6ea2660mr919474215ad.20.1736263465563; Tue, 07 Jan 2025 07:24:25 -0800 (PST) X-Google-Smtp-Source: AGHT+IEwL+L4Yu6gkeuH6L4kRpEj3GhoIfAVrVBJUTBvLS9I31S8uil4OM+NQH3cYgnWwuWHzvSIJA== X-Received: by 2002:a17:903:2281:b0:216:386e:dbf with SMTP id d9443c01a7336-219e6ea2660mr919473755ad.20.1736263465150; Tue, 07 Jan 2025 07:24:25 -0800 (PST) From: Georgia Garcia To: devel@lists.libvirt.org Subject: [PATCH v3 4/4] virt-aa-helper: store dynamically generated rules Date: Tue, 7 Jan 2025 12:23:39 -0300 Message-ID: <20250107152357.1026544-5-georgia.garcia@canonical.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250107152357.1026544-1-georgia.garcia@canonical.com> References: <20250107152357.1026544-1-georgia.garcia@canonical.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 5BXVTADPZLRHY5YPDX6BMQ24I63OJYTW X-Message-ID-Hash: 5BXVTADPZLRHY5YPDX6BMQ24I63OJYTW X-MailFrom: georgia.garcia@canonical.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Georgia Garcia X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Computed bodyhash is different from the expected one) X-ZM-MESSAGEID: 1736263955630116600 Content-Type: text/plain; charset="utf-8" Some rules are generated dynamically during boot and added to the AppArmor policy. An example of that is macvtap devices that call the AppArmorSetFDLabel hook to add a rule for the tap device path. Since this information is dynamic, it is not available in the xml config, therefore whenever a "Restore" hook is called, the entire profile is regenerated by virt-aa-helper based only the information from the VM definition, so the dynamic/runtime information is lost. This patch stores the dynamically generated rules in a new file called libvirt-uuid.runtime_files which is included by the AppArmor policy. This file should exist while the domain is running and should be reloaded automatically whenever there's a restore operation. These rules only make sense when the VM is running, so the file is removed when the VM is shutdown. Note that there are no hooks for restoring FD labels, so that information is not removed from the set of rules while the domain is running. Closes: https://gitlab.com/libvirt/libvirt/-/issues/692 Signed-off-by: Georgia Garcia --- src/security/security_apparmor.c | 38 +++++++++++++++++++-------- src/security/virt-aa-helper.c | 45 ++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/security/security_apparmor.c b/src/security/security_appar= mor.c index 91c51f6395..907b01577c 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -147,7 +147,8 @@ load_profile(virSecurityManager *mgr G_GNUC_UNUSED, const char *profile, virDomainDef *def, const char *fn, - bool append) + bool append, + bool runtime) { bool create =3D true; g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; @@ -173,6 +174,8 @@ load_profile(virSecurityManager *mgr G_GNUC_UNUSED, } else { virCommandAddArgList(cmd, "-f", fn, NULL); } + if (runtime) + virCommandAddArgList(cmd, "-t", NULL); } =20 virCommandAddEnvFormat(cmd, @@ -245,10 +248,11 @@ use_apparmor(void) * NULL. */ static int -reload_profile(virSecurityManager *mgr, - virDomainDef *def, - const char *fn, - bool append) +reload_runtime_profile(virSecurityManager *mgr, + virDomainDef *def, + const char *fn, + bool append, + bool runtime) { virSecurityLabelDef *secdef =3D virDomainDefGetSecurityLabelDef( def, SECURITY_APPARMOR_NAM= E); @@ -258,7 +262,7 @@ reload_profile(virSecurityManager *mgr, =20 /* Update the profile only if it is loaded */ if (profile_loaded(secdef->imagelabel) >=3D 0) { - if (load_profile(mgr, secdef->imagelabel, def, fn, append) < 0) { + if (load_profile(mgr, secdef->imagelabel, def, fn, append, runtime= ) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot update AppArmor profile \'%1$s\'"), secdef->imagelabel); @@ -268,6 +272,18 @@ reload_profile(virSecurityManager *mgr, return 0; } =20 +/* reload the profile, adding read/write file specified by fn if it is not + * NULL. + */ +static int +reload_profile(virSecurityManager *mgr, + virDomainDef *def, + const char *fn, + bool append) +{ + return reload_runtime_profile(mgr, def, fn, append, false); +} + static int AppArmorSetSecurityHostdevLabelHelper(const char *file, void *opaque) { @@ -388,7 +404,7 @@ AppArmorGenSecurityLabel(virSecurityManager *mgr G_GNUC= _UNUSED, secdef->model =3D g_strdup(SECURITY_APPARMOR_NAME); =20 /* Now that we have a label, load the profile into the kernel. */ - if (load_profile(mgr, secdef->label, def, NULL, false) < 0) { + if (load_profile(mgr, secdef->label, def, NULL, false, false) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot load AppArmor profile \'%1$s\'"), secdef->label); @@ -420,7 +436,7 @@ AppArmorSetSecurityAllLabel(virSecurityManager *mgr, /* Reload the profile if incomingPath is specified. Note that GenSecurityLabel() will have already been run. */ if (incomingPath) - return reload_profile(mgr, def, incomingPath, true); + return reload_runtime_profile(mgr, def, incomingPath, true, true); =20 return 0; } @@ -1074,9 +1090,9 @@ AppArmorSetPathLabel(virSecurityManager *mgr, =20 if (allowSubtree) { full_path =3D g_strdup_printf("%s/{,**}", path); - rc =3D reload_profile(mgr, def, full_path, true); + rc =3D reload_runtime_profile(mgr, def, full_path, true, true); } else { - rc =3D reload_profile(mgr, def, path, true); + rc =3D reload_runtime_profile(mgr, def, path, true, true); } =20 return rc; @@ -1112,7 +1128,7 @@ AppArmorSetFDLabel(virSecurityManager *mgr, return 0; } =20 - return reload_profile(mgr, def, fd_path, true); + return reload_runtime_profile(mgr, def, fd_path, true, true); } =20 static char * diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 1626d5a89c..3a217fa3d1 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -71,6 +71,7 @@ typedef struct { virArch arch; /* machine architecture */ char *newfile; /* newly added file */ bool append; /* append to .files instead of rewrite */ + bool runtime; /* file should be added to .runtime_files = */ } vahControl; =20 static int @@ -110,6 +111,7 @@ vah_usage(void) " Extra File:\n" " -f | --add-file add file to a profile gene= rated from XML\n" " -F | --append-file append file to an existing= profile\n" + " -t | --runtime file is valid only during = runtime\n" "\n"), progname); =20 puts(_("This command is intended to be used by libvirtd and not used d= irectly.\n")); @@ -1356,10 +1358,11 @@ vahParseArgv(vahControl * ctl, int argc, char **arg= v) { "replace", 0, 0, 'r' }, { "remove", 0, 0, 'R' }, { "uuid", 1, 0, 'u' }, + { "runtime", 0, 0, 't' }, { 0, 0, 0, 0 }, }; =20 - while ((arg =3D getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt, + while ((arg =3D getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:t", opt, &idx)) !=3D -1) { switch (arg) { case 'a': @@ -1396,6 +1399,9 @@ vahParseArgv(vahControl * ctl, int argc, char **argv) PROFILE_NAME_SIZE) < 0) vah_error(ctl, 1, _("error copying UUID")); break; + case 't': + ctl->runtime =3D true; + break; default: vah_error(ctl, 1, _("unsupported option")); break; @@ -1445,9 +1451,16 @@ main(int argc, char **argv) int rc =3D -1; g_autofree char *profile =3D NULL; g_autofree char *include_file =3D NULL; + g_autofree char *include_runtime_file =3D NULL; off_t size; bool purged =3D 0; =20 +#if defined(WITH_APPARMOR_3) + const char *ifexists =3D "if exists "; +#else + const char *ifexists =3D ""; +#endif + if (virGettextInitialize() < 0 || virErrorInitialize() < 0) { fprintf(stderr, _("%1$s: initialization failed\n"), argv[0]); @@ -1479,13 +1492,16 @@ main(int argc, char **argv) =20 profile =3D g_strdup_printf("%s/%s", APPARMOR_DIR "/libvirt", ctl->uui= d); include_file =3D g_strdup_printf("%s/%s.files", APPARMOR_DIR "/libvirt= ", ctl->uuid); + include_runtime_file =3D g_strdup_printf("%s/%s.runtime_files", APPARM= OR_DIR "/libvirt", ctl->uuid); =20 if (ctl->cmd =3D=3D 'a') { rc =3D parserLoad(ctl->uuid); } else if (ctl->cmd =3D=3D 'R' || ctl->cmd =3D=3D 'D') { rc =3D parserRemove(ctl->uuid); - if (ctl->cmd =3D=3D 'D') + if (ctl->cmd =3D=3D 'D') { unlink(include_file); + unlink(include_runtime_file); + } } else if (ctl->cmd =3D=3D 'c' || ctl->cmd =3D=3D 'r') { g_autofree char *included_files =3D NULL; g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; @@ -1513,6 +1529,7 @@ main(int argc, char **argv) if (vah_add_file(&buf, ctl->newfile, "rwk") !=3D 0) goto cleanup; } else { + virBufferAsprintf(&buf, " #include %s\n", ifexists, ctl->uuid); if (ctl->def->virtType =3D=3D VIR_DOMAIN_VIRT_QEMU || ctl->def->virtType =3D=3D VIR_DOMAIN_VIRT_KQEMU || ctl->def->virtType =3D=3D VIR_DOMAIN_VIRT_KVM) { @@ -1535,11 +1552,20 @@ main(int argc, char **argv) =20 /* (re)create the include file using included_files */ if (ctl->dryrun) { - vah_info(include_file); + if (ctl->runtime) + vah_info(include_runtime_file); + else + vah_info(include_file); vah_info(included_files); rc =3D 0; } else if (ctl->def->virtType =3D=3D VIR_DOMAIN_VIRT_LXC) { rc =3D 0; + } else if (ctl->runtime) { + /* runtime should only update include_runtime_file */ + if ((rc =3D update_include_file(include_runtime_file, + included_files, + ctl->append)) !=3D 0) + goto cleanup; } else if ((rc =3D update_include_file(include_file, included_files, ctl->append)) !=3D 0) { @@ -1550,11 +1576,12 @@ main(int argc, char **argv) /* create the profile from TEMPLATE */ if (ctl->cmd =3D=3D 'c' || purged) { g_autofree char *tmp =3D NULL; -#if defined(WITH_APPARMOR_3) - const char *ifexists =3D "if exists "; -#else - const char *ifexists =3D ""; -#endif + + /* ideally libvirt-uuid.files and + * libvirt-uuid.runtime_files should be in libvirt-uuid.d/ + * and the directory should be included instead, but how + * to deal with running domains when the libvirt-uuid + * profile is not recreated? */ tmp =3D g_strdup_printf(" #include %s\n", i= fexists, ctl->uuid); =20 if (ctl->dryrun) { @@ -1566,6 +1593,7 @@ main(int argc, char **argv) ctl->def->virtType)) !=3D 0) { vah_error(ctl, 0, _("could not create profile")); unlink(include_file); + unlink(include_runtime_file); } } =20 @@ -1578,6 +1606,7 @@ main(int argc, char **argv) /* cleanup */ if (rc !=3D 0) { unlink(include_file); + unlink(include_runtime_file); if (ctl->cmd =3D=3D 'c') unlink(profile); } --=20 2.43.0