[apparmor] Some apparmor profile statements are not honored for an application

John Ernberg john.ernberg at actia.se
Wed Sep 2 09:30:13 UTC 2020


Hi John.

(I am leaving most of my original reply in here in case others run into 
the same problem, I was missing the --features-file because I missed 
this part of the docs)

On 9/2/20 9:47 AM, John Johansen wrote:
> On 8/31/20 7:54 AM, John Ernberg wrote:
>> Hi,
>>
>> I seem to have a bit of an odd problem in that some of the profile
>> statements do not appear honored and the library loads they statements
>> allow are thus denied. There is the only program in the system so far
>> that is running confined. The program in question only handles files and
>> library loads.
>>
>> I double checked the syntax of the failing rules, they are aligned with
>> functional rules. I re-did them by copy-pasting the file name in the
>> audit message and then copy-pasted the flags from a functional rule,
>> just to be sure. No change.
>>
> example rules and profiles would help. If you don't want them on the
> public list you can send them to me directly
> 
>> I have checked the statemachine produced with apparmor_parse -D
>> dfa-states and it looks correct, however, when I dump the statemachine
>> transitions in the kernel they are a little off compared to the
>> statemachine generated by apparmor_parse -D dfa-states command when
>> logging in aa_dfa_match on the kernel side, it really looks like the
>> machine makes incorrect lookups.
>>
> this is weird. What are you using to dump the kernel state machine?

