Thread (2 messages) 2 messages, 2 authors, 2023-08-01

Re: [PATCH RFC v10 8/17] uapi|audit|ipe: add ipe auditing support

From: Paul Moore <paul@paul-moore.com>
Date: 2023-08-01 19:24:48
Also in: dm-devel, linux-block, linux-doc, linux-fscrypt, linux-integrity, lkml

Possibly related (same subject, not in this thread)

On Fri, Jul 14, 2023 at 11:57 PM Fan Wu [off-list ref] wrote:
On Sat, Jul 08, 2023 at 12:23:05AM -0400, Paul Moore wrote:
quoted
On Jun 28, 2023 Fan Wu [off-list ref] wrote:
quoted
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 path="/root/vol/bin/hello" dev="sda"
      ino=3897 rule="op=EXECUTE boot_verified=TRUE action=ALLOW"
The 'dev' field is already in use by audit, and is used to log the
device major and minor numbers, see audit_log_name() for an example.

I would suggest adopting the existing 'dev' field format, but if you
really want to log the device name as a string you will need to find
another audit field name.
Actually it was copied from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/security/lsm_audit.c#n228
Personally I think using device name is better, I will try to add a new field.
Ha, yes, it does look like the LSM code uses the device name as
opposed to the major:minor format.  Given that existing use, and that
IPE is a LSM, sticking with 'dev=<name>' seems like the right thing to
do.  Sorry about that :/
quoted
quoted
    audit: AUDIT1420 path="/mnt/ipe/bin/hello" dev="dm-0"
      ino=2 rule="DEFAULT action=DENY"

    audit: AUDIT1420 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 initial booted drive(sda) to run. The three
identical `hello` binary were placed at different locations, only the
first hello from sda was allowed.

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 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:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
      new_active_pol_name="boot_verified" new_active_pol_version=0.0.0
      new_policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F26765076DD8EED7B8F4DB
      auid=4294967295 ses=4294967295 lsm=ipe res=1
You can trim hash digest strings so they better fit in terminals, for
example:

  old_policy_digest=sha256:E3B0C44....
Do you mean I could trim it in the documentation and for the real audit
record I still record the whole hash?
Yes.  Failure to record the full hash digest string in the record
would be Very Bad, but for the sake of keeping the line lengths in the
docs and commit description reasonable you can trim as necessary.
After all, we all know what a full hash string looks like :)
quoted
quoted
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:820EEA5B40CA42B51F68962354BA083122A20BB846F26765076DD8EED7B8F4DB
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 <redacted>
Signed-off-by: Fan Wu <redacted>
---
 include/uapi/linux/audit.h |   3 +
 security/ipe/Kconfig       |   2 +-
 security/ipe/Makefile      |   1 +
 security/ipe/audit.c       | 197 +++++++++++++++++++++++++++++++++++++
 security/ipe/audit.h       |  18 ++++
 security/ipe/eval.c        |  26 ++++-
 security/ipe/eval.h        |   8 ++
 security/ipe/fs.c          |  71 +++++++++++++
 security/ipe/policy.c      |   5 +
 9 files changed, 327 insertions(+), 4 deletions(-)
 create mode 100644 security/ipe/audit.c
 create mode 100644 security/ipe/audit.h
...
quoted
quoted
+/**
+ * ipe_audit_match - audit a match for IPE policy.
+ * @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.
+ * @enforce: Supplies the enforcement/permissive state at the point
+ *      the enforcement decision was made.
+ */
+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)
+{
+   struct inode *inode;
+   struct audit_buffer *ab;
+   const char *op = audit_op_names[ctx->op];
+
+   if (act != __IPE_ACTION_DENY && !READ_ONCE(success_audit))
+           return;
+
+   ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_IPE_ACCESS);
+   if (!ab)
+           return;
+
+   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);
See my comments above about using the 'dev' field name, however, you
shouldn't need to log the device name as an untrusted string as the
string is coming from a trusted source within the kernel (the driver).
I was trying to follow the existing code at https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/security/lsm_audit.c#n229
But I do agree as it is already in the kernel, it should be trusted.
Hmm.  Given the existing code, I guess stick with the untrusted string
variant for now.  I'm concerned that there is some device naming code
which might allow funky device names; although if you can prove that
is not the case then you can use the normal audit logging functions.

For reference, the characters that audit finds problematic can be
found in audit_string_contains_control().

-- 
paul-moore.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help