2020-07-30 00:32:55

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE)

Overview:
------------------------------------

IPE is a Linux Security Module which allows for a configurable
policy to enforce integrity requirements on the whole system. It
attempts to solve the issue of Code Integrity: that any code being
executed (or files being read), are identical to the version that
was built by a trusted source.

The type of system for which IPE is designed for use is an embedded device
with a specific purpose (e.g. network firewall device in a data center),
where all software and configuration is built and provisioned by the owner.

Specifically, a system which leverages IPE is not intended for general
purpose computing and does not utilize any software or configuration
built by a third party. An ideal system to leverage IPE has both mutable
and immutable components, however, all binary executable code is immutable.

The scope of IPE is constrained to the OS. It is assumed that platform
firmware verifies the the kernel and optionally the root filesystem (e.g.
via U-Boot verified boot). IPE then utilizes LSM hooks to enforce a
flexible, kernel-resident integrity verification policy.

IPE differs from other LSMs which provide integrity checking (for instance,
IMA), as it has no dependency on the filesystem metadata itself. The
attributes that IPE checks are deterministic properties that exist solely
in the kernel. Additionally, IPE provides no additional mechanisms of
verifying these files (e.g. IMA Signatures) - all of the attributes of
verifying files are existing features within the kernel, such as dm-verity
or fsverity.

IPE provides a policy that allows owners of the system to easily specify
integrity requirements and uses dm-verity signatures to simplify the
authentication of allowed objects like authorized code and data.

IPE supports two modes, permissive (similar to SELinux's permissive mode)
and enforce. Permissive mode performs the same checks, and logs policy
violations as enforce mode, but will not enforce the policy. This allows
users to test policies before enforcing them.

The default mode is enforce, and can be changed via the kernel commandline
parameter `ipe.enforce=(0|1)`, or the securityfs node
`/sys/kernel/security/ipe/enforce`. The ability to switch modes can be
compiled out of the LSM via setting the config
CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH to N.

IPE additionally supports success auditing. When enabled, all events
that pass IPE policy and are not blocked will emit an audit event. This
is disabled by default, and can be enabled via the kernel commandline
`ipe.success_audit=(0|1)` or the securityfs node
`/sys/kernel/security/ipe/success_audit`.

Policies can be staged at runtime through securityfs and activated through
sysfs. Please see the Deploying Policies section of this cover letter for
more information.

The IPE LSM is compiled under CONFIG_SECURITY_IPE.

Policy:
------------------------------------

IPE policy is designed to be both forward compatible and backwards
compatible. There is one required line, at the top of the policy,
indicating the policy name, and the policy version, for instance:

policy_name="Ex Policy" policy_version=0.0.0

The policy version indicates the current version of the policy (NOT the
policy syntax version). This is used to prevent roll-back of policy to
potentially insecure previous versions of the policy.

The next portion of IPE policy, are rules. Rules are formed by key=value
pairs, known as properties. IPE rules require two properties: "action",
which determines what IPE does when it encounters a match against the
policy, and "op", which determines when that rule should be evaluated.
Thus, a minimal rule is:

op=EXECUTE action=ALLOW

This example will allow any execution. Additional properties are used to
restrict attributes about the files being evaluated. These properties are
intended to be deterministic attributes that are resident in the kernel.
Available properties for IPE described in the properties section of this
cover-letter, the repository available in Appendix A, and the kernel
documentation page.

Order does not matter for the rule's properties - they can be listed in
any order, however it is encouraged to have the "op" property be first,
and the "action" property be last, for readability.

Additionally, rules are evaluated top-to-bottom. As a result, any
revocation rules, or denies should be placed early in the file to ensure
that these rules are evaluated before a rule with "action=ALLOW" is hit.

Any unknown syntax in IPE policy will result in a fatal error to parse
the policy. User mode can interrogate the kernel to understand what
properties and the associated versions through the securityfs node,
$securityfs/ipe/property_config, which will return a string of form:

key1=version1
key2=version2
.
.
.
keyN=versionN

User-mode should correlate these versions with the supported values
identified in the documentation to determine whether a policy should
be accepted by the system.

Additionally, a DEFAULT operation must be set for all understood
operations within IPE. For policies to remain completely forwards
compatible, it is recommended that users add a "DEFAULT action=ALLOW"
and override the defaults on a per-operation basis.

For more information about the policy syntax, please see Appendix A or
the kernel documentation page.

Early Usermode Protection:
--------------------------

IPE can be provided with a policy at startup to load and enforce.
This is intended to be a minimal policy to get the system to a state
where userland is setup and ready to receive commands, at which
point a policy can be deployed via securityfs. This "boot policy" can be
specified via the config, SECURITY_IPE_BOOT_POLICY, which accepts a path
to a plain-text version of the IPE policy to apply. This policy will be
compiled into the kernel. If not specified, IPE will be disabled until a
policy is deployed and activated through the method above.

Policy Examples:
------------------------------------

Allow all:

policy_name="Allow All" policy_version=0.0.0
DEFAULT action=ALLOW

Allow only initial superblock:

policy_name="Allow All Initial SB" policy_version=0.0.0
DEFAULT action=DENY

op=EXECUTE boot_verified=TRUE action=ALLOW

Allow any signed dm-verity volume and the initial superblock:

policy_name="AllowSignedAndInitial" policy_version=0.0.0
DEFAULT action=DENY

op=EXECUTE boot_verified=TRUE action=ALLOW
op=EXECUTE dmverity_signature=TRUE action=ALLOW

Prohibit execution from a specific dm-verity volume:

policy_name="AllowSignedAndInitial" policy_version=0.0.0
DEFAULT action=DENY

op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=DENY
op=EXECUTE boot_verified=TRUE action=ALLOW
op=EXECUTE dmverity_signature=TRUE action=ALLOW

Allow only a specific dm-verity volume:

policy_name="AllowSignedAndInitial" policy_version=0.0.0
DEFAULT action=DENY

op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=ALLOW

Deploying Policies:
-------------------

Deploying policies is simple. First sign a plain text policy, with a
certificate that is present in the SYSTEM_TRUSTED_KEYRING of your test
machine. Through openssl, the signing can be done via:

openssl smime -sign -in "$MY_POLICY" -signer "$MY_CERTIFICATE" \
-inkey "$MY_PRIVATE_KEY" -binary -outform der -noattr -nodetach \
-out "$MY_POLICY.p7s"

Then, simply cat the file into the IPE's "new_policy" securityfs node:

cat "$MY_POLICY.p7s" > /sys/kernel/security/ipe/new_policy

The policy should now be present under the policies/ subdirectory, under
its "policy_name" attribute.

The policy is now present in the kernel and can be marked as active,
via the sysctl "ipe.active_policy":

echo -n 1 > "/sys/kernel/security/ipe/$MY_POLICY_NAME/active"

This will now mark the policy as active and the system will be enforcing
$MY_POLICY_NAME. At any point the policy can be updated on the provision
that the policy version to be deployed is greater than or equal to the
running version (to prevent roll-back attacks). This update can be done
by redirecting the file into the policy's "raw" node, under the policies
subdirectory:

cat "$MY_UPDATED_POLICY.p7s" > \
"/sys/kernel/security/ipe/policies/$MY_POLICY_NAME/raw"

Additionally, policies can be deleted via the "del_policy" securityfs
node. Simply write the name of the policy to be deleted to that node:

echo -n 1 >
"/sys/kernel/security/ipe/policies/$MY_POLICY_NAME/delete"

There are two requirements to delete policies:

1. The policy being deleted must not be the active policy.
2. The policy being deleted must not be the boot policy.

It's important to know above that the "echo" command will add a newline
to the end of the input, and this will be considered as part of the
filename. You can remove the newline via the -n parameter.

NOTE: If a MAC LSM is enabled, the securityfs commands will require
CAP_MAC_ADMIN. This is due to sysfs supporting fine-grained MAC
attributes, while securityfs at the current moment does not.

Properties:
------------------------------------

This initial patchset introducing IPE adds three properties:
'boot_verified', 'dmverity_signature' and 'dmverity_roothash'.

boot_verified (CONFIG_IPE_BOOT_PROP):
This property can be utilized for authorization of the first
super-block that is mounted on the system, where IPE attempts
to evaluate a file. Typically this is used for systems with
an initramfs or other initial disk, where this is unmounted before
the system becomes available, and is not covered by any other property.
The format of this property is:

boot_verified=(TRUE|FALSE)

WARNING: This property will trust any disk where the first IPE
evaluation occurs. If you do not have a startup disk that is
unpacked and unmounted (like initramfs), then it will automatically
trust the root filesystem and potentially overauthorize the entire
disk.

dmverity_roothash (CONFIG_IPE_DM_VERITY_ROOTHASH):
This property can be utilized for authorization or revocation of
specific dmverity volumes, identified via root hash. It has a
dependency on the DM_VERITY module. The format of this property is:

dmverity_roothash=<HashHexDigest>

dmverity_signature (CONFIG_IPE_DM_VERITY_SIGNATURE):
This property can be utilized for authorization of all dm-verity
volumes that have a signed roothash that chains to the system
trusted keyring. It has a dependency on the
DM_VERITY_VERIFY_ROOTHASH_SIG config. The format of this property is:

dmverity_signature=(TRUE|FALSE)

Testing:
------------------------------------

A test suite is available (Appendix B) for ease of use. For manual
instructions:

Enable IPE through the following Kconfigs:

CONFIG_SECURITY_IPE=y
CONFIG_SECURITY_IPE_BOOT_POLICY="../AllowAllInitialSB.pol"
CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH=y
CONFIG_IPE_BOOT_PROP=y
CONFIG_IPE_DM_VERITY_ROOTHASH=y
CONFIG_IPE_DM_VERITY_SIGNATURE=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS="/path/to/my/cert/list.pem"

Start a test system, that boots directly from the filesystem, without
an initrd. I recommend testing in permissive mode until all tests
pass, then switch to enforce to ensure behavior remains identical.

boot_verified:

If booted correctly, the filesystem mounted on / should be marked as
boot_verified. Verify by turning on success auditing (sysctl
ipe.success_audit=1), and run a binary. In the audit output,
`prop_boot_verified` should be `TRUE`.

To test denials, mount a temporary filesystem (mount -t tmpfs -o
size=4M tmp tmp), and copy a binary (e.g. ls) to this new
filesystem. Disable success auditing and attempt to run the file.
The file should have an audit event, but be allowed to execute in
permissive mode, and prop_boot_verified should be FALSE.

dmverity_roothash:

First, you must create a dm-verity volume. This can be done through
squashfs-tools and veritysetup (provided by cryptsetup).

Creating a squashfs volume:

mksquashfs /path/to/directory/with/executable /path/to/output.squashfs

Format the volume for use with dm-verity & save the root hash:

output_rh=$(veritysetup format output.squashfs output.hashtree | \
tee verity_out.txt | awk "/Root hash/" | \
sed -E "s/Root hash:\s+//g")

echo -n $output_rh > output.roothash

Create a two policies, filling in the appropriate fields below:

Policy 1:

policy_name="roothash-denial" policy_version=0.0.0
DEFAULT action=ALLOW
op=EXECUTE dmverity_roothash=$output_rh action=DENY

Policy 2:

policy_name="roothash-allow" policy_version=0.0.0
DEFAULT action=ALLOW
DEFAULT op=EXECUTE action=DENY

op=EXECUTE boot_verified=TRUE action=ALLOW
op=EXECUTE dmverity_roothash=$output_rh action=ALLOW

Deploy each policy, then mark the first, "roothash-denial" as active,
per the "Deploying Policies" section of this cover letter. Mount the
dm-verity volume:

veritysetup open output.squashfs output.hashtree unverified \
`cat output.roothash`

mount /dev/mapper/unverified /my/mount/point

Attempt to execute a binary in the mount point, and it should emit an
audit event for a match against the rule:

op=EXECUTE dmverity_roothash=$output_rh action=DENY

To test the second policy, perform the same steps, but this time, enable
success auditing before running the executable. The success audit event
should be a match against this rule:

op=EXECUTE dmverity_roothash=$output_rh action=ALLOW

dmverity_signature:

Follow the setup steps for dmverity_roothash. Sign the roothash via:

openssl smime -sign -in "output.roothash" -signer "$MY_CERTIFICATE" \
-inkey "$MY_PRIVATE_KEY" -binary -outform der -noattr \
-out "output.p7s"

Create a policy:

policy_name="verified" policy_version=0.0.0
DEFAULT action=DENY

op=EXECUTE boot_verified=TRUE action=ALLOW
op=EXECUTE dmverity_verified=TRUE action=ALLOW

Deploy the policy, and mark as active, per the "Deploying Policies"
section of this cover letter. Mount the dm-verity volume with
verification:

veritysetup open output.squashfs output.hashtree unverified \
`cat output.roothash` --root-hash-signature=output.p7s

mount /dev/mapper/unverified /my/mount/point

NOTE: The --root-hash-signature option was introduced in veritysetup
2.3.0

Turn on success auditing and attempt to execute a binary in the mount
point, and it should emit an audit event for a match against the rule:

op=EXECUTE dmverity_verified=TRUE action=ALLOW

To test denials, mount the dm-verity volume the same way as the
"dmverity_roothash" section, and attempt to execute a binary. Failure
should occur.

Documentation:
------------------------------------

Full documentation is available on github in IPE's master repository
(Appendix A). This is intended to be an exhaustive source of documentation
around IPE.

Additionally, there is higher level documentation in the admin-guide.

Technical diagrams are available here:

http://microsoft.github.io/ipe/technical/diagrams/

Known Gaps:
------------------------------------

IPE has two known gaps:

1. IPE cannot verify the integrity of anonymous executable memory, such as
the trampolines created by gcc closures and libffi, or JIT'd code.
Unfortunately, as this is dynamically generated code, there is no way for
IPE to detect that this code has not been tampered with in transition
from where it was built, to where it is running. As a result, IPE is
incapable of tackling this problem for dynamically generated code.
However, there is a patch series being prepared that addresses this
problem for libffi and gcc closures by implemeting a safer kernel
trampoline API.

2. IPE cannot verify the integrity of interpreted languages' programs when
these scripts invoked via `<interpreter> <file>`. This is because the way
interpreters execute these files, the scripts themselves are not
evaluated as executable code through one of IPE's hooks. Interpreters
can be enlightened to the usage of IPE by trying to mmap a file into
executable memory (+X), after opening the file and responding to the
error code appropriately. This also applies to included files, or high
value files, such as configuration files of critical system components.
This specific gap is planned on being addressed within IPE. For more
information on how we plan to address this gap, please see the Future
Development section, below.

Future Development:
------------------------------------

Support for filtering signatures by specific certificates. In this case,
our "dmverity_signature" (or a separate property) can be set to a
specific certificate declared in IPE's policy, allowing for more
controlled use-cases determine by a user's PKI structure.

Support for integrity verification for general file reads. This addresses
the script interpreter issue indicated in the "Known Gaps" section, as
these script files are typically opened with O_RDONLY. We are evaluating
whether to do this by comparing the original userland filepath passed into
the open syscall, thereby allowing existing callers to take advantage
without any code changes; the alternate design is to extend the new
openat2(2) syscall, with an new flag, tentatively called "O_VERIFY". While
the second option requires a code change for all the interpreters,
frameworks and languages that wish to leverage it, it is a wholly cleaner
implementation in the kernel. For interpreters specifically, the O_MAYEXEC
patch series published by Mickaël Salaün[1] is a similar implementation
to the O_VERIFY idea described above.

Onboarding IPE's test suite to KernelCI. Currently we are developing a
test suite in the same vein as SELinux's test suite. Once development
of the test suite is complete, and provided IPE is accepted, we intend
to onboard this test suite onto KernelCI.

Hardened resistance against roll-back attacks. Currently there exists a
window of opportunity between user-mode setup and the user-policy being
deployed, where a prior user-policy can be loaded, that is potentially
insecure. However, with a kernel update, you can revise the boot policy's
version to be the same version as the latest policy, closing this window.
In the future, I would like to close this window of opportunity without
a kernel update, using some persistent storage mechanism.

Open Issues:
------------

For linux-audit/integrity folks:
1. Introduction of new audit definitions in the kernel integrity range - is
this preferred, as opposed to reusing definitions with existing IMA
definitions?

TODOs:
------

linux-audit changes to support the new audit events.


Appendix:
------------------------------------

A. IPE Github Repository: https://github.com/microsoft/ipe
Hosted Documentation: https://microsoft.github.io/ipe
B. IPE Users' Guide: Documentation/admin-guide/LSM/ipe.rst
C. IPE Test Suite: *TBA* (under development)

References:
------------------------------------

1. https://lore.kernel.org/linux-integrity/[email protected]/

Changelog:
------------------------------------

v1: Introduced

v2:
Split the second patch of the previous series into two.
Minor corrections in the cover-letter and documentation
comments regarding CAP_MAC_ADMIN checks in IPE.

v3:
Address various comments by Jann Horn. Highlights:
Switch various audit allocators to GFP_KERNEL.
Utilize rcu_access_pointer() in various locations.
Strip out the caching system for properties
Strip comments from headers
Move functions around in patches
Remove kernel command line parameters
Reconcile the race condition on the delete node for policy by
expanding the policy critical section.

Address a few comments by Jonathan Corbet around the documentation
pages for IPE.

Fix an issue with the initialization of IPE policy with a "-0"
version, caused by not initializing the hlist entries before
freeing.

v4:
Address a concern around IPE's behavior with unknown syntax.
Specifically, make any unknown syntax a fatal error instead of a
warning, as suggested by Mickaël Salaün.
Introduce a new securityfs node, $securityfs/ipe/property_config,
which provides a listing of what properties are enabled by the
kernel and their versions. This allows usermode to predict what
policies should be allowed.
Strip some comments from c files that I missed.
Clarify some documentation comments around 'boot_verified'.
While this currently does not functionally change the property
itself, the distinction is important when IPE can enforce verified
reads. Additionally, 'KERNEL_READ' was omitted from the documentation.
This has been corrected.
Change SecurityFS and SHA1 to a reverse dependency.
Update the cover-letter with the updated behavior of unknown syntax.
Remove all sysctls, making an equivalent function in securityfs.
Rework the active/delete mechanism to be a node under the policy in
$securityfs/ipe/policies.
The kernel command line parameters ipe.enforce and ipe.success_audit
have returned as this functionality is no longer exposed through
sysfs.

v5:
Correct some grammatical errors reported by Randy Dunlap.
Fix some warnings reported by kernel test bot.
Change convention around security_bdev_setsecurity. -ENOSYS
is now expected if an LSM does not implement a particular @name,
as suggested by Casey Schaufler.
Minor string corrections related to the move from sysfs to securityfs
Correct a spelling of an #ifdef for the permissive argument.
Add the kernel parameters re-added to the documentation.
Fix a minor bug where the mode being audited on permissive switch
was the original mode, not the mode being swapped to.
Cleanup doc comments, fix some whitespace alignment issues.

v6:
Change if statement condition in security_bdev_setsecurity to be
more concise, as suggested by Casey Schaufler and Al Viro
Drop the 6th patch in the series, "dm-verity move signature check..."
due to numerous issues, and it ultimately providing no real value.
Fix the patch tree - the previous iteration appears to have been in a
torn state (patches 8+9 were merged). This has since been corrected.

Deven Bowers (11):
scripts: add ipe tooling to generate boot policy
security: add ipe lsm evaluation loop and audit system
security: add ipe lsm policy parser and policy loading
ipe: add property for trust of boot volume
fs: add security blob and hooks for block_device
dm-verity: add bdev_setsecurity hook for dm-verity signature
ipe: add property for signed dmverity volumes
dm-verity: add bdev_setsecurity hook for root-hash
ipe: add property for dmverity roothash
documentation: add ipe documentation
cleanup: uapi/linux/audit.h

Documentation/admin-guide/LSM/index.rst | 1 +
Documentation/admin-guide/LSM/ipe.rst | 508 +++++++
.../admin-guide/kernel-parameters.txt | 12 +
MAINTAINERS | 8 +
drivers/md/dm-verity-target.c | 10 +-
drivers/md/dm-verity-verify-sig.c | 14 +-
drivers/md/dm-verity-verify-sig.h | 10 +-
fs/block_dev.c | 8 +
include/linux/device-mapper.h | 3 +
include/linux/fs.h | 1 +
include/linux/lsm_hook_defs.h | 5 +
include/linux/lsm_hooks.h | 12 +
include/linux/security.h | 22 +
include/uapi/linux/audit.h | 36 +-
scripts/Makefile | 1 +
scripts/ipe/Makefile | 2 +
scripts/ipe/polgen/.gitignore | 1 +
scripts/ipe/polgen/Makefile | 7 +
scripts/ipe/polgen/polgen.c | 136 ++
security/Kconfig | 12 +-
security/Makefile | 2 +
security/ipe/.gitignore | 2 +
security/ipe/Kconfig | 48 +
security/ipe/Makefile | 33 +
security/ipe/ipe-audit.c | 303 ++++
security/ipe/ipe-audit.h | 24 +
security/ipe/ipe-blobs.c | 95 ++
security/ipe/ipe-blobs.h | 18 +
security/ipe/ipe-engine.c | 213 +++
security/ipe/ipe-engine.h | 49 +
security/ipe/ipe-hooks.c | 169 +++
security/ipe/ipe-hooks.h | 70 +
security/ipe/ipe-parse.c | 889 +++++++++++
security/ipe/ipe-parse.h | 17 +
security/ipe/ipe-pin.c | 93 ++
security/ipe/ipe-pin.h | 36 +
security/ipe/ipe-policy.c | 149 ++
security/ipe/ipe-policy.h | 69 +
security/ipe/ipe-prop-internal.h | 49 +
security/ipe/ipe-property.c | 143 ++
security/ipe/ipe-property.h | 100 ++
security/ipe/ipe-secfs.c | 1309 +++++++++++++++++
security/ipe/ipe-secfs.h | 14 +
security/ipe/ipe.c | 115 ++
security/ipe/ipe.h | 22 +
security/ipe/properties/Kconfig | 36 +
security/ipe/properties/Makefile | 13 +
security/ipe/properties/boot-verified.c | 82 ++
security/ipe/properties/dmverity-roothash.c | 153 ++
security/ipe/properties/dmverity-signature.c | 82 ++
security/ipe/properties/prop-entry.h | 38 +
security/ipe/utility.h | 32 +
security/security.c | 70 +
53 files changed, 5316 insertions(+), 30 deletions(-)
create mode 100644 Documentation/admin-guide/LSM/ipe.rst
create mode 100644 scripts/ipe/Makefile
create mode 100644 scripts/ipe/polgen/.gitignore
create mode 100644 scripts/ipe/polgen/Makefile
create mode 100644 scripts/ipe/polgen/polgen.c
create mode 100644 security/ipe/.gitignore
create mode 100644 security/ipe/Kconfig
create mode 100644 security/ipe/Makefile
create mode 100644 security/ipe/ipe-audit.c
create mode 100644 security/ipe/ipe-audit.h
create mode 100644 security/ipe/ipe-blobs.c
create mode 100644 security/ipe/ipe-blobs.h
create mode 100644 security/ipe/ipe-engine.c
create mode 100644 security/ipe/ipe-engine.h
create mode 100644 security/ipe/ipe-hooks.c
create mode 100644 security/ipe/ipe-hooks.h
create mode 100644 security/ipe/ipe-parse.c
create mode 100644 security/ipe/ipe-parse.h
create mode 100644 security/ipe/ipe-pin.c
create mode 100644 security/ipe/ipe-pin.h
create mode 100644 security/ipe/ipe-policy.c
create mode 100644 security/ipe/ipe-policy.h
create mode 100644 security/ipe/ipe-prop-internal.h
create mode 100644 security/ipe/ipe-property.c
create mode 100644 security/ipe/ipe-property.h
create mode 100644 security/ipe/ipe-secfs.c
create mode 100644 security/ipe/ipe-secfs.h
create mode 100644 security/ipe/ipe.c
create mode 100644 security/ipe/ipe.h
create mode 100644 security/ipe/properties/Kconfig
create mode 100644 security/ipe/properties/Makefile
create mode 100644 security/ipe/properties/boot-verified.c
create mode 100644 security/ipe/properties/dmverity-roothash.c
create mode 100644 security/ipe/properties/dmverity-signature.c
create mode 100644 security/ipe/properties/prop-entry.h
create mode 100644 security/ipe/utility.h

--
2.27.0


2020-07-30 00:32:59

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 10/11] documentation: add ipe documentation

Add IPE's documentation to the kernel tree.

Signed-off-by: Deven Bowers <[email protected]>
Acked-by: Jonathan Corbet <[email protected]>
---
Documentation/admin-guide/LSM/index.rst | 1 +
Documentation/admin-guide/LSM/ipe.rst | 508 ++++++++++++++++++
.../admin-guide/kernel-parameters.txt | 12 +
MAINTAINERS | 1 +
4 files changed, 522 insertions(+)
create mode 100644 Documentation/admin-guide/LSM/ipe.rst

diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index a6ba95fbaa9f..ce63be6d64ad 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -47,3 +47,4 @@ subdirectories.
tomoyo
Yama
SafeSetID
+ ipe
diff --git a/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
new file mode 100644
index 000000000000..2e6610c4a134
--- /dev/null
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -0,0 +1,508 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Integrity Policy Enforcement (IPE)
+==================================
+
+Overview
+--------
+
+IPE is a Linux Security Module which allows for a configurable policy
+to enforce integrity requirements on the whole system. It attempts to
+solve the issue of Code Integrity: that any code being executed (or
+files being read), are identical to the version that was built by a
+trusted source.
+
+There are multiple implementations within the Linux kernel that solve
+some measure of integrity verification. For instance, device-mapper
+verity, which ensures integrity for a block device, and fs-verity which
+is a system that ensures integrity for a filesystem. What these
+implementations lack is a measure of run-time verification that binaries
+are sourced from these locations. IPE aims to address this gap.
+
+IPE is separated between two major components: A configurable policy,
+provided by the LSM ("IPE Core"), and deterministic attributes provided
+by the kernel to evaluate files against, ("IPE Properties").
+
+Use Cases
+---------
+
+IPE is designed for use is an embedded device with a specific purpose
+(e.g. network firewall device in a data center), where all software and
+configuration is built and provisioned by the owner.
+
+Ideally, a system which leverages IPE is not intended for general
+purpose computing and does not utilize any software or configuration
+built by a third party. An ideal system to leverage IPE has both mutable
+and immutable components, however, all binary executable code is
+immutable.
+
+For the highest level of security, platform firmware should verify the
+the kernel and optionally the root filesystem (e.g. via U-Boot verified
+boot). This allows the entire system to be integrity verified.
+
+Known Gaps
+----------
+
+IPE cannot verify the integrity of anonymous executable memory, such as
+the trampolines created by gcc closures and libffi, or JIT'd code.
+Unfortunately, as this is dynamically generated code, there is no way
+for IPE to detect that this code has not been tampered with in
+transition from where it was built, to where it is running. As a result,
+IPE is incapable of tackling this problem for dynamically generated
+code.
+
+IPE cannot verify the integrity of interpreted languages' programs when
+these scripts invoked via ``<interpreter> <file>``. This is because the
+way interpreters execute these files, the scripts themselves are not
+evaluated as executable code through one of IPE's hooks. Interpreters
+can be enlightened to the usage of IPE by trying to mmap a file into
+executable memory (+X), after opening the file and responding to the
+error code appropriately. This also applies to included files, or high
+value files, such as configuration files of critical system components.
+This specific gap is planned on being addressed within IPE.
+
+Threat Model
+------------
+
+The threat type addressed by IPE is tampering of executable user-land
+code beyond the initially booted kernel, and the initial verification of
+kernel modules that are loaded in userland through ``modprobe`` or
+``insmod``.
+
+Tampering violates the property of integrity. IPE's role in mitigating
+this threat is to verify the integrity (and authenticity) of all
+executable code and to deny their use if integrity verification fails.
+IPE generates audit logs which may be utilized to detect integrity
+verification failures.
+
+Tampering threat scenarios include modification or replacement of
+executable code by a range of actors including:
+
+- Insiders with physical access to the hardware
+- Insiders with local network access to the system
+- Insiders with access to the deployment system
+- Compromised internal systems under external control
+- Malicious end users of the system
+- Compromised end users of the system
+- Remote (external) compromise of the system
+
+IPE does not mitigate threats arising from malicious authorized
+developers, or compromised developer tools used by authorized
+developers. Additionally, IPE draws hard security boundary between user
+mode and kernel mode. As a result, IPE does not provide any protections
+against a kernel level exploit, and a kernel-level exploit can disable
+or tamper with IPE's protections.
+
+The root of trust for all of IPE's verifications is the
+``SYSTEM_TRUSTED_KEYRING``.
+
+IPE Core
+--------
+
+IPE Policy
+~~~~~~~~~~
+
+IPE policy is designed to be both forward compatible and backwards
+compatible. There is one required line, at the top of the policy,
+indicating the policy name, and the policy version, for instance::
+
+ policy_name="Ex Policy" policy_version=0.0.0
+
+The policy name is a unique key identifying this policy in a human
+readable name. This is used to create nodes under securityfs as well as
+uniquely identify policies to deploy new policies vs update existing
+policies.
+
+The policy version indicates the current version of the policy (NOT the
+policy syntax version). This is used to prevent roll-back of policy to
+potentially insecure previous versions of the policy.
+
+The next portion of IPE policy, are rules. Rules are formed by key=value
+pairs, known as properties. IPE rules require two properties: "action",
+which determines what IPE does when it encounters a match against the
+rule, and "op", which determines when that rule should be evaluated.
+Thus, a minimal rule is::
+
+ op=EXECUTE action=ALLOW
+
+This example will allow any execution. Additional properties are used to
+restrict attributes about the files being evaluated. These properties
+are intended to be deterministic attributes that are resident in the
+kernel.
+
+Order does not matter for the rule's properties - they can be listed in
+any order, however it is encouraged to have the "op" property be first,
+and the "action" property be last for readability. Rules are evaluated
+top-to-bottom. As a result, any revocation rules, or denies should be
+placed early in the file to ensure that these rules are evaluated before
+as rule with "action=ALLOW" is hit.
+
+IPE policy is designed to be forward compatible and backwards
+compatible, thus any failure to parse a rule will result in the line
+being ignored, and a warning being emitted. If backwards compatibility
+is not required, the kernel command line parameter and sysctl,
+``ipe.strict_parse`` can be enabled, which will cause these warnings to
+be fatal.
+
+IPE policy supports comments. The character '#' will function as a
+comment, ignoring all characters to the right of '#' until the newline.
+
+The default behavior of IPE evaluations can also be expressed in policy,
+through the ``DEFAULT`` statement. This can be done at a global level,
+or a per-operation level::
+
+ # Global
+ DEFAULT action=ALLOW
+
+ # Operation Specific
+ DEFAULT op=EXECUTE action=ALLOW
+
+A default must be set for all known operations in IPE. If you want to
+preserve older policies being compatible with newer kernels that can introduce
+new operations, please set a global default of 'ALLOW', and override the
+defaults on a per-operation basis.
+
+With configurable policy-based LSMs, there's several issues with
+enforcing the configurable policies at startup, around reading and
+parsing the policy:
+
+1. The kernel *should* not read files from userland, so directly reading
+ the policy file is prohibited.
+2. The kernel command line has a character limit, and one kernel module
+ should not reserve the entire character limit for its own
+ configuration.
+3. There are various boot loaders in the kernel ecosystem, so handing
+ off a memory block would be costly to maintain.
+
+As a result, IPE has addressed this problem through a concept of a "boot
+policy". A boot policy is a minimal policy, compiled into the kernel.
+This policy is intended to get the system to a state where userland is
+setup and ready to receive commands, at which point a more complex
+policy ("user policies") can be deployed via securityfs. The boot policy
+can be specified via the Kconfig, ``SECURITY_IPE_BOOT_POLICY``, which
+accepts a path to a plain-text version of the IPE policy to apply. This
+policy will be compiled into the kernel. If not specified, IPE will be
+disabled until a policy is deployed through securityfs, and activated
+through sysfs.
+
+Deploying Policies
+^^^^^^^^^^^^^^^^^^
+
+User policies as explained above, are policies that are deployed from
+userland, through securityfs. These policies are signed to enforce some
+level of authorization of the policies (prohibiting an attacker from
+gaining root, and deploying an "allow all" policy), through the PKCS#7
+enveloped data format. These policies must be signed by a certificate
+that chains to the ``SYSTEM_TRUSTED_KEYRING``. Through openssl, the
+signing can be done via::
+
+ openssl smime -sign -in "$MY_POLICY" -signer "$MY_CERTIFICATE" \
+ -inkey "$MY_PRIVATE_KEY" -binary -outform der -noattr -nodetach \
+ -out "$MY_POLICY.p7s"
+
+Deploying the policies is done through securityfs, through the
+``new_policy`` node. To deploy a policy, simply cat the file into the
+securityfs node::
+
+ cat "$MY_POLICY.p7s" > /sys/kernel/security/ipe/new_policy
+
+Upon success, this will create one subdirectory under
+``/sys/kernel/security/ipe/policies/``. The subdirectory will be the
+``policy_name`` field of the policy deployed, so for the example above,
+the directory will be ``/sys/kernel/security/ipe/policies/Ex\ Policy``.
+Within this directory, there will be four files: ``raw``, ``content``,
+``active``, and ``delete``.
+
+The ``raw`` file is rw, reading will provide the raw PKCS#7 data that
+was provided to the kernel, representing the policy. Writing, will
+deploy an in-place policy update - if this policy is the currently
+running policy, the new updated policy will replace it immediately upon
+success.
+
+The ``content`` file is read only. Reading will provide the PKCS#7 inner
+content of the policy, which will be the plain text policy.
+
+The ``active`` file is used to set a policy as the currently active policy.
+This file is rw, and accepts a value of ``"1"`` to set the policy as active.
+Since only a single policy can be active at one time, all other policies
+will be marked inactive.
+
+Similarly, the ``cat`` command above will result in an error upon
+syntactically invalid or untrusted policies. It will also error if a
+policy already exists with the same ``policy_name``. The write to the
+``raw`` node will error upon syntactically invalid, untrusted policies,
+or if the payload fails the version check. The write will also fail if
+the ``policy_name`` in the payload does not match the existing policy.
+
+Deploying these policies will *not* cause IPE to start enforcing this
+policy. Once deployment is successful, a policy can be marked as active,
+via ``/sys/kernel/security/ipe/$policy_name/active``. IPE will enforce
+whatever policy is marked as active. For our example, we can activate
+the ``Ex Policy`` via::
+
+ echo -n 1 > "/sys/kernel/security/ipe/Ex Policy/active"
+
+At which point, ``Ex Policy`` will now be the enforced policy on the
+system.
+
+.. NOTE::
+
+ The -n parameter is important, as it strips an additional newline.
+
+IPE also provides a way to delete policies. This can be done via the
+``delete`` securityfs node, ``/sys/kernel/security/ipe/$policy_name/delete``.
+Writing ``1`` to that file will delete that node::
+
+ echo -n 1 > "/sys/kernel/security/ipe/$policy_name/delete"
+
+There are two requirements to delete policies:
+
+1. The policy being deleted must not be the active policy.
+2. The policy being deleted must not be the boot policy.
+
+.. NOTE::
+
+ If a MAC system is enabled, all writes to ipe's securityfs nodes require
+ ``CAP_MAC_ADMIN``.
+
+Modes
+~~~~~
+
+IPE supports two modes of operation: permissive (similar to SELinux's
+permissive mode) and enforce. Permissive mode performs the same checks
+as enforce mode, and logs policy violations, but will not enforce the
+policy. This allows users to test policies before enforcing them.
+
+The default mode is enforce, and can be changed via the kernel command
+line parameter ``ipe.enforce=(0|1)``, or the securityfs node
+``/sys/kernel/security/ipe/enforce``. The ability to switch modes can
+be compiled out of the LSM via setting the Kconfig
+``CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH`` to N.
+
+.. NOTE::
+
+ If a MAC system is enabled, all writes to ipe's securityfs nodes require
+ ``CAP_MAC_ADMIN``.
+
+Audit Events
+~~~~~~~~~~~~
+
+Success Auditing
+^^^^^^^^^^^^^^^^
+
+IPE supports success auditing. When enabled, all events that pass IPE
+policy and are not blocked will emit an audit event. This is disabled by
+default, and can be enabled via the kernel command line
+``ipe.success_audit=(0|1)`` or the securityfs node,
+``/sys/kernel/security/ipe/success_audit``.
+
+This is very noisy, as IPE will check every user-mode binary on the
+system, but is useful for debugging policies.
+
+.. NOTE::
+
+ If a MAC system is enabled, all writes to ipe's securityfs nodes require
+ ``CAP_MAC_ADMIN``.
+
+IPE Properties
+--------------
+
+As explained above, IPE properties are ``key=value`` pairs expressed in
+IPE policy. Two properties are built-into the policy parser: 'op' and
+'action'. The other properties are determinstic attributes to express
+across files. Currently those properties are: 'boot_verified',
+'dmverity_signature', 'dmverity_roothash'. A description of all
+properties supported by IPE are listed below:
+
+op
+~~
+
+Indicates the operation for a rule to apply to. Must be in every rule.
+IPE supports the following operations:
+
+Version 1
+^^^^^^^^^
+
+``EXECUTE``
+
+ Pertains to any file attempting to be executed, or loaded as an
+ executable.
+
+``FIRMWARE``:
+
+ Pertains to firmware being loaded via the firmware_class interface.
+ This covers both the preallocated buffer and the firmware file
+ itself.
+
+``KMODULE``:
+
+ Pertains to loading kernel modules via ``modprobe`` or ``insmod``.
+
+``KEXEC_IMAGE``:
+
+ Pertains to kernel images loading via ``kexec``.
+
+``KEXEC_INITRAMFS``
+
+ Pertains to initrd images loading via ``kexec --initrd``.
+
+``POLICY``:
+
+ Controls loading IMA policies through the
+ ``/sys/kernel/security/ima/policy`` securityfs entry.
+
+``X509_CERT``:
+
+ Controls loading IMA certificates through the Kconfigs,
+ ``CONFIG_IMA_X509_PATH`` and ``CONFIG_EVM_X509_PATH``.
+
+``KERNEL_READ``:
+
+ Short hand for all of the following: ``FIRMWARE``, ``KMODULE``,
+ ``KEXEC_IMAGE``, ``KEXEC_INITRAMFS``, ``POLICY``, and ``X509_CERT``.
+
+action
+~~~~~~
+
+Version 1
+^^^^^^^^^
+
+Determines what IPE should do when a rule matches. Must be in every
+rule. Can be one of:
+
+``ALLOW``:
+
+ If the rule matches, explicitly allow the call to proceed without
+ executing any more rules.
+
+``DENY``:
+
+ If the rule matches, explicitly prohibit the call from proceeding
+ without executing any more rules.
+
+boot_verified
+~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization of the first super-block
+that executes a file. This is almost always init. Typically this is used
+for systems with an initramfs or other initial disk, where this is unmounted
+before the system becomes available, and is not covered by any other property.
+This property is controlled by the Kconfig, ``CONFIG_IPE_BOOT_PROP``. The
+format of this property is::
+
+ boot_verified=(TRUE|FALSE)
+
+
+.. WARNING::
+
+ This property will trust any disk where the first execution occurs
+ evaluation occurs. If you do not have a startup disk that is
+ unpacked and unmounted (like initramfs), then it will automatically
+ trust the root filesystem and potentially overauthorize the entire
+ disk.
+
+dmverity_roothash
+~~~~~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization or revocation of
+specific dm-verity volumes, identified via root hash. It has a
+dependency on the DM_VERITY module. This property is controlled by the
+property: ``CONFIG_IPE_DM_VERITY_ROOTHASH``. The format of this property
+is::
+
+ dmverity_roothash=HashHexDigest
+
+dmverity_signature
+~~~~~~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization of all dm-verity volumes
+that have a signed roothash that chains to the system trusted keyring.
+It has a dependency on the ``DM_VERITY_VERIFY_ROOTHASH_SIG`` Kconfig.
+This property is controlled by the Kconfig:
+``CONFIG_IPE_DM_VERITY_SIGNATURE``. The format of this property is::
+
+ dmverity_signature=(TRUE|FALSE)
+
+Policy Examples
+---------------
+
+Allow all
+~~~~~~~~~
+
+::
+
+ policy_name="Allow All" policy_version=0.0.0
+ DEFAULT action=ALLOW
+
+Allow only initial superblock
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name="Allow All Initial SB" policy_version=0.0.0
+ DEFAULT action=DENY
+
+ op=EXECUTE boot_verified=TRUE action=ALLOW
+
+Allow any signed dm-verity volume and the initial superblock
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name="AllowSignedAndInitial" policy_version=0.0.0
+ DEFAULT action=DENY
+
+ op=EXECUTE boot_verified=TRUE action=ALLOW
+ op=EXECUTE dmverity_signature=TRUE action=ALLOW
+
+Prohibit execution from a specific dm-verity volume
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name="AllowSignedAndInitial" policy_version=0.0.0
+ DEFAULT action=DENY
+
+ op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=DENY
+ op=EXECUTE boot_verified=TRUE action=ALLOW
+ op=EXECUTE dmverity_signature=TRUE action=ALLOW
+
+Allow only a specific dm-verity volume
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name="AllowSignedAndInitial" policy_version=0.0.0
+ DEFAULT action=DENY
+
+ op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=ALLOW
+
+External Information
+--------------------
+
+Please see the github repository at: https://github.com/microsoft/ipe
+
+FAQ
+---
+
+Q: What's the difference between other LSMs which provide integrity
+verification (i.e. IMA)?
+
+A: IPE differs from other LSMs which provide integrity checking, as it
+has no dependency on the filesystem metadata itself. The attributes that
+IPE checks are deterministic properties that exist solely in the kernel.
+Additionally, IPE provides no additional mechanisms of verifying these
+files (e.g. IMA Signatures) - all of the attributes of verifying files
+are existing features within the kernel.
+
+Additionally, IPE is completely restricted to integrity. It offers no
+measurement or attestation features, which IMA addresses.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index fb95fad81c79..5309813f25f7 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1953,6 +1953,18 @@
ipcmni_extend [KNL] Extend the maximum number of unique System V
IPC identifiers from 32,768 to 16,777,216.

+ ipe.enforce= [IPE]
+ Format: <bool>
+ Determine whether IPE starts in permissive (0) or
+ enforce (1) mode. The default is enforce.
+
+ ipe.success_audit=
+ [IPE]
+ Format: <bool>
+ Start IPE with success auditing enabled, emitting
+ an audit event when a binary is allowed. The default
+ is 0.
+
irqaffinity= [SMP] Set the default irq affinity mask
The argument is a cpu list, as described above.

diff --git a/MAINTAINERS b/MAINTAINERS
index e817e4bfeae3..b448fd435712 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8584,6 +8584,7 @@ INTEGRITY POLICY ENFORCEMENT (IPE)
M: Deven Bowers <[email protected]>
L: [email protected]
S: Supported
+F: Documentation/admin-guide/LSM/ipe.rst
F: scripts/ipe/
F: security/ipe/

--
2.27.0

2020-07-30 00:33:07

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 07/11] ipe: add property for signed dmverity volumes

Allow IPE to leverage the stacked security blob infrastructure,
and enlighten IPE to the block_device security blob.

This allows IPE to have a property to express rules around a device-mapper
verity volume whose root-hash has been signed, and the signature has been
verified against the system keyring. This is property is also added in
this patch.

Signed-off-by: Deven Bowers <[email protected]>
---
security/ipe/Kconfig | 2 +-
security/ipe/Makefile | 1 +
security/ipe/ipe-blobs.c | 84 ++++++++++++++++++++
security/ipe/ipe-blobs.h | 18 +++++
security/ipe/ipe-engine.c | 4 +
security/ipe/ipe-engine.h | 9 +++
security/ipe/ipe-hooks.c | 1 +
security/ipe/ipe-hooks.h | 7 ++
security/ipe/ipe.c | 18 +++++
security/ipe/ipe.h | 2 +
security/ipe/properties/Kconfig | 10 +++
security/ipe/properties/Makefile | 1 +
security/ipe/properties/dmverity-signature.c | 82 +++++++++++++++++++
security/ipe/properties/prop-entry.h | 9 +++
security/ipe/utility.h | 10 +++
15 files changed, 257 insertions(+), 1 deletion(-)
create mode 100644 security/ipe/ipe-blobs.c
create mode 100644 security/ipe/ipe-blobs.h
create mode 100644 security/ipe/properties/dmverity-signature.c

diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index 469ef78c2f4f..11b50ef9abca 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -5,7 +5,7 @@

menuconfig SECURITY_IPE
bool "Integrity Policy Enforcement (IPE)"
- depends on SECURITY && AUDIT
+ depends on SECURITY && AUDIT && BLOCK
select SYSTEM_DATA_VERIFICATION
select SECURITYFS
select CRYPTO_SHA1
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index 7e98982c5035..98a2245b6956 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_SECURITY_IPE) += \
ipe-property.o \
ipe-hooks.o \
ipe-secfs.o \
+ ipe-blobs.o \

clean-files := ipe-bp.c

diff --git a/security/ipe/ipe-blobs.c b/security/ipe/ipe-blobs.c
new file mode 100644
index 000000000000..041d7d47b723
--- /dev/null
+++ b/security/ipe/ipe-blobs.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-engine.h"
+#include "ipe-blobs.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/device-mapper.h>
+
+/**
+ * ipe_bdev_alloc_security: Performs the initialization of IPE's security blob.
+ * @bdev: The block device to source the security blob from.
+ *
+ * The allocation is performed earlier by the LSM infrastructure,
+ * (on behalf of all LSMs) in lsm_alloc_bdev. At the moment, IPE uses
+ * this time to zero out the region of memory reserved for IPE.
+ *
+ * Return:
+ * 0 - OK
+ */
+int ipe_bdev_alloc_security(struct block_device *bdev)
+{
+ struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+ memset(bdev_sec, 0x0, sizeof(*bdev_sec));
+
+ return 0;
+}
+
+/**
+ * ipe_bdev_free_security: Frees all fields of IPE's block dev security blob.
+ * @bdev: The block device to source the security blob from.
+ *
+ * The deallocation of the blob itself is performed later by the LSM
+ * infrastructure, (on behalf of all LSMs) in lsm_free_bdev.
+ *
+ * Pointers allocated by the bdev_setsecurity hook and alloc_security
+ * hook need to be deallocated here.
+ */
+void ipe_bdev_free_security(struct block_device *bdev)
+{
+ struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+ kfree(bdev_sec->dmverity_rh_sig);
+
+ memset(bdev_sec, 0x0, sizeof(*bdev_sec));
+}
+
+/**
+ * ipe_bdev_setsecurity: Sets the a certain field of a block device security
+ * blob, based on @key.
+ * @bdev: The block device to source the security blob from.
+ * @key: The key representing the information to be stored.
+ * @value: The value to be stored.
+ * @len: The length of @value.
+ *
+ * As block-devices are a generic implementation across specific stacks,
+ * this allows information to be stored from various stacks.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+ const void *value, size_t len)
+{
+ struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+ if (!strcmp(key, DM_VERITY_SIGNATURE_SEC_NAME)) {
+ bdev_sec->dmverity_rh_sig = kmemdup(value, len, GFP_KERNEL);
+ if (!bdev_sec->dmverity_rh_sig)
+ return -ENOMEM;
+
+ bdev_sec->dmv_rh_sig_len = len;
+
+ return 0;
+ }
+
+ return -ENOSYS;
+}
diff --git a/security/ipe/ipe-blobs.h b/security/ipe/ipe-blobs.h
new file mode 100644
index 000000000000..7561f4cef558
--- /dev/null
+++ b/security/ipe/ipe-blobs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#include "ipe.h"
+
+#ifndef IPE_BLOB_H
+#define IPE_BLOB_H
+
+static inline struct ipe_bdev_blob *ipe_bdev(struct block_device *bdev)
+{
+ return bdev->security + ipe_blobs.lbs_bdev;
+}
+
+#endif /* IPE_BLOB_H */
diff --git a/security/ipe/ipe-engine.c b/security/ipe/ipe-engine.c
index 0291ced99d64..043faf64ceef 100644
--- a/security/ipe/ipe-engine.c
+++ b/security/ipe/ipe-engine.c
@@ -10,6 +10,7 @@
#include "ipe-engine.h"
#include "ipe-audit.h"
#include "ipe-pin.h"
+#include "ipe-blobs.h"
#include "utility.h"

#include <linux/types.h>
@@ -112,6 +113,9 @@ static struct ipe_engine_ctx *build_ctx(const struct file *file,
local->op = op;
local->hook = hook;

+ if (has_bdev(file))
+ local->sec_bdev = ipe_bdev(bdev(file));
+
return local;
}

diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
index d9a95674e70d..038c39a8973e 100644
--- a/security/ipe/ipe-engine.h
+++ b/security/ipe/ipe-engine.h
@@ -3,20 +3,29 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*/

+#include "ipe.h"
#include "ipe-hooks.h"

#include <linux/types.h>
#include <linux/rbtree.h>
#include <linux/fs.h>

+#include <crypto/pkcs7.h>
+
#ifndef IPE_ENGINE_H
#define IPE_ENGINE_H

+struct ipe_bdev_blob {
+ u8 *dmverity_rh_sig;
+ size_t dmv_rh_sig_len;
+};
+
struct ipe_engine_ctx {
enum ipe_op op;
enum ipe_hook hook;
const struct file *file;
const char *audit_pathname;
+ const struct ipe_bdev_blob *sec_bdev;
};

struct ipe_prop_cache {
diff --git a/security/ipe/ipe-hooks.c b/security/ipe/ipe-hooks.c
index 45efe022be04..18ab2dcd74d1 100644
--- a/security/ipe/ipe-hooks.c
+++ b/security/ipe/ipe-hooks.c
@@ -15,6 +15,7 @@
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/security.h>
+#include <linux/device-mapper.h>

/**
* ipe_on_exec: LSM hook called on the exec family of system calls.
diff --git a/security/ipe/ipe-hooks.h b/security/ipe/ipe-hooks.h
index 5e46726f2562..b2417831cfc1 100644
--- a/security/ipe/ipe-hooks.h
+++ b/security/ipe/ipe-hooks.h
@@ -60,4 +60,11 @@ int ipe_on_kernel_load_data(enum kernel_load_data_id id);

void ipe_sb_free_security(struct super_block *mnt_sb);

+int ipe_bdev_alloc_security(struct block_device *bdev);
+
+void ipe_bdev_free_security(struct block_device *bdev);
+
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+ const void *value, size_t len);
+
#endif /* IPE_HOOK_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 706ff38083c6..8a612eb62879 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -23,6 +23,9 @@ static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
LSM_HOOK_INIT(file_mprotect, ipe_on_mprotect),
LSM_HOOK_INIT(sb_free_security, ipe_sb_free_security),
+ LSM_HOOK_INIT(bdev_alloc_security, ipe_bdev_alloc_security),
+ LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security),
+ LSM_HOOK_INIT(bdev_setsecurity, ipe_bdev_setsecurity),
};

/**
@@ -40,6 +43,10 @@ static int __init ipe_load_properties(void)
if (rc != 0)
return rc;

+ rc = ipe_init_dm_verity_signature();
+ if (rc != 0)
+ return rc;
+
return rc;
}

@@ -72,9 +79,20 @@ static int __init ipe_init(void)
return rc;
}

+struct lsm_blob_sizes ipe_blobs = {
+ .lbs_cred = 0,
+ .lbs_file = 0,
+ .lbs_inode = 0,
+ .lbs_ipc = 0,
+ .lbs_msg_msg = 0,
+ .lbs_task = 0,
+ .lbs_bdev = sizeof(struct ipe_bdev_blob),
+};
+
DEFINE_LSM(ipe) = {
.name = "ipe",
.init = ipe_init,
+ .blobs = &ipe_blobs,
};

bool ipe_enforce = true;
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index af72bb574f73..a59cae2deec6 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -10,11 +10,13 @@

#include <linux/types.h>
#include <linux/fs.h>
+#include <linux/lsm_hooks.h>

#define IPE_MODE_ENFORCE "enforce"
#define IPE_MODE_PERMISSIVE "permissive"

extern bool ipe_enforce;
extern bool ipe_success_audit;
+extern struct lsm_blob_sizes ipe_blobs;

#endif /* IPE_H */
diff --git a/security/ipe/properties/Kconfig b/security/ipe/properties/Kconfig
index 75c6c6ff6cd8..4046f7e5eaef 100644
--- a/security/ipe/properties/Kconfig
+++ b/security/ipe/properties/Kconfig
@@ -13,3 +13,13 @@ config IPE_BOOT_PROP
superblock.

if unsure, answer N.
+
+config IPE_DM_VERITY_SIGNATURE
+ bool "Enable property for signature verified dm-verity volumes"
+ depends on DM_VERITY_VERIFY_ROOTHASH_SIG
+ help
+ This option enables IPE's integration with Device-Mapper Verity's
+ signed root-hashes. This enables the usage of the property,
+ "dmverity_signature" in IPE's policy.
+
+ if unsure, answer Y.
diff --git a/security/ipe/properties/Makefile b/security/ipe/properties/Makefile
index e3e7fe17cf58..6b67cbe36e31 100644
--- a/security/ipe/properties/Makefile
+++ b/security/ipe/properties/Makefile
@@ -9,3 +9,4 @@
obj-$(CONFIG_SECURITY_IPE) += properties.o

properties-$(CONFIG_IPE_BOOT_PROP) += boot-verified.o
+properties-$(CONFIG_IPE_DM_VERITY_SIGNATURE) += dmverity-signature.o
diff --git a/security/ipe/properties/dmverity-signature.c b/security/ipe/properties/dmverity-signature.c
new file mode 100644
index 000000000000..819222f226b7
--- /dev/null
+++ b/security/ipe/properties/dmverity-signature.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "../ipe.h"
+#include "../ipe-pin.h"
+#include "../ipe-property.h"
+#include "../utility.h"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include <linux/mount.h>
+
+#define PROPERTY_NAME "dmverity_signature"
+
+static void audit(struct audit_buffer *ab, bool value)
+{
+ audit_log_format(ab, "%s", (value) ? "TRUE" : "FALSE");
+}
+
+static inline void audit_rule_value(struct audit_buffer *ab,
+ const void *value)
+{
+ audit(ab, (bool)value);
+}
+
+static inline void audit_ctx(struct audit_buffer *ab,
+ const struct ipe_engine_ctx *ctx)
+{
+ bool b = has_bdev(ctx->file) && ctx->sec_bdev->dmverity_rh_sig;
+
+ audit(ab, b);
+}
+
+static bool evaluate(const struct ipe_engine_ctx *ctx,
+ const void *value)
+{
+ bool expect = (bool)value;
+
+ if (!has_bdev(ctx->file))
+ return false;
+
+ return ((bool)ctx->sec_bdev->dmverity_rh_sig) == expect;
+}
+
+static int parse(const char *val_str, void **value)
+{
+ if (strcmp("TRUE", val_str) == 0)
+ *value = (void *)true;
+ else if (strcmp("FALSE", val_str) == 0)
+ *value = (void *)false;
+ else
+ return -EBADMSG;
+
+ return 0;
+}
+
+static inline int duplicate(const void *src, void **dest)
+{
+ *dest = (void *)(bool)src;
+
+ return 0;
+}
+
+static const struct ipe_property dmv_signature = {
+ .property_name = PROPERTY_NAME,
+ .version = 1,
+ .eval = evaluate,
+ .parse = parse,
+ .rule_audit = audit_rule_value,
+ .ctx_audit = audit_ctx,
+ .dup = duplicate,
+ .free_val = NULL,
+};
+
+int ipe_init_dm_verity_signature(void)
+{
+ return ipe_register_property(&dmv_signature);
+}
diff --git a/security/ipe/properties/prop-entry.h b/security/ipe/properties/prop-entry.h
index f598dd9608b9..85366366ff0d 100644
--- a/security/ipe/properties/prop-entry.h
+++ b/security/ipe/properties/prop-entry.h
@@ -17,4 +17,13 @@ static inline int __init ipe_init_bootv(void)
int __init ipe_init_bootv(void);
#endif /* CONFIG_IPE_BOOT_PROP */

+#ifndef CONFIG_IPE_DM_VERITY_SIGNATURE
+static inline int __init ipe_init_dm_verity_signature(void)
+{
+ return 0;
+}
+#else
+int __init ipe_init_dm_verity_signature(void);
+#endif /* CONFIG_IPE_DM_VERITY_SIGNATURE */
+
#endif /* IPE_PROP_ENTRY_H */
diff --git a/security/ipe/utility.h b/security/ipe/utility.h
index a13089bb0d8f..7580f17274a3 100644
--- a/security/ipe/utility.h
+++ b/security/ipe/utility.h
@@ -19,4 +19,14 @@ static inline bool has_sb(const struct file *file)
return has_mount(file) && file->f_path.mnt->mnt_sb;
}

+static inline bool has_bdev(const struct file *file)
+{
+ return has_sb(file) && file->f_path.mnt->mnt_sb->s_bdev;
+}
+
+static inline struct block_device *bdev(const struct file *file)
+{
+ return file->f_path.mnt->mnt_sb->s_bdev;
+}
+
#endif /* IPE_UTILITY_H */
--
2.27.0

2020-07-30 00:33:50

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 02/11] security: add ipe lsm evaluation loop and audit system

Add the core logic of the IPE LSM, the evaluation loop (engine),
a portion of the audit system, and the skeleton of the policy
structure.

Signed-off-by: Deven Bowers <[email protected]>
---
MAINTAINERS | 1 +
include/uapi/linux/audit.h | 4 +
security/Kconfig | 12 +-
security/Makefile | 2 +
security/ipe/.gitignore | 2 +
security/ipe/Kconfig | 44 ++++++
security/ipe/Makefile | 25 ++++
security/ipe/ipe-audit.c | 231 +++++++++++++++++++++++++++++++
security/ipe/ipe-audit.h | 18 +++
security/ipe/ipe-engine.c | 205 +++++++++++++++++++++++++++
security/ipe/ipe-engine.h | 37 +++++
security/ipe/ipe-hooks.c | 149 ++++++++++++++++++++
security/ipe/ipe-hooks.h | 61 ++++++++
security/ipe/ipe-policy.h | 62 +++++++++
security/ipe/ipe-prop-internal.h | 37 +++++
security/ipe/ipe-property.c | 142 +++++++++++++++++++
security/ipe/ipe-property.h | 99 +++++++++++++
security/ipe/ipe.c | 67 +++++++++
security/ipe/ipe.h | 20 +++
19 files changed, 1212 insertions(+), 6 deletions(-)
create mode 100644 security/ipe/.gitignore
create mode 100644 security/ipe/Kconfig
create mode 100644 security/ipe/Makefile
create mode 100644 security/ipe/ipe-audit.c
create mode 100644 security/ipe/ipe-audit.h
create mode 100644 security/ipe/ipe-engine.c
create mode 100644 security/ipe/ipe-engine.h
create mode 100644 security/ipe/ipe-hooks.c
create mode 100644 security/ipe/ipe-hooks.h
create mode 100644 security/ipe/ipe-policy.h
create mode 100644 security/ipe/ipe-prop-internal.h
create mode 100644 security/ipe/ipe-property.c
create mode 100644 security/ipe/ipe-property.h
create mode 100644 security/ipe/ipe.c
create mode 100644 security/ipe/ipe.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d29c2e4612e4..e817e4bfeae3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8585,6 +8585,7 @@ M: Deven Bowers <[email protected]>
L: [email protected]
S: Supported
F: scripts/ipe/
+F: security/ipe/

INTEL 810/815 FRAMEBUFFER DRIVER
M: Antonino Daplas <[email protected]>
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 9b6a973f4cc3..5a634cca1d42 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -154,6 +154,10 @@
#define AUDIT_INTEGRITY_RULE 1805 /* policy rule */
#define AUDIT_INTEGRITY_EVM_XATTR 1806 /* New EVM-covered xattr */
#define AUDIT_INTEGRITY_POLICY_RULE 1807 /* IMA policy rules */
+#define AUDIT_INTEGRITY_POLICY_LOAD 1808 /* IPE Policy Load */
+#define AUDIT_INTEGRITY_POLICY_ACTIVATE 1809 /* IPE Policy Activation */
+#define AUDIT_INTEGRITY_EVENT 1810 /* IPE Evaluation Event */
+#define AUDIT_INTEGRITY_MODE 1811 /* IPE Mode Switch */

#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */

diff --git a/security/Kconfig b/security/Kconfig
index cd3cc7da3a55..94924556b637 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -238,6 +238,7 @@ source "security/loadpin/Kconfig"
source "security/yama/Kconfig"
source "security/safesetid/Kconfig"
source "security/lockdown/Kconfig"
+source "security/ipe/Kconfig"

source "security/integrity/Kconfig"

@@ -277,11 +278,11 @@ endchoice

config LSM
string "Ordered list of enabled LSMs"
- default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
- default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
- default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
- default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
- default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
+ default "lockdown,yama,loadpin,ipe,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
+ default "lockdown,yama,loadpin,ipe,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
+ default "lockdown,yama,loadpin,ipe,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
+ default "lockdown,yama,loadpin,ipe,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
+ default "lockdown,yama,loadpin,ipe,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
help
A comma-separated list of LSMs, in initialization order.
Any LSMs left off this list will be ignored. This can be
@@ -292,4 +293,3 @@ config LSM
source "security/Kconfig.hardening"

endmenu
-
diff --git a/security/Makefile b/security/Makefile
index 3baf435de541..48bd063d66e1 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -13,6 +13,7 @@ subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
subdir-$(CONFIG_BPF_LSM) += bpf
+subdir-$(CONFIG_SECURITY_IPE) += ipe

# always enable default capabilities
obj-y += commoncap.o
@@ -32,6 +33,7 @@ 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_IPE) += ipe/

# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
diff --git a/security/ipe/.gitignore b/security/ipe/.gitignore
new file mode 100644
index 000000000000..bbf824e665d7
--- /dev/null
+++ b/security/ipe/.gitignore
@@ -0,0 +1,2 @@
+# Generated Boot Policy
+ipe-bp.c
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
new file mode 100644
index 000000000000..7615109a19ca
--- /dev/null
+++ b/security/ipe/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Integrity Policy Enforcement (IPE) configuration
+#
+
+menuconfig SECURITY_IPE
+ bool "Integrity Policy Enforcement (IPE)"
+ depends on SECURITY && AUDIT
+ select SYSTEM_DATA_VERIFICATION
+ help
+ This option enables the Integrity Policy Enforcement subsystem
+ allowing systems to enforce integrity having no dependencies
+ on filesystem metadata, making its decisions based off of kernel-
+ resident features and data structures. A key feature of IPE is a
+ customizable policy to allow admins to reconfigure integrity
+ requirements on the fly.
+
+ If unsure, answer N.
+
+if SECURITY_IPE
+
+config SECURITY_IPE_BOOT_POLICY
+ string "Integrity policy to apply on system startup"
+ help
+ This option specifies a filepath to a IPE policy that is compiled
+ into the kernel. This policy will be enforced until a policy update
+ is deployed via the $securityfs/ipe/policies/$policy_name/active
+ interface.
+
+ If unsure, leave blank.
+
+config SECURITY_IPE_PERMISSIVE_SWITCH
+ bool "Enable the ability to switch IPE to permissive mode"
+ default y
+ help
+ This option enables two ways of switching IPE to permissive mode,
+ a securityfs node, `$securityfs/ipe/enforce`, or a kernel command line
+ parameter, `ipe.enforce`. If either of these is set to 0, files
+ will be subject to IPE's policy, audit messages will be logged, but
+ the policy will not be enforced.
+
+ If unsure, answer Y.
+
+endif
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
new file mode 100644
index 000000000000..40565b73fac2
--- /dev/null
+++ b/security/ipe/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) Microsoft Corporation. All rights reserved.
+#
+# Makefile for building the IPE module as part of the kernel tree.
+#
+
+quiet_cmd_polgen = IPE_POL $(patsubst "%",%,$(2))
+ cmd_polgen = scripts/ipe/polgen/polgen security/ipe/ipe-bp.c $(2)
+
+$(eval $(call config_filename,SECURITY_IPE_BOOT_POLICY))
+
+targets += ipe-bp.c
+$(obj)/ipe-bp.c: scripts/ipe/polgen/polgen $(SECURITY_IPE_BOOT_POLICY_FILENAME) FORCE
+ $(call if_changed,polgen,$(SECURITY_IPE_BOOT_POLICY_FILENAME))
+
+obj-$(CONFIG_SECURITY_IPE) += \
+ ipe.o \
+ ipe-audit.o \
+ ipe-bp.o \
+ ipe-engine.o \
+ ipe-property.o \
+ ipe-hooks.o \
+
+clean-files := ipe-bp.c
diff --git a/security/ipe/ipe-audit.c b/security/ipe/ipe-audit.c
new file mode 100644
index 000000000000..2c754851bd40
--- /dev/null
+++ b/security/ipe/ipe-audit.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-audit.h"
+#include "ipe-engine.h"
+#include "ipe-prop-internal.h"
+
+#include <linux/types.h>
+#include <linux/audit.h>
+#include <linux/rcupdate.h>
+#include <linux/lsm_audit.h>
+#include <linux/rbtree.h>
+#include <crypto/hash.h>
+#include <crypto/sha1_base.h>
+
+#define ACTION_STR(a) ((a) == ipe_action_allow ? "ALLOW" : "DENY")
+
+#define IPE_UNKNOWN "UNKNOWN"
+
+/* Keep in sync with ipe_op in ipe-hooks.h */
+const char *audit_op_names[] = {
+ IPE_OP_EXECUTE,
+ IPE_OP_FIRMWARE,
+ IPE_OP_KEXEC_IMAGE,
+ IPE_OP_KEXEC_INITRAMFS,
+ IPE_OP_X509_CERTIFICATE,
+ IPE_OP_POLICY,
+ IPE_OP_KMODULE,
+ IPE_OP_KERNEL_READ,
+ IPE_UNKNOWN,
+};
+
+/* Keep in sync with ipe_hook in ipe-hooks.h */
+const char *audit_hook_names[] = {
+ IPE_HOOK_EXEC,
+ IPE_HOOK_MMAP,
+ IPE_HOOK_MPROTECT,
+ IPE_HOOK_KERNEL_READ,
+ IPE_HOOK_KERNEL_LOAD,
+ IPE_UNKNOWN,
+};
+
+/**
+ * ipe_audit_mode: Emit an audit event indicating what mode IPE is currently
+ * in.
+ *
+ * This event is of form "IPE mode=(enforce|audit)"
+ */
+void ipe_audit_mode(bool enforcing)
+{
+ struct audit_buffer *ab;
+ const char *mode_str = (enforcing) ? IPE_MODE_ENFORCE :
+ IPE_MODE_PERMISSIVE;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL,
+ AUDIT_INTEGRITY_MODE);
+ if (!ab)
+ return;
+
+ audit_log_format(ab, "IPE mode=%s", mode_str);
+
+ audit_log_end(ab);
+}
+
+/**
+ * audit_engine_ctx: Add the string representation of ipe_engine_ctx to the
+ * end of an audit buffer.
+ * @ab: the audit buffer to append the string representation of @ctx
+ * @ctx: the ipe_engine_ctx structure to transform into a string
+ * representation
+ *
+ * This string representation is of form:
+ * "ctx_pid=%d ctx_op=%s ctx_hook=%s ctx_comm=%s ctx_audit_pathname=%s ctx_ino=%ld ctx_dev=%s"
+ *
+ * Certain fields may be omitted or replaced with ERR(%d).
+ *
+ */
+static void audit_engine_ctx(struct audit_buffer *ab,
+ const struct ipe_engine_ctx *ctx)
+{
+ audit_log_format(ab, "ctx_pid=%d ctx_op=%s ctx_hook=%s ctx_comm=",
+ task_tgid_nr(current),
+ audit_op_names[ctx->op],
+ audit_hook_names[ctx->hook]);
+
+ audit_log_untrustedstring(ab, current->comm);
+
+ if (ctx->file) {
+ if (IS_ERR(ctx->audit_pathname)) {
+ audit_log_format(ab, " ctx_audit_pathname=ERR(%ld) ",
+ PTR_ERR(ctx->audit_pathname));
+ } else {
+ audit_log_format(ab, " ctx_audit_pathname=\"%s\" ",
+ ctx->audit_pathname);
+ }
+
+ audit_log_format(ab, "ctx_ino=%ld ctx_dev=%s",
+ ctx->file->f_inode->i_ino,
+ ctx->file->f_inode->i_sb->s_id);
+ }
+}
+
+struct prop_audit_ctx {
+ struct audit_buffer *ab;
+ const struct ipe_engine_ctx *ctx;
+};
+
+/**
+ * audit_property: callback to print a property, used with ipe_for_each_prop.
+ * @prop: property to print an audit record for.
+ * @ctx: context passed to ipe_for_each_prop. In this case, it is of type
+ * prop_audit_ctx, containing the audit buffer and engine ctx.
+ *
+ * Return:
+ * 0 - Always
+ */
+static int audit_property(const struct ipe_property *prop, void *ctx)
+{
+ const struct prop_audit_ctx *aud_ctx = (struct prop_audit_ctx *)ctx;
+
+ audit_log_format(aud_ctx->ab, "prop_%s=", prop->property_name);
+ prop->ctx_audit(aud_ctx->ab, aud_ctx->ctx);
+ audit_log_format(aud_ctx->ab, " ");
+
+ return 0;
+}
+
+/**
+ * audit_eval_properties: Append the string representation of evaluated
+ * properties to an audit buffer.
+ * @ab: the audit buffer to append the string representation of the evaluated
+ * properties.
+ * @ctx: the ipe_engine_ctx structure to pass to property audit function.
+ *
+ * This string representation is of form:
+ * "prop_key1=value1 prop_key2=value2 ... "
+ *
+ * Certain values may be replaced with ERR(%d). Prop may also be empty,
+ * and thus omitted entirely.
+ *
+ */
+static inline void audit_eval_properties(struct audit_buffer *ab,
+ const struct ipe_engine_ctx *ctx)
+{
+ const struct prop_audit_ctx aud_ctx = {
+ .ab = ab,
+ .ctx = ctx
+ };
+
+ (void)ipe_for_each_prop(audit_property, (void *)&aud_ctx);
+}
+
+/**
+ * audit_rule: Add the string representation of a non-default IPE rule to the
+ * end of an audit buffer.
+ * @ab: the audit buffer to append the string representation of a rule.
+ * @rule: the ipe_rule structure to transform into a string representation.
+ *
+ * This string representation is of form:
+ * 'rule="op=%s key1=value1 key2=value2 ... action=%s"'
+ *
+ * Certain values may be replaced with ERR(%d).
+ *
+ */
+static void audit_rule(struct audit_buffer *ab,
+ const struct ipe_rule *rule)
+{
+ struct ipe_prop_container *ptr;
+
+ audit_log_format(ab, "rule=\"op=%s ", audit_op_names[rule->op]);
+
+ list_for_each_entry(ptr, &rule->props, next) {
+ audit_log_format(ab, "%s=", ptr->prop->property_name);
+
+ ptr->prop->rule_audit(ab, ptr->value);
+
+ audit_log_format(ab, " ");
+ }
+
+ audit_log_format(ab, "action=%s\"", ACTION_STR(rule->action));
+}
+
+/**
+ * ipe_audit_match: Emit an audit event indicating that the IPE engine has
+ * determined a match to a rule in IPE policy.
+ * @ctx: the engine context structure to audit
+ * @rule: The rule that was matched. If NULL, then assumed to be a default
+ * either operation specific, indicated by table, or global.
+ * @table: the operation-specific rule table. If NULL, then it assumed
+ * that the global default is matched.
+ * @match_type: The type of match that the engine used during evaluation
+ * @action: The action that the engine decided to take
+ * @rule: The rule that was matched. Must be set if @match_type is
+ * ipe_match_rule and NULL otherwise.
+ */
+void ipe_audit_match(const struct ipe_engine_ctx *ctx,
+ enum ipe_match match_type, enum ipe_action action,
+ const struct ipe_rule *rule)
+{
+ struct audit_buffer *ab;
+
+ if (!ipe_success_audit && action == ipe_action_allow)
+ return;
+
+ ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
+ AUDIT_INTEGRITY_EVENT);
+ if (!ab)
+ return;
+
+ audit_log_format(ab, "IPE ");
+
+ audit_engine_ctx(ab, ctx);
+
+ audit_log_format(ab, " ");
+
+ audit_eval_properties(ab, ctx);
+
+ if (match_type == ipe_match_rule)
+ audit_rule(ab, rule);
+ else if (match_type == ipe_match_table)
+ audit_log_format(ab, "rule=\"DEFAULT op=%s action=%s\"",
+ audit_op_names[ctx->op], ACTION_STR(action));
+ else if (match_type == ipe_match_global)
+ audit_log_format(ab, "rule=\"DEFAULT action=%s\"",
+ ACTION_STR(action));
+
+ audit_log_end(ab);
+}
diff --git a/security/ipe/ipe-audit.h b/security/ipe/ipe-audit.h
new file mode 100644
index 000000000000..e00f415bed2c
--- /dev/null
+++ b/security/ipe/ipe-audit.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-engine.h"
+#include "ipe-policy.h"
+
+#ifndef IPE_AUDIT_H
+#define IPE_AUDIT_H
+
+void ipe_audit_mode(bool enforcing);
+
+void ipe_audit_match(const struct ipe_engine_ctx *ctx,
+ enum ipe_match match_type, enum ipe_action action,
+ const struct ipe_rule *rule);
+
+#endif /* IPE_AUDIT_H */
diff --git a/security/ipe/ipe-engine.c b/security/ipe/ipe-engine.c
new file mode 100644
index 000000000000..ac526d4ea5e6
--- /dev/null
+++ b/security/ipe/ipe-engine.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-property.h"
+#include "ipe-prop-internal.h"
+#include "ipe-policy.h"
+#include "ipe-engine.h"
+#include "ipe-audit.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/rcupdate.h>
+#include <linux/security.h>
+
+const struct ipe_policy *ipe_active_policy;
+
+/**
+ * get_audit_pathname: Return the absolute path of the file struct passed in
+ * @file: file to derive an absolute path from.
+ *
+ * This function walks past chroots and mount points.
+ *
+ * Return:
+ * !NULL - OK
+ * ERR_PTR(-ENOENT) - No File
+ * ERR_PTR(-ENOMEM) - No Memory
+ * ERR_PTR(-ENAMETOOLONG) - Path Exceeds PATH_MAX
+ */
+static char *get_audit_pathname(const struct file *file)
+{
+ int rc = 0;
+ char *pos = NULL;
+ char *pathbuf = NULL;
+ char *temp_path = NULL;
+
+ /* No File to get Path From */
+ if (!file)
+ return ERR_PTR(-ENOENT);
+
+ pathbuf = __getname();
+ if (!pathbuf)
+ return ERR_PTR(-ENOMEM);
+
+ pos = d_absolute_path(&file->f_path, pathbuf, PATH_MAX);
+ if (IS_ERR(pos)) {
+ rc = PTR_ERR(pos);
+ goto err;
+ }
+
+ temp_path = __getname();
+ if (!temp_path) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ strlcpy(temp_path, pos, PATH_MAX);
+
+ __putname(pathbuf);
+
+ return temp_path;
+
+err:
+ __putname(pathbuf);
+ return ERR_PTR(rc);
+}
+
+/**
+ * free_ctx: free a previously allocated ipe_engine_ctx struct
+ * @ctx: structure to deallocate.
+ *
+ */
+static void free_ctx(struct ipe_engine_ctx *ctx)
+{
+ if (IS_ERR_OR_NULL(ctx))
+ return;
+
+ if (!IS_ERR_OR_NULL(ctx->audit_pathname))
+ __putname(ctx->audit_pathname);
+
+ kfree(ctx);
+}
+
+/**
+ * build_ctx: allocate a new ipe_engine_ctx structure
+ * @file: File that is being evaluated against IPE policy.
+ * @op: Operation that the file is being evaluated against.
+ * @hook: Specific hook that the file is being evaluated through.
+ *
+ * Return:
+ * !NULL - OK
+ * ERR_PTR(-ENOMEM) - no memory
+ */
+static struct ipe_engine_ctx *build_ctx(const struct file *file,
+ enum ipe_op op, enum ipe_hook hook)
+{
+ struct ipe_engine_ctx *local;
+
+ local = kzalloc(sizeof(*local), GFP_KERNEL);
+ if (!local)
+ return ERR_PTR(-ENOMEM);
+
+ /* if there's an error here, it's O.K. */
+ local->audit_pathname = get_audit_pathname(file);
+ local->file = file;
+ local->op = op;
+ local->hook = hook;
+
+ return local;
+}
+
+/**
+ * evaluate: Process an @ctx against IPE's current active policy.
+ * @ctx: the engine ctx to perform an evaluation on.
+ *
+ * Return:
+ * -EACCES - A match occurred against a "action=DENY" rule
+ * -ENOMEM - Out of memory
+ */
+static int evaluate(struct ipe_engine_ctx *ctx)
+{
+ int rc = 0;
+ bool match = false;
+ enum ipe_action action;
+ enum ipe_match match_type;
+ const struct ipe_rule *rule;
+ const struct ipe_policy *pol;
+ const struct ipe_rule_table *rules;
+ const struct ipe_prop_container *prop;
+
+ if (!rcu_access_pointer(ipe_active_policy))
+ return rc;
+
+ rcu_read_lock();
+
+ pol = rcu_dereference(ipe_active_policy);
+
+ rules = &pol->ops[ctx->op];
+
+ list_for_each_entry(rule, &rules->rules, next) {
+ match = true;
+
+ list_for_each_entry(prop, &rule->props, next)
+ match = match && prop->prop->eval(ctx, prop->value);
+
+ if (match)
+ break;
+ }
+
+ if (match) {
+ match_type = ipe_match_rule;
+ action = rule->action;
+ } else if (rules->def != ipe_action_unset) {
+ match_type = ipe_match_table;
+ action = rules->def;
+ rule = NULL;
+ } else {
+ match_type = ipe_match_global;
+ action = pol->def;
+ rule = NULL;
+ }
+
+ ipe_audit_match(ctx, match_type, action, rule);
+
+ if (action == ipe_action_deny)
+ rc = -EACCES;
+
+ if (ipe_enforce == 0)
+ rc = 0;
+
+ rcu_read_unlock();
+ return rc;
+}
+
+/**
+ * ipe_process_event: Perform an evaluation of @file, @op, and @hook against
+ * IPE's current active policy.
+ * @file: File that is being evaluated against IPE policy.
+ * @op: Operation that the file is being evaluated against.
+ * @hook: Specific hook that the file is being evaluated through.
+ *
+ * Return:
+ * -ENOMEM: (No Memory)
+ * -EACCES: (A match occurred against a "action=DENY" rule)
+ */
+int ipe_process_event(const struct file *file, enum ipe_op op,
+ enum ipe_hook hook)
+{
+ int rc = 0;
+ struct ipe_engine_ctx *ctx;
+
+ ctx = build_ctx(file, op, hook);
+ if (IS_ERR(ctx))
+ goto cleanup;
+
+ rc = evaluate(ctx);
+
+cleanup:
+ free_ctx(ctx);
+ return rc;
+}
diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
new file mode 100644
index 000000000000..d9a95674e70d
--- /dev/null
+++ b/security/ipe/ipe-engine.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-hooks.h"
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/fs.h>
+
+#ifndef IPE_ENGINE_H
+#define IPE_ENGINE_H
+
+struct ipe_engine_ctx {
+ enum ipe_op op;
+ enum ipe_hook hook;
+ const struct file *file;
+ const char *audit_pathname;
+};
+
+struct ipe_prop_cache {
+ struct rb_node node;
+ void *storage;
+ const struct ipe_property *prop;
+};
+
+enum ipe_match {
+ ipe_match_rule = 0,
+ ipe_match_table,
+ ipe_match_global
+};
+
+int ipe_process_event(const struct file *file, enum ipe_op op,
+ enum ipe_hook hook);
+
+#endif /* IPE_ENGINE_H */
diff --git a/security/ipe/ipe-hooks.c b/security/ipe/ipe-hooks.c
new file mode 100644
index 000000000000..071c4af23a3d
--- /dev/null
+++ b/security/ipe/ipe-hooks.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-hooks.h"
+#include "ipe-engine.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/security.h>
+
+/**
+ * ipe_on_exec: LSM hook called on the exec family of system calls.
+ * @bprm: A structure to hold arguments that are used when loading binaries,
+ * used to extract the file being executed.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_exec(struct linux_binprm *bprm)
+{
+ return ipe_process_event(bprm->file, ipe_op_execute, ipe_hook_exec);
+}
+
+/**
+ * ipe_on_mmap: LSM hook called on the mmap system call.
+ * @file: File being mapped into memory.
+ * @reqprot: Unused.
+ * @prot: A protection mapping of the memory region, calculated based on
+ * @reqprot, and the system configuration.
+ * @flags: Unused.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_mmap(struct file *file, unsigned long reqprot, unsigned long prot,
+ unsigned long flags)
+{
+ if (prot & PROT_EXEC)
+ return ipe_process_event(file, ipe_op_execute, ipe_hook_mmap);
+
+ return 0;
+}
+
+/**
+ * ipe_on_mprotect: LSM hook called on the mprotect system call
+ * @vma: A structure representing the existing memory region.
+ * @reqprot: Unused.
+ * @prot: A protection mapping of the memory region, calculated based on
+ * @reqprot, and the system configuration.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
+{
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC))
+ return ipe_process_event(vma->vm_file, ipe_op_execute,
+ ipe_hook_mprotect);
+
+ return 0;
+}
+
+/**
+ * ipe_on_kernel_read: LSM hook called on kernel_read_file.
+ * @file: File being read by the hook kernel_read_file.
+ * @id: Enumeration indicating the type of file being read.
+ *
+ * For more information, see the LSM hook, kernel_read_file.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_kernel_read(struct file *file, enum kernel_read_file_id id)
+{
+ switch (id) {
+ case READING_FIRMWARE:
+ case READING_FIRMWARE_PREALLOC_BUFFER:
+ return ipe_process_event(file, ipe_op_firmware,
+ ipe_hook_kernel_read);
+ case READING_MODULE:
+ return ipe_process_event(file, ipe_op_kmodule,
+ ipe_hook_kernel_read);
+ case READING_KEXEC_INITRAMFS:
+ return ipe_process_event(file, ipe_op_kexec_initramfs,
+ ipe_hook_kernel_read);
+ case READING_KEXEC_IMAGE:
+ return ipe_process_event(file, ipe_op_kexec_image,
+ ipe_hook_kernel_read);
+ case READING_POLICY:
+ return ipe_process_event(file, ipe_op_policy,
+ ipe_hook_kernel_read);
+ case READING_X509_CERTIFICATE:
+ return ipe_process_event(file, ipe_op_x509,
+ ipe_hook_kernel_read);
+ default:
+ return ipe_process_event(file, ipe_op_kernel_read,
+ ipe_hook_kernel_read);
+ }
+}
+
+/**
+ * ipe_on_kernel_load_data: LSM hook called on kernel_load_data.
+ * @id: Enumeration indicating what type of data is being loaded.
+ *
+ * For more information, see the LSM hook, kernel_load_data.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_kernel_load_data(enum kernel_load_data_id id)
+{
+ switch (id) {
+ case LOADING_FIRMWARE:
+ case LOADING_FIRMWARE_PREALLOC_BUFFER:
+ return ipe_process_event(NULL, ipe_op_firmware,
+ ipe_hook_kernel_load);
+ case LOADING_MODULE:
+ return ipe_process_event(NULL, ipe_op_kmodule,
+ ipe_hook_kernel_load);
+ case LOADING_KEXEC_INITRAMFS:
+ return ipe_process_event(NULL, ipe_op_kexec_initramfs,
+ ipe_hook_kernel_load);
+ case LOADING_KEXEC_IMAGE:
+ return ipe_process_event(NULL, ipe_op_kexec_image,
+ ipe_hook_kernel_load);
+ case LOADING_POLICY:
+ return ipe_process_event(NULL, ipe_op_policy,
+ ipe_hook_kernel_load);
+ case LOADING_X509_CERTIFICATE:
+ return ipe_process_event(NULL, ipe_op_x509,
+ ipe_hook_kernel_load);
+ default:
+ return ipe_process_event(NULL, ipe_op_kernel_read,
+ ipe_hook_kernel_load);
+ }
+}
diff --git a/security/ipe/ipe-hooks.h b/security/ipe/ipe-hooks.h
new file mode 100644
index 000000000000..806659b7cdbe
--- /dev/null
+++ b/security/ipe/ipe-hooks.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/binfmts.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/security.h>
+
+#ifndef IPE_HOOK_H
+#define IPE_HOOK_H
+
+#define IPE_HOOK_EXEC "EXEC"
+#define IPE_HOOK_MMAP "MMAP"
+#define IPE_HOOK_MPROTECT "MPROTECT"
+#define IPE_HOOK_KERNEL_READ "KERNEL_READ"
+#define IPE_HOOK_KERNEL_LOAD "KERNEL_LOAD"
+
+enum ipe_hook {
+ ipe_hook_exec = 0,
+ ipe_hook_mmap,
+ ipe_hook_mprotect,
+ ipe_hook_kernel_read,
+ ipe_hook_kernel_load,
+ ipe_hook_max
+};
+
+/*
+ * The sequence between ipe_op_firmware and ipe_op_kmodule
+ * must remain the same for ipe_op_kernel read to function
+ * appropriately.
+ */
+enum ipe_op {
+ ipe_op_execute = 0,
+ ipe_op_firmware,
+ ipe_op_kexec_image,
+ ipe_op_kexec_initramfs,
+ ipe_op_x509,
+ ipe_op_policy,
+ ipe_op_kmodule,
+ ipe_op_kernel_read,
+ ipe_op_max
+};
+
+int ipe_on_exec(struct linux_binprm *bprm);
+
+int ipe_on_mmap(struct file *file, unsigned long reqprot, unsigned long prot,
+ unsigned long flags);
+
+int ipe_on_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot);
+
+int ipe_on_kernel_read(struct file *file, enum kernel_read_file_id id);
+
+int ipe_on_kernel_load_data(enum kernel_load_data_id id);
+
+#endif /* IPE_HOOK_H */
diff --git a/security/ipe/ipe-policy.h b/security/ipe/ipe-policy.h
new file mode 100644
index 000000000000..c0c9f2962c92
--- /dev/null
+++ b/security/ipe/ipe-policy.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-hooks.h"
+#include "ipe-property.h"
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+
+#ifndef IPE_POLICY_H
+#define IPE_POLICY_H
+
+#define IPE_HEADER_POLICY_NAME "policy_name"
+#define IPE_HEADER_POLICY_VERSION "policy_version"
+
+extern const char *const ipe_boot_policy;
+extern const struct ipe_policy *ipe_active_policy;
+
+enum ipe_action {
+ ipe_action_unset = 0,
+ ipe_action_allow,
+ ipe_action_deny
+};
+
+struct ipe_prop_container {
+ struct list_head next;
+ void *value;
+ const struct ipe_property *prop;
+};
+
+struct ipe_rule {
+ struct list_head props;
+ struct list_head next;
+ enum ipe_action action;
+ enum ipe_op op;
+};
+
+struct ipe_rule_table {
+ struct list_head rules;
+ enum ipe_action def;
+};
+
+struct ipe_pol_ver {
+ u16 major;
+ u16 minor;
+ u16 rev;
+};
+
+struct ipe_policy {
+ char *policy_name;
+ struct ipe_pol_ver policy_version;
+ enum ipe_action def;
+
+ /* KERNEL_READ stores no data itself */
+ struct ipe_rule_table ops[ipe_op_max - 1];
+};
+
+#endif /* IPE_POLICY_H */
diff --git a/security/ipe/ipe-prop-internal.h b/security/ipe/ipe-prop-internal.h
new file mode 100644
index 000000000000..95a2081e77ee
--- /dev/null
+++ b/security/ipe/ipe-prop-internal.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-property.h"
+
+#include <linux/types.h>
+
+#ifndef IPE_PROPERTY_INTERNAL_H
+#define IPE_PROPERTY_INTERNAL_H
+
+#define IPE_PROPERTY_OPERATION "op"
+#define IPE_PROPERTY_DEFAULT "DEFAULT"
+#define IPE_PROPERTY_ACTION "action"
+
+#define IPE_OP_EXECUTE "EXECUTE"
+#define IPE_OP_FIRMWARE "FIRMWARE"
+#define IPE_OP_KEXEC_IMAGE "KEXEC_IMAGE"
+#define IPE_OP_KEXEC_INITRAMFS "KEXEC_INITRAMFS"
+#define IPE_OP_X509_CERTIFICATE "X509_CERT"
+#define IPE_OP_POLICY "POLICY"
+#define IPE_OP_KMODULE "KMODULE"
+#define IPE_OP_KERNEL_READ "KERNEL_READ"
+
+struct ipe_prop_reg {
+ struct rb_node node;
+ const struct ipe_property *prop;
+};
+
+int ipe_for_each_prop(int (*view)(const struct ipe_property *prop,
+ void *ctx),
+ void *ctx);
+
+const struct ipe_property *ipe_lookup_prop(const char *key);
+
+#endif /* IPE_PROPERTY_INTERNAL_H */
diff --git a/security/ipe/ipe-property.c b/security/ipe/ipe-property.c
new file mode 100644
index 000000000000..d4b0283f86bd
--- /dev/null
+++ b/security/ipe/ipe-property.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-prop-internal.h"
+#include "ipe-property.h"
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+
+/* global root containing all registered properties */
+struct rb_root ipe_registry_root = RB_ROOT;
+
+/**
+ * reg_lookup: Attempt to find a `prop_reg` structure with property_name @key.
+ * @key: The property_name to look for in the tree.
+ *
+ * Return:
+ * ipe_prop_reg structure - OK
+ * NULL - No such property exists
+ */
+static struct ipe_prop_reg *reg_lookup(const char *key)
+{
+ struct rb_node *n = ipe_registry_root.rb_node;
+
+ while (n) {
+ int r;
+ struct ipe_prop_reg *reg =
+ container_of(n, struct ipe_prop_reg, node);
+
+ r = strcmp(reg->prop->property_name, key);
+ if (r == 0)
+ return reg;
+ else if (r > 0)
+ n = n->rb_right;
+ else
+ n = n->rb_left;
+ }
+
+ return NULL;
+}
+
+/**
+ * ipe_lookup_prop: Attempt to find a ipe_property structure by name @key.
+ * @key: The property_name to look for in the tree.
+ *
+ * Return:
+ * ipe_property structure - OK
+ * NULL - No property exists under @key
+ */
+const struct ipe_property *ipe_lookup_prop(const char *key)
+{
+ struct ipe_prop_reg *reg = reg_lookup(key);
+
+ if (!reg)
+ return NULL;
+
+ return reg->prop;
+}
+
+/**
+ * ipe_register_property: Insert a property into the registration system.
+ * @prop: Read-only property structure containing the property_name, as well
+ * as the necessary function pointers for a property.
+ *
+ * The caller needs to maintain the lifetime of @prop throughout the life of
+ * the system, after calling ipe_register_property.
+ *
+ * All necessary properties need to be loaded via this method before
+ * loading a policy, otherwise the properties will be ignored as unknown.
+ *
+ * Return:
+ * 0 - OK
+ * -EEXIST - A key exists with the name @prop->property_name
+ * -ENOMEM - Out of Memory
+ */
+int ipe_register_property(const struct ipe_property *prop)
+{
+ struct rb_node *parent = NULL;
+ struct ipe_prop_reg *new_data = NULL;
+ struct rb_node **new = &ipe_registry_root.rb_node;
+
+ while (*new) {
+ int r;
+ struct ipe_prop_reg *reg =
+ container_of(*new, struct ipe_prop_reg, node);
+
+ parent = *new;
+
+ r = strcmp(reg->prop->property_name, prop->property_name);
+ if (r == 0)
+ return -EEXIST;
+ else if (r > 0)
+ new = &((*new)->rb_right);
+ else
+ new = &((*new)->rb_left);
+ }
+
+ new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
+ if (!new_data)
+ return -ENOMEM;
+
+ new_data->prop = prop;
+
+ rb_link_node(&new_data->node, parent, new);
+ rb_insert_color(&new_data->node, &ipe_registry_root);
+
+ return 0;
+}
+
+/**
+ * ipe_for_each_prop: Iterate over all currently-registered properties
+ * calling @fn on the values, and providing @view @ctx.
+ * @view: The function to call for each property. This is given the property
+ * structure as the first argument, and @ctx as the second.
+ * @ctx: caller-specified context that is passed to the function. Can be NULL.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Proper errno as returned by @view.
+ */
+int ipe_for_each_prop(int (*view)(const struct ipe_property *prop,
+ void *ctx),
+ void *ctx)
+{
+ struct rb_node *node;
+ struct ipe_prop_reg *val;
+ int rc = 0;
+
+ for (node = rb_first(&ipe_registry_root); node; node = rb_next(node)) {
+ val = container_of(node, struct ipe_prop_reg, node);
+
+ rc = view(val->prop, ctx);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
diff --git a/security/ipe/ipe-property.h b/security/ipe/ipe-property.h
new file mode 100644
index 000000000000..cf570d52d0d2
--- /dev/null
+++ b/security/ipe/ipe-property.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-engine.h"
+
+#include <linux/types.h>
+#include <linux/lsm_audit.h>
+
+#ifndef IPE_PROPERTY_H
+#define IPE_PROPERTY_H
+
+/**
+ * ipe_property_evaluator: Determines whether a file subject matches the
+ * property.
+ * @value: Value to compare against for a match
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ * allocations are prohibited.
+ *
+ * Return:
+ * true - The property matches evaluation
+ * false - The property does not match evaluation
+ */
+typedef bool (*ipe_property_evaluator)(const struct ipe_engine_ctx *ctx,
+ const void *value);
+
+/**
+ * ipe_property_audit: Transform a rule value into a string representation.
+ * @ab: Audit buffer to add the string representation of @value to.
+ * @value: Value to transform into a string representation.
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ * allocations are prohibited.
+ */
+typedef void (*ipe_property_audit)(struct audit_buffer *ab, const void *value);
+
+/**
+ * ipe_ctx_audit: Called by the auditing to provide the values
+ * that were evaluated about the subject, @ctx->file, to determine how
+ * a value was evaluated.
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ * allocations are prohibited.
+ *
+ * @ab: Audit buffer to add the string representation of @value to.
+ * @value: Value to transform into a string representation.
+ *
+ */
+typedef void (*ipe_ctx_audit)(struct audit_buffer *ab,
+ const struct ipe_engine_ctx *ctx);
+
+/**
+ * ipe_parse_value: Transform a string representation of a rule into an
+ * internal ipe data-structure, opaque to the engine.
+ * @val_str: String-value parsed by the policy parser.
+ * @value: Valid-pointer indicating address to store parsed value.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - ERR, use Standard Return Codes
+ */
+typedef int(*ipe_parse_value)(const char *val_str, void **value);
+
+/**
+ * ipe_dup_val: Called by the policy parser to make duplicate properties for
+ * pseudo-properties like "KERNEL_READ".
+ * @src: Value to copy.
+ * @dest: Pointer to the destination where the value should be copied.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - ERR, use Standard Return Codes
+ */
+typedef int (*ipe_dup_val)(const void *src, void **dest);
+
+/**
+ * ipe_free_value: Free a policy value, created by ipe_parse_value.
+ * @value: Valid-pointer to the value to be interpreted and
+ * freed by the property.
+ *
+ * Optional, can be NULL - in which case, this will not be called.
+ */
+typedef void (*ipe_free_value)(void **value);
+
+struct ipe_property {
+ const char *const property_name;
+ ipe_property_evaluator eval;
+ ipe_property_audit rule_audit;
+ ipe_ctx_audit ctx_audit;
+ ipe_parse_value parse;
+ ipe_dup_val dup;
+ ipe_free_value free_val;
+};
+
+int ipe_register_property(const struct ipe_property *prop);
+
+#endif /* IPE_PROPERTY_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
new file mode 100644
index 000000000000..6e3b9a10813c
--- /dev/null
+++ b/security/ipe/ipe.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-policy.h"
+#include "ipe-hooks.h"
+
+#include <linux/module.h>
+#include <linux/lsm_hooks.h>
+#include <linux/sysctl.h>
+#include <linux/rcupdate.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+
+static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
+ LSM_HOOK_INIT(bprm_check_security, ipe_on_exec),
+ LSM_HOOK_INIT(mmap_file, ipe_on_mmap),
+ LSM_HOOK_INIT(kernel_read_file, ipe_on_kernel_read),
+ LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
+ LSM_HOOK_INIT(file_mprotect, ipe_on_mprotect),
+};
+
+/**
+ * ipe_init: Entry point of IPE.
+ *
+ * This is called at LSM init, which happens occurs early during kernel
+ * start up. During this phase, IPE loads the
+ * properties compiled into the kernel, and register's IPE's hooks.
+ * The boot policy is loaded later, during securityfs init, at which point
+ * IPE will start enforcing its policy.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOMEM - sysctl registration failed.
+ */
+static int __init ipe_init(void)
+{
+ pr_info("mode=%s", (ipe_enforce == 1) ? IPE_MODE_ENFORCE :
+ IPE_MODE_PERMISSIVE);
+
+ security_add_hooks(ipe_hooks, ARRAY_SIZE(ipe_hooks), "IPE");
+
+ return 0;
+}
+
+DEFINE_LSM(ipe) = {
+ .name = "ipe",
+ .init = ipe_init,
+};
+
+bool ipe_enforce = true;
+bool ipe_success_audit;
+
+#ifdef CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH
+
+/* Module Parameter for Default Behavior on Boot */
+module_param_named(enforce, ipe_enforce, bool, 0644);
+MODULE_PARM_DESC(enforce, "IPE Permissive Switch");
+
+#endif /* CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH */
+
+/* Module Parameter for Success Audit on Boot */
+module_param_named(success_audit, ipe_success_audit, bool, 0644);
+MODULE_PARM_DESC(success_audit, "IPE Audit on Success");
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
new file mode 100644
index 000000000000..af72bb574f73
--- /dev/null
+++ b/security/ipe/ipe.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef IPE_H
+#define IPE_H
+
+#define pr_fmt(fmt) "IPE " fmt "\n"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#define IPE_MODE_ENFORCE "enforce"
+#define IPE_MODE_PERMISSIVE "permissive"
+
+extern bool ipe_enforce;
+extern bool ipe_success_audit;
+
+#endif /* IPE_H */
--
2.27.0

2020-07-30 00:34:16

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 09/11] ipe: add property for dmverity roothash

Add a property to allow IPE policy to express rules around a specific
root-hash of a dm-verity volume.

This can be used for revocation, (when combined with the previous dm-verity
property) or the authorization of a single dm-verity volume.

Signed-off-by: Deven Bowers <[email protected]>
---
security/ipe/ipe-blobs.c | 11 ++
security/ipe/ipe-engine.h | 3 +
security/ipe/ipe.c | 4 +
security/ipe/properties/Kconfig | 13 +-
security/ipe/properties/Makefile | 1 +
security/ipe/properties/dmverity-roothash.c | 153 ++++++++++++++++++++
security/ipe/properties/prop-entry.h | 9 ++
7 files changed, 193 insertions(+), 1 deletion(-)
create mode 100644 security/ipe/properties/dmverity-roothash.c

diff --git a/security/ipe/ipe-blobs.c b/security/ipe/ipe-blobs.c
index 041d7d47b723..6a09d5c6dea8 100644
--- a/security/ipe/ipe-blobs.c
+++ b/security/ipe/ipe-blobs.c
@@ -46,6 +46,7 @@ void ipe_bdev_free_security(struct block_device *bdev)
struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);

kfree(bdev_sec->dmverity_rh_sig);
+ kfree(bdev_sec->dmverity_rh);

memset(bdev_sec, 0x0, sizeof(*bdev_sec));
}
@@ -80,5 +81,15 @@ int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
return 0;
}

+ if (!strcmp(key, DM_VERITY_ROOTHASH_SEC_NAME)) {
+ bdev_sec->dmverity_rh = kmemdup(value, len, GFP_KERNEL);
+ if (!bdev_sec->dmverity_rh)
+ return -ENOMEM;
+
+ bdev_sec->rh_size = len;
+
+ return 0;
+ }
+
return -ENOSYS;
}
diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
index 038c39a8973e..696baaa423ff 100644
--- a/security/ipe/ipe-engine.h
+++ b/security/ipe/ipe-engine.h
@@ -18,6 +18,9 @@
struct ipe_bdev_blob {
u8 *dmverity_rh_sig;
size_t dmv_rh_sig_len;
+
+ u8 *dmverity_rh;
+ size_t rh_size;
};

struct ipe_engine_ctx {
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 8a612eb62879..8f4dfb8c547f 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -47,6 +47,10 @@ static int __init ipe_load_properties(void)
if (rc != 0)
return rc;

+ rc = ipe_init_dm_verity_rh();
+ if (rc != 0)
+ return rc;
+
return rc;
}

diff --git a/security/ipe/properties/Kconfig b/security/ipe/properties/Kconfig
index 4046f7e5eaef..4f09092522d9 100644
--- a/security/ipe/properties/Kconfig
+++ b/security/ipe/properties/Kconfig
@@ -14,8 +14,19 @@ config IPE_BOOT_PROP

if unsure, answer N.

+config IPE_DM_VERITY_ROOTHASH
+ bool "Enable property for authorizing dm-verity volumes via root-hash"
+ depends on DM_VERITY
+ help
+ This option enables IPE's integration with Device-Mapper Verity.
+ This enables the usage of the property "dmverity_roothash" in IPE's
+ policy. This property allows authorization or revocation via a
+ a hex-string representing the roothash of a dmverity volume.
+
+ if unsure, answer Y.
+
config IPE_DM_VERITY_SIGNATURE
- bool "Enable property for signature verified dm-verity volumes"
+ bool "Enable property for verified dm-verity volumes"
depends on DM_VERITY_VERIFY_ROOTHASH_SIG
help
This option enables IPE's integration with Device-Mapper Verity's
diff --git a/security/ipe/properties/Makefile b/security/ipe/properties/Makefile
index 6b67cbe36e31..d9a3807797f4 100644
--- a/security/ipe/properties/Makefile
+++ b/security/ipe/properties/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SECURITY_IPE) += properties.o

properties-$(CONFIG_IPE_BOOT_PROP) += boot-verified.o
properties-$(CONFIG_IPE_DM_VERITY_SIGNATURE) += dmverity-signature.o
+properties-$(CONFIG_IPE_DM_VERITY_ROOTHASH) += dmverity-roothash.o
diff --git a/security/ipe/properties/dmverity-roothash.c b/security/ipe/properties/dmverity-roothash.c
new file mode 100644
index 000000000000..09112e1af753
--- /dev/null
+++ b/security/ipe/properties/dmverity-roothash.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "../ipe.h"
+#include "../ipe-pin.h"
+#include "../ipe-property.h"
+#include "../utility.h"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <linux/kernel.h>
+
+#define PROPERTY_NAME "dmverity_roothash"
+
+struct counted_array {
+ u8 *arr;
+ size_t len;
+};
+
+static void audit(struct audit_buffer *ab, const void *value)
+{
+ const struct counted_array *a = (const struct counted_array *)value;
+
+ if (!a || a->len == 0)
+ audit_log_format(ab, "NULL");
+ else
+ audit_log_n_hex(ab, a->arr, a->len);
+}
+
+static inline void audit_rule_value(struct audit_buffer *ab,
+ const void *value)
+{
+ audit(ab, value);
+}
+
+static inline void audit_ctx(struct audit_buffer *ab,
+ const struct ipe_engine_ctx *ctx)
+{
+ struct counted_array a;
+
+ if (!has_bdev(ctx->file))
+ return audit(ab, NULL);
+
+ a.arr = ctx->sec_bdev->dmverity_rh;
+ a.len = ctx->sec_bdev->rh_size;
+
+ return audit(ab, &a);
+}
+
+static bool evaluate(const struct ipe_engine_ctx *ctx,
+ const void *value)
+{
+ const struct counted_array *a = (const struct counted_array *)value;
+
+ if (!has_bdev(ctx->file))
+ return false;
+
+ if (a->len != ctx->sec_bdev->rh_size)
+ return false;
+
+ return memcmp(a->arr, ctx->sec_bdev->dmverity_rh, a->len) == 0;
+}
+
+static int parse(const char *val_str, void **value)
+{
+ struct counted_array *arr = NULL;
+ int rv = 0;
+
+ arr = kzalloc(sizeof(*arr), GFP_KERNEL);
+ if (!arr) {
+ rv = -ENOMEM;
+ goto err;
+ }
+
+ arr->len = strlen(val_str) / 2;
+
+ arr->arr = kzalloc(arr->len, GFP_KERNEL);
+ if (!arr->arr) {
+ rv = -ENOMEM;
+ goto err;
+ }
+
+ rv = hex2bin(arr->arr, val_str, arr->len);
+ if (rv != 0)
+ goto err;
+
+ *value = arr;
+ return rv;
+err:
+ if (arr)
+ kfree(arr->arr);
+ kfree(arr);
+ return rv;
+}
+
+static int duplicate(const void *src, void **dest)
+{
+ struct counted_array *arr = NULL;
+ const struct counted_array *src_arr = src;
+ int rv = 0;
+
+ arr = kmemdup(src_arr, sizeof(*arr), GFP_KERNEL);
+ if (!arr) {
+ rv = -ENOMEM;
+ goto err;
+ }
+
+ arr->arr = kmemdup(src_arr->arr, src_arr->len, GFP_KERNEL);
+ if (!arr->arr) {
+ rv = -ENOMEM;
+ goto err;
+ }
+
+ *dest = arr;
+ return rv;
+err:
+ if (arr)
+ kfree(arr->arr);
+ kfree(arr);
+
+ return rv;
+}
+
+static void free_val(void **value)
+{
+ struct counted_array *a = (struct counted_array *)*value;
+
+ if (a)
+ kfree(a->arr);
+ kfree(a);
+ *value = NULL;
+}
+
+static const struct ipe_property dmv_roothash = {
+ .property_name = PROPERTY_NAME,
+ .version = 1,
+ .eval = evaluate,
+ .parse = parse,
+ .rule_audit = audit_rule_value,
+ .ctx_audit = audit_ctx,
+ .dup = duplicate,
+ .free_val = free_val,
+};
+
+int ipe_init_dm_verity_rh(void)
+{
+ return ipe_register_property(&dmv_roothash);
+}
diff --git a/security/ipe/properties/prop-entry.h b/security/ipe/properties/prop-entry.h
index 85366366ff0d..86a360570f3b 100644
--- a/security/ipe/properties/prop-entry.h
+++ b/security/ipe/properties/prop-entry.h
@@ -26,4 +26,13 @@ static inline int __init ipe_init_dm_verity_signature(void)
int __init ipe_init_dm_verity_signature(void);
#endif /* CONFIG_IPE_DM_VERITY_SIGNATURE */

+#ifndef CONFIG_IPE_DM_VERITY_ROOTHASH
+static inline int __init ipe_init_dm_verity_rh(void)
+{
+ return 0;
+}
+#else
+int __init ipe_init_dm_verity_rh(void);
+#endif /* CONFIG_IPE_DM_VERITY_ROOTHASH */
+
#endif /* IPE_PROP_ENTRY_H */
--
2.27.0

2020-07-30 00:35:29

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 05/11] fs: add security blob and hooks for block_device

Add a security blob and associated allocation, deallocation and set hooks
for a block_device structure.

