Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to reject specific hashes in a IMA file signature based runtime policy #1540

Open
kkaarreell opened this issue Mar 27, 2024 · 12 comments

Comments

@kkaarreell
Copy link
Contributor

kkaarreell commented Mar 27, 2024

Is your issue a feature request? If so, please raise it as an enhancement

Description

Fedora includes signed hashes for files in a RPM package and with rpm-plugin-ima installed I can get those IMA file signatures to extended attributes when the package gets installed or updated. However, the problem with the file signatures is that they are "forever". When I update a package, replacing a file with its newer version, I would like to be able to add the old hash on an reject list so that a system fails attestation if the "old" file lands on a disk again (e.g. due an RPM downgrade).

Another, slightly related topic is the order of runtime policy lists evaluation. I believe that the current sequence is:

  1. excludelist
  2. allowlist
  3. IMA file signatures

Is it correct that an excludelist to always takes a preference? Would it be reasonable to make the order configurable, probably as a part of the runtime policy itself?

@kkaarreell
Copy link
Contributor Author

@ansasaki @mruffin FYI

@stefanberger
Copy link
Contributor

Is it correct that an excludelist to always takes a preference?

I would let a use case drive this decision. So far I at least have regarded the exclude list as a first level of filtering. I am not sure what we want to see happening when the exclude list was to be applied last for example.

@kkaarreell
Copy link
Contributor Author

kkaarreell commented Apr 4, 2024

Is it correct that an excludelist to always takes a preference?

I would let a use case drive this decision. So far I at least have regarded the exclude list as a first level of filtering. I am not sure what we want to see happening when the exclude list was to be applied last for example.

Hi @stefanberger
So the use case I had in mind is when a user wants to measure a specific list of mutable files. For example, a specific set of configuration files or container images. From this perspective, creating an exclude list that covers "everything else" may not be simple, at least I can't think of an easy way of doing so. Being able to provide a list of files that must be measured (and not being excluded) or a list of regular expressions identifying files that always should be match against the allowlist and not excluded could help I believe.

@stefanberger
Copy link
Contributor

stefanberger commented Apr 4, 2024

So the use case I had in mind is when a user wants to measure a specific list of mutable files. For example, a specific set of configuration files or container images. From this perspective, creating an exclude list that covers "everything else" may not be simple, at least I can't think of an easy way of doing so. Being able to provide a list of files that must be measured (and not being excluded) or a list of regular expressions identifying files that always should be match against the allowlist and not excluded could help I believe.

In terms of ordering in this case one could look at the current allowlist with their path names and then have something like a policy for skipping files (accept) that match regular expressions or rejecting files also due to regular expressions? This way we could keep the exclude list check first but then have a check for files to accept and reject based on regular expressions. It would then look like this, with additional steps 4 beyond what we have today:

  1. Ignore a file if its path is matched by a regex of the exclude list.
  2. If a file is signed, check its signature. If signature verification passes then processing ends with 'success'.
  3. If a file's hash is in the allowlist then processing ends with 'success'.
  4. If a file's path is matched by a ACCEPT regex then processing ends with 'success'.
    If a file's path is matched by a REJECT regex then processing ends with 'reject'.
    Processing ends with 'accept' or 'reject'.

We would have to design how to write a policy for 4.
Maybe it could look like the following where each rule is processed in sequence:

ACCEPT: /var/lib/containers/
REJECT: /

The above would accept (effectively ignore) all files with path prefix /var/lib/containers/ and have a default policy of REJECT for everything that couldn't be evaluated before.

@kkaarreell
Copy link
Contributor Author

I have a concern that keeping the excludelist prioritized will negatively impact user experience. I think that when a user is interested in a single subdirectory within a directory, creating excludelist that covers everything else within that directory except that single subdirectory is unnecessarily complicated.

Also, the idea of rejectlist was that a user could provide a list of files and corresponding hashes that would be rejected even in case the file has a valid IMA signature. This doesn't seem to be covered by the proposal above.

OTOH, I like the idea of ACCEPT, REJECT, .., maybe we could utilize this idea in creating a new runtime policy source format. Users could define a policy using rules like this:

# allowlist record alternative
ACCEPT: /etc/hosts sha256:aa11bb...
# rejectlist records
REJECT: /etc/hosts sha256:bb22cc...
# excludelist record alternative
EXCLUDE: /var/tmp/.*
# accept valid IMA signatures for files that got thus far
ACCEPT_IMASIG
# reject everything else
REJECT .*

The possibility to mix ACCEPT and REJECT or EXCLUDE might provide more flexibility. We could get inspired by approaches use in firewall configuration etc and even implement additional keywords.

@stefanberger
Copy link
Contributor

I have a concern that keeping the excludelist prioritized will negatively impact user experience. I think that when a user is interested in a single subdirectory within a directory, creating excludelist that covers everything else within that directory except that single subdirectory is unnecessarily complicated.

There's a difference between going with what we have right now versus starting over with something completely new...

Users could define a policy using rules like this:

I like the format of the rules, though we will need scripts/tools to do this and they will have to generate the rules basically in the right order. With hosts having potentially thousands of lines for measurement to deal with it's something users will have difficulties with writing such a policy by hand.

@stefanberger
Copy link
Contributor

stefanberger commented Apr 5, 2024

Also, the idea of rejectlist was that a user could provide a list of files and corresponding hashes that would be rejected even in case the file has a valid IMA signature. This doesn't seem to be covered by the proposal above.

If we wanted to continue with the current code (before starting something completely new) then my proposal here would be the following:

  1. Ignore a file if its path is matched by a regex of the exclude list.
  2. Reject a file if its hash is found in a reject-list.
  3. If a file is signed, check its signature. If signature verification passes then processing ends with 'success'.
  4. If a file's hash is in the allowlist then processing ends with 'success'.
  5. If a file's path is matched by a ACCEPT regex then processing ends with 'success'.
    If a file's path is matched by a REJECT regex then processing ends with 'reject'.
    Processing ends with 'accept' or 'reject'.

