SELinux: How to create a CIL from a TE file

For a project, I wanted to compile and install a SELinux module, but only if necessary. Unfortunatelly, a stored module contains no version number which I can check and compare with the source module. So I came up with this idea: using the MD5 signatures.

Modules are stored in /var/lib/selinux/Policy/active/modules/400/ModuleName/cil
The Policy can be found with sestatus:

# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
...
  
Modules are stored as compressed CIL, which can be easily dumped with bzcat:
# bzcat /var/lib/selinux/targeted/active/modules/400/my-httpd/cil
(typeattributeset cil_gen_require httpd_t)
(typeattributeset cil_gen_require default_t)
(typeattributeset cil_gen_require sysstat_log_t)
(typeattributeset cil_gen_require usr_t)
(allow httpd_t usr_t (file (write create unlink setattr)))
(allow httpd_t default_t (file (open read)))
(allow httpd_t sysstat_log_t (dir (add_name write)))
(allow httpd_t sysstat_log_t (file (open create)))
Piping the result to md5sum provides a signature.

The trickiest part is transforming the source (a .te file) to a CIL. A TE files looks like:

module my-httpd 1.2;

require {
        type httpd_t;
        type default_t;
        type sysstat_log_t;
        type usr_t;
        class dir { add_name write };
        class file { open read write create unlink setattr };
}
#============= httpd_t ==============
allow httpd_t usr_t:file { write create unlink setattr };
allow httpd_t default_t:file { open read };
allow httpd_t sysstat_log_t:dir { add_name write };
allow httpd_t sysstat_log_t:file { create open };
Hopefully, both syntax are similar, so the transformation can be achieved via sed with this simple program:
# remove semi-columns
s/;//g

# remove comments
s/#.*$//g        

# turn multiple space chars into one
s/[ \t]\+/ /g    

# remove leading and trailing spaces
s/^ \+\| \+$//   

# Turn { to (
s/ *{ */ (/      

# Turn } to )
s/ *}/)/         

# For each declaration of new type, add a line with the typeattribute,
# another line with the typeattributeset and a third line with the type.
s/^type \(.\+_t\), *\(.\+\)/(typeattribute \2)\n(typeattributeset \2 (\1))\n(type \2)/

# Turn the declaration of a existing type to a typeattributeset
s/^type \(.\+_t\)/(typeattributeset cil_gen_require \1)/

# Rule with 3 levels
s/^\(allow\|deny\|dontaudit\) *\(.\+_t\) \(.\+\):\(.\+\) \((.\+)\)/(\1 \2 \3 (\4 \5))/

# Rule with 4 levels
s/^\(allow\|deny\|dontaudit\) *\(.\+_t\) \(.\+\):\(.\+\) \(.\+\)/(\1 \2 \3 (\4 (\5)))/

# Remove any space before )
s/ \+)/)/g

# Remove lines not starting with (
/^(/!d           
Then, to turn the TE into CIL, simply do:
# sed -f te2cil.sed source-module.te 
However, I came up with another issue: the order of attributes within a class:
(allow ... (file (open create)))
will not provide the same signature as
(allow ... (file (create open)))
.
Sure enough, I could check all my sources and make sure the attributes are in alphabetical order, but I always prefer automation. So I came up with this awk file which does the trick:
NF>4 {  gsub(/\(|\)/,"");
        for(i=5;i<=NF;i++) A[i-5]=$i;
        n=asort(A);
        C="";
        for(i=5;i<=NF;i++)C=C" "A[i-4];
        delete A;
        print "("$1,$2,$3" ("$4,"("substr(C,2)")))"
}
Hence, getting the signature of the CIL becomes:
# sed -f te2cil.sed source-module.te | awk -f te2cil.awk | sort | md5sum

Finally, I make sure to sort the stored module as well:

# bzcat /var/lib/selinux/targeted/active/modules/400/my-httpd/cil | sed -f te2cil.sed | awk -f te2cil.awk | sort | md5sum

Comments

Popular Posts