(email client mangling risk)
@@ -464,8 +466,10 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, 
unsigned int start,
                                    equiv[(u8) *str++]);
         } else {
                 /* default is direct to next state */
-               while (*str)
+               while (*str) {
+                       pr_err("apparmor: dfa: [%u] testing perm for 
%s\n", state, str);
                         match_char(state, def, base, next, check, (u8) 
*str++);
+               }
         }

         return state;

Then I match the [xxx] number with the statemachine from dfa-states 
output. A bit into the string the transitions stop matching, and after 
another bit into the string segments might get skipped or read past end 
of string.

> 
> you can compare the binary in userspace and the equivalent kernel
> blobs to make sure they are the same.
> 
> Eg. for the ping profile on my system
> 
>    $ cat /sys/kernel/security/apparmor/policy/profiles/ping.3/raw_sha1
>    704b8eafff4dc11ede6b1843384fb7379f13028b
>    $ sudo sha1sum /etc/apparmor.d/cache/bin.ping
>    704b8eafff4dc11ede6b1843384fb7379f13028b  /etc/apparmor.d/cache/bin.ping
> 
> and diff of binaries
> 
>    $ sudo diff -q /sys/kernel/security/apparmor/policy/profiles/ping.3/raw_data /etc/apparmor.d/cache/bin.ping
>    $
> 
> You need to be aware that the sha1 and raw_sha1 are different, you want
> the raw_sha1 and raw_data when comparing to the userspace file.
> 
> Those two will correspond to the compiled blob in the file that is loaded
> into the kernel. If that blob contains multiple profiles each profile
> in the blob will point to the same raw_data, raw_sha1, but their
> individual sha1 file will differ (its taken from a subsection).
> 
> The raw_data and raw_sha1 files reported by the profiles are the same
> that can be found in /sys/kernel/security/apparmor/policy/raw_data
> going through the profile just makes it easier to correlate with the
> policy on disk.
> 
> Doing this check does not guarantee that what the kernel is using is
> the same as what you see in userspace, but it does show what the
> kernel should be using. This check will help us determine where we
> should be focusing the search
> 
> The kernel unpacks the loaded blob into a native format so there
> is some data shuffling of what is actually used by the kernel. So what
> the kernel is using could be off for 3 reasons
> - the unpack is broken
> - there is kernel memory corrupts
> - there was a previous version of the profile loaded into the kernel
>    and it is in use. Profile replacement is not atomic, when a mediation
>    hook is entered the current policy what ever it may be at that
>    point will be used, even if profile replacement happens while the
>    hook is running. The new profile should be used in the next call to
>    a mediation hook but there have in the past been bugs, causing the
>    old profile to be used long past when it should.
> 
> 
>> I have noticed transitions to IDs other than those expected when
>> comparing to the dfa-states, and it seems to start skipping characters
>> in the paths or going beyond the end of the path string for some paths.
>> These paths are the paths for which statements are not honored.
>>
> how are you checking this? This should not be possible as it is the path
> that drives the state machine. That is the path is walked linearly one
> character at a time and we step to the next state in the state machine.

See diff chunk earlier in the mail.
> 
> The only time we walk a path more than once per character is when doing
> profile matching, and the overlapping express match allows for a limited
> nfa stack of N states for backing up. iirc in 5.4 it is limited to 8
> states worth of backup.
> 
>> Trying to add debug to the match_char macro on the kernel side seems to
>> break the statemachine completely, so I wasn't able to debug this route
>> further.
>>
> you can add stuff to the macro but you do need to be careful it is
> easy to break it
@@ -376,6 +376,7 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t 
size, int flags)
  do {                                                   \
         u32 b = (base)[(state)];                        \
         unsigned int pos = base_idx(b) + (C);           \
+       pr_err("apparmor: dfa: current char %c\n", (C));\
         if ((check)[pos] != (state)) {                  \
                 (state) = (def)[(state)];               \
                 if (b & MATCH_FLAG_DIFF_ENCODE)         \

That chunk is all that I added, after that no statement in the profile 
applies and (C) is mostly > 127

> 
>> I'm running version 2.13.3 of the userspace tools, and kernel 5.4.24
>> (vendor kernel, can't upgrade, can't try mainline due to missing support
>> for the SoC I'm using), with the latest 5.4 stable patches for apparmor
>> applied on top.
>>
> can you run a debug kernel with custom patches if needed?
Slightly limited with my debug options due to working from home with 
limited equipment, but I can definitely try patches as needed.

> 
>> The rules is compiled on my machine (x86_64) and embedded in my target
>> (aarch64) readonly rootfs. Target has a readonly filesystem.
>>
> this should be fine the compiled policy tries to arch independent but it
> is always possible we have an bug in the native mappings. It is something
> to keep in mind, while trying to figure out what is going on
> 
>> The rule is loaded on target with the following command:
>>       /sbin/apparmor_parser -K -B /etc/apparmor.d/myprogram
>> Where myprogram is the profile for my program
>>
> what audit message/messages are logged when you do this

[    4.478643] audit: type=1400 audit(1603443648.037:3): 
apparmor="STATUS" operation="profile_load" profile="unconfined
" name="/usr/bin/myprogram" pid=533 comm="apparmor_parser"

The program is started later

> 
>> So far I have not been able to create a shareable reproducer which,
>> unfortunately, makes this all the more harder.
>>
>> I would appreciate any suggestions in how to proceed or what kind of
>> info I should be looking at in order to find out what is going wrong.
>>
> First verify that the user space binary file and what the kernel expects
> are the same.

(from target)
# sha1sum /etc/apparmor.d/usr.bin.myprogram
90ca35aa2f57b3ab562c14a6927cef11fbc24d0c  /etc/apparmor.d/usr.bin.myprogram
# cat 
/sys/kernel/security/apparmor/policy/profiles/usr.bin.myprogram.1/raw_sha1
90ca35aa2f57b3ab562c14a6927cef11fbc24d0c
> 
> Second, are you pinning the policy feature abi or using the kernel feature
> abi, and if you are using the kernel feature abi, what is the feature
> abi of the machine you are compiling on vs. the kernel you are loading
> policy on.

I wasn't, I had completely missed this feature. I extracted the 
.features file from target kernel and provided that to the compiler with 
--features-file

Everything now looks like I expect it to look. Thank you very much for 
this hint.

Very curious results when this is skipped!

> 
> Extracting the feature abi in apparmor 2.13 is a bit of a pita. For the
> machine you are compiling on look for the .features file in the cache
> directory. If you are not writing the cache, make a temporary cache
> in tmp
>    mkdir /tmp/cache
> 
> and add
> 
>    --cache-loc=/tmp/cache --write-cache
> 
> to your compile.

My local machine doesn't have apparmor enabled, but compiled in.
> 
> On the machine you are loading the policy on do the same thing. Make
> a temporary cache and compile a simple profile
> 
>    profile test { }
> 
> will do.
> 
> Compare the two feature abi files. >
> Also can you be more specific about the rules that are having problems?
> File rules, signal, and even which components. The more detail the
> better, feel free to substitue example values instead of real ones if
> you need to.

Only file rules are used at this time.
> 
> 
>> Thank you in advance.
>>
>> Best regards // John Ernberg
>>
>> (not subscribed to the mailing list)
>>
> thats fine just expect some moderation delay
> 

Best regards // John Ernberg


More information about the AppArmor mailing list