danwalsh


Dan Walsh's Blog

Got SELinux?


Previous Entry Add to Memories Share Next Entry
Another blog on writing SELinux Policy - Icecast
danwalsh
The bug report https://bugzilla.redhat.com/show_bug.cgi?id=561817 triggered me to write a blog on writing policy.  Some people think writing SELinux policy is difficult. I figured I would show how quickly it can be done.

The bug report shows rtkit-daemon trying to setsched on a process running with the type intirc_t.  initrc_t is the default domain for all init script process and programs run from them.  By default n targeted policy, initrct_t is an unconfined domain.  We would prefer all daemons in Fedora have a policy defined.   I asked the bug reporter what bug caused the AVC.  He identified the icecast daemon as one of the initrc_t deamons.  Here is how I wrote the policy for it.

Note: This example was done on F13/Rawhide but it could have been done on Fedora 12 or Red Hat Enterprise Linux 6, when it ships.

Knowing nothing about icecast, I figured I could get a pretty good start writing policy using sepolgen and rpm.

rpm -qi icecast
...
Summary     : ShoutCast compatible streaming media server
Description :
Icecast is a streaming media server which currently supports Ogg Vorbis and MP3 audio streams. It can be used to create an Internet radio station or a privately running jukebox and many things in between.  It is very versatile in that new formats can be added relatively easily and supports open standards for communication and interaction.


I login as root and cd /root
# sepolgen /usr/bin/icecast
Created the following files:
Type Enforcement file     ./icecast.te
Interface file            ./icecast.if
File Contexts file        ./icecast.fc
Setup Script              ./icecast.sh


This is the initial icecast.te file generated by sepolgen
policy_module(icecast,1.0.0)

########################################
#
# Declarations
#

type icecast_t;
type icecast_exec_t;
init_daemon_domain(icecast_t, icecast_exec_t)

permissive icecast_t;

type icecast_initrc_exec_t;
init_script_file(icecast_initrc_exec_t)

type icecast_var_run_t;
files_pid_file(icecast_var_run_t)

type icecast_log_t;
logging_log_file(icecast_log_t)

########################################
#
# icecast local policy
#

allow icecast_t self:capability { setgid setuid };
allow icecast_t self:process { fork signal };

# Init script handling
domain_use_interactive_fds(icecast_t)

# internal communication is often done using fifo and unix sockets.
allow icecast_t self:fifo_file rw_fifo_file_perms;
allow icecast_t self:unix_stream_socket create_stream_socket_perms;

files_read_etc_files(icecast_t)

miscfiles_read_localization(icecast_t)

manage_dirs_pattern(icecast_t, icecast_var_run_t,  icecast_var_run_t)
manage_files_pattern(icecast_t, icecast_var_run_t,  icecast_var_run_t)
files_pid_filetrans(icecast_t, icecast_var_run_t, { file dir })

manage_dirs_pattern(icecast_t, icecast_log_t,  icecast_log_t)
manage_files_pattern(icecast_t, icecast_log_t,  icecast_log_t)
logging_log_filetrans(icecast_t, icecast_log_t, { file dir } )

auth_use_nsswitch(icecast_t)

sysnet_dns_name_resolve(icecast_t)

Looks good, but before I continued I wanted to know if I could find any add
Now I decided to look at the output of rpm -ql icecast

# rpm -ql icecast
/etc/icecast.xml
/etc/logrotate.d/icecast
/etc/rc.d/init.d/icecast
/usr/bin/icecast
/usr/share/doc/icecast-2.3.2
...
/usr/share/icecast
/usr/share/icecast/admin
/usr/share/icecast/admin/listclients.xsl
...
/usr/share/icecast/web
/usr/share/icecast/web/auth.xsl
....
/usr/share/man/man1/icecast.1.gz
/var/log/icecast
/var/run/icecast


/etc/logrotate.d/icecast cought my eye, so I investigated. 