Signed-off-by: Deven Bowers <[email protected]>
---
fs/block_dev.c | 8 ++++
include/linux/fs.h | 1 +
include/linux/lsm_hook_defs.h | 5 +++
include/linux/lsm_hooks.h | 12 ++++++
include/linux/security.h | 22 +++++++++++
security/security.c | 70 +++++++++++++++++++++++++++++++++++
6 files changed, 118 insertions(+)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 0ae656e022fd..8602dd62c3e2 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -34,6 +34,7 @@
#include <linux/falloc.h>
#include <linux/uaccess.h>
#include <linux/suspend.h>
+#include <linux/security.h>
#include "internal.h"

struct bdev_inode {
@@ -768,11 +769,18 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, GFP_KERNEL);
if (!ei)
return NULL;
+
+ if (unlikely(security_bdev_alloc(&ei->bdev))) {
+ kmem_cache_free(bdev_cachep, ei);
+ return NULL;
+ }
+
return &ei->vfs_inode;
}

static void bdev_free_inode(struct inode *inode)
{
+ security_bdev_free(&BDEV_I(inode)->bdev);
kmem_cache_free(bdev_cachep, BDEV_I(inode));
}

diff --git a/include/linux/fs.h b/include/linux/fs.h
index f5abba86107d..42d7e3ce7712 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -509,6 +509,7 @@ struct block_device {
int bd_fsfreeze_count;
/* Mutex for freeze */
struct mutex bd_fsfreeze_mutex;
+ void *security;
} __randomize_layout;

/* XArray tags, for tagging dirty and writeback pages in the pagecache. */
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index af998f93d256..f3c0da0db4e8 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -391,3 +391,8 @@ LSM_HOOK(void, LSM_RET_VOID, perf_event_free, struct perf_event *event)
LSM_HOOK(int, 0, perf_event_read, struct perf_event *event)
LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
#endif /* CONFIG_PERF_EVENTS */
+
+LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
+LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
+LSM_HOOK(int, 0, bdev_setsecurity, struct block_device *bdev, const char *name,
+ const void *value, size_t size)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 95b7c1d32062..8670c19a8cef 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1507,6 +1507,17 @@
*
* @what: kernel feature being accessed
*
+ * @bdev_alloc_security:
+ * Initialize the security field inside a block_device structure.
+ *
+ * @bdev_free_security:
+ * Cleanup the security information stored inside a block_device structure.
+ *
+ * @bdev_setsecurity:
+ * Set a security property associated with @name for @bdev with
+ * value @value. @size indicates the size of @value in bytes.
+ * If a @name is not implemented, return -ENOSYS.
+ *
* Security hooks for perf events
*
* @perf_event_open:
@@ -1553,6 +1564,7 @@ struct lsm_blob_sizes {
int lbs_ipc;
int lbs_msg_msg;
int lbs_task;
+ int lbs_bdev;
};

/*
diff --git a/include/linux/security.h b/include/linux/security.h
index 0a0a03b36a3b..8f83fdc6c65d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -451,6 +451,11 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
int security_locked_down(enum lockdown_reason what);
+int security_bdev_alloc(struct block_device *bdev);
+void security_bdev_free(struct block_device *bdev);
+int security_bdev_setsecurity(struct block_device *bdev,
+ const char *name, const void *value,
+ size_t size);
#else /* CONFIG_SECURITY */

static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -1291,6 +1296,23 @@ static inline int security_locked_down(enum lockdown_reason what)
{
return 0;
}
+
+static inline int security_bdev_alloc(struct block_device *bdev)
+{
+ return 0;
+}
+
+static inline void security_bdev_free(struct block_device *bdev)
+{
+}
+
+static inline int security_bdev_setsecurity(struct block_device *bdev,
+ const char *name,
+ const void *value, size_t size)
+{
+ return 0;
+}
+
#endif /* CONFIG_SECURITY */

#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
diff --git a/security/security.c b/security/security.c
index 70a7ad357bc6..de523b0d8f56 100644
--- a/security/security.c
+++ b/security/security.c
@@ -28,6 +28,7 @@
#include <linux/string.h>
#include <linux/msg.h>
#include <net/flow.h>
+#include <linux/fs.h>

#define MAX_LSM_EVM_XATTR 2

@@ -202,6 +203,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+ lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
}

/* Prepare LSM for initialization. */
@@ -337,6 +339,7 @@ static void __init ordered_lsm_init(void)
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+ init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);

/*
* Create any kmem_caches needed for blobs
@@ -654,6 +657,28 @@ static int lsm_msg_msg_alloc(struct msg_msg *mp)
return 0;
}

+/**
+ * lsm_bdev_alloc - allocate a composite block_device blob
+ * @bdev: the block_device that needs a blob
+ *
+ * Allocate the block_device blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_bdev_alloc(struct block_device *bdev)
+{
+ if (blob_sizes.lbs_bdev == 0) {
+ bdev->security = NULL;
+ return 0;
+ }
+
+ bdev->security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
+ if (!bdev->security)
+ return -ENOMEM;
+
+ return 0;
+}
+
/**
* lsm_early_task - during initialization allocate a composite task blob
* @task: the task that needs a blob
@@ -2516,6 +2541,51 @@ int security_locked_down(enum lockdown_reason what)
}
EXPORT_SYMBOL(security_locked_down);

+int security_bdev_alloc(struct block_device *bdev)
+{
+ int rc = 0;
+
+ rc = lsm_bdev_alloc(bdev);
+ if (unlikely(rc))
+ return rc;
+
+ rc = call_int_hook(bdev_alloc_security, 0, bdev);
+ if (unlikely(rc))
+ security_bdev_free(bdev);
+
+ return 0;
+}
+EXPORT_SYMBOL(security_bdev_alloc);
+
+void security_bdev_free(struct block_device *bdev)
+{
+ if (!bdev->security)
+ return;
+
+ call_void_hook(bdev_free_security, bdev);
+
+ kfree(bdev->security);
+ bdev->security = NULL;
+}
+EXPORT_SYMBOL(security_bdev_free);
+
+int security_bdev_setsecurity(struct block_device *bdev,
+ const char *name, const void *value,
+ size_t size)
+{
+ int rc = 0;
+ struct security_hook_list *p;
+
+ hlist_for_each_entry(p, &security_hook_heads.bdev_setsecurity, list) {
+ rc = p->hook.bdev_setsecurity(bdev, name, value, size);
+ if (rc && rc != -ENOSYS)
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(security_bdev_setsecurity);
+
#ifdef CONFIG_PERF_EVENTS
int security_perf_event_open(struct perf_event_attr *attr, int type)
{
--
2.27.0

2020-07-30 00:35:41

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 06/11] dm-verity: add bdev_setsecurity hook for dm-verity signature

Add a security hook call to set a security property of a block_device
in dm-verity with the results of a verified, signed root-hash.

Signed-off-by: Deven Bowers <[email protected]>
---
drivers/md/dm-verity-target.c | 2 +-
drivers/md/dm-verity-verify-sig.c | 14 +++++++++++---
drivers/md/dm-verity-verify-sig.h | 10 ++++++----
include/linux/device-mapper.h | 2 ++
4 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index eec9f252e935..9970488e67ed 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -1134,7 +1134,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
}

/* Root hash signature is a optional parameter*/
- r = verity_verify_root_hash(root_hash_digest_to_validate,
+ r = verity_verify_root_hash(v, root_hash_digest_to_validate,
strlen(root_hash_digest_to_validate),
verify_args.sig,
verify_args.sig_size);
diff --git a/drivers/md/dm-verity-verify-sig.c b/drivers/md/dm-verity-verify-sig.c
index 614e43db93aa..2cd9f8c85449 100644
--- a/drivers/md/dm-verity-verify-sig.c
+++ b/drivers/md/dm-verity-verify-sig.c
@@ -8,7 +8,10 @@
#include <linux/device-mapper.h>
#include <linux/verification.h>
#include <keys/user-type.h>
+#include <linux/security.h>
+#include <linux/list.h>
#include <linux/module.h>
+#include "dm-core.h"
#include "dm-verity.h"
#include "dm-verity-verify-sig.h"

@@ -103,8 +106,9 @@ int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
* @sig_len: Size of the signature.
*
*/
-int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
- const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct dm_verity *v, const void *root_hash,
+ size_t root_hash_len, const void *sig_data,
+ size_t sig_len)
{
int ret;

@@ -121,8 +125,12 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data,
sig_len, NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
NULL, NULL);
+ if (ret)
+ return ret;

- return ret;
+ return security_bdev_setsecurity(dm_table_get_md(v->ti->table)->bdev,
+ DM_VERITY_SIGNATURE_SEC_NAME,
+ sig_data, sig_len);
}

void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
diff --git a/drivers/md/dm-verity-verify-sig.h b/drivers/md/dm-verity-verify-sig.h
index 19b1547aa741..6b7de1d48e5a 100644
--- a/drivers/md/dm-verity-verify-sig.h
+++ b/drivers/md/dm-verity-verify-sig.h
@@ -20,8 +20,9 @@ struct dm_verity_sig_opts {

#define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 2

-int verity_verify_root_hash(const void *data, size_t data_len,
- const void *sig_data, size_t sig_len);
+int verity_verify_root_hash(struct dm_verity *v, const void *data,
+ size_t data_len, const void *sig_data,
+ size_t sig_len);
bool verity_verify_is_sig_opt_arg(const char *arg_name);

int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
@@ -34,8 +35,9 @@ void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts);

#define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0

-int verity_verify_root_hash(const void *data, size_t data_len,
- const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct dm_verity *v, const void *data,
+ size_t data_len, const void *sig_data,
+ size_t sig_len)
{
return 0;
}
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 73dec4b5d5be..ab6b8eb0a150 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -625,4 +625,6 @@ static inline unsigned long to_bytes(sector_t n)
return (n << SECTOR_SHIFT);
}

+#define DM_VERITY_SIGNATURE_SEC_NAME DM_NAME ".verity-sig"
+
#endif /* _LINUX_DEVICE_MAPPER_H */
--
2.27.0

2020-07-30 00:36:00

by Deven Bowers

[permalink] [raw]
Subject: [RFC PATCH v6 01/11] scripts: add ipe tooling to generate boot policy

Add a tool for the generation of an IPE policy to be compiled into the
kernel. This policy will be enforced until userland deploys and activates
a new policy.

Signed-off-by: Deven Bowers <[email protected]>
---
MAINTAINERS | 6 ++
scripts/Makefile | 1 +
scripts/ipe/Makefile | 2 +
scripts/ipe/polgen/.gitignore | 1 +
scripts/ipe/polgen/Makefile | 7 ++
scripts/ipe/polgen/polgen.c | 136 ++++++++++++++++++++++++++++++++++
6 files changed, 153 insertions(+)
create mode 100644 scripts/ipe/Makefile
create mode 100644 scripts/ipe/polgen/.gitignore
create mode 100644 scripts/ipe/polgen/Makefile
create mode 100644 scripts/ipe/polgen/polgen.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f0569cf304ca..d29c2e4612e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8580,6 +8580,12 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
F: security/integrity/ima/

+INTEGRITY POLICY ENFORCEMENT (IPE)
+M: Deven Bowers <[email protected]>
+L: [email protected]
+S: Supported
+F: scripts/ipe/
+
INTEL 810/815 FRAMEBUFFER DRIVER
M: Antonino Daplas <[email protected]>
L: [email protected]
diff --git a/scripts/Makefile b/scripts/Makefile
index 95ecf970c74c..b3c1882fd6dd 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -34,6 +34,7 @@ hostprogs += unifdef
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
subdir-$(CONFIG_MODVERSIONS) += genksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_IPE) += ipe

# Let clean descend into subdirs
subdir- += basic dtc gdb kconfig mod
diff --git a/scripts/ipe/Makefile b/scripts/ipe/Makefile
new file mode 100644
index 000000000000..e87553fbb8d6
--- /dev/null
+++ b/scripts/ipe/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+subdir-y := polgen
diff --git a/scripts/ipe/polgen/.gitignore b/scripts/ipe/polgen/.gitignore
new file mode 100644
index 000000000000..80f32f25d200
--- /dev/null
+++ b/scripts/ipe/polgen/.gitignore
@@ -0,0 +1 @@
+polgen
diff --git a/scripts/ipe/polgen/Makefile b/scripts/ipe/polgen/Makefile
new file mode 100644
index 000000000000..a519b594e13c
--- /dev/null
+++ b/scripts/ipe/polgen/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-y := polgen
+HOST_EXTRACFLAGS += \
+ -I$(srctree)/include \
+ -I$(srctree)/include/uapi \
+
+always := $(hostprogs-y)
diff --git a/scripts/ipe/polgen/polgen.c b/scripts/ipe/polgen/polgen.c
new file mode 100644
index 000000000000..a80fffe1b27c
--- /dev/null
+++ b/scripts/ipe/polgen/polgen.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void usage(const char *const name)
+{
+ printf("Usage: %s OutputFile (PolicyFile)\n", name);
+ exit(EINVAL);
+}
+
+static int policy_to_buffer(const char *pathname, char **buffer, size_t *size)
+{
+ int rc = 0;
+ FILE *fd;
+ char *lbuf;
+ size_t fsize;
+ size_t read;
+
+ fd = fopen(pathname, "r");
+ if (!fd) {
+ rc = errno;
+ goto out;
+ }
+
+ fseek(fd, 0, SEEK_END);
+ fsize = ftell(fd);
+ rewind(fd);
+
+ lbuf = malloc(fsize);
+ if (!lbuf) {
+ rc = ENOMEM;
+ goto out_close;
+ }
+
+ read = fread((void *)lbuf, sizeof(*lbuf), fsize, fd);
+ if (read != fsize) {
+ rc = -1;
+ goto out_free;
+ }
+
+ *buffer = lbuf;
+ *size = fsize;
+ fclose(fd);
+
+ return rc;
+
+out_free:
+ free(lbuf);
+out_close:
+ fclose(fd);
+out:
+ return rc;
+}
+
+static int write_boot_policy(const char *pathname, const char *buf, size_t size)
+{
+ FILE *fd;
+ size_t i;
+
+ fd = fopen(pathname, "w");
+ if (!fd)
+ goto err;
+
+ fprintf(fd, "/* This file is automatically generated.");
+ fprintf(fd, " Do not edit. */\n");
+ fprintf(fd, "#include <stddef.h>\n");
+ fprintf(fd, "const char *const ipe_boot_policy =\n");
+
+ if (!buf || size == 0) {
+ fprintf(fd, "\tNULL;\n");
+ fclose(fd);
+ return 0;
+ }
+
+ for (i = 0; i < size; ++i) {
+ if (i == 0)
+ fprintf(fd, "\t\"");
+
+ switch (buf[i]) {
+ case '"':
+ fprintf(fd, "\\\"");
+ break;
+ case '\'':
+ fprintf(fd, "'");
+ break;
+ case '\n':
+ fprintf(fd, "\\n\"\n\t\"");
+ break;
+ case '\\':
+ fprintf(fd, "\\\\");
+ break;
+ default:
+ fprintf(fd, "%c", buf[i]);
+ }
+ }
+ fprintf(fd, "\";\n");
+ fclose(fd);
+
+ return 0;
+
+err:
+ if (fd)
+ fclose(fd);
+ return errno;
+}
+
+int main(int argc, const char *argv[])
+{
+ int rc = 0;
+ size_t len = 0;
+ char *policy = NULL;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ if (argc > 2) {
+ rc = policy_to_buffer(argv[2], &policy, &len);
+ if (rc != 0)
+ goto cleanup;
+ }
+
+ rc = write_boot_policy(argv[1], policy, len);
+cleanup:
+ if (policy)
+ free(policy);
+ if (rc != 0)
+ perror("An error occurred during policy conversion: ");
+ return rc;
+}
--
2.27.0