From 119e9db233ad0f4cbd4cfb9f9385a7bca378b37d Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Mon, 21 Sep 2026 18:24:42 +0800 Subject: KVM: Don't pre-reserve xarray entries when storing empty/NULL attributes Skip the xarray reservation loop when clearing all memory attributes, as storing NULL only erases the entry and never needs to allocate, so no reservation (and no cleanup of a failed one) is required in that case. Suggested-by: Sean Christopherson Cc: David Ballesteros Signed-off-by: Zeng Chi Link: https://patch.msgid.link/20260921102442.1232375-1-zeng_chi911@163.com [sean: split to separate patch] Signed-off-by: Sean Christopherson --- security/Kconfig | 305 + security/Kconfig.hardening | 349 + security/Makefile | 31 + security/apparmor/.gitignore | 4 + security/apparmor/.kunitconfig | 5 + security/apparmor/Kconfig | 134 + security/apparmor/Makefile | 114 + security/apparmor/af_inet.c | 565 ++ security/apparmor/af_unix.c | 812 ++ security/apparmor/apparmorfs.c | 2981 ++++++++ security/apparmor/audit.c | 325 + security/apparmor/capability.c | 223 + security/apparmor/crypto.c | 61 + security/apparmor/domain.c | 1663 ++++ security/apparmor/file.c | 754 ++ security/apparmor/include/af_inet.h | 36 + security/apparmor/include/af_unix.h | 55 + security/apparmor/include/apparmor.h | 69 + security/apparmor/include/apparmorfs.h | 151 + security/apparmor/include/audit.h | 218 + security/apparmor/include/capability.h | 49 + security/apparmor/include/cred.h | 246 + security/apparmor/include/crypto.h | 38 + security/apparmor/include/domain.h | 33 + security/apparmor/include/file.h | 133 + security/apparmor/include/ipc.h | 23 + security/apparmor/include/label.h | 492 ++ security/apparmor/include/lib.h | 363 + security/apparmor/include/match.h | 195 + security/apparmor/include/mount.h | 60 + security/apparmor/include/net.h | 120 + security/apparmor/include/path.h | 36 + security/apparmor/include/perms.h | 220 + security/apparmor/include/policy.h | 457 ++ security/apparmor/include/policy_compat.h | 33 + security/apparmor/include/policy_ns.h | 153 + security/apparmor/include/policy_unpack.h | 217 + security/apparmor/include/procattr.h | 19 + security/apparmor/include/resource.h | 47 + security/apparmor/include/secid.h | 37 + security/apparmor/include/sig_names.h | 97 + security/apparmor/include/signal.h | 19 + security/apparmor/include/task.h | 117 + security/apparmor/ipc.c | 116 + security/apparmor/label.c | 2170 ++++++ security/apparmor/lib.c | 514 ++ security/apparmor/lsm.c | 2642 +++++++ security/apparmor/match.c | 835 ++ security/apparmor/mount.c | 709 ++ security/apparmor/net.c | 415 + security/apparmor/nulldfa.in | 107 + security/apparmor/path.c | 222 + security/apparmor/policy.c | 1537 ++++ security/apparmor/policy_compat.c | 333 + security/apparmor/policy_ns.c | 398 + security/apparmor/policy_unpack.c | 1821 +++++ security/apparmor/policy_unpack_test.c | 616 ++ security/apparmor/procattr.c | 141 + security/apparmor/resource.c | 206 + security/apparmor/secid.c | 155 + security/apparmor/stacksplitdfa.in | 114 + security/apparmor/task.c | 399 + security/bpf/Makefile | 5 + security/bpf/hooks.c | 42 + security/commoncap.c | 1527 ++++ security/commoncap_test.c | 288 + security/device_cgroup.c | 870 +++ security/inode.c | 380 + security/integrity/Kconfig | 137 + security/integrity/Makefile | 24 + security/integrity/digsig.c | 228 + security/integrity/digsig_asymmetric.c | 302 + security/integrity/efi_secureboot.c | 56 + security/integrity/evm/Kconfig | 76 + security/integrity/evm/Makefile | 8 + security/integrity/evm/evm.h | 89 + security/integrity/evm/evm_crypto.c | 459 ++ security/integrity/evm/evm_main.c | 1236 +++ security/integrity/evm/evm_posix_acl.c | 23 + security/integrity/evm/evm_secfs.c | 346 + security/integrity/iint.c | 71 + security/integrity/ima/Kconfig | 360 + security/integrity/ima/Makefile | 20 + security/integrity/ima/ima.h | 630 ++ security/integrity/ima/ima_api.c | 470 ++ security/integrity/ima/ima_appraise.c | 836 ++ security/integrity/ima/ima_asymmetric_keys.c | 66 + security/integrity/ima/ima_crypto.c | 519 ++ security/integrity/ima/ima_efi.c | 35 + security/integrity/ima/ima_fs.c | 856 +++ security/integrity/ima/ima_iint.c | 138 + security/integrity/ima/ima_init.c | 174 + security/integrity/ima/ima_kexec.c | 351 + security/integrity/ima/ima_main.c | 1406 ++++ security/integrity/ima/ima_modsig.c | 151 + security/integrity/ima/ima_mok.c | 49 + security/integrity/ima/ima_policy.c | 2473 ++++++ security/integrity/ima/ima_queue.c | 577 ++ security/integrity/ima/ima_queue_keys.c | 177 + security/integrity/ima/ima_template.c | 537 ++ security/integrity/ima/ima_template_lib.c | 765 ++ security/integrity/ima/ima_template_lib.h | 69 + security/integrity/integrity.h | 281 + security/integrity/integrity_audit.c | 69 + security/integrity/platform_certs/efi_parser.c | 108 + .../integrity/platform_certs/keyring_handler.c | 102 + .../integrity/platform_certs/keyring_handler.h | 55 + security/integrity/platform_certs/load_ipl_s390.c | 36 + security/integrity/platform_certs/load_powerpc.c | 160 + security/integrity/platform_certs/load_uefi.c | 238 + .../integrity/platform_certs/machine_keyring.c | 91 + .../integrity/platform_certs/platform_keyring.c | 58 + security/ipe/.gitignore | 2 + security/ipe/Kconfig | 117 + security/ipe/Makefile | 31 + security/ipe/audit.c | 279 + security/ipe/audit.h | 19 + security/ipe/digest.c | 119 + security/ipe/digest.h | 26 + security/ipe/eval.c | 393 + security/ipe/eval.h | 70 + security/ipe/fs.c | 246 + security/ipe/fs.h | 16 + security/ipe/hooks.c | 342 + security/ipe/hooks.h | 55 + security/ipe/ipe.c | 100 + security/ipe/ipe.h | 28 + security/ipe/policy.c | 244 + security/ipe/policy.h | 98 + security/ipe/policy_fs.c | 490 ++ security/ipe/policy_parser.c | 559 ++ security/ipe/policy_parser.h | 11 + security/ipe/policy_tests.c | 297 + security/keys/Kconfig | 131 + security/keys/Makefile | 32 + security/keys/big_key.c | 290 + security/keys/compat.c | 132 + security/keys/compat_dh.c | 36 + security/keys/dh.c | 313 + security/keys/encrypted-keys/Makefile | 11 + security/keys/encrypted-keys/ecryptfs_format.c | 76 + security/keys/encrypted-keys/ecryptfs_format.h | 27 + security/keys/encrypted-keys/encrypted.c | 1002 +++ security/keys/encrypted-keys/encrypted.h | 67 + security/keys/encrypted-keys/masterkey_trusted.c | 43 + security/keys/gc.c | 374 + security/keys/internal.h | 378 + security/keys/key.c | 1293 ++++ security/keys/keyctl.c | 2038 +++++ security/keys/keyctl_pkey.c | 335 + security/keys/keyring.c | 1800 +++++ security/keys/permission.c | 123 + security/keys/persistent.c | 167 + security/keys/proc.c | 323 + security/keys/process_keys.c | 965 +++ security/keys/request_key.c | 821 ++ security/keys/request_key_auth.c | 312 + security/keys/sysctl.c | 76 + security/keys/trusted-keys/Kconfig | 82 + security/keys/trusted-keys/Makefile | 20 + security/keys/trusted-keys/tpm2key.asn1 | 11 + security/keys/trusted-keys/trusted_caam.c | 191 + security/keys/trusted-keys/trusted_core.c | 410 + security/keys/trusted-keys/trusted_dcp.c | 363 + security/keys/trusted-keys/trusted_pkwm.c | 190 + security/keys/trusted-keys/trusted_tee.c | 287 + security/keys/trusted-keys/trusted_tpm1.c | 998 +++ security/keys/trusted-keys/trusted_tpm2.c | 605 ++ security/keys/user_defined.c | 207 + security/landlock/.kunitconfig | 6 + security/landlock/Kconfig | 42 + security/landlock/Makefile | 22 + security/landlock/access.h | 151 + security/landlock/audit.c | 237 + security/landlock/audit.h | 43 + security/landlock/common.h | 22 + security/landlock/cred.c | 69 + security/landlock/cred.h | 157 + security/landlock/domain.c | 775 ++ security/landlock/domain.h | 326 + security/landlock/errata.h | 99 + security/landlock/errata/abi-1.h | 47 + security/landlock/errata/abi-4.h | 22 + security/landlock/errata/abi-6.h | 29 + security/landlock/fs.c | 2136 ++++++ security/landlock/fs.h | 188 + security/landlock/id.c | 295 + security/landlock/id.h | 25 + security/landlock/limits.h | 42 + security/landlock/log.c | 587 ++ security/landlock/log.h | 86 + security/landlock/net.c | 413 + security/landlock/net.h | 34 + security/landlock/object.c | 67 + security/landlock/object.h | 91 + security/landlock/ruleset.c | 354 + security/landlock/ruleset.h | 250 + security/landlock/setup.c | 81 + security/landlock/setup.h | 22 + security/landlock/syscalls.c | 706 ++ security/landlock/task.c | 509 ++ security/landlock/task.h | 14 + security/landlock/trace.c | 185 + security/landlock/trace.h | 44 + security/landlock/tsync.c | 639 ++ security/landlock/tsync.h | 18 + security/loadpin/Kconfig | 44 + security/loadpin/Makefile | 2 + security/loadpin/loadpin.c | 442 ++ security/lockdown/Kconfig | 47 + security/lockdown/Makefile | 1 + security/lockdown/lockdown.c | 172 + security/lsm.h | 50 + security/lsm_audit.c | 457 ++ security/lsm_init.c | 577 ++ security/lsm_notifier.c | 31 + security/lsm_syscalls.c | 129 + security/min_addr.c | 62 + security/safesetid/Kconfig | 15 + security/safesetid/Makefile | 7 + security/safesetid/lsm.c | 293 + security/safesetid/lsm.h | 75 + security/safesetid/securityfs.c | 347 + security/security.c | 5717 ++++++++++++++ security/selinux/.gitignore | 4 + security/selinux/Kconfig | 100 + security/selinux/Makefile | 44 + security/selinux/avc.c | 1207 +++ security/selinux/genheaders.c | 154 + security/selinux/hooks.c | 8008 ++++++++++++++++++++ security/selinux/ibpkey.c | 235 + security/selinux/ima.c | 121 + security/selinux/include/audit.h | 71 + security/selinux/include/avc.h | 172 + security/selinux/include/avc_ss.h | 23 + security/selinux/include/classmap.h | 193 + security/selinux/include/conditional.h | 23 + security/selinux/include/hash.h | 47 + security/selinux/include/ibpkey.h | 35 + security/selinux/include/ima.h | 32 + security/selinux/include/initcalls.h | 19 + security/selinux/include/initial_sid_to_string.h | 38 + security/selinux/include/netif.h | 24 + security/selinux/include/netlabel.h | 137 + security/selinux/include/netnode.h | 26 + security/selinux/include/netport.h | 25 + security/selinux/include/objsec.h | 290 + security/selinux/include/policycap.h | 29 + security/selinux/include/policycap_names.h | 29 + security/selinux/include/security.h | 414 + security/selinux/include/xfrm.h | 94 + security/selinux/initcalls.c | 52 + security/selinux/netif.c | 283 + security/selinux/netlabel.c | 619 ++ security/selinux/netlink.c | 120 + security/selinux/netnode.c | 307 + security/selinux/netport.c | 235 + security/selinux/nlmsgtab.c | 234 + security/selinux/selinuxfs.c | 2023 +++++ security/selinux/ss/avtab.c | 618 ++ security/selinux/ss/avtab.h | 141 + security/selinux/ss/conditional.c | 765 ++ security/selinux/ss/conditional.h | 84 + security/selinux/ss/constraint.h | 65 + security/selinux/ss/context.c | 32 + security/selinux/ss/context.h | 204 + security/selinux/ss/ebitmap.c | 603 ++ security/selinux/ss/ebitmap.h | 156 + security/selinux/ss/hashtab.c | 199 + security/selinux/ss/hashtab.h | 154 + security/selinux/ss/mls.c | 653 ++ security/selinux/ss/mls.h | 96 + security/selinux/ss/mls_types.h | 52 + security/selinux/ss/policydb.c | 4155 ++++++++++ security/selinux/ss/policydb.h | 428 ++ security/selinux/ss/services.c | 3962 ++++++++++ security/selinux/ss/services.h | 47 + security/selinux/ss/sidtab.c | 638 ++ security/selinux/ss/sidtab.h | 160 + security/selinux/ss/symtab.c | 56 + security/selinux/ss/symtab.h | 26 + security/selinux/status.c | 122 + security/selinux/xfrm.c | 466 ++ security/smack/Kconfig | 55 + security/smack/Makefile | 9 + security/smack/smack.h | 543 ++ security/smack/smack_access.c | 765 ++ security/smack/smack_lsm.c | 5411 +++++++++++++ security/smack/smack_netfilter.c | 78 + security/smack/smackfs.c | 2954 ++++++++ security/tomoyo/.gitignore | 3 + security/tomoyo/Kconfig | 85 + security/tomoyo/Makefile | 21 + security/tomoyo/audit.c | 477 ++ security/tomoyo/common.c | 3025 ++++++++ security/tomoyo/common.h | 1292 ++++ security/tomoyo/condition.c | 1122 +++ security/tomoyo/domain.c | 950 +++ security/tomoyo/environ.c | 124 + security/tomoyo/file.c | 1050 +++ security/tomoyo/gc.c | 682 ++ security/tomoyo/group.c | 209 + security/tomoyo/load_policy.c | 110 + security/tomoyo/memory.c | 207 + security/tomoyo/mount.c | 242 + security/tomoyo/network.c | 780 ++ .../tomoyo/policy/exception_policy.conf.default | 2 + security/tomoyo/realpath.c | 310 + security/tomoyo/securityfs_if.c | 271 + security/tomoyo/tomoyo.c | 621 ++ security/tomoyo/util.c | 1106 +++ security/yama/Kconfig | 14 + security/yama/Makefile | 4 + security/yama/yama_lsm.c | 481 ++ 314 files changed, 131257 insertions(+) create mode 100644 security/Kconfig create mode 100644 security/Kconfig.hardening create mode 100644 security/Makefile create mode 100644 security/apparmor/.gitignore create mode 100644 security/apparmor/.kunitconfig create mode 100644 security/apparmor/Kconfig create mode 100644 security/apparmor/Makefile create mode 100644 security/apparmor/af_inet.c create mode 100644 security/apparmor/af_unix.c create mode 100644 security/apparmor/apparmorfs.c create mode 100644 security/apparmor/audit.c create mode 100644 security/apparmor/capability.c create mode 100644 security/apparmor/crypto.c create mode 100644 security/apparmor/domain.c create mode 100644 security/apparmor/file.c create mode 100644 security/apparmor/include/af_inet.h create mode 100644 security/apparmor/include/af_unix.h create mode 100644 security/apparmor/include/apparmor.h create mode 100644 security/apparmor/include/apparmorfs.h create mode 100644 security/apparmor/include/audit.h create mode 100644 security/apparmor/include/capability.h create mode 100644 security/apparmor/include/cred.h create mode 100644 security/apparmor/include/crypto.h create mode 100644 security/apparmor/include/domain.h create mode 100644 security/apparmor/include/file.h create mode 100644 security/apparmor/include/ipc.h create mode 100644 security/apparmor/include/label.h create mode 100644 security/apparmor/include/lib.h create mode 100644 security/apparmor/include/match.h create mode 100644 security/apparmor/include/mount.h create mode 100644 security/apparmor/include/net.h create mode 100644 security/apparmor/include/path.h create mode 100644 security/apparmor/include/perms.h create mode 100644 security/apparmor/include/policy.h create mode 100644 security/apparmor/include/policy_compat.h create mode 100644 security/apparmor/include/policy_ns.h create mode 100644 security/apparmor/include/policy_unpack.h create mode 100644 security/apparmor/include/procattr.h create mode 100644 security/apparmor/include/resource.h create mode 100644 security/apparmor/include/secid.h create mode 100644 security/apparmor/include/sig_names.h create mode 100644 security/apparmor/include/signal.h create mode 100644 security/apparmor/include/task.h create mode 100644 security/apparmor/ipc.c create mode 100644 security/apparmor/label.c create mode 100644 security/apparmor/lib.c create mode 100644 security/apparmor/lsm.c create mode 100644 security/apparmor/match.c create mode 100644 security/apparmor/mount.c create mode 100644 security/apparmor/net.c create mode 100644 security/apparmor/nulldfa.in create mode 100644 security/apparmor/path.c create mode 100644 security/apparmor/policy.c create mode 100644 security/apparmor/policy_compat.c create mode 100644 security/apparmor/policy_ns.c create mode 100644 security/apparmor/policy_unpack.c create mode 100644 security/apparmor/policy_unpack_test.c create mode 100644 security/apparmor/procattr.c create mode 100644 security/apparmor/resource.c create mode 100644 security/apparmor/secid.c create mode 100644 security/apparmor/stacksplitdfa.in create mode 100644 security/apparmor/task.c create mode 100644 security/bpf/Makefile create mode 100644 security/bpf/hooks.c create mode 100644 security/commoncap.c create mode 100644 security/commoncap_test.c create mode 100644 security/device_cgroup.c create mode 100644 security/inode.c create mode 100644 security/integrity/Kconfig create mode 100644 security/integrity/Makefile create mode 100644 security/integrity/digsig.c create mode 100644 security/integrity/digsig_asymmetric.c create mode 100644 security/integrity/efi_secureboot.c create mode 100644 security/integrity/evm/Kconfig create mode 100644 security/integrity/evm/Makefile create mode 100644 security/integrity/evm/evm.h create mode 100644 security/integrity/evm/evm_crypto.c create mode 100644 security/integrity/evm/evm_main.c create mode 100644 security/integrity/evm/evm_posix_acl.c create mode 100644 security/integrity/evm/evm_secfs.c create mode 100644 security/integrity/iint.c create mode 100644 security/integrity/ima/Kconfig create mode 100644 security/integrity/ima/Makefile create mode 100644 security/integrity/ima/ima.h create mode 100644 security/integrity/ima/ima_api.c create mode 100644 security/integrity/ima/ima_appraise.c create mode 100644 security/integrity/ima/ima_asymmetric_keys.c create mode 100644 security/integrity/ima/ima_crypto.c create mode 100644 security/integrity/ima/ima_efi.c create mode 100644 security/integrity/ima/ima_fs.c create mode 100644 security/integrity/ima/ima_iint.c create mode 100644 security/integrity/ima/ima_init.c create mode 100644 security/integrity/ima/ima_kexec.c create mode 100644 security/integrity/ima/ima_main.c create mode 100644 security/integrity/ima/ima_modsig.c create mode 100644 security/integrity/ima/ima_mok.c create mode 100644 security/integrity/ima/ima_policy.c create mode 100644 security/integrity/ima/ima_queue.c create mode 100644 security/integrity/ima/ima_queue_keys.c create mode 100644 security/integrity/ima/ima_template.c create mode 100644 security/integrity/ima/ima_template_lib.c create mode 100644 security/integrity/ima/ima_template_lib.h create mode 100644 security/integrity/integrity.h create mode 100644 security/integrity/integrity_audit.c create mode 100644 security/integrity/platform_certs/efi_parser.c create mode 100644 security/integrity/platform_certs/keyring_handler.c create mode 100644 security/integrity/platform_certs/keyring_handler.h create mode 100644 security/integrity/platform_certs/load_ipl_s390.c create mode 100644 security/integrity/platform_certs/load_powerpc.c create mode 100644 security/integrity/platform_certs/load_uefi.c create mode 100644 security/integrity/platform_certs/machine_keyring.c create mode 100644 security/integrity/platform_certs/platform_keyring.c create mode 100644 security/ipe/.gitignore create mode 100644 security/ipe/Kconfig create mode 100644 security/ipe/Makefile create mode 100644 security/ipe/audit.c create mode 100644 security/ipe/audit.h create mode 100644 security/ipe/digest.c create mode 100644 security/ipe/digest.h create mode 100644 security/ipe/eval.c create mode 100644 security/ipe/eval.h create mode 100644 security/ipe/fs.c create mode 100644 security/ipe/fs.h create mode 100644 security/ipe/hooks.c create mode 100644 security/ipe/hooks.h create mode 100644 security/ipe/ipe.c create mode 100644 security/ipe/ipe.h create mode 100644 security/ipe/policy.c create mode 100644 security/ipe/policy.h create mode 100644 security/ipe/policy_fs.c create mode 100644 security/ipe/policy_parser.c create mode 100644 security/ipe/policy_parser.h create mode 100644 security/ipe/policy_tests.c create mode 100644 security/keys/Kconfig create mode 100644 security/keys/Makefile create mode 100644 security/keys/big_key.c create mode 100644 security/keys/compat.c create mode 100644 security/keys/compat_dh.c create mode 100644 security/keys/dh.c create mode 100644 security/keys/encrypted-keys/Makefile create mode 100644 security/keys/encrypted-keys/ecryptfs_format.c create mode 100644 security/keys/encrypted-keys/ecryptfs_format.h create mode 100644 security/keys/encrypted-keys/encrypted.c create mode 100644 security/keys/encrypted-keys/encrypted.h create mode 100644 security/keys/encrypted-keys/masterkey_trusted.c create mode 100644 security/keys/gc.c create mode 100644 security/keys/internal.h create mode 100644 security/keys/key.c create mode 100644 security/keys/keyctl.c create mode 100644 security/keys/keyctl_pkey.c create mode 100644 security/keys/keyring.c create mode 100644 security/keys/permission.c create mode 100644 security/keys/persistent.c create mode 100644 security/keys/proc.c create mode 100644 security/keys/process_keys.c create mode 100644 security/keys/request_key.c create mode 100644 security/keys/request_key_auth.c create mode 100644 security/keys/sysctl.c create mode 100644 security/keys/trusted-keys/Kconfig create mode 100644 security/keys/trusted-keys/Makefile create mode 100644 security/keys/trusted-keys/tpm2key.asn1 create mode 100644 security/keys/trusted-keys/trusted_caam.c create mode 100644 security/keys/trusted-keys/trusted_core.c create mode 100644 security/keys/trusted-keys/trusted_dcp.c create mode 100644 security/keys/trusted-keys/trusted_pkwm.c create mode 100644 security/keys/trusted-keys/trusted_tee.c create mode 100644 security/keys/trusted-keys/trusted_tpm1.c create mode 100644 security/keys/trusted-keys/trusted_tpm2.c create mode 100644 security/keys/user_defined.c create mode 100644 security/landlock/.kunitconfig create mode 100644 security/landlock/Kconfig create mode 100644 security/landlock/Makefile create mode 100644 security/landlock/access.h create mode 100644 security/landlock/audit.c create mode 100644 security/landlock/audit.h create mode 100644 security/landlock/common.h create mode 100644 security/landlock/cred.c create mode 100644 security/landlock/cred.h create mode 100644 security/landlock/domain.c create mode 100644 security/landlock/domain.h create mode 100644 security/landlock/errata.h create mode 100644 security/landlock/errata/abi-1.h create mode 100644 security/landlock/errata/abi-4.h create mode 100644 security/landlock/errata/abi-6.h create mode 100644 security/landlock/fs.c create mode 100644 security/landlock/fs.h create mode 100644 security/landlock/id.c create mode 100644 security/landlock/id.h create mode 100644 security/landlock/limits.h create mode 100644 security/landlock/log.c create mode 100644 security/landlock/log.h create mode 100644 security/landlock/net.c create mode 100644 security/landlock/net.h create mode 100644 security/landlock/object.c create mode 100644 security/landlock/object.h create mode 100644 security/landlock/ruleset.c create mode 100644 security/landlock/ruleset.h create mode 100644 security/landlock/setup.c create mode 100644 security/landlock/setup.h create mode 100644 security/landlock/syscalls.c create mode 100644 security/landlock/task.c create mode 100644 security/landlock/task.h create mode 100644 security/landlock/trace.c create mode 100644 security/landlock/trace.h create mode 100644 security/landlock/tsync.c create mode 100644 security/landlock/tsync.h create mode 100644 security/loadpin/Kconfig create mode 100644 security/loadpin/Makefile create mode 100644 security/loadpin/loadpin.c create mode 100644 security/lockdown/Kconfig create mode 100644 security/lockdown/Makefile create mode 100644 security/lockdown/lockdown.c create mode 100644 security/lsm.h create mode 100644 security/lsm_audit.c create mode 100644 security/lsm_init.c create mode 100644 security/lsm_notifier.c create mode 100644 security/lsm_syscalls.c create mode 100644 security/min_addr.c create mode 100644 security/safesetid/Kconfig create mode 100644 security/safesetid/Makefile create mode 100644 security/safesetid/lsm.c create mode 100644 security/safesetid/lsm.h create mode 100644 security/safesetid/securityfs.c create mode 100644 security/security.c create mode 100644 security/selinux/.gitignore create mode 100644 security/selinux/Kconfig create mode 100644 security/selinux/Makefile create mode 100644 security/selinux/avc.c create mode 100644 security/selinux/genheaders.c create mode 100644 security/selinux/hooks.c create mode 100644 security/selinux/ibpkey.c create mode 100644 security/selinux/ima.c create mode 100644 security/selinux/include/audit.h create mode 100644 security/selinux/include/avc.h create mode 100644 security/selinux/include/avc_ss.h create mode 100644 security/selinux/include/classmap.h create mode 100644 security/selinux/include/conditional.h create mode 100644 security/selinux/include/hash.h create mode 100644 security/selinux/include/ibpkey.h create mode 100644 security/selinux/include/ima.h create mode 100644 security/selinux/include/initcalls.h create mode 100644 security/selinux/include/initial_sid_to_string.h create mode 100644 security/selinux/include/netif.h create mode 100644 security/selinux/include/netlabel.h create mode 100644 security/selinux/include/netnode.h create mode 100644 security/selinux/include/netport.h create mode 100644 security/selinux/include/objsec.h create mode 100644 security/selinux/include/policycap.h create mode 100644 security/selinux/include/policycap_names.h create mode 100644 security/selinux/include/security.h create mode 100644 security/selinux/include/xfrm.h create mode 100644 security/selinux/initcalls.c create mode 100644 security/selinux/netif.c create mode 100644 security/selinux/netlabel.c create mode 100644 security/selinux/netlink.c create mode 100644 security/selinux/netnode.c create mode 100644 security/selinux/netport.c create mode 100644 security/selinux/nlmsgtab.c create mode 100644 security/selinux/selinuxfs.c create mode 100644 security/selinux/ss/avtab.c create mode 100644 security/selinux/ss/avtab.h create mode 100644 security/selinux/ss/conditional.c create mode 100644 security/selinux/ss/conditional.h create mode 100644 security/selinux/ss/constraint.h create mode 100644 security/selinux/ss/context.c create mode 100644 security/selinux/ss/context.h create mode 100644 security/selinux/ss/ebitmap.c create mode 100644 security/selinux/ss/ebitmap.h create mode 100644 security/selinux/ss/hashtab.c create mode 100644 security/selinux/ss/hashtab.h create mode 100644 security/selinux/ss/mls.c create mode 100644 security/selinux/ss/mls.h create mode 100644 security/selinux/ss/mls_types.h create mode 100644 security/selinux/ss/policydb.c create mode 100644 security/selinux/ss/policydb.h create mode 100644 security/selinux/ss/services.c create mode 100644 security/selinux/ss/services.h create mode 100644 security/selinux/ss/sidtab.c create mode 100644 security/selinux/ss/sidtab.h create mode 100644 security/selinux/ss/symtab.c create mode 100644 security/selinux/ss/symtab.h create mode 100644 security/selinux/status.c create mode 100644 security/selinux/xfrm.c create mode 100644 security/smack/Kconfig create mode 100644 security/smack/Makefile create mode 100644 security/smack/smack.h create mode 100644 security/smack/smack_access.c create mode 100644 security/smack/smack_lsm.c create mode 100644 security/smack/smack_netfilter.c create mode 100644 security/smack/smackfs.c create mode 100644 security/tomoyo/.gitignore create mode 100644 security/tomoyo/Kconfig create mode 100644 security/tomoyo/Makefile create mode 100644 security/tomoyo/audit.c create mode 100644 security/tomoyo/common.c create mode 100644 security/tomoyo/common.h create mode 100644 security/tomoyo/condition.c create mode 100644 security/tomoyo/domain.c create mode 100644 security/tomoyo/environ.c create mode 100644 security/tomoyo/file.c create mode 100644 security/tomoyo/gc.c create mode 100644 security/tomoyo/group.c create mode 100644 security/tomoyo/load_policy.c create mode 100644 security/tomoyo/memory.c create mode 100644 security/tomoyo/mount.c create mode 100644 security/tomoyo/network.c create mode 100644 security/tomoyo/policy/exception_policy.conf.default create mode 100644 security/tomoyo/realpath.c create mode 100644 security/tomoyo/securityfs_if.c create mode 100644 security/tomoyo/tomoyo.c create mode 100644 security/tomoyo/util.c create mode 100644 security/yama/Kconfig create mode 100644 security/yama/Makefile create mode 100644 security/yama/yama_lsm.c (limited to 'security') diff --git a/security/Kconfig b/security/Kconfig new file mode 100644 index 000000000..f7bf6cdc6 --- /dev/null +++ b/security/Kconfig @@ -0,0 +1,305 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Security configuration +# + +menu "Security options" + +source "security/keys/Kconfig" + +config SECURITY_DMESG_RESTRICT + bool "Restrict unprivileged access to the kernel syslog" + default n + help + This enforces restrictions on unprivileged users reading the kernel + syslog via dmesg(8). + + If this option is not selected, no restrictions will be enforced + unless the dmesg_restrict sysctl is explicitly set to (1). + + If you are unsure how to answer this question, answer N. + +choice + prompt "Allow /proc/pid/mem access override" + default PROC_MEM_FORCE_PTRACE + help + Traditionally /proc/pid/mem allows users to override memory + permissions for users like ptrace, assuming they have ptrace + capability. + + This allows people to limit that - either never override, or + require actual active ptrace attachment (default). + +config PROC_MEM_ALWAYS_FORCE + bool "Traditional /proc/pid/mem behavior" + help + This allows /proc/pid/mem accesses to override memory mapping + permissions if you have ptrace access rights. + +config PROC_MEM_FORCE_PTRACE + bool "Require active ptrace() use for access override" + help + This allows /proc/pid/mem accesses to override memory mapping + permissions for active ptracers like gdb. + +config PROC_MEM_NO_FORCE + bool "Never" + help + Never override memory mapping permissions + +endchoice + +config MSEAL_SYSTEM_MAPPINGS + bool "mseal system mappings" + depends on 64BIT + depends on ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS + depends on !CHECKPOINT_RESTORE + help + Apply mseal on system mappings. + The system mappings includes vdso, vvar, vvar_vclock, + vectors (arm compat-mode), sigpage (arm compat-mode), uprobes. + + A 64-bit kernel is required for the memory sealing feature. + No specific hardware features from the CPU are needed. + + WARNING: This feature breaks programs which rely on relocating + or unmapping system mappings. Known broken software at the time + of writing includes CHECKPOINT_RESTORE, UML, gVisor, rr. Therefore + this config can't be enabled universally. + + For complete descriptions of memory sealing, please see + Documentation/userspace-api/mseal.rst + +config SECURITY + bool "Enable different security models" + depends on SYSFS + depends on MULTIUSER + help + This allows you to choose different security modules to be + configured into your kernel. + + If this option is not selected, the default Linux security + model will be used. + + If you are unsure how to answer this question, answer N. + +config HAS_SECURITY_AUDIT + def_bool y + depends on AUDIT + depends on SECURITY + +config SECURITYFS + bool "Enable the securityfs filesystem" + help + This will build the securityfs filesystem. It is currently used by + various security modules (AppArmor, IMA, SafeSetID, TOMOYO, TPM). + + If you are unsure how to answer this question, answer N. + +config SECURITY_NETWORK + bool "Socket and Networking Security Hooks" + depends on SECURITY + help + This enables the socket and networking security hooks. + If enabled, a security module can use these hooks to + implement socket and networking access controls. + If you are unsure how to answer this question, answer N. + +config SECURITY_INFINIBAND + bool "Infiniband Security Hooks" + depends on SECURITY && INFINIBAND + help + This enables the Infiniband security hooks. + If enabled, a security module can use these hooks to + implement Infiniband access controls. + If you are unsure how to answer this question, answer N. + +config SECURITY_NETWORK_XFRM + bool "XFRM (IPSec) Networking Security Hooks" + depends on XFRM && SECURITY_NETWORK + help + This enables the XFRM (IPSec) networking security hooks. + If enabled, a security module can use these hooks to + implement per-packet access controls based on labels + derived from IPSec policy. Non-IPSec communications are + designated as unlabelled, and only sockets authorized + to communicate unlabelled data can send without using + IPSec. + If you are unsure how to answer this question, answer N. + +config SECURITY_PATH + bool "Security hooks for pathname based access control" + depends on SECURITY + help + This enables the security hooks for pathname based access control. + If enabled, a security module can use these hooks to + implement pathname based access controls. + If you are unsure how to answer this question, answer N. + +config INTEL_TXT + bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)" + depends on HAVE_INTEL_TXT + help + This option enables support for booting the kernel with the + Trusted Boot (tboot) module. This will utilize + Intel(R) Trusted Execution Technology to perform a measured launch + of the kernel. If the system does not support Intel(R) TXT, this + will have no effect. + + Intel TXT will provide higher assurance of system configuration and + initial state as well as data reset protection. This is used to + create a robust initial kernel measurement and verification, which + helps to ensure that kernel security mechanisms are functioning + correctly. This level of protection requires a root of trust outside + of the kernel itself. + + Intel TXT also helps solve real end user concerns about having + confidence that their hardware is running the VMM or kernel that + it was configured with, especially since they may be responsible for + providing such assurances to VMs and services running on it. + + See for more information + about Intel(R) TXT. + See for more information about tboot. + See Documentation/arch/x86/intel_txt.rst for a description of how to enable + Intel TXT support in a kernel boot. + + If you are unsure as to whether this is required, answer N. + +config LSM_MMAP_MIN_ADDR + int "Low address space for LSM to protect from user allocation" + depends on SECURITY && SECURITY_SELINUX + default 32768 if ARM || (ARM64 && COMPAT) + default 65536 + help + This is the portion of low virtual memory which should be protected + from userspace allocation. Keeping a user from writing to low pages + can help reduce the impact of kernel NULL pointer bugs. + + For most ia64, ppc64 and x86 users with lots of address space + a value of 65536 is reasonable and should cause no problems. + On arm and other archs it should not be higher than 32768. + Programs which use vm86 functionality or have some need to map + this low address space will need the permission specific to the + systems running LSM. + +config STATIC_USERMODEHELPER + bool "Force all usermode helper calls through a single binary" + help + By default, the kernel can call many different userspace + binary programs through the "usermode helper" kernel + interface. Some of these binaries are statically defined + either in the kernel code itself, or as a kernel configuration + option. However, some of these are dynamically created at + runtime, or can be modified after the kernel has started up. + To provide an additional layer of security, route all of these + calls through a single executable that can not have its name + changed. + + Note, it is up to this single binary to then call the relevant + "real" usermode helper binary, based on the first argument + passed to it. If desired, this program can filter and pick + and choose what real programs are called. + + If you wish for all usermode helper programs are to be + disabled, choose this option and then set + STATIC_USERMODEHELPER_PATH to an empty string. + +config STATIC_USERMODEHELPER_PATH + string "Path to the static usermode helper binary" + depends on STATIC_USERMODEHELPER + default "/sbin/usermode-helper" + help + The binary called by the kernel when any usermode helper + program is wish to be run. The "real" application's name will + be in the first argument passed to this program on the command + line. + + If you wish for all usermode helper programs to be disabled, + specify an empty string here (i.e. ""). + +source "security/selinux/Kconfig" +source "security/smack/Kconfig" +source "security/tomoyo/Kconfig" +source "security/apparmor/Kconfig" +source "security/loadpin/Kconfig" +source "security/yama/Kconfig" +source "security/safesetid/Kconfig" +source "security/lockdown/Kconfig" +source "security/landlock/Kconfig" +source "security/ipe/Kconfig" + +source "security/integrity/Kconfig" + +choice + prompt "First legacy 'major LSM' to be initialized" + default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX + default DEFAULT_SECURITY_SMACK if SECURITY_SMACK + default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO + default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR + default DEFAULT_SECURITY_DAC + + help + This choice is there only for converting CONFIG_DEFAULT_SECURITY + in old kernel configs to CONFIG_LSM in new kernel configs. Don't + change this choice unless you are creating a fresh kernel config, + for this choice will be ignored after CONFIG_LSM has been set. + + Selects the legacy "major security module" that will be + initialized first. Overridden by non-default CONFIG_LSM. + + config DEFAULT_SECURITY_SELINUX + bool "SELinux" if SECURITY_SELINUX=y + + config DEFAULT_SECURITY_SMACK + bool "Simplified Mandatory Access Control" if SECURITY_SMACK=y + + config DEFAULT_SECURITY_TOMOYO + bool "TOMOYO" if SECURITY_TOMOYO=y + + config DEFAULT_SECURITY_APPARMOR + bool "AppArmor" if SECURITY_APPARMOR=y + + config DEFAULT_SECURITY_DAC + bool "Unix Discretionary Access Controls" + +endchoice + +config LSM + string "Ordered list of enabled LSMs" + depends on SECURITY + default "landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,ipe,bpf" if DEFAULT_SECURITY_SMACK + default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR + default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO + default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC + default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf" + help + A comma-separated list of LSMs, in initialization order. + Any LSMs left off this list, except for those with order + LSM_ORDER_FIRST and LSM_ORDER_LAST, which are always enabled + if selected in the kernel configuration, will be ignored. + This can be controlled at boot with the "lsm=" parameter. + + If unsure, leave this as the default. + +config SECURITY_COMMONCAP_KUNIT_TEST + bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS + depends on KUNIT=y && USER_NS + default KUNIT_ALL_TESTS + help + This builds the commoncap KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + +source "security/Kconfig.hardening" + +endmenu + diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening new file mode 100644 index 000000000..6923036e1 --- /dev/null +++ b/security/Kconfig.hardening @@ -0,0 +1,349 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "Kernel hardening options" + +menu "Memory initialization" + +config CC_HAS_AUTO_VAR_INIT_PATTERN + def_bool $(cc-option,-ftrivial-auto-var-init=pattern) + +config CC_HAS_AUTO_VAR_INIT_ZERO_BARE + def_bool $(cc-option,-ftrivial-auto-var-init=zero) + +config CC_HAS_AUTO_VAR_INIT_ZERO_ENABLER + # Clang 16 and later warn about using the -enable flag, but it + # is required before then. + def_bool $(cc-option,-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang) + depends on !CC_HAS_AUTO_VAR_INIT_ZERO_BARE + +config CC_HAS_AUTO_VAR_INIT_ZERO + def_bool CC_HAS_AUTO_VAR_INIT_ZERO_BARE || CC_HAS_AUTO_VAR_INIT_ZERO_ENABLER + +choice + prompt "Initialize kernel stack variables at function entry" + default INIT_STACK_ALL_PATTERN if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT_PATTERN + default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_ZERO + default INIT_STACK_NONE + help + This option enables initialization of stack variables at + function entry time. This has the possibility to have the + greatest coverage (since all functions can have their + variables initialized), but the performance impact depends + on the function calling complexity of a given workload's + syscalls. + + This chooses the level of coverage over classes of potentially + uninitialized variables. The selected class of variable will be + initialized before use in a function. + + config INIT_STACK_NONE + bool "no automatic stack variable initialization (weakest)" + help + Disable automatic stack variable initialization. + This leaves the kernel vulnerable to the standard + classes of uninitialized stack variable exploits + and information exposures. + + config INIT_STACK_ALL_PATTERN + bool "pattern-init everything (strongest)" + depends on CC_HAS_AUTO_VAR_INIT_PATTERN + depends on !KMSAN + help + Initializes everything on the stack (including padding) + with a specific debug value. This is intended to eliminate + all classes of uninitialized stack variable exploits and + information exposures, even variables that were warned about + having been left uninitialized. + + Pattern initialization is known to provoke many existing bugs + related to uninitialized locals, e.g. pointers receive + non-NULL values, buffer sizes and indices are very big. The + pattern is situation-specific; Clang on 64-bit uses 0xAA + repeating for all types and padding except float and double + which use 0xFF repeating (-NaN). Clang on 32-bit uses 0xFF + repeating for all types and padding. + GCC uses 0xFE repeating for all types, and zero for padding. + + config INIT_STACK_ALL_ZERO + bool "zero-init everything (strongest and safest)" + depends on CC_HAS_AUTO_VAR_INIT_ZERO + depends on !KMSAN + help + Initializes everything on the stack (including padding) + with a zero value. This is intended to eliminate all + classes of uninitialized stack variable exploits and + information exposures, even variables that were warned + about having been left uninitialized. + + Zero initialization provides safe defaults for strings + (immediately NUL-terminated), pointers (NULL), indices + (index 0), and sizes (0 length), so it is therefore more + suitable as a production security mitigation than pattern + initialization. + +endchoice + +config CC_HAS_SANCOV_STACK_DEPTH_CALLBACK + def_bool $(cc-option,-fsanitize-coverage-stack-depth-callback-min=1) + +config KSTACK_ERASE + bool "Poison kernel stack before returning from syscalls" + depends on HAVE_ARCH_KSTACK_ERASE + depends on GCC_PLUGINS || CC_HAS_SANCOV_STACK_DEPTH_CALLBACK + help + This option makes the kernel erase the kernel stack before + returning from system calls. This has the effect of leaving + the stack initialized to the poison value, which both reduces + the lifetime of any sensitive stack contents and reduces + potential for uninitialized stack variable exploits or information + exposures (it does not cover functions reaching the same stack + depth as prior functions during the same syscall). This blocks + most uninitialized stack variable attacks, with the performance + impact being driven by the depth of the stack usage, rather than + the function calling complexity. + + The performance impact on a single CPU system kernel compilation + sees a 1% slowdown, other systems and workloads may vary and you + are advised to test this feature on your expected workload before + deploying it. + +config GCC_PLUGIN_STACKLEAK + def_bool KSTACK_ERASE + depends on GCC_PLUGINS + help + This plugin was ported from grsecurity/PaX. More information at: + * https://grsecurity.net/ + * https://pax.grsecurity.net/ + +config GCC_PLUGIN_STACKLEAK_VERBOSE + bool "Report stack depth analysis instrumentation" if EXPERT + depends on GCC_PLUGIN_STACKLEAK + depends on !COMPILE_TEST # too noisy + help + This option will cause a warning to be printed each time the + stackleak plugin finds a function it thinks needs to be + instrumented. This is useful for comparing coverage between + builds. + +config KSTACK_ERASE_TRACK_MIN_SIZE + int "Minimum stack frame size of functions tracked by KSTACK_ERASE" + default 100 + range 0 4096 + depends on KSTACK_ERASE + help + The KSTACK_ERASE option instruments the kernel code for tracking + the lowest border of the kernel stack (and for some other purposes). + It inserts the __sanitizer_cov_stack_depth() call for the functions + with a stack frame size greater than or equal to this parameter. + If unsure, leave the default value 100. + +config KSTACK_ERASE_METRICS + bool "Show KSTACK_ERASE metrics in the /proc file system" + depends on KSTACK_ERASE + depends on PROC_FS + help + If this is set, KSTACK_ERASE metrics for every task are available + in the /proc file system. In particular, /proc//stack_depth + shows the maximum kernel stack consumption for the current and + previous syscalls. Although this information is not precise, it + can be useful for estimating the KSTACK_ERASE performance impact + for your workloads. + +config KSTACK_ERASE_RUNTIME_DISABLE + bool "Allow runtime disabling of kernel stack erasing" + depends on KSTACK_ERASE + help + This option provides 'stack_erasing' sysctl, which can be used in + runtime to control kernel stack erasing for kernels built with + CONFIG_KSTACK_ERASE. + +config INIT_ON_ALLOC_DEFAULT_ON + bool "Enable heap memory zeroing on allocation by default" + depends on !KMSAN + help + This has the effect of setting "init_on_alloc=1" on the kernel + command line. This can be disabled with "init_on_alloc=0". + When "init_on_alloc" is enabled, all page allocator and slab + allocator memory will be zeroed when allocated, eliminating + many kinds of "uninitialized heap memory" flaws, especially + heap content exposures. The performance impact varies by + workload, but most cases see <1% impact. Some synthetic + workloads have measured as high as 7%. + +config INIT_ON_FREE_DEFAULT_ON + bool "Enable heap memory zeroing on free by default" + depends on !KMSAN + help + This has the effect of setting "init_on_free=1" on the kernel + command line. This can be disabled with "init_on_free=0". + Similar to "init_on_alloc", when "init_on_free" is enabled, + all page allocator and slab allocator memory will be zeroed + when freed, eliminating many kinds of "uninitialized heap memory" + flaws, especially heap content exposures. The primary difference + with "init_on_free" is that data lifetime in memory is reduced, + as anything freed is wiped immediately, making live forensics or + cold boot memory attacks unable to recover freed memory contents. + The performance impact varies by workload, but is more expensive + than "init_on_alloc" due to the negative cache effects of + touching "cold" memory areas. Most cases see 3-5% impact. Some + synthetic workloads have measured as high as 8%. + +config CC_HAS_ZERO_CALL_USED_REGS + # supported by gcc-11 or newer and all supported versions of clang + def_bool $(cc-option,-fzero-call-used-regs=used-gpr) + +config ZERO_CALL_USED_REGS + bool "Enable register zeroing on function exit" + depends on CC_HAS_ZERO_CALL_USED_REGS + help + At the end of functions, always zero any caller-used register + contents. This helps ensure that temporary values are not + leaked beyond the function boundary. This means that register + contents are less likely to be available for side channels + and information exposures. Additionally, this helps reduce the + number of useful ROP gadgets by about 20% (and removes compiler + generated "write-what-where" gadgets) in the resulting kernel + image. This has a less than 1% performance impact on most + workloads. Image size growth depends on architecture, and should + be evaluated for suitability. For example, x86_64 grows by less + than 1%, and arm64 grows by about 5%. + +endmenu + +menu "Bounds checking" + +config FORTIFY_SOURCE + bool "Harden common str/mem functions against buffer overflows" + depends on ARCH_HAS_FORTIFY_SOURCE + help + Detect overflows of buffers in common string and memory functions + where the compiler can determine and validate the buffer sizes. + +config HARDENED_USERCOPY + bool "Harden memory copies between kernel and userspace" + imply STRICT_DEVMEM + help + This option checks for obviously wrong memory regions when + copying memory to/from the kernel (via copy_to_user() and + copy_from_user() functions) by rejecting memory ranges that + are larger than the specified heap object, span multiple + separately allocated pages, are not on the process stack, + or are part of the kernel text. This prevents entire classes + of heap overflow exploits and similar kernel memory exposures. + +config HARDENED_USERCOPY_DEFAULT_ON + bool "Harden memory copies by default" + depends on HARDENED_USERCOPY + default HARDENED_USERCOPY + help + This has the effect of setting "hardened_usercopy=on" on the kernel + command line. This can be disabled with "hardened_usercopy=off". + +endmenu + +menu "Hardening of kernel data structures" + +config LIST_HARDENED + bool "Check integrity of linked list manipulation" + help + Minimal integrity checking in the linked-list manipulation routines + to catch memory corruptions that are not guaranteed to result in an + immediate access fault. + + If unsure, say N. + +config RUST_BITMAP_HARDENED + bool "Check integrity of bitmap Rust API" + depends on RUST + help + Enables additional assertions in the Rust Bitmap API to catch + arguments that are not guaranteed to result in an immediate access + fault. + + If unsure, say N. + +config BUG_ON_DATA_CORRUPTION + bool "Trigger a BUG when data corruption is detected" + select LIST_HARDENED + help + Select this option if the kernel should BUG when it encounters + data corruption in kernel memory structures when they get checked + for validity. + + If unsure, say N. + +endmenu + +config CC_HAS_RANDSTRUCT + def_bool $(cc-option,-frandomize-layout-seed-file=/dev/null) + +choice + prompt "Randomize layout of sensitive kernel structures" + default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT) + default RANDSTRUCT_NONE + help + If you enable this, the layouts of structures that are entirely + function pointers (and have not been manually annotated with + __no_randomize_layout), or structures that have been explicitly + marked with __randomize_layout, will be randomized at compile-time. + This can introduce the requirement of an additional information + exposure vulnerability for exploits targeting these structure + types. + + Enabling this feature will introduce some performance impact, + slightly increase memory usage, and prevent the use of forensic + tools like Volatility against the system (unless the kernel + source tree isn't cleaned after kernel installation). + + The seed used for compilation is in scripts/basic/randomize.seed. + It remains after a "make clean" to allow for external modules to + be compiled with the existing seed and will be removed by a + "make mrproper" or "make distclean". This file should not be made + public, or the structure layout can be determined. + + config RANDSTRUCT_NONE + bool "Disable structure layout randomization" + help + Build normally: no structure layout randomization. + + config RANDSTRUCT_FULL + bool "Fully randomize structure layout" + depends on CC_HAS_RANDSTRUCT || GCC_PLUGINS + select MODVERSIONS if MODULES && !COMPILE_TEST + help + Fully randomize the member layout of sensitive + structures as much as possible, which may have both a + memory size and performance impact. + + One difference between the Clang and GCC plugin + implementations is the handling of bitfields. The GCC + plugin treats them as fully separate variables, + introducing sometimes significant padding. Clang tries + to keep adjacent bitfields together, but with their bit + ordering randomized. + + config RANDSTRUCT_PERFORMANCE + bool "Limit randomization of structure layout to cache-lines" + depends on GCC_PLUGINS + select MODVERSIONS if MODULES && !COMPILE_TEST + help + Randomization of sensitive kernel structures will make a + best effort at restricting randomization to cacheline-sized + groups of members. It will further not randomize bitfields + in structures. This reduces the performance hit of RANDSTRUCT + at the cost of weakened randomization. +endchoice + +config RANDSTRUCT + def_bool !RANDSTRUCT_NONE + +config GCC_PLUGIN_RANDSTRUCT + def_bool GCC_PLUGINS && RANDSTRUCT + help + Use GCC plugin to randomize structure layout. + + This plugin was ported from grsecurity/PaX. More + information at: + * https://grsecurity.net/ + * https://pax.grsecurity.net/ + +endmenu diff --git a/security/Makefile b/security/Makefile new file mode 100644 index 000000000..4601230ba --- /dev/null +++ b/security/Makefile @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the kernel security code +# + +obj-$(CONFIG_KEYS) += keys/ + +# always enable default capabilities +obj-y += commoncap.o +obj-$(CONFIG_SECURITY) += lsm_syscalls.o +obj-$(CONFIG_MMU) += min_addr.o + +# Object file lists +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o +obj-$(CONFIG_SECURITYFS) += inode.o +obj-$(CONFIG_SECURITY_SELINUX) += selinux/ +obj-$(CONFIG_SECURITY_SMACK) += smack/ +obj-$(CONFIG_HAS_SECURITY_AUDIT) += lsm_audit.o +obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ +obj-$(CONFIG_SECURITY_YAMA) += yama/ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ +obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ +obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ +obj-$(CONFIG_CGROUPS) += device_cgroup.o +obj-$(CONFIG_BPF_LSM) += bpf/ +obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/ +obj-$(CONFIG_SECURITY_IPE) += ipe/ + +# Object integrity file lists +obj-$(CONFIG_INTEGRITY) += integrity/ diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore new file mode 100644 index 000000000..6d1eb1c15 --- /dev/null +++ b/security/apparmor/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +net_names.h +capability_names.h +rlim_names.h diff --git a/security/apparmor/.kunitconfig b/security/apparmor/.kunitconfig new file mode 100644 index 000000000..aa842a026 --- /dev/null +++ b/security/apparmor/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=y +CONFIG_NET=y +CONFIG_SECURITY=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_KUNIT_TEST=y diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig new file mode 100644 index 000000000..a1f3749bd --- /dev/null +++ b/security/apparmor/Kconfig @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SECURITY_APPARMOR + bool "AppArmor support" + depends on SECURITY && NET + select AUDIT + select SECURITY_PATH + select SECURITYFS + select SECURITY_NETWORK + default n + help + This enables the AppArmor security module. + Required userspace tools (if they are not included in your + distribution) and further information may be found at + https://apparmor.wiki.kernel.org + + If you are unsure how to answer this question, answer N. + +config SECURITY_APPARMOR_DEBUG + bool "Build AppArmor with debug code" + depends on SECURITY_APPARMOR + default n + help + Build apparmor with debugging logic in apparmor. Not all + debugging logic will necessarily be enabled. A submenu will + provide fine grained control of the debug options that are + available. + +config SECURITY_APPARMOR_DEBUG_ASSERTS + bool "Build AppArmor with debugging asserts" + depends on SECURITY_APPARMOR_DEBUG + default y + help + Enable code assertions made with AA_BUG. These are primarily + function entry preconditions but also exist at other key + points. If the assert is triggered it will trigger a WARN + message. + +config SECURITY_APPARMOR_DEBUG_MESSAGES + bool "Debug messages enabled by default" + depends on SECURITY_APPARMOR_DEBUG + default n + help + Set the default value of the apparmor.debug kernel parameter. + When enabled, various debug messages will be logged to + the kernel message buffer. + +config SECURITY_APPARMOR_INTROSPECT_POLICY + bool "Allow loaded policy to be introspected" + depends on SECURITY_APPARMOR + default y + help + This option selects whether introspection of loaded policy + is available to userspace via the apparmor filesystem. This + adds to kernel memory usage. It is required for introspection + of loaded policy, and check point and restore support. It + can be disabled for embedded systems where reducing memory and + cpu is paramount. + +config SECURITY_APPARMOR_HASH + bool "Enable introspection of sha256 hashes for loaded profiles" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select CRYPTO_LIB_SHA256 + default y + help + This option selects whether introspection of loaded policy + hashes is available to userspace via the apparmor + filesystem. This option provides a light weight means of + checking loaded policy. This option adds to policy load + time and can be disabled for small embedded systems. + +config SECURITY_APPARMOR_HASH_DEFAULT + bool "Enable policy hash introspection by default" + depends on SECURITY_APPARMOR_HASH + default y + help + This option selects whether sha256 hashing of loaded policy + is enabled by default. The generation of sha256 hashes for + loaded policy provide system administrators a quick way to + verify that policy in the kernel matches what is expected, + however it can slow down policy load on some devices. In + these cases policy hashing can be disabled by default and + enabled only if needed. + +config SECURITY_APPARMOR_EXPORT_BINARY + bool "Allow exporting the raw binary policy" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select ZSTD_COMPRESS + select ZSTD_DECOMPRESS + default y + help + This option allows reading back binary policy as it was loaded. + It increases the amount of kernel memory needed by policy and + also increases policy load time. This option is required for + checkpoint and restore support, and debugging of loaded policy. + +config SECURITY_APPARMOR_COMPRESSED_POLICY + bool "Allow loading policy in a compressed format" + depends on SECURITY_APPARMOR + select ZSTD_DECOMPRESS + default y + help + This option allows loading policy from userspace in a + compressed format. This allows for userspace to not have to + decrompress caches before loading policy, and also allows + for less kernel memory to be used when "exporting the raw + binary policy" is enabled. + +config SECURITY_APPARMOR_PARANOID_LOAD + bool "Perform full verification of loaded policy" + depends on SECURITY_APPARMOR + default y + help + This options allows controlling whether apparmor does a full + verification of loaded policy. This should not be disabled + except for embedded systems where the image is read only, + includes policy, and has some form of integrity check. + Disabling the check will speed up policy loads. + +config SECURITY_APPARMOR_KUNIT_TEST + tristate "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS + depends on KUNIT && SECURITY_APPARMOR + default KUNIT_ALL_TESTS + help + This builds the AppArmor KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile new file mode 100644 index 000000000..8aed0d7ed --- /dev/null +++ b/security/apparmor/Makefile @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for AppArmor Linux Security Module +# +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + +apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \ + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ + resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ + policy_compat.o af_unix.o af_inet.o +apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + +obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o +apparmor_policy_unpack_test-objs += policy_unpack_test.o + +clean-files := capability_names.h rlim_names.h net_names.h + +# Build a lower case string table of address family names +# Transform lines from +# #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# [1] = "local", +# [2] = "inet", +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define AF_INET 2 /* Internet IP Protocol */ +# to +# #define AA_SFS_AF_MASK "local inet" +quiet_cmd_make-af = GEN $@ +cmd_make-af = echo "static const char *const address_family_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ + sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +# Build a lower case string table of sock type names +# Transform lines from +# SOCK_STREAM = 1, +# to +# [1] = "stream", +quiet_cmd_make-sock = GEN $@ +cmd_make-sock = echo "static const char *const sock_type_names[] = {" >> $@ ;\ + sed $^ >>$@ -r -n \ + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ + echo "};" >> $@ + +# Build a lower case string table of capability names +# Transforms lines from +# #define CAP_DAC_OVERRIDE 1 +# to +# [1] = "dac_override", +quiet_cmd_make-caps = GEN $@ +cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ + sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ + -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ + echo "};" >> $@ ;\ + printf '%s' '\#define AA_SFS_CAPS_MASK "' >> $@ ;\ + sed $< -r -n -e '/CAP_FS_MASK/d' \ + -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + + +# Build a lower case string table of rlimit names. +# Transforms lines from +# #define RLIMIT_STACK 3 /* max stack size */ +# to +# [RLIMIT_STACK] = "stack", +# +# and build a second integer table (with the second sed cmd), that maps +# RLIMIT defines to the order defined in asm-generic/resource.h This is +# required by policy load to map policy ordering of RLIMITs to internal +# ordering for architectures that redefine an RLIMIT. +# Transforms lines from +# #define RLIMIT_STACK 3 /* max stack size */ +# to +# RLIMIT_STACK, +# +# and build the securityfs entries for the mapping. +# Transforms lines from +# #define RLIMIT_FSIZE 1 /* Maximum filesize */ +# #define RLIMIT_STACK 3 /* max stack size */ +# to +# #define AA_SFS_RLIMIT_MASK "fsize stack" +quiet_cmd_make-rlim = GEN $@ +cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ + > $@ ;\ + sed $< >> $@ -r -n \ + -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\ + echo "};" >> $@ ;\ + echo "static const int rlim_map[RLIM_NLIMITS] = {" >> $@ ;\ + sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\ + echo "};" >> $@ ; \ + printf '%s' '\#define AA_SFS_RLIMIT_MASK "' >> $@ ;\ + sed -r -n 's/^\# ?define[ \t]+RLIMIT_([A-Z0-9_]+).*/\L\1/p' $< | \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + +$(obj)/capability.o : $(obj)/capability_names.h +$(obj)/net.o : $(obj)/net_names.h +$(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(src)/Makefile + $(call cmd,make-caps) +$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ + $(src)/Makefile + $(call cmd,make-rlim) +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ + $(srctree)/include/linux/net.h \ + $(src)/Makefile + $(call cmd,make-af) + $(call cmd,make-sock) diff --git a/security/apparmor/af_inet.c b/security/apparmor/af_inet.c new file mode 100644 index 000000000..516c90165 --- /dev/null +++ b/security/apparmor/af_inet.c @@ -0,0 +1,565 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor inet fine grained mediation + * + * Copyright 2024 Canonical Ltd. + * + */ + +#include + +#include "include/audit.h" +#include "include/af_inet.h" +#include "include/apparmor.h" +#include "include/file.h" +#include "include/label.h" +#include "include/net.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/cred.h" + + +static inline aa_state_t RULE_MEDIATES_SK(struct aa_ruleset *rules, + const struct sock *sk) +{ + return RULE_MEDIATES_NET(rules); +} + + +enum addr_type { + ADDR_LOCAL = 0, + ADDR_LOCAL_PRIV = 1, + ADDR_REMOTE = 2, +}; + +struct match_addr { + const char *addrp; + enum addr_type addrtype; + int len; + __be16 port; +}; + +struct stored_match_addr { + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + }; + int addrlen; + struct match_addr maddr; +}; + +static void set_ad_create(struct apparmor_audit_data *ad, + int family, int type, int protocol) +{ + ad->common.u.net->family = family; + ad->net.type = type; + ad->net.protocol = protocol; +} + +static int set_ad_addr(struct apparmor_audit_data *ad, + u16 family, bool source, struct match_addr *maddr) +{ + ad->common.u.net->family = family; + + if (source) { + ad->common.u.net->sport = maddr->port; + if (maddr->addrp) { + if (family == AF_INET) + /* ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; */ + ad->common.u.net->v4info.saddr = *(__be32 *)maddr->addrp; + else + /* ad.u.net->v4info.saddr = addr6->sin6_addr.s6_addr; */ + ad->common.u.net->v6info.saddr = *(struct in6_addr *)maddr->addrp; + } + } else { + ad->common.u.net->dport = maddr->port; + if (maddr->addrp) { + if (family == AF_INET) + /* ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; */ + ad->common.u.net->v4info.daddr = *(__be32 *)maddr->addrp; + else + /* ad.u.net->v4info.saddr = addr6->sin6_addr.s6_addr; */ + ad->common.u.net->v6info.daddr = *(struct in6_addr *)maddr->addrp; + } + } + return 0; +} + +/* returns 0 on success +* raw_port - if set raw_port (protocol) when SOCK_RAW */ +static int map_addr(struct sockaddr *addr, int addrlen, u16 raw_port, + enum addr_type addrtype, struct match_addr *maddr, + struct apparmor_audit_data *ad) +{ + struct sockaddr_in *addr4 = NULL; + struct sockaddr_in6 *addr6 = NULL; + + AA_BUG(!addr); + AA_BUG(!maddr); + + maddr->addrtype = addrtype; + if (!addr || addrlen < offsetofend(struct sockaddr, sa_family)) { + maddr->addrp = NULL; + maddr->port = 0; + maddr->len = 0; + return 0; + } + + /* + * its possibly to have sk->sk_family == PF_INET6 and + * addr->sa_family == AF_INET. sk_family is used for socket + * mediation, sa_family for when we have address ... + */ + switch (addr->sa_family) { + case AF_INET: + addr4 = (struct sockaddr_in *)addr; + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; + maddr->port = addr4->sin_port; + maddr->addrp = (char *)&addr4->sin_addr.s_addr; + maddr->len = 4; + break; + case AF_INET6: + addr6 = (struct sockaddr_in6 *)addr; + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; + maddr->port = addr6->sin6_port; + maddr->addrp = (char *)&addr6->sin6_addr.s6_addr; + maddr->len = 16; + break; + default: + return -EAFNOSUPPORT; + } + /* per ip spec, && sk->sk_type == SOCK_RAW*/ + if (raw_port && addrtype != ADDR_REMOTE) + maddr->port = htons(raw_port); + if (ad) + set_ad_addr(ad, addr->sa_family, addrtype != ADDR_REMOTE, maddr); + + return 0; +} + +/* -ENOTCONN if not connected */ +static int map_sock_addr(struct socket *sock, enum addr_type addrtype, + struct stored_match_addr *maddr, + struct apparmor_audit_data *ad) +{ + /* do we need early bailout for !family ... */ + maddr->addrlen = sock->ops->getname(sock, (struct sockaddr *) &maddr->addr, addrtype != ADDR_REMOTE ? 0 : 1); + if (maddr->addrlen == -ENOTCONN) { + maddr->addrlen = 0; + return map_addr(NULL, 0, 0, addrtype, &maddr->maddr, ad); + } else if (maddr->addrlen < 0) + return maddr->addrlen; + return map_addr(&maddr->addr, maddr->addrlen, 0, addrtype, + &maddr->maddr, ad); +} + +/* TODO: combine with connect map addr */ +/* TODO: raw_port */ +static int bind_map_addr(const struct sock *sk, struct sockaddr *addr, + int addrlen, + struct match_addr *maddr, + struct apparmor_audit_data *ad) +{ + struct sockaddr_in *addr4 = NULL; + struct sockaddr_in6 *addr6 = NULL; + u16 family; + + AA_BUG(!sk); + AA_BUG(!addr); + AA_BUG(!maddr); + + if (addrlen < offsetofend(struct sockaddr, sa_family)) + return -EINVAL; + + maddr->addrtype = ADDR_LOCAL; + /* + * its possibly to have sk->sk_family == PF_INET6 and + * addr->sa_family == AF_INET. sk_family is used for socket + * mediation, sa_family for when we have address ... + */ + family = addr->sa_family; + switch (addr->sa_family) { + case AF_UNSPEC: + if (sk->sk_family == PF_INET6) { + /* Length check from inet6_bind_sk() */ + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; + /* Family check from __inet6_bind() */ + return -EAFNOSUPPORT; + } + /* see __inet_bind(), we only want to allow + * AF_UNSPEC if the address is INADDR_ANY + */ + addr4 = (struct sockaddr_in *)addr; + if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) + return -EAFNOSUPPORT; + family = AF_INET; + fallthrough; + case AF_INET: + addr4 = (struct sockaddr_in *)addr; + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; + maddr->port = addr4->sin_port; + maddr->addrp = (char *)&addr4->sin_addr.s_addr; + maddr->len = 4; + break; + case AF_INET6: + addr6 = (struct sockaddr_in6 *)addr; + if (addrlen < SIN6_LEN_RFC2133) + return -EINVAL; + maddr->port = addr6->sin6_port; + maddr->addrp = (char *)&addr6->sin6_addr.s6_addr; + maddr->len = 16; + break; + default: + return -EAFNOSUPPORT; + } + + if (ad) + set_ad_addr(ad, family, true, maddr); + + return 0; +} + + +static inline int profile_sk_perm(struct aa_profile *profile, u32 request, + const struct sock *sk, + struct match_addr *maddr, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!sk); + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* no kernel_t bailout */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + + return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type, + protocol); +} + + +/* sendmsg/rcvmsg/connect */ +static int profile_remote_perm(struct aa_profile *profile, + const struct sock *sk, + u32 request, struct match_addr *raddr, + struct match_addr *laddr, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!raddr); + AA_BUG(!laddr); + AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6, + "family=%d", sk->sk_family); + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +static int profile_bind_perm(struct aa_profile *profile, + const struct sock *sk, + struct match_addr *maddr, + struct apparmor_audit_data *ad) +{ + return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk); + +} + +static int profile_listen_perm(struct aa_profile *profile, + const struct sock *sk, + struct match_addr *maddr, int backlog, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!maddr); + AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6, + "family=%d", sk->sk_family); + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk); +} + +static inline int profile_accept_perm(struct aa_profile *profile, + const struct sock *sk, + struct match_addr *maddr, + const struct sock *newsk, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!sk); + /* AA_BUG(!newsk); newsk can be null here, since not using atm ... */ + AA_BUG(!maddr); + AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6, + "family=%d", sk->sk_family); + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk); +} + +/* getopt/setopt */ +static int profile_opt_perm(struct aa_profile *profile, u32 request, + const struct sock *sk, struct match_addr *maddr, + int level, int optname, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!maddr); + AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6, + "family=%d", sk->sk_family); + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* ---------------------------------------------------------------------- */ + +// TODO: cleanup init to use recursion, so we can have N init fns, in 1 macro +// TODO: lift DEFINE_AUDIT out of macro into init fn??? + +/* no kernel_t bailout */ +#define label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXX, YYYY, CALLBACKFN) \ +({ \ + int __EERROR = 0; \ + if (label_mediates(LABEL, AA_CLASS_NET)) { \ + struct aa_profile *PROFILE; \ + DEFINE_AUDIT_SK(AAD, OP, CRED, SOCKSK); \ + (AAD).subj_cred = (CRED); \ + (AAD).request = (REQUEST); \ + __EERROR = (XXXX); \ + if (__EERROR == 0) { \ + __EERROR = (YYYY); \ + if (__EERROR == 0) { \ + __EERROR = fn_for_each(LABEL, PROFILE, \ + (CALLBACKFN)); \ + } \ + } \ + } \ + __EERROR; \ +}) + +/* no kernel_t bailout */ +#define label_sk_has_perm(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, CALLBACKFN) \ + label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, \ + 0, 0, CALLBACKFN) + +/* no kernel_t bailout */ +#define label_sk_has_perm1(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXX, CALLBACKFN) \ + label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, \ + XXXX, 0, CALLBACKFN) + + +/* Early bailout for kernel_t - 2 init args before callback */ +#define sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, YYYYX, CALLBACKFN) \ +({ \ + struct aa_label *__label; \ + struct aa_sk_ctx *__ctx = aa_sock(SOCKSK); \ + int __ERROR = 0; \ + bool __needput; \ + if (rcu_access_pointer(__ctx->label) != kernel_t) { \ + \ + __label = begin_current_label_crit_section(&__needput); \ + __ERROR = label_sk_has_perm2(current_cred(), __label, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, YYYYX, CALLBACKFN); \ + end_current_label_crit_section(__label, __needput); \ + } \ + __ERROR; \ +}) + +/* Early bailout for kernel_t - no init args before callback */ +#define sk_has_perm(SOCKSK, OP, REQUEST, PROFILE, AAD, CALLBACKFN) \ + sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, 0, 0, CALLBACKFN) + + +/* Early bailout for kernel_t - 1 init arg before callback */ +#define sk_has_perm1(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, CALLBACKFN) \ + sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, 0, CALLBACKFN) + + + +/* no kernel_t early bailout */ +/* NOTE: already lifted label_mediates into lsm.c */ +int aa_inet_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + struct aa_profile *profile; + int error = 0; + DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family, type, + protocol); + + ad.subj_cred = current_cred(); + set_ad_create(&ad, family, type, protocol); + error = fn_for_each(label, profile, + profile_create_perm(profile, family, type, + protocol, &ad)); + + return error; +} + +int aa_inet_bind_perm(struct socket *sock, struct sockaddr *addr, + int addrlen) +{ + struct match_addr maddr; + + return sk_has_perm1(sock->sk, OP_BIND, AA_MAY_BIND, profile, ad, + bind_map_addr(sock->sk, addr, addrlen, &maddr, + &ad), + profile_bind_perm(profile, sock->sk, &maddr, &ad)); +} + +int aa_inet_connect_perm(struct socket *sock, struct sockaddr *addr, + int addrlen) +{ + struct stored_match_addr laddr; + struct match_addr raddr; + + /* disconnect socket */ + if (addrlen < offsetofend(struct sockaddr, sa_family)) + return -EINVAL; + if (addr->sa_family == AF_UNSPEC) + return 0; + + /* do we need early bailout for !family ... */ + return sk_has_perm2(sock->sk, OP_CONNECT, AA_MAY_CONNECT, profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad), + map_addr(addr, addrlen, 0, ADDR_REMOTE, &raddr, + &ad), + profile_remote_perm(profile, sock->sk, + AA_MAY_CONNECT, &raddr, + &laddr.maddr, &ad)); +} + +int aa_inet_listen_perm(struct socket *sock, int backlog) +{ + struct stored_match_addr maddr; + + /* do we need early bailout for !family ... */ + return sk_has_perm1(sock->sk, OP_LISTEN, AA_MAY_LISTEN, profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad), + profile_listen_perm(profile, sock->sk, &maddr.maddr, + backlog, &ad)); +} + +/* ability of sock to connect, not peer address binding */ +int aa_inet_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct stored_match_addr maddr; + int error; + + error = sk_has_perm1(sock->sk, OP_ACCEPT, AA_MAY_ACCEPT, profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad), + profile_accept_perm(profile, sock->sk, + &maddr.maddr, + newsock->sk, &ad)); + + /* selinux updates inode - need to investigate this more */ + return error; +} + +/* sendmsg, recvmsg. */ +int aa_inet_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + struct stored_match_addr laddr; + struct match_addr raddr; + + /* do we need early bailout for !family ... */ + return sk_has_perm2(sock->sk, op, request, profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad), + map_addr(msg->msg_name, msg->msg_namelen, 0, + ADDR_REMOTE, &raddr, &ad), + profile_remote_perm(profile, sock->sk, request, + &raddr, &laddr.maddr, &ad)); +} + +/* getopt, setopt */ +int aa_inet_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + struct stored_match_addr maddr; + + return sk_has_perm1(sock->sk, op, request, profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad), + profile_opt_perm(profile, request, sock->sk, + &maddr.maddr, level, optname, &ad)); +} + +static int inet_label_sock_perm(const struct cred *cred, struct aa_label *label, + const char *op, u32 request, + struct socket *sock) +{ + struct stored_match_addr maddr; + + return label_sk_has_perm1(cred, label, sock->sk, op, request, profile, + ad, + map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad), + profile_sk_perm(profile, request, sock->sk, + &maddr.maddr, &ad)); +} + +/* revalidation, get/set attr/getsockname/peername */ +int aa_inet_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_sk_ctx *ctx = aa_sock(sock->sk); + struct aa_label *label; + bool needput; + int error; + + if (rcu_access_pointer(ctx->label) == kernel_t) + return 0; + + label = begin_current_label_crit_section(&needput); + error = inet_label_sock_perm(current_cred(), label, op, request, sock); + end_current_label_crit_section(label, needput); + + return error; +} + +int aa_inet_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, struct socket *sock) +{ + u32 sk_req = request & ~NET_PEER_MASK; + struct stored_match_addr laddr; + const struct sock *sk = sock->sk; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6, + "family=%d", sk->sk_family); + + /* access to the local sock */ + error = label_sk_has_perm1(subj_cred, label, sock->sk, op, request, + profile, ad, + map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad), + profile_sk_perm(profile, sk_req, sock->sk, &laddr.maddr, + &ad)); + + if (!error) { + struct stored_match_addr raddr; + + /* TODO: have ad here: instead of in CB so we do have to redo */ + error = map_sock_addr(sock, ADDR_REMOTE, &raddr, NULL); + if (!error && raddr.maddr.addrp) { + error = label_sk_has_perm1(subj_cred, label, sock->sk, + op, request, profile, ad, + set_ad_addr(&ad, raddr.addr.sa_family, + false, &raddr.maddr), + profile_remote_perm(profile, sock->sk, + request, + &raddr.maddr, + &laddr.maddr, &ad)); + } + } + + return error; +} diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c new file mode 100644 index 000000000..b908e7448 --- /dev/null +++ b/security/apparmor/af_unix.c @@ -0,0 +1,812 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2023 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include + +#include "include/audit.h" +#include "include/af_unix.h" +#include "include/apparmor.h" +#include "include/file.h" +#include "include/label.h" +#include "include/net.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/cred.h" + + +static inline struct sock *aa_unix_sk(struct unix_sock *u) +{ + return &u->sk; +} + +static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred, + struct aa_label *label, const struct path *path) +{ + AA_BUG(!label); + AA_BUG(!path); + + if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE)) + return 0; + + mask &= NET_FS_PERMS; + /* if !u->path.dentry socket is being shutdown - implicit delegation + * until obj delegation is supported + */ + if (path->dentry) { + /* the sunpath may not be valid for this ns so use the path */ + struct inode *inode = path->dentry->d_inode; + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_idmap(path->mnt), inode); + struct path_cond cond = { + .uid = vfsuid_into_kuid(vfsuid), + .mode = inode->i_mode, + }; + + return aa_path_perm(op, subj_cred, label, path, + PATH_SOCK_COND, mask, &cond); + } /* else implicitly delegated */ + + return 0; +} + +/* match_addr special constants */ +#define ABSTRACT_ADDR "\x00" /* abstract socket addr */ +#define ANONYMOUS_ADDR "\x01" /* anonymous endpoint, no addr */ +#define DISCONNECTED_ADDR "\x02" /* addr is another namespace */ +#define SHUTDOWN_ADDR "\x03" /* path addr is shutdown and cleared */ +#define FS_ADDR "/" /* path addr in fs */ + +static aa_state_t match_addr(const struct aa_dfa *dfa, aa_state_t state, + struct sockaddr_un *addr, int addrlen) +{ + if (addr) + /* include leading \0 */ + state = aa_dfa_match_len(dfa, state, addr->sun_path, + unix_addr_len(addrlen)); + else + state = aa_dfa_match_len(dfa, state, ANONYMOUS_ADDR, 1); + /* todo: could change to out of band for cleaner separation */ + state = aa_dfa_null_transition(dfa, state); + + return state; +} + +static aa_state_t match_to_local(struct aa_policydb *policy, + aa_state_t state, u32 request, + int type, int protocol, + struct sockaddr_un *addr, int addrlen, + struct aa_perms **p, + const char **info) +{ + state = aa_match_to_prot(policy, state, request, PF_UNIX, type, + protocol, NULL, info); + if (state) { + state = match_addr(policy->dfa, state, addr, addrlen); + if (state) { + /* todo: local label matching */ + state = aa_dfa_null_transition(policy->dfa, state); + if (!state) + *info = "failed local label match"; + } else { + *info = "failed local address match"; + } + } + + return state; +} + +struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen) +{ + struct unix_address *addr; + + /* memory barrier is sufficient see note in net/unix/af_unix.c */ + addr = smp_load_acquire(&u->addr); + if (addr) { + *addrlen = addr->len; + return addr->name; + } + *addrlen = 0; + return NULL; +} + +static aa_state_t match_to_sk(struct aa_policydb *policy, + aa_state_t state, u32 request, + struct unix_sock *u, struct aa_perms **p, + const char **info) +{ + int addrlen; + struct sockaddr_un *addr = aa_sunaddr(u, &addrlen); + + return match_to_local(policy, state, request, u->sk.sk_type, + u->sk.sk_protocol, addr, addrlen, p, info); +} + +#define CMD_ADDR 1 +#define CMD_LISTEN 2 +#define CMD_OPT 4 + +static aa_state_t match_to_cmd(struct aa_policydb *policy, aa_state_t state, + u32 request, struct unix_sock *u, + char cmd, struct aa_perms **p, + const char **info) +{ + AA_BUG(!p); + + state = match_to_sk(policy, state, request, u, p, info); + if (state && !*p) { + state = aa_dfa_match_len(policy->dfa, state, &cmd, 1); + if (!state) + *info = "failed cmd selection match"; + } + + return state; +} + +static aa_state_t match_to_peer(struct aa_policydb *policy, aa_state_t state, + u32 request, struct unix_sock *u, + struct sockaddr_un *peer_addr, int peer_addrlen, + struct aa_perms **p, const char **info) +{ + AA_BUG(!p); + + state = match_to_cmd(policy, state, request, u, CMD_ADDR, p, info); + if (state && !*p) { + state = match_addr(policy->dfa, state, peer_addr, peer_addrlen); + if (!state) + *info = "failed peer address match"; + } + + return state; +} + +static aa_state_t match_label(struct aa_profile *profile, + struct aa_ruleset *rule, aa_state_t state, + u32 request, struct aa_profile *peer, + const struct aa_perms *p, + struct apparmor_audit_data *ad) +{ + AA_BUG(!profile); + AA_BUG(!peer); + + ad->peer = &peer->label; + + if (state && !p) { + state = aa_dfa_match(rule->policy->dfa, state, + peer->base.hname); + if (!state) + ad->info = "failed peer label match"; + + } + + return aa_do_perms(profile, rule->policy, state, request, p, ad); +} + + +/* unix sock creation comes before we know if the socket will be an fs + * socket + * v6 - semantics are handled by mapping in profile load + * v7 - semantics require sock create for tasks creating an fs socket. + * v8 - same as v7 + */ +static int profile_create_perm(struct aa_profile *profile, int family, + int type, int protocol, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + state = aa_match_to_prot(rules->policy, state, AA_MAY_CREATE, + PF_UNIX, type, protocol, NULL, + &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_CREATE, + NULL, ad); + } + + return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type, + protocol); +} + +static int profile_sk_perm(struct aa_profile *profile, + struct apparmor_audit_data *ad, + u32 request, struct sock *sk, const struct path *path) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, + &unix_sk(sk)->path); + + state = match_to_sk(rules->policy, state, request, unix_sk(sk), + &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, request, p, + ad); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + if (is_unix_addr_fs(ad->net.addr, ad->net.addrlen)) + /* under v7-9 fs hook handles bind */ + return 0; + /* bind for abstract socket */ + state = match_to_local(rules->policy, state, AA_MAY_BIND, + sk->sk_type, sk->sk_protocol, + unix_addr(ad->net.addr), + ad->net.addrlen, + &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_BIND, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk); +} + +static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, + int backlog, struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + __be16 b = cpu_to_be16(backlog); + + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, AA_MAY_LISTEN, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_cmd(rules->policy, state, AA_MAY_LISTEN, + unix_sk(sk), CMD_LISTEN, &p, &ad->info); + if (state && !p) { + state = aa_dfa_match_len(rules->policy->dfa, state, + (char *) &b, 2); + if (!state) + ad->info = "failed listen backlog match"; + } + return aa_do_perms(profile, rules->policy, state, AA_MAY_LISTEN, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk); +} + +static int profile_accept_perm(struct aa_profile *profile, + struct sock *sk, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, AA_MAY_ACCEPT, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_sk(rules->policy, state, AA_MAY_ACCEPT, + unix_sk(sk), &p, &ad->info); + + return aa_do_perms(profile, rules->policy, state, AA_MAY_ACCEPT, + p, ad); + } + + return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk); +} + +static int profile_opt_perm(struct aa_profile *profile, u32 request, + struct sock *sk, int optname, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(!sk); + AA_BUG(!ad); + AA_BUG(profile_unconfined(profile)); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + __be16 b = cpu_to_be16(optname); + if (is_unix_fs(sk)) + return unix_fs_perm(ad->op, request, + ad->subj_cred, &profile->label, + &unix_sk(sk)->path); + + state = match_to_cmd(rules->policy, state, request, unix_sk(sk), + CMD_OPT, &p, &ad->info); + if (state && !p) { + state = aa_dfa_match_len(rules->policy->dfa, state, + (char *) &b, 2); + if (!state) + ad->info = "failed sockopt match"; + } + return aa_do_perms(profile, rules->policy, state, request, p, + ad); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* null peer_label is allowed, in which case the peer_sk label is used */ +static int profile_peer_perm(struct aa_profile *profile, u32 request, + struct sock *sk, const struct path *path, + struct sockaddr_un *peer_addr, + int peer_addrlen, const struct path *peer_path, + struct aa_label *peer_label, + struct apparmor_audit_data *ad) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms *p = NULL; + aa_state_t state; + + AA_BUG(!profile); + AA_BUG(profile_unconfined(profile)); + AA_BUG(!sk); + AA_BUG(!peer_label); + AA_BUG(!ad); + + state = RULE_MEDIATES_UNIX(rules); + if (state) { + struct aa_profile *peerp; + + if (peer_path) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, peer_path); + else if (path) + return unix_fs_perm(ad->op, request, ad->subj_cred, + &profile->label, path); + state = match_to_peer(rules->policy, state, request, + unix_sk(sk), + peer_addr, peer_addrlen, &p, &ad->info); + + return fn_for_each_in_scope(peer_label, peerp, + match_label(profile, rules, state, request, + peerp, p, ad)); + } + + return aa_profile_af_sk_perm(profile, ad, request, sk); +} + +/* -------------------------------- */ + +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol) +{ + if (!unconfined(label)) { + struct aa_profile *profile; + DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family, + type, protocol); + + return fn_for_each_confined(label, profile, + profile_create_perm(profile, family, type, + protocol, &ad)); + } + + return 0; +} + +static int aa_unix_label_sk_perm(const struct cred *subj_cred, + struct aa_label *label, + const char *op, u32 request, struct sock *sk, + const struct path *path) +{ + if (!unconfined(label)) { + struct aa_profile *profile; + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); + + return fn_for_each_confined(label, profile, + profile_sk_perm(profile, &ad, request, sk, + path)); + } + return 0; +} + +/* revalidation, get/set attr, shutdown */ +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) +{ + struct aa_label *label; + bool needput; + int error; + + label = begin_current_label_crit_section(&needput); + error = aa_unix_label_sk_perm(current_cred(), label, op, + request, sock->sk, + is_unix_fs(sock->sk) ? &unix_sk(sock->sk)->path : NULL); + end_current_label_crit_section(label, needput); + + return error; +} + +static int valid_addr(struct sockaddr *addr, int addr_len) +{ + struct sockaddr_un *sunaddr = unix_addr(addr); + + /* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */ + if (addr_len < offsetof(struct sockaddr_un, sun_path) || + addr_len > sizeof(*sunaddr)) + return -EINVAL; + return 0; +} + +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr, + int addrlen) +{ + struct aa_profile *profile; + struct aa_label *label; + bool needput; + int error = 0; + + error = valid_addr(addr, addrlen); + if (error) + return error; + + label = begin_current_label_crit_section(&needput); + /* fs bind is handled by mknod */ + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_BIND, current_cred(), sock->sk); + + ad.net.addr = unix_addr(addr); + ad.net.addrlen = addrlen; + + error = fn_for_each_confined(label, profile, + profile_bind_perm(profile, sock->sk, &ad)); + } + end_current_label_crit_section(label, needput); + + return error; +} + +/* + * unix connections are covered by the + * - unix_stream_connect (stream) and unix_may_send hooks (dgram) + * - fs connect is handled by open + * This is just here to document this is not needed for af_unix + * +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 0; +} +*/ + +int aa_unix_listen_perm(struct socket *sock, int backlog) +{ + struct aa_profile *profile; + struct aa_label *label; + bool needput; + int error = 0; + + label = begin_current_label_crit_section(&needput); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_LISTEN, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_listen_perm(profile, sock->sk, + backlog, &ad)); + } + end_current_label_crit_section(label, needput); + + return error; +} + + +/* ability of sock to connect, not peer address binding */ +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) +{ + struct aa_profile *profile; + struct aa_label *label; + bool needput; + int error = 0; + + label = begin_current_label_crit_section(&needput); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, OP_ACCEPT, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_accept_perm(profile, sock->sk, &ad)); + } + end_current_label_crit_section(label, needput); + + return error; +} + + +/* + * dgram handled by unix_may_sendmsg, right to send on stream done at connect + * could do per msg unix_stream here, but connect + socket transfer is + * sufficient. This is just here to document this is not needed for af_unix + * + * sendmsg, recvmsg +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size) +{ + return 0; +} +*/ + +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, + int level, int optname) +{ + struct aa_profile *profile; + struct aa_label *label; + bool needput; + int error = 0; + + label = begin_current_label_crit_section(&needput); + if (!unconfined(label)) { + DEFINE_AUDIT_SK(ad, op, current_cred(), sock->sk); + + error = fn_for_each_confined(label, profile, + profile_opt_perm(profile, request, sock->sk, + optname, &ad)); + } + end_current_label_crit_section(label, needput); + + return error; +} + +static int unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, const struct path *path, + struct sockaddr_un *peer_addr, int peer_addrlen, + const struct path *peer_path, struct aa_label *peer_label) +{ + struct aa_profile *profile; + DEFINE_AUDIT_SK(ad, op, subj_cred, sk); + + ad.net.peer.addr = peer_addr; + ad.net.peer.addrlen = peer_addrlen; + + return fn_for_each_confined(label, profile, + profile_peer_perm(profile, request, sk, path, + peer_addr, peer_addrlen, peer_path, + peer_label, &ad)); +} + +/* + * + * Requires: lock held on both @sk and @peer_sk + * called by unix_stream_connect, unix_may_send + */ +int aa_unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label) +{ + struct unix_sock *peeru = unix_sk(peer_sk); + struct unix_sock *u = unix_sk(sk); + int plen; + struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk), &plen); + + AA_BUG(!label); + AA_BUG(!sk); + AA_BUG(!peer_sk); + AA_BUG(!peer_label); + + return unix_peer_perm(subj_cred, label, op, request, sk, + is_unix_fs(sk) ? &u->path : NULL, + paddr, plen, + is_unix_fs(peer_sk) ? &peeru->path : NULL, + peer_label); +} + +/* sk_plabel for comparison only */ +static void update_sk_ctx(struct sock *sk, struct aa_label *label, + struct aa_label *plabel) +{ + struct aa_label *l, *old; + struct aa_sk_ctx *ctx = aa_sock(sk); + bool update_sk; + + rcu_read_lock(); + update_sk = (plabel && + (plabel != rcu_access_pointer(ctx->peer_lastupdate) || + !aa_label_is_subset(plabel, rcu_dereference(ctx->peer)))) || + !__aa_subj_label_is_cached(label, rcu_dereference(ctx->label)); + rcu_read_unlock(); + if (!update_sk) + return; + + spin_lock(&unix_sk(sk)->lock); + old = rcu_dereference_protected(ctx->label, + lockdep_is_held(&unix_sk(sk)->lock)); + l = aa_label_merge(old, label, GFP_ATOMIC); + if (l) { + if (l != old) { + rcu_assign_pointer(ctx->label, l); + aa_put_label(old); + } else + aa_put_label(l); + } + if (plabel && rcu_access_pointer(ctx->peer_lastupdate) != plabel) { + old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock)); + + if (old == plabel) { + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); + } else if (aa_label_is_subset(plabel, old)) { + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); + rcu_assign_pointer(ctx->peer, aa_get_label(plabel)); + aa_put_label(old); + } /* else race or a subset - don't update */ + } + spin_unlock(&unix_sk(sk)->lock); +} + +static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx, + struct aa_label *label) +{ + struct aa_label *l, *old; + + spin_lock(&unix_sk(sk)->lock); + old = rcu_dereference_protected(ctx->peer, + lockdep_is_held(&unix_sk(sk)->lock)); + l = aa_label_merge(old, label, GFP_ATOMIC); + if (l) { + if (l != old) { + rcu_assign_pointer(ctx->peer, l); + aa_put_label(old); + } else + aa_put_label(l); + } + spin_unlock(&unix_sk(sk)->lock); +} + +/* This fn is only checked if something has changed in the security + * boundaries. Otherwise cached info off file is sufficient + */ +int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, struct file *file) +{ + struct socket *sock = (struct socket *) file->private_data; + struct sockaddr_un *addr, *peer_addr; + int addrlen, peer_addrlen; + struct aa_label *plabel = NULL; + struct sock *peer_sk = NULL; + u32 sk_req = request & ~NET_PEER_MASK; + struct path path; + bool is_sk_fs; + int error = 0; + + AA_BUG(!label); + AA_BUG(!sock); + AA_BUG(!sock->sk); + AA_BUG(sock->sk->sk_family != PF_UNIX); + + /* investigate only using lock via unix_peer_get() + * addr only needs the memory barrier, but need to investigate + * path + */ + unix_state_lock(sock->sk); + peer_sk = unix_peer(sock->sk); + if (peer_sk) + sock_hold(peer_sk); + + is_sk_fs = is_unix_fs(sock->sk); + addr = aa_sunaddr(unix_sk(sock->sk), &addrlen); + path = unix_sk(sock->sk)->path; + unix_state_unlock(sock->sk); + + if (is_sk_fs && peer_sk) + sk_req = request; + if (sk_req) { + error = aa_unix_label_sk_perm(subj_cred, label, op, + sk_req, sock->sk, + is_sk_fs ? &path : NULL); + } + if (!peer_sk) + goto out; + + if (!is_sk_fs) { + bool is_peer_fs = is_unix_fs(peer_sk); + + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + if (is_peer_fs) { + struct path peer_path; + + unix_state_lock(peer_sk); + peer_path = unix_sk(peer_sk)->path; + if (peer_path.dentry) + path_get(&peer_path); + unix_state_unlock(peer_sk); + + last_error(error, + unix_fs_perm(op, request, subj_cred, label, + &peer_path)); + if (peer_path.dentry) + path_put(&peer_path); + } else { + struct aa_sk_ctx *pctx = aa_sock(peer_sk); + + rcu_read_lock(); + plabel = aa_get_newest_label(pctx->label); + rcu_read_unlock(); + /* no fs check of aa_unix_peer_perm because conditions + * above ensure they will never be done + */ + last_error(error, + xcheck(unix_peer_perm(subj_cred, label, op, + MAY_READ | MAY_WRITE, sock->sk, + is_sk_fs ? &path : NULL, + peer_addr, peer_addrlen, + NULL, plabel), + unix_peer_perm(file->f_cred, plabel, op, + MAY_READ | MAY_WRITE, peer_sk, + NULL, addr, addrlen, + is_sk_fs ? &path : NULL, + label))); + if (!error && !__aa_subj_label_is_cached(plabel, label)) + update_peer_ctx(peer_sk, pctx, label); + } + } + sock_put(peer_sk); + +out: + + /* update peer cache to latest successful perm check */ + if (error == 0) + update_sk_ctx(sock->sk, label, plabel); + aa_put_label(plabel); + + return error; +} + diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c new file mode 100644 index 000000000..5b42140e1 --- /dev/null +++ b/security/apparmor/apparmorfs.c @@ -0,0 +1,2981 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/security/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/apparmorfs.h" +#include "include/audit.h" +#include "include/cred.h" +#include "include/crypto.h" +#include "include/ipc.h" +#include "include/label.h" +#include "include/net.h" +#include "include/lib.h" +#include "include/policy.h" +#include "include/policy_ns.h" +#include "include/resource.h" +#include "include/policy_unpack.h" +#include "include/task.h" + +/* + * The apparmor filesystem interface used for policy load and introspection + * The interface is split into two main components based on their function + * a securityfs component: + * used for static files that are always available, and which allows + * userspace to specify the location of the security filesystem. + * + * fns and data are prefixed with + * aa_sfs_ + * + * an apparmorfs component: + * used loaded policy content and introspection. It is not part of a + * regular mounted filesystem and is available only through the magic + * policy symlink in the root of the securityfs apparmor/ directory. + * Tasks queries will be magically redirected to the correct portion + * of the policy tree based on their confinement. + * + * fns and data are prefixed with + * aafs_ + * + * The aa_fs_ prefix is used to indicate the fn is used by both the + * securityfs and apparmorfs filesystems. + */ + +#define IREF_POISON 101 + +/* + * support fns + */ + +struct rawdata_f_data { + struct aa_loaddata *loaddata; + DECLARE_FLEX_ARRAY(char, data); +}; + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY + +static void rawdata_f_data_free(struct rawdata_f_data *private) +{ + if (!private) + return; + + aa_put_i_loaddata(private->loaddata); + kvfree(private); +} + +static struct rawdata_f_data *rawdata_f_data_alloc(size_t size) +{ + struct rawdata_f_data *ret; + + if (size > SIZE_MAX - sizeof(*ret)) + return ERR_PTR(-EINVAL); + + ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + return ret; +} +#endif + +/** + * mangle_name - mangle a profile name to std profile layout form + * @name: profile name to mangle (NOT NULL) + * @target: buffer to store mangled name, same length as @name (MAYBE NULL) + * + * Returns: length of mangled name + */ +static int mangle_name(const char *name, char *target) +{ + char *t = target; + + while (*name == '/' || *name == '.') + name++; + + if (target) { + for (; *name; name++) { + if (*name == '/') + *(t)++ = '.'; + else if (isspace(*name)) + *(t)++ = '_'; + else if (isalnum(*name) || strchr("._-", *name)) + *(t)++ = *name; + } + + *t = 0; + } else { + int len = 0; + for (; *name; name++) { + if (isalnum(*name) || isspace(*name) || + strchr("/._-", *name)) + len++; + } + + return len; + } + + return t - target; +} + + +/* + * aafs - core fns and data for the policy tree + */ + +#define AAFS_NAME "apparmorfs" +static struct vfsmount *aafs_mnt; +static int aafs_count; + + +static int aafs_show_path(struct seq_file *seq, struct dentry *dentry) +{ + seq_printf(seq, "%s:[%llu]", AAFS_NAME, d_inode(dentry)->i_ino); + return 0; +} + +static struct aa_ns *get_ns_common_ref(struct aa_common_ref *ref) +{ + if (ref) { + struct aa_label *reflabel = container_of(ref, struct aa_label, + count); + return aa_get_ns(labels_ns(reflabel)); + } + + return NULL; +} + +static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) +{ + if (ref) + return aa_get_proxy(container_of(ref, struct aa_proxy, count)); + + return NULL; +} + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY +static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) +{ + if (ref) + return aa_get_i_loaddata(container_of(ref, struct aa_loaddata, + count)); + return NULL; +} +#endif + +static void aa_put_common_ref(struct aa_common_ref *ref) +{ + if (!ref) + return; + + switch (ref->reftype) { + case REF_RAWDATA: + aa_put_i_loaddata(container_of(ref, struct aa_loaddata, + count)); + break; + case REF_PROXY: + aa_put_proxy(container_of(ref, struct aa_proxy, + count)); + break; + case REF_NS: + /* ns count is held on its unconfined label */ + aa_put_ns(labels_ns(container_of(ref, struct aa_label, count))); + break; + default: + AA_BUG(true, "unknown refcount type"); + break; + } +} + +static void aa_get_common_ref(struct aa_common_ref *ref) +{ + kref_get(&ref->count); +} + +static void aafs_evict(struct inode *inode) +{ + struct aa_common_ref *ref = inode->i_private; + + clear_inode(inode); + aa_put_common_ref(ref); + inode->i_private = (void *) IREF_POISON; +} + +static void aafs_free_inode(struct inode *inode) +{ + if (S_ISLNK(inode->i_mode)) + kfree(inode->i_link); + free_inode_nonrcu(inode); +} + +static const struct super_operations aafs_super_ops = { + .statfs = simple_statfs, + .evict_inode = aafs_evict, + .free_inode = aafs_free_inode, + .show_path = aafs_show_path, +}; + +static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc) +{ + static struct tree_descr files[] = { {""} }; + int error; + + error = simple_fill_super(sb, AAFS_MAGIC, files); + if (error) + return error; + sb->s_op = &aafs_super_ops; + + return 0; +} + +static int apparmorfs_get_tree(struct fs_context *fc) +{ + return get_tree_single(fc, apparmorfs_fill_super); +} + +static const struct fs_context_operations apparmorfs_context_ops = { + .get_tree = apparmorfs_get_tree, +}; + +static int apparmorfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &apparmorfs_context_ops; + return 0; +} + +static struct file_system_type aafs_ops = { + .owner = THIS_MODULE, + .name = AAFS_NAME, + .init_fs_context = apparmorfs_init_fs_context, + .kill_sb = kill_anon_super, +}; + +/** + * __aafs_setup_d_inode - basic inode setup for apparmorfs + * @dir: parent directory for the dentry + * @dentry: dentry we are setting the inode up for + * @mode: permissions the file should have + * @data: data to store on inode.i_private, available in open() + * @link: if symlink, symlink target string + * @fops: struct file_operations that should be used + * @iops: struct of inode_operations that should be used + */ +static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry, + umode_t mode, void *data, char *link, + const struct file_operations *fops, + const struct inode_operations *iops) +{ + struct inode *inode = new_inode(dir->i_sb); + + AA_BUG(!dir); + AA_BUG(!dentry); + + if (!inode) + return -ENOMEM; + + inode->i_ino = get_next_ino(); + inode->i_mode = mode; + simple_inode_init_ts(inode); + inode->i_private = data; + if (S_ISDIR(mode)) { + inode->i_op = iops ? iops : &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + inc_nlink(inode); + inc_nlink(dir); + } else if (S_ISLNK(mode)) { + inode->i_op = iops ? iops : &simple_symlink_inode_operations; + inode->i_link = link; + } else { + inode->i_fop = fops; + } + d_instantiate(dentry, inode); + dget(dentry); + + return 0; +} + +/** + * aafs_create - create a dentry in the apparmorfs filesystem + * + * @name: name of dentry to create + * @mode: permissions the file should have + * @parent: parent directory for this dentry + * @data: data to store on inode.i_private, available in open() + * @link: if symlink, symlink target string + * @fops: struct file_operations that should be used for + * @iops: struct of inode_operations that should be used + * + * This is the basic "create a xxx" function for apparmorfs. + * + * Returns a pointer to a dentry if it succeeds, that must be free with + * aafs_remove(). Will return ERR_PTR on failure. + */ +static struct dentry *aafs_create(const char *name, umode_t mode, + struct dentry *parent, + struct aa_common_ref *data, void *link, + const struct file_operations *fops, + const struct inode_operations *iops) +{ + struct dentry *dentry; + struct inode *dir; + int error; + + AA_BUG(!name); + AA_BUG(!parent); + + if (!(mode & S_IFMT)) + mode = (mode & S_IALLUGO) | S_IFREG; + + error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count); + if (error) + return ERR_PTR(error); + + dir = d_inode(parent); + + dentry = simple_start_creating(parent, name); + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto fail; + } + + error = __aafs_setup_d_inode(dir, dentry, mode, data, link, fops, iops); + simple_done_creating(dentry); + if (error) + goto fail; + + if (data) + aa_get_common_ref(data); + + return dentry; + +fail: + simple_release_fs(&aafs_mnt, &aafs_count); + return ERR_PTR(error); +} + +/** + * aafs_create_file - create a file in the apparmorfs filesystem + * + * @name: name of dentry to create + * @mode: permissions the file should have + * @parent: parent directory for this dentry + * @data: data to store on inode.i_private, available in open() + * @fops: struct file_operations that should be used for + * + * see aafs_create + */ +static struct dentry *aafs_create_file(const char *name, umode_t mode, + struct dentry *parent, + struct aa_common_ref *data, + const struct file_operations *fops) +{ + return aafs_create(name, mode, parent, data, NULL, fops, NULL); +} + +/** + * aafs_create_dir - create a directory in the apparmorfs filesystem + * + * @name: name of dentry to create + * @parent: parent directory for this dentry + * + * see aafs_create + */ +static struct dentry *aafs_create_dir(const char *name, struct dentry *parent) +{ + return aafs_create(name, S_IFDIR | 0755, parent, NULL, NULL, NULL, + NULL); +} + +/** + * aafs_remove - removes a file or directory from the apparmorfs filesystem + * + * @dentry: dentry of the file/directory/symlink to removed. + */ +static void aafs_remove(struct dentry *dentry) +{ + struct inode *dir; + + if (!dentry || IS_ERR(dentry)) + return; + + /* ->d_parent is stable as rename is not supported */ + dir = d_inode(dentry->d_parent); + dentry = start_removing_dentry(dentry->d_parent, dentry); + if (!IS_ERR(dentry) && simple_positive(dentry)) { + if (d_is_dir(dentry)) { + if (!WARN_ON(!simple_empty(dentry))) { + __simple_rmdir(dir, dentry); + dput(dentry); + } + } else { + __simple_unlink(dir, dentry); + dput(dentry); + } + d_delete(dentry); + } + end_removing(dentry); + simple_release_fs(&aafs_mnt, &aafs_count); +} + + +/* + * aa_fs - policy load/replace/remove + */ + +/** + * aa_simple_write_to_buffer - common routine for getting policy from user + * @userbuf: user buffer to copy data from (NOT NULL) + * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) + * @copy_size: size of data to copy from user buffer + * @pos: position write is at in the file (NOT NULL) + * + * Returns: kernel buffer containing copy of user buffer data or an + * ERR_PTR on failure. + */ +static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, + size_t alloc_size, + size_t copy_size, + loff_t *pos) +{ + struct aa_loaddata *data; + + AA_BUG(copy_size > alloc_size); + + if (*pos != 0) + /* only writes from pos 0, that is complete writes */ + return ERR_PTR(-ESPIPE); + + /* freed by caller to simple_write_to_buffer */ + data = aa_loaddata_alloc(alloc_size); + if (IS_ERR(data)) + return data; + + data->size = copy_size; + if (copy_from_user(data->data, userbuf, copy_size)) { + /* trigger free - don't need to put pcount */ + aa_put_i_loaddata(data); + return ERR_PTR(-EFAULT); + } + + return data; +} + +#if defined(CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY) || \ + defined(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY) +static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen) +{ + if (slen < dlen) { + const size_t wksp_len = zstd_dctx_workspace_bound(); + zstd_dctx *ctx; + void *wksp; + size_t out_len; + int ret = 0; + + wksp = kvzalloc(wksp_len, GFP_KERNEL); + if (!wksp) { + ret = -ENOMEM; + goto cleanup; + } + ctx = zstd_init_dctx(wksp, wksp_len); + if (ctx == NULL) { + ret = -ENOMEM; + goto cleanup; + } + out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen); + if (zstd_is_error(out_len)) { + ret = -EINVAL; + goto cleanup; + } +cleanup: + kvfree(wksp); + return ret; + } + + if (dlen < slen) + return -EINVAL; + memcpy(dst, src, slen); + return 0; +} +#endif + + +#ifdef CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY +/** + * aa_get_data_from_compressed - common routine for getting compressed policy + * from user and get both compressed and uncompressed version. + * @userbuf: user buffer to copy data from (NOT NULL) + * @buffer_size: size of user buffer + * @pos: position write is at in the file (NOT NULL) + * @compressed_data: Ptr on compressed data. *compressed_data is allocated there + * + * Returns: kernel buffer containing copy of user buffer data or an + * ERR_PTR on failure. + */ +static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf, + size_t buffer_size, + loff_t *pos, + char **compressed_data) +{ + struct aa_loaddata *data; + zstd_frame_header header; + int error; + + if (!userbuf || !pos) + return ERR_PTR(-EINVAL); + if (*pos) + return ERR_PTR(-ESPIPE); + + *compressed_data = kvmalloc(buffer_size, GFP_KERNEL); + if (!*compressed_data) + return ERR_PTR(-ENOMEM); + if (copy_from_user(*compressed_data, userbuf, buffer_size)) { + error = -EFAULT; + goto fail; + } + error = zstd_get_frame_header(&header, *compressed_data, buffer_size); + if (error || header.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN || + header.frameContentSize == ZSTD_CONTENTSIZE_ERROR) { + error = -EINVAL; + goto fail; + } + + data = aa_loaddata_alloc(header.frameContentSize); + if (IS_ERR(data)) { + error = PTR_ERR(data); + goto fail; + } + + // We then decompress the data + error = decompress_zstd(*compressed_data, buffer_size, data->data, + header.frameContentSize); + if (error) + goto fail_decompress; + + data->size = header.frameContentSize; + return data; + +fail_decompress: + aa_put_i_loaddata(data); +fail: + kvfree(*compressed_data); + return ERR_PTR(error); + +} +#else +static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf __always_unused, + size_t buffer_size __always_unused, + loff_t *pos __always_unused, + char **compressed_data __always_unused) +{ + return ERR_PTR(-EINVAL); +} +#endif /* CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY */ + +struct aa_user_hdr { + uint8_t version; + uint8_t compress_level; + uint8_t padding[6]; /* force 8-byte alignment */ +} __packed __aligned(8); + +#define aa_hdr_magic "\x04\x08\x00\x76\x65\x72\x73\x69\x6f\x6e\x00\x02" +#define aa_hdr_magic_size 12 + +static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, + loff_t *pos, struct aa_ns *ns, + const struct cred *ocred) +{ + struct aa_loaddata *data; + struct aa_label *label; + ssize_t error; + char *compressed_data = NULL; + __le32 magic_le; + bool is_compressed; + u8 aahdr[aa_hdr_magic_size]; + bool needput; + + label = begin_current_label_crit_section(&needput); + + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask); + if (error) + goto end_section; + + /* If the policy is userspace compressed we start by decompressing it + * to make the required checks (computing hash, verifying profile, ...) + * + * Getting a userspace-compressed version then decompressing it in the + * kernel actually makes sense since zstd decompression is ~3.5x faster + * than compression. This also allow to increase the compression level. + */ + + if (size >= sizeof(__le32) && + !copy_from_user(&magic_le, buf, sizeof(magic_le)) && + le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) { + is_compressed = true; + } else if (size >= sizeof(struct aa_user_hdr) + sizeof(__le32) && + !copy_from_user(&magic_le, + buf + sizeof(struct aa_user_hdr), + sizeof(magic_le)) && + le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) { + is_compressed = true; + /* skip the userspace header if present */ + buf += sizeof(struct aa_user_hdr); + size -= sizeof(struct aa_user_hdr); + } else if (size >= sizeof(struct aa_user_hdr) + + aa_hdr_magic_size && + !copy_from_user(&aahdr, + buf + sizeof(struct aa_user_hdr), + aa_hdr_magic_size) && + memcmp(&aahdr, aa_hdr_magic, aa_hdr_magic_size) == 0) { + /* uncompressed blob with user hdr */ + buf += sizeof(struct aa_user_hdr); + size -= sizeof(struct aa_user_hdr); + is_compressed = false; + } else { + is_compressed = false; + } + if (is_compressed) { + + data = aa_get_data_from_compressed(buf, size, pos, &compressed_data); + error = PTR_ERR(data); + } else { + data = aa_simple_write_to_buffer(buf, size, size, pos); + error = PTR_ERR(data); + } + + if (!IS_ERR(data)) { + error = aa_replace_profiles(ns, label, mask, data, + compressed_data, size); + /* put pcount, which will put count and free if no + * profiles referencing it. + */ + aa_put_profile_loaddata(data); + } +end_section: + end_current_label_crit_section(label, needput); + + return error; +} + +/* .load file hook fn to load policy */ +static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, + loff_t *pos) +{ + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); + int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, + f->f_cred); + + aa_put_ns(ns); + + return error; +} + +static const struct file_operations aa_fs_profile_load = { + .write = profile_load, + .llseek = default_llseek, +}; + +/* .replace file hook fn to load and/or replace policy */ +static ssize_t profile_replace(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); + int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, + buf, size, pos, ns, f->f_cred); + aa_put_ns(ns); + + return error; +} + +static const struct file_operations aa_fs_profile_replace = { + .write = profile_replace, + .llseek = default_llseek, +}; + + +/* .remove file hook fn to remove loaded policy */ +static ssize_t profile_remove(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + struct aa_loaddata *data; + struct aa_label *label; + ssize_t error; + struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); + bool needput; + + label = begin_current_label_crit_section(&needput); + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(current_cred(), label, ns, + f->f_cred, AA_MAY_REMOVE_POLICY); + if (error) + goto out; + + /* + * aa_remove_profile needs a null terminated string so 1 extra + * byte is allocated and the copied data is null terminated. + */ + data = aa_simple_write_to_buffer(buf, size + 1, size, pos); + + error = PTR_ERR(data); + if (!IS_ERR(data)) { + data->data[size] = 0; + error = aa_remove_profiles(ns, label, data->data, size); + aa_put_profile_loaddata(data); + } + out: + end_current_label_crit_section(label, needput); + aa_put_ns(ns); + return error; +} + +static const struct file_operations aa_fs_profile_remove = { + .write = profile_remove, + .llseek = default_llseek, +}; + +struct aa_revision { + struct aa_ns *ns; + long last_read; +}; + +/* revision file hook fn for policy loads */ +static int ns_revision_release(struct inode *inode, struct file *file) +{ + struct aa_revision *rev = file->private_data; + + if (rev) { + aa_put_ns(rev->ns); + kfree(rev); + } + + return 0; +} + +static ssize_t ns_revision_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct aa_revision *rev = file->private_data; + char buffer[32]; + long last_read; + int avail; + + mutex_lock_nested(&rev->ns->lock, rev->ns->level); + last_read = rev->last_read; + if (last_read == rev->ns->revision) { + mutex_unlock(&rev->ns->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(rev->ns->wait, + last_read != + READ_ONCE(rev->ns->revision))) + return -ERESTARTSYS; + mutex_lock_nested(&rev->ns->lock, rev->ns->level); + } + + avail = sprintf(buffer, "%ld\n", rev->ns->revision); + if (*ppos + size > avail) { + rev->last_read = rev->ns->revision; + *ppos = 0; + } + mutex_unlock(&rev->ns->lock); + + return simple_read_from_buffer(buf, size, ppos, buffer, avail); +} + +static int ns_revision_open(struct inode *inode, struct file *file) +{ + struct aa_revision *rev = kzalloc_obj(*rev); + + if (!rev) + return -ENOMEM; + + rev->ns = get_ns_common_ref(inode->i_private); + if (!rev->ns) + rev->ns = aa_get_current_ns(); + file->private_data = rev; + + return 0; +} + +static __poll_t ns_revision_poll(struct file *file, poll_table *pt) +{ + struct aa_revision *rev = file->private_data; + __poll_t mask = 0; + + if (rev) { + mutex_lock_nested(&rev->ns->lock, rev->ns->level); + poll_wait(file, &rev->ns->wait, pt); + if (rev->last_read < rev->ns->revision) + mask |= EPOLLIN | EPOLLRDNORM; + mutex_unlock(&rev->ns->lock); + } + + return mask; +} + +void __aa_bump_ns_revision(struct aa_ns *ns) +{ + WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1); + wake_up_interruptible(&ns->wait); +} + +static const struct file_operations aa_fs_ns_revision_fops = { + .owner = THIS_MODULE, + .open = ns_revision_open, + .poll = ns_revision_poll, + .read = ns_revision_read, + .llseek = generic_file_llseek, + .release = ns_revision_release, +}; + +static void profile_query_cb(const struct aa_profile *profile, + struct aa_perms *perms, + const char *match_str, size_t match_len) +{ + struct aa_ruleset *rules = profile->label.rules[0]; + struct aa_perms tmp = { }; + aa_state_t state = DFA_NOMATCH; + + if (profile_unconfined(profile)) + return; + if (rules->file->dfa && *match_str == AA_CLASS_FILE) { + state = aa_dfa_match_len(rules->file->dfa, + rules->file->start[AA_CLASS_FILE], + match_str + 1, match_len - 1); + if (state) { + struct path_cond cond = { }; + + tmp = *(aa_lookup_condperms(current_fsuid(), + rules->file, state, &cond)); + } + } else if (rules->policy->dfa) { + if (!RULE_MEDIATES(rules, *match_str)) + return; /* no change to current perms */ + /* old user space does not correctly detect dbus mediation + * support so we may get dbus policy and requests when + * the abi doesn't support it. This can cause mediation + * regressions, so explicitly test for this situation. + */ + if (*match_str == AA_CLASS_DBUS && + !RULE_MEDIATES_v9NET(rules)) + return; /* no change to current perms */ + state = aa_dfa_match_len(rules->policy->dfa, + rules->policy->start[0], + match_str, match_len); + if (state) + tmp = *aa_lookup_perms(rules->policy, state); + } + aa_apply_modes_to_perms(profile, &tmp); + aa_perms_accum_raw(perms, &tmp); +} + + +/** + * query_data - queries a policy and writes its data to buf + * @buf: the resulting data is stored here (NOT NULL) + * @buf_len: size of buf + * @query: query string used to retrieve data + * @query_len: size of query including second NUL byte + * + * The buffers pointed to by buf and query may overlap. The query buffer is + * parsed before buf is written to. + * + * The query should look like "