audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/audit.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <crypto/hash.h>
|
|
|
|
|
|
|
|
#include "ipe.h"
|
|
|
|
#include "eval.h"
|
|
|
|
#include "hooks.h"
|
|
|
|
#include "policy.h"
|
|
|
|
#include "audit.h"
|
2024-08-02 23:08:27 -07:00
|
|
|
#include "digest.h"
|
audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
|
|
|
|
#define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY")
|
|
|
|
|
|
|
|
#define IPE_AUDIT_HASH_ALG "sha256"
|
|
|
|
|
|
|
|
#define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\
|
|
|
|
"policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
|
|
#define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\
|
|
|
|
"old_active_pol_version=%hu.%hu.%hu "\
|
|
|
|
"old_policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
|
|
#define AUDIT_OLD_ACTIVE_POLICY_NULL_FMT "old_active_pol_name=? "\
|
|
|
|
"old_active_pol_version=? "\
|
|
|
|
"old_policy_digest=?"
|
|
|
|
#define AUDIT_NEW_ACTIVE_POLICY_FMT "new_active_pol_name=\"%s\" "\
|
|
|
|
"new_active_pol_version=%hu.%hu.%hu "\
|
|
|
|
"new_policy_digest=" IPE_AUDIT_HASH_ALG ":"
|
|
|
|
|
|
|
|
static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
|
|
|
|
"EXECUTE",
|
|
|
|
"FIRMWARE",
|
|
|
|
"KMODULE",
|
|
|
|
"KEXEC_IMAGE",
|
|
|
|
"KEXEC_INITRAMFS",
|
|
|
|
"POLICY",
|
|
|
|
"X509_CERT",
|
|
|
|
"UNKNOWN",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
|
|
|
|
"BPRM_CHECK",
|
|
|
|
"MMAP",
|
|
|
|
"MPROTECT",
|
|
|
|
"KERNEL_READ",
|
|
|
|
"KERNEL_LOAD",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *const audit_prop_names[__IPE_PROP_MAX] = {
|
|
|
|
"boot_verified=FALSE",
|
|
|
|
"boot_verified=TRUE",
|
2024-08-02 23:08:27 -07:00
|
|
|
"dmverity_roothash=",
|
|
|
|
"dmverity_signature=FALSE",
|
|
|
|
"dmverity_signature=TRUE",
|
2024-08-02 23:08:30 -07:00
|
|
|
"fsverity_digest=",
|
|
|
|
"fsverity_signature=FALSE",
|
|
|
|
"fsverity_signature=TRUE",
|
audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
};
|
|
|
|
|
2024-08-02 23:08:27 -07:00
|
|
|
/**
|
|
|
|
* audit_dmv_roothash() - audit the roothash of a dmverity_roothash property.
|
|
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
|
|
* @rh: Supplies a pointer to the digest structure.
|
|
|
|
*/
|
|
|
|
static void audit_dmv_roothash(struct audit_buffer *ab, const void *rh)
|
|
|
|
{
|
|
|
|
audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_DMV_ROOTHASH]);
|
|
|
|
ipe_digest_audit(ab, rh);
|
|
|
|
}
|
|
|
|
|
2024-08-02 23:08:30 -07:00
|
|
|
/**
|
|
|
|
* audit_fsv_digest() - audit the digest of a fsverity_digest property.
|
|
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
|
|
* @d: Supplies a pointer to the digest structure.
|
|
|
|
*/
|
|
|
|
static void audit_fsv_digest(struct audit_buffer *ab, const void *d)
|
|
|
|
{
|
|
|
|
audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_FSV_DIGEST]);
|
|
|
|
ipe_digest_audit(ab, d);
|
|
|
|
}
|
|
|
|
|
audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
/**
|
|
|
|
* audit_rule() - audit an IPE policy rule.
|
|
|
|
* @ab: Supplies a pointer to the audit_buffer to append to.
|
|
|
|
* @r: Supplies a pointer to the ipe_rule to approximate a string form for.
|
|
|
|
*/
|
|
|
|
static void audit_rule(struct audit_buffer *ab, const struct ipe_rule *r)
|
|
|
|
{
|
|
|
|
const struct ipe_prop *ptr;
|
|
|
|
|
|
|
|
audit_log_format(ab, " rule=\"op=%s ", audit_op_names[r->op]);
|
|
|
|
|
2024-08-02 23:08:27 -07:00
|
|
|
list_for_each_entry(ptr, &r->props, next) {
|
|
|
|
switch (ptr->type) {
|
|
|
|
case IPE_PROP_DMV_ROOTHASH:
|
|
|
|
audit_dmv_roothash(ab, ptr->value);
|
|
|
|
break;
|
2024-08-02 23:08:30 -07:00
|
|
|
case IPE_PROP_FSV_DIGEST:
|
|
|
|
audit_fsv_digest(ab, ptr->value);
|
|
|
|
break;
|
2024-08-02 23:08:27 -07:00
|
|
|
default:
|
|
|
|
audit_log_format(ab, "%s", audit_prop_names[ptr->type]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
audit_log_format(ab, " ");
|
|
|
|
}
|
audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
|
|
|
|
audit_log_format(ab, "action=%s\"", ACTSTR(r->action));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_audit_match() - Audit a rule match in a policy evaluation.
|
|
|
|
* @ctx: Supplies a pointer to the evaluation context that was used in the
|
|
|
|
* evaluation.
|
|
|
|
* @match_type: Supplies the scope of the match: rule, operation default,
|
|
|
|
* global default.
|
|
|
|
* @act: Supplies the IPE's evaluation decision, deny or allow.
|
|
|
|
* @r: Supplies a pointer to the rule that was matched, if possible.
|
|
|
|
*/
|
|
|
|
void ipe_audit_match(const struct ipe_eval_ctx *const ctx,
|
|
|
|
enum ipe_match match_type,
|
|
|
|
enum ipe_action_type act, const struct ipe_rule *const r)
|
|
|
|
{
|
|
|
|
const char *op = audit_op_names[ctx->op];
|
|
|
|
char comm[sizeof(current->comm)];
|
|
|
|
struct audit_buffer *ab;
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
if (act != IPE_ACTION_DENY && !READ_ONCE(success_audit))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
|
|
|
|
AUDIT_IPE_ACCESS);
|
|
|
|
if (!ab)
|
|
|
|
return;
|
|
|
|
|
2024-08-02 23:08:24 -07:00
|
|
|
audit_log_format(ab, "ipe_op=%s ipe_hook=%s enforcing=%d pid=%d comm=",
|
|
|
|
op, audit_hook_names[ctx->hook], READ_ONCE(enforce),
|
audit,ipe: add IPE auditing support
Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE
itself.
This patch introduces 3 new audit events.
AUDIT_IPE_ACCESS(1420) indicates the result of an IPE policy evaluation
of a resource.
AUDIT_IPE_CONFIG_CHANGE(1421) indicates the current active IPE policy
has been changed to another loaded policy.
AUDIT_IPE_POLICY_LOAD(1422) indicates a new IPE policy has been loaded
into the kernel.
This patch also adds support for success auditing, allowing users to
identify why an allow decision was made for a resource. However, it is
recommended to use this option with caution, as it is quite noisy.
Here are some examples of the new audit record types:
AUDIT_IPE_ACCESS(1420):
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=297 comm="sh" path="/root/vol/bin/hello" dev="tmpfs"
ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=299 comm="sh" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=300 path="/tmp/tmpdp2h1lub/deny/bin/hello" dev="tmpfs"
ino=131 rule="DEFAULT action=DENY"
The above three records were generated when the active IPE policy only
allows binaries from the initramfs to run. The three identical `hello`
binary were placed at different locations, only the first hello from
the rootfs(initramfs) was allowed.
Field ipe_op followed by the IPE operation name associated with the log.
Field ipe_hook followed by the name of the LSM hook that triggered the IPE
event.
Field enforcing followed by the enforcement state of IPE. (it will be
introduced in the next commit)
Field pid followed by the pid of the process that triggered the IPE
event.
Field comm followed by the command line program name of the process that
triggered the IPE event.
Field path followed by the file's path name.
Field dev followed by the device name as found in /dev where the file is
from.
Note that for device mappers it will use the name `dm-X` instead of
the name in /dev/mapper.
For a file in a temp file system, which is not from a device, it will use
`tmpfs` for the field.
The implementation of this part is following another existing use case
LSM_AUDIT_DATA_INODE in security/lsm_audit.c
Field ino followed by the file's inode number.
Field rule followed by the IPE rule made the access decision. The whole
rule must be audited because the decision is based on the combination of
all property conditions in the rule.
Along with the syscall audit event, user can know why a blocked
happened. For example:
audit: AUDIT1420 ipe_op=EXECUTE ipe_hook=BPRM_CHECK enforcing=1
pid=2138 comm="bash" path="/mnt/ipe/bin/hello" dev="dm-0"
ino=2 rule="DEFAULT action=DENY"
audit[1956]: SYSCALL arch=c000003e syscall=59
success=no exit=-13 a0=556790138df0 a1=556790135390 a2=5567901338b0
a3=ab2a41a67f4f1f4e items=1 ppid=147 pid=1956 auid=4294967295 uid=0
gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
ses=4294967295 comm="bash" exe="/usr/bin/bash" key=(null)
The above two records showed bash used execve to run "hello" and got
blocked by IPE. Note that the IPE records are always prior to a SYSCALL
record.
AUDIT_IPE_CONFIG_CHANGE(1421):
audit: AUDIT1421
old_active_pol_name="Allow_All" old_active_pol_version=0.0.0
old_policy_digest=sha256:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649
new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed the current IPE active policy switch from
`Allow_All` to `boot_verified` along with the version and the hash
digest of the two policies. Note IPE can only have one policy active
at a time, all access decision evaluation is based on the current active
policy.
The normal procedure to deploy a policy is loading the policy to deploy
into the kernel first, then switch the active policy to it.
AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="boot_verified" policy_version=0.0.0
policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F2676
auid=4294967295 ses=4294967295 lsm=ipe res=1
The above record showed a new policy has been loaded into the kernel
with the policy name, policy version and policy hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: subject line tweak]
Signed-off-by: Paul Moore <paul@paul-moore.com>
2024-08-02 23:08:23 -07:00
|
|
|
task_tgid_nr(current));
|
|
|
|
audit_log_untrustedstring(ab, get_task_comm(comm, current));
|
|
|
|
|
|
|
|
if (ctx->file) {
|
|
|
|
audit_log_d_path(ab, " path=", &ctx->file->f_path);
|
|
|
|
inode = file_inode(ctx->file);
|
|
|
|
if (inode) {
|
|
|
|
audit_log_format(ab, " dev=");
|
|
|
|
audit_log_untrustedstring(ab, inode->i_sb->s_id);
|
|
|
|
audit_log_format(ab, " ino=%lu", inode->i_ino);
|
|
|
|
} else {
|
|
|
|
audit_log_format(ab, " dev=? ino=?");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
audit_log_format(ab, " path=? dev=? ino=?");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match_type == IPE_MATCH_RULE)
|
|
|
|
audit_rule(ab, r);
|
|
|
|
else if (match_type == IPE_MATCH_TABLE)
|
|
|
|
audit_log_format(ab, " rule=\"DEFAULT op=%s action=%s\"", op,
|
|
|
|
ACTSTR(act));
|
|
|
|
else
|
|
|
|
audit_log_format(ab, " rule=\"DEFAULT action=%s\"",
|
|
|
|
ACTSTR(act));
|
|
|
|
|
|
|
|
audit_log_end(ab);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* audit_policy() - Audit a policy's name, version and thumbprint to @ab.
|
|
|
|
* @ab: Supplies a pointer to the audit buffer to append to.
|
|
|
|
* @audit_format: Supplies a pointer to the audit format string
|
|
|
|
* @p: Supplies a pointer to the policy to audit.
|
|
|
|
*/
|
|
|
|
static void audit_policy(struct audit_buffer *ab,
|
|
|
|
const char *audit_format,
|
|
|
|
const struct ipe_policy *const p)
|
|
|
|
{
|
|
|
|
SHASH_DESC_ON_STACK(desc, tfm);
|
|
|
|
struct crypto_shash *tfm;
|
|
|
|
u8 *digest = NULL;
|
|
|
|
|
|
|
|
tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, 0, 0);
|
|
|
|
if (IS_ERR(tfm))
|
|
|
|
return;
|
|
|
|
|
|
|
|
desc->tfm = tfm;
|
|
|
|
|
|
|
|
digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL);
|
|
|
|
if (!digest)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (crypto_shash_init(desc))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (crypto_shash_update(desc, p->pkcs7, p->pkcs7len))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (crypto_shash_final(desc, digest))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
audit_log_format(ab, audit_format, p->parsed->name,
|
|
|
|
p->parsed->version.major, p->parsed->version.minor,
|
|
|
|
p->parsed->version.rev);
|
|
|
|
audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm));
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(digest);
|
|
|
|
crypto_free_shash(tfm);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_audit_policy_activation() - Audit a policy being activated.
|
|
|
|
* @op: Supplies a pointer to the previously activated policy to audit.
|
|
|
|
* @np: Supplies a pointer to the newly activated policy to audit.
|
|
|
|
*/
|
|
|
|
void ipe_audit_policy_activation(const struct ipe_policy *const op,
|
|
|
|
const struct ipe_policy *const np)
|
|
|
|
{
|
|
|
|
struct audit_buffer *ab;
|
|
|
|
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL,
|
|
|
|
AUDIT_IPE_CONFIG_CHANGE);
|
|
|
|
if (!ab)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (op) {
|
|
|
|
audit_policy(ab, AUDIT_OLD_ACTIVE_POLICY_FMT, op);
|
|
|
|
audit_log_format(ab, " ");
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* old active policy can be NULL if there is no kernel
|
|
|
|
* built-in policy
|
|
|
|
*/
|
|
|
|
audit_log_format(ab, AUDIT_OLD_ACTIVE_POLICY_NULL_FMT);
|
|
|
|
audit_log_format(ab, " ");
|
|
|
|
}
|
|
|
|
audit_policy(ab, AUDIT_NEW_ACTIVE_POLICY_FMT, np);
|
|
|
|
audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
|
|
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
|
|
audit_get_sessionid(current));
|
|
|
|
|
|
|
|
audit_log_end(ab);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_audit_policy_load() - Audit a policy being loaded into the kernel.
|
|
|
|
* @p: Supplies a pointer to the policy to audit.
|
|
|
|
*/
|
|
|
|
void ipe_audit_policy_load(const struct ipe_policy *const p)
|
|
|
|
{
|
|
|
|
struct audit_buffer *ab;
|
|
|
|
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL,
|
|
|
|
AUDIT_IPE_POLICY_LOAD);
|
|
|
|
if (!ab)
|
|
|
|
return;
|
|
|
|
|
|
|
|
audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
|
|
|
|
audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
|
|
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
|
|
audit_get_sessionid(current));
|
|
|
|
|
|
|
|
audit_log_end(ab);
|
|
|
|
}
|
2024-08-02 23:08:24 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_audit_enforce() - Audit a change in IPE's enforcement state.
|
|
|
|
* @new_enforce: The new value enforce to be set.
|
|
|
|
* @old_enforce: The old value currently in enforce.
|
|
|
|
*/
|
|
|
|
void ipe_audit_enforce(bool new_enforce, bool old_enforce)
|
|
|
|
{
|
|
|
|
struct audit_buffer *ab;
|
|
|
|
|
|
|
|
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS);
|
|
|
|
if (!ab)
|
|
|
|
return;
|
|
|
|
|
|
|
|
audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS,
|
|
|
|
"enforcing=%d old_enforcing=%d auid=%u ses=%u"
|
|
|
|
" enabled=1 old-enabled=1 lsm=ipe res=1",
|
|
|
|
new_enforce, old_enforce,
|
|
|
|
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
|
|
|
audit_get_sessionid(current));
|
|
|
|
|
|
|
|
audit_log_end(ab);
|
|
|
|
}
|