# cat /etc/logrotate.d/icecast
/var/log/icecast/*log {
    missingok
    notifempty
    sharedscripts
    postrotate
    /bin/kill -HUP `cat /var/run/icecast/icecast.pid 2>/dev/null` 2> /dev/null || true
    endscript
}


I know that logrotate runs as logrotate_t, it will need to send a signal to icecast_t.  I need to add an interface to icecast.if for this

########################################
## <summary>
##    Allow domain signal icecast
## </summary>
## <param name="domain">
##    <summary>
##    Domain to not audit.
##    </summary>
## </param>
#
interface(`icecast_signal',`
    gen_require(`
        type icecast_t;
    ')

    allow $1 icecast_t:process signal;
')


In icecast.te I added the following

gen_require(`
    type logrotate_t;
')

icecast_signal(logrotate_t)


Although I will eventually put this into logrotate.te

I also noted /usr/share/icecast in the icecast package seems to be apache content.

/usr/share/icecast
/usr/share/icecast/admin
/usr/share/icecast/admin/listclients.xsl
...
/usr/share/icecast/web
/usr/share/icecast/web/auth.xsl
....

I figured I would add
/usr/share/icecast(/.*)?        gen_context(system_u:object_r:httpd_sys_content_t,s0)
to icecast.fc for now, eventually this should go in apache.fc in the policy package.

Finally the original idea for writing this policy was caused by the AVC
allow rtkit_daemon_t initrc_t:process setsched;   
But I am creating a policy that will label icecast as icecast_t rather then initrc_t.  I need to look for an interface that allows rtkit_daemon_t to setsched.  Upstream SELinux Policy will not accept any "te" files that include types that are defined in a different policy file (rtkit_daemon_t).  This means, I need to find an interface that will allow rktkit_daemon_t to setched on my process type, icecast_t.  SELinux interface definitions are installed in the /usr/share/selinux/devel/include directory tree.  Grepping through this for the correct interface, I find:

grep -R rtkit_daemon_t.*setsched /usr/share/selinux/devel/include
/usr/share/selinux/devel/include/services/rtkit.if:    allow rtkit_daemon_t $1:process { getsched setsched };


Looking into
/usr/share/selinux/devel/include/services/rtkit.if, I see this access is granted using the interface rtkit_daemon_system_domain, which   I add to icecast.te.

optional_policy(`
         rtkit_daemon_system_domain(icecast_t)
')

The optional_policy block will allow my policy to be installed systems that might not have rtkit policy defined.

Compile and install the policy.

# sh icecast.sh
Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile
Compiling targeted icecast module
/usr/bin/checkmodule:  loading policy configuration from tmp/icecast.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 10) to tmp/icecast.mod
Creating targeted icecast.pp policy package
rm tmp/icecast.mod tmp/icecast.mod.fc
+ /usr/sbin/semodule -i icecast.pp


I also want to set the correct apache labels for icecast.

# restorecon -R -v /usr/share/icecast/
restorecon reset /usr/share/icecast context system_u:object_r:usr_t:s0->system_u:object_r:httpd_sys_content_t:s0
...

# service icecast start
Starting icecast streaming daemon:                         [  OK  ]
# service icecast stop
Shutting down icecast streaming daemon:                    [  OK  ]

The daemon runs and creates avc messages.  Now I use the audit2allow command to generate allow rules.

# audit2allow -la
#============= icecast_t ==============
allow icecast_t self:capability { sys_nice dac_override };
allow icecast_t self:process { setsched getsched };
#!!!! This avc can be allowed using the boolean 'allow_ypbind'

allow icecast_t self:tcp_socket listen;
allow icecast_t soundd_port_t:tcp_socket name_bind;


While audit2allow suggested the allow_ypbind boolean, I realize that is not necessary.   lets examine the allow rules.
allow icecast_t self:capability { sys_nice dac_override };
allow icecast_t self:process { setsched getsched };
The sys_nice, getched and setsched  permissions are caused by icecast_t setting its priority, which is probably ok.  dac_override means that UID protection is being overridden.  Looking closer at icecast I notice that the daemon runs as the icecast user and the avc is probably caused by writing to the /var/run/icecast directory.  If this directory when owned by icecast, it probably would not generate this avc, but I am not sure.  I will allow it for now.  And open a bug with the icecast team to see if the directory should be owned by icecast.

allow icecast_t self:tcp_socket listen;
allow icecast_t soundd_port_t:tcp_socket name_bind;

Since rpm -qi told me that icecast is a "streaming media server", Icecast listening on a soundd_port seems legitimate. 

I will add full permissions to the tcp_socket to icecast_t by adding the line.

allow icecast_t self:tcp_socket create_stream_socket_perms;

  
Note: SELinux permission macros are defined in /usr/share/selinux/devel/include/support/obj_perm_sets.spt.   The sepolgen generated policy gave Icecast the ability to connect to tcp_sockets via the macro sysnet_dns_name_resolve(icecast_t).  This macro allows icecast_t to connect to the dns port via tcp_socket. 

I can not use soundd_port_t in my te file since I did not define this type.  I need to find an interface that allows icecast_t to bind to the soundd_port_t tcp port.   Grepping through the SELinux interface files for the correct interface, if find:

# grep -R interface.*soundd_port /usr/share/selinux/devel/include | grep tcp | grep bind
/usr/share/selinux/devel/include/kernel/corenetwork.if:interface(`corenet_tcp_bind_soundd_port',`
/usr/share/selinux/devel/include/kernel.xml:<interface name="corenet_tcp_bind_soundd_port" lineno="59287">


corenet_tcp_bind_soundd_port looks like the correct interface.

corenet_tcp_bind_soundd_port(icecast_t)

After I compile and install my policy,  I restart the daemon and get no additional avc messages.  
If I knew how to test icecast, I would run all lots of tests on it and and gather the avc messages.  But I have Rawhide :^).  I can just add my policy into rawhide and have people who actually use icecast generate AVC messages.  Since the icecast policy runs in permissive mode, SELinux should not break icecast while I gather avc messages.

Total time to write this policy was less then a half hour.  It took me a lot longer to write this blog.



 

Is there a way to deny explicitly some actions? I wrote this module for instance:

#========START POLICY =====#
module unserverd 1.0;

require {
class tcp_socket { create name_bind listen write accept };
class udp_socket{ create name_bind listen write accept };
type staff_t;
type unreserved_port_t ;
type ephemeral_port_t;
attribute domain;
}
neverallow staff_t unreserved_port_t: tcp_socket{ create name_bind listen write accept};
neverallow staff_t unreserved_port_t: udp_socket{ create name_bind listen write accept};
neverallow staff_t ephemeral_port_t: tcp_socket{ create name_bind listen write accept };
neverallow staff_t ephemeral_port_t: udp_socket{ create name_bind listen write accept };

#========END POLICY =====#

but i can still bind, no avc denials in the log. Should i recompile-reinstall the whole policy for the policy compiler to see my module? However using auditallow logs my name_bind to a port.

Re: Explicit deny rule

danwalsh

2014-02-17 04:04 pm (UTC)

No. SELinux does not have a deny. It is a deny everything system and then you write allow rules.

neverallow are more like "C" assertions. They will blow up the compile when people add policy to allow staff_t to listen on these ports.

We do not process Neverallow rules in the shipping product on in the base build. They cause the policy compile to take too long.

There is a boolean for controlling this in Fedora/RHEL7 selinuxuser_tcp_server
user_tcp_server on RHEL6.

# sesearch -A -s staff_t -t unreserved_port_t -C -p name_bind
Found 4 semantic av rules:
allow staff_usertype unreserved_port_t : udp_socket name_bind ;
DT allow staff_usertype unreserved_port_type : tcp_socket name_bind ; [ selinuxuser_tcp_server ]
DT allow nsswitch_domain unreserved_port_t : tcp_socket { name_bind name_connect } ; [ nis_enabled ]
DT allow nsswitch_domain unreserved_port_t : udp_socket name_bind ; [ nis_enabled ]


You are viewing danwalsh