For better or worse we will have to maintain backwards compatibility with the current policy and how it's being processed even if we moved to a new way of describing the policy. For internal processing the above may look like this in the new format:

   # 1. Ignore files whose path is found in the exclude list
   PROCESS_EXCLUDE_LIST
   # 2. Reject files whose has is found in a reject-list (map)
   PROCESS_REJECT_LIST
   # 3. accept valid IMA signatures for files that got thus far
   ACCEPT_IMASIG
   # 4. accept files found in allow-list (map)
   PROCESS_ALLOW_LIST
   # 5. user-provided rules for accepting/rejecting
   ACCEPT: /var/lib/containers/*
   REJECT: .*

iptables has ipsets which are used for bulk-processing of ip addresses. We could regard our current maps of path and hashes and lists of regexs as hash & regex sets where our current JSON policy could have them with implicit names, so we could rewrite the above to this:

    EXCLUDE-REGEX: exclude-list
    REJECT-MAP: reject-list
    ACCEPT_IMASIG
    ACCEPT-MAP: accept-list
    ACCEPT: /var/lib/containers/*
    REJECT: .*

It may be worth consider that the user shouldn't initially write the above last two lines but provide some sort of REGEX-LIST with rules like this ['ACCEPT:/var/lib/containers/', 'REJECT: .'], which then leads to the following:

    REGEX-LIST: exclude-list    # all elements in this list are prefixed with ACCEPT:
    REJECT-MAP: reject-list
    ACCEPT_IMASIG
    ACCEPT-MAP: accept-list
    REGEX-LIST: filter-list

@stefanberger
Copy link
Contributor

I have a branch for converting the current code to be executed by a policy here: https://github.com/stefanberger/keylime/commits/stefanberger/new_ima_policy/

I am currently working with this policy here that does not support device mapper stuff at all (@THS-on ) and adds two new rules for a reject list and and a potential filter list, though support for these 2 can be deferred:

LEARN-KEYS: ignore-keyrings=ignored_keyrings allowed-hashes=keyrings
REGEX-LIST: exclude-list
REJECT-MAP: reject-list
ACCEPT-IMASIG
ACCEPT-MAP: allow-list
REGEX-LIST: filter-list

What I was going to possibly convert this towards is this here:

LEARN-KEYS: ignore-keyrings=ignored_keyrings map=keyrings
FILE-NAME-FILTER: list=excludes
FILE-HASHES-FILTER: map=rejects target=REJECT
IMA-SIGNATURE-CHECK
FILE-HASHES-FILTER: map=digests target=ACCEPT
FILE-NAME-FILTER: list=filters

Is this the right direction? Comments? Obviously we need to get the language right before we ever allow users to write a policy like this. I guess something like the above would then be embedded in our current JSON runtime policy and be able to reference existing maps of filenames and hashes (like 'digests') and lists of regular expressions (like 'excludes'). I think support for these containers for bulk process will still be necessary considering that some systems will have huge amounts of filenames and hashes, which also won't make it any easier and requires tools to be able to create a policy.

Suggestions for future support of other rules and their parameters are welcome -- we need to find a pattern on how they will all look like. The above also needs support for device mapper so it then ends up behaving 100% like the current code does.

@THS-on
Copy link
Member

THS-on commented Apr 9, 2024

Regarding the device mapper stuff, I think it is absolutely fine to exclude this for now. I'm pretty sure we are the only ones that are using this (also only in testing) and for it to be useful it requires IMA caching disabled and for dm-verity also a further kernel patch.

@stefanberger
Copy link
Contributor

Regarding the device mapper stuff, I think it is absolutely fine to exclude this for now. I'm pretty sure we are the only ones that are using this (also only in testing) and for it to be useful it requires IMA caching disabled and for dm-verity also a further kernel patch.

I added a rule, for now called 'DEVICE-MAPPER-CHECK', that runs the existing code.

@kkaarreell
Copy link
Contributor Author

Hi @stefanberger , I would like to have it reviewed by @ansasaki but he won't be available for the next two weeks.
@sergio-correia , could you please review it as well?

@stefanberger
Copy link
Contributor

OTOH, I like the idea of ACCEPT, REJECT, .., maybe we could utilize this idea in creating a new runtime policy source format. Users could define a policy using rules like this:

# allowlist record alternative
ACCEPT: /etc/hosts sha256:aa11bb...
# rejectlist records
REJECT: /etc/hosts sha256:bb22cc...
# excludelist record alternative
EXCLUDE: /var/tmp/.*
# accept valid IMA signatures for files that got thus far
ACCEPT_IMASIG
# reject everything else
REJECT .*

@kkaarreell I have support for something like this now as well in a bit different format:

FILE-HASHES: target=ACCEPT filename=/usr/bin/dd hash=sha256:d33d5d13792292e202dbf69a6f1b07bc8a02f01424db8489ba7bb7d43c0290ef
FILE-HASHES: target=ACCEPT filename=/usr/bin/zmore hash=sha256:b8ae0b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b

or

FILE-NAMES: regex=ACCEPT:/usr/bin/dd
FILE-HASHES: target=ACCEPT filename=/usr/bin/zmore hash=sha256:0000000000000000000000000000000000000000000000000000000000000000 hash=sha256:b8ae0b8dd04a5935cd8165aa2260cd11b658bd71629bdb52256a675a1f73907b

or

FILE-NAMES: regex=ACCEPT:/usr/bin/dd regex=ACCEPT:/usr/bin/zmore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants