Ravi Kishore

Linux Engg @Qualcomm with an attention span of 10 minutes

github twitter linkedin email rss
SELinux Encounter
Mar 18, 2018
5 minutes read

SELinux Encounter

My day to day work involves running Daemons in Android/Linux that interact with Modems. Recently i came across a really hard issue.

The Premise

A userspace daemon, say D is trying to copy a file to another location. C Partial code seems as below:

#define SRC_FILE  /path/to/file1
#define DEST_FILE /path/to/file2

function Helper() {
    
    if( !access(SRC_FILE, R_OK|W_OK) ) {
        //Source file available. Copy it
        ret = system("cp -f %s %s", SRC_FILE, DEST_FILE);
        print_error(ret);
    } else {
        //Source file not available. Create new Dest file
        FILE fd = fopen(DEST_FILE, "wb+");
        add_bytes(fd);
        fclose(fd);
    }
    
}

Thats a simple straight forward method. If SRC_FILE exists, copy it over. Else create a new DEST_FILE. But this never worked!

Error: error 13 permission denied

Lets see the folder permissions

➜  (workspace) whoami
bob

➜  (workspace) cd /path/to/

➜  (/path/to/) ls -al
drwxrwxr-x  9 bob     bob  4096 Mar 17 22:46 .
drwxr-xr-x  6 bob     bob  4096 Mar 18 21:43 ..
-rw-------  4 bob     bob  1000 Mar 16 17:52 file1

Scrutiny

Permissions seem completely fine. As an experiment, lets give 777. That’s highly unprofessional and totally bad advice. But what’s stopping us from a little experiment ?

777

➜  (/path/to/) chmod 777 file1
➜  (/path/to/) ls -al
drwxrwxrwt  9 bob     bob  4096 Mar 17 22:46 .
drwxr-xr-x  6 bob     bob  4096 Mar 18 21:43 ..
-rwxrwxrwx  4 bob     bob  1000 Mar 16 17:52 file1
➜  (/path/to/) run D

Error: error 13 permission denied

Okay, no progress. Lets fallback to the other case and check if the daemon is able to create DEST_FILE instead of copy.

➜  (/path/to/) mv file1 file3
➜  (/path/to/) ls -al
drwxrwxrwt  9 bob     bob  4096 Mar 17 22:46 .
drwxr-xr-x  6 bob     bob  4096 Mar 18 21:43 ..
-rwxrwxrwx  4 bob     bob  1000 Mar 16 17:52 file3
➜  (/path/to/) run D
➜  (/path/to/) ls -al
drwxrwxrwt  9 bob     bob  4096 Mar 17 22:46 .
drwxr-xr-x  6 bob     bob  4096 Mar 18 21:43 ..
-rwxrwxrwx  4 bob     bob  1000 Mar 16 17:52 file3
-rw-------  4 bob     bob  1000 Mar 18 12:51 file2

This is something! Daemon is able to create and write files. But prohibits copy. Now it’s not really the file permissions. So is it the user? Is the daemon falling back to any other unpriviliged User ?

However uncertain these reasons sound, there wasn’t anything else that i knew i could try. Let’s add a line to print the executing user

++      system("whoami > whoami.txt");
        ret = system("cp -f %s %s", SRC_FILE, DEST_FILE);
        print_error(ret);
    } else {
    

Now let’s rollback previous changes and run the daemon

➜  (/path/to/) mv file3 file1; rm file2
➜  (/path/to/) run D

Error: error 13 permission denied
➜  (/path/to/) ls -al
drwxrwxrwt  9 bob     bob  4096 Mar 17 22:46 .
drwxr-xr-x  6 bob     bob  4096 Mar 18 21:43 ..
-rwxrwxrwx  4 bob     bob  1000 Mar 18 17:52 file1

BUG

Aaarghh! Now looks like there’s really something i’m missing here. Let’s go back to the basics.

  1. Daemon is surely running as user bob and there’s no fallback to unpriviliged user at run time, as referred through code.
  2. Both whoami and cp commands have failed.
  3. I know that i had added explicit Sepolicy rules to allow access of /path/to/ directory for D with Read and Write permissions.
  4. Discretionary Access Control is all PASS. So it surely has to be the Mandatory Access Contrl(SELinux/Sepolicy) that i have to revisit

Let’s see the violations report. There has to be something that i have been missing all along.

➜  (/path/to/) cat /var/log/audit.log | wc -l 
3

Okay there are violations. Let’s check if they are related to daemon D

➜  (/path/to/) cat /var/log/audit.log | less
03-18 11:03:15.759  2750  2750 I D    : type=1400 audit(0.0:377): avc: denied { execute_no_trans } for path="/<Redacted>/bin/sh"
dev="sde15" ino=249 scontext=u:r:D:s0 tcontext=u:object_r:<Redacted>_shell_exec:s0 tclass=file permissive=1

FACEPALM

All the while was i wondering about the permission issues, i never even thought about the system command. The system command in turn tries to access and execute the shell binary to pass over the arguments. Apparently, the daemon has to explicitly add rules to allow the access/execution of shell binary from D.

Quoting the POSIX notes-

The POSIX system() function passes string to the sh shell command for execution. The environment is established by the runtime library through a spawn() of the shell.

How could i miss that? This surely has to go to my notes. Rookie mistake.

Okay, now lets generate the sepolicy type enforcement rules to allow this:

➜  (/path/to/) cat /var/log/audit.log | audit2allow -p policy

#============= D ==============
allow D <Redacted>_shell_exec:file execute_no_trans;

All i had to do now was to add the above line to my policy and run the daemon again. It worked and resolved my problem now.

Anything Else

  • How could i have debugged this problem a bit better?

Answer to that is very simple in my opinion. Set the SELinux environment to Permissive and check. That would have narrowed me down to the MAC without those early attempts.

  • Why did whoami not work ?

Same. Passed on to system command

  • Should every denial be solved with a new SELinux rule ?

No. It shouldn’t. Sepolicies are there at-place for a reason. The elegant solution here should be to remove those system command operations and write a native copy method. Beautiful Linux has provided every imaginable functionality. fopen->fwrite->fclose

  • Why was system even used if it never worked on the platform/daemon ?

This is a tricky question. There’s a background to it. SELinux and Sepolicy enforcement on Android is a new player in the scene. It has been in the pipeline since Lollipop but finally got enforced strictly from Oreo. So the daemon got lucky the earlier times.

Drop me a mail, if you have something interesting to say - me[at]rkravi.com



Further:

  • SELinux on Android: Link
  • Concise SELinux Hands on: Link
  • Beginner SELinux Videos:Link
  • I would strongly suggest you to experiment SELinux on a simple CentOS droplet at Digitial Ocean.

Back to posts