Rate this page del.icio.us  Digg slashdot StumbleUpon

A step-by-step guide to building a new SELinux policy module

by Dan Walsh

Who’s afraid of SELinux? Well, if you are, you shouldn’t be! Thanks to the introduction of new GUI tools, customizing your system’s protection by creating new policy modules is easier than ever. In this article, Dan Walsh gently walks you through the policy module creation process.

A lot of people think that building a new SELinux policy is magic, but magic tricks never seem quite as difficult once you know how they’re done. This article explains how I build a policy module and gives you the step-by-step process for using the tools to build your own.

Before we start, let’s review why we work with policy modules. In the past, in order to modify the current SELinux policy on a system running Red Hat Enterprise Linux 4, a system administrator would have had to to download the policy source, edit the policy source code, and rebuild and install the policy using tools like make install. The introduction of policy modules made this process easier and less error-prone. A system administrator could use the audit2allow utility to generate policy module updates directly from audit.log error messages. These modules function in a way similar to kernel modules in that they enable system administrators to modify part of the policy (a specific module) without having to rebuild the entire thing.

Remember to start small

One reminder! When you start to write policy, start small. Often, people send me email telling me they want to write SELinux policy and then they choose to write it for something huge like Firefox. When you decide to write policy for an application, you should have an idea of what the application does and what your security goals are for the application.For example:

  • Least privilege.
    An application's security goals are often based on “least privilege,” meaning that the application is only allowed to do the things it was designed to do. For example, FTP isn't allowed to talk to the telnet port.
  • Modified privilege.
    Sometimes your security goal might be to give the application less privilege than it was designed to have. This is for when you want to prevent Firefox from writing to your home directories.
  • Booleans. You might want to add booleans so your end user could modify the security policy depending on his security goals for the application. The best example of this is FTP servers. Most people run anonymous FTP servers, but some want full access to the users' home directories. I can write policy to satisfy both users and have a boolean to arrive at least privilege.

Since simple daemon applications usually have security goals close to what the application is designed to do,a good place to begin writing policy is for daemons started during the system startup routines or CGI scripts. Avoid writing policy for user applications until you thoroughly understand SELinux and your security goals for that application.

For this article I will describe confining the rwho daemon. This daemon generates output similar to who, but for users logged in to hosts on the local network. It's helpful (but not necessary) if policy writers have an intimate understanding of the applications they're going to confine.You certainly don't have to know every application you confine.

The new policy generation GUI tool: polgengui

To build things, you need tools. Starting with RHEL5 and Fedora 7, I began building a GUI tool to help people generate templates for policy generation. I examined the upstream policy and built templates around how that policy is written. The goal was to easily get policy upstreamed to get the maximum usage. So I added the polgengui tool to the system-config-selinux utility. This tool generates four files:

  • Type enforcement (te) file--contains all of the code used to confine my application
  • File context (fc) file--contains the mappings between files and file context
  • Interface (if) file--contains all of the interfaces that other domains might want to use to communicate with my domain and the file types created by my applications.
  • Shell script (sh)--used to compile, install and fix the labeling on the test system.

Now let's start writing policy. There are three main steps that we'll perform.

Step 1 – Use system-config-selinux to create a new policy module

Start by running system-config-selinux:

system-config-selinux screenshot

To begin building a new policy module, click the New button to run the SELinux Policy Generation Druid.

policy generation druid screenshot

This druid is a wizard that chunks the policy creation process into seven quick dialog boxes:

  • Name of application to be confined
  • Application type
  • Incoming network port connections
  • Outgoing network port connections
  • Common application traits
  • Files and directories (where the application will write)
  • Generate policy in this directory

Name of application to be confined

application name screenshot

This screen will prompt you for a name for your confined application and the path to the executable used to start it. The tool will use this information to create two SELinux types, rwho_t and rwho_exec_t. The running process (domain) will use the rwho_t type. File context on disk will use rwho_exec_t.

Application type

application type screenshot

This screen asks you to identify the type of application. This allows you to set up all of the policy to correctly transition from other domains. The supported application types are:

  • Standard Init Daemon. These applications are started during the init process either by rc.sysinit directly or as a start up script in /etc/init.d.
  • Internet Services Daemon. These applications are started by inetd or xinetd.
  • Web Application/Script (CGI). These Applications are actually executed by apache as separate process. This type does not currently work for applications that run in-process, like mod_perl or mod_php.
  • User Application. These applications are usually started by a logged in user.

In the future we will be adding a mechanism for writing policy to actually confine a logged in user.

Incoming network port connections

incoming network connections screenshot

This screen allows you to enter a space-separated list of network ports that the application will bind/listen on for incoming connections. If you're not sure, you can leave this blank and come back to it later.

In this screenshot, rwho will listen on UDP port 513.

Outgoing network port connections

outgoing network connections screenshot

This screen allows you to specify TCP and UDP ports that the confined application needs to connect to. Because rhwod doesn't connect to any ports, I leave it blank.

Both of the ports screens search through the existing policy to see if a type is already defined for that port number. If the port is defined, it will write the appropriate interface to use the port. If the port is not currently defined, polgengui will generate a new type for the port and the appropriate interface. If you define a new port, you will need to run some semanage commands to define the ports during install. The sh script file that will be generated by polgengui will contain the correct semanage command.

Common application traits

common application traits screenshot

This screen allows you to specify some common traits that applications exhibit. Checking these boxes will add policy to your template and allow the application to perform the selected functions. If you are not sure whether your applications need any of these, leave them blank, and polgengui will generate the policy by running the application. If you have access to the source a you can use grep to quickly find out if the application requires any of these. For example, grep -r syslog . will tell whether or not your application uses syslog, and grep -r getpw . will tell whether or not you want to use nsswitch.

Files and directories

This screen allows you to designate files and directories where the confined application needs write privileges. The tool looks at the paths that you enter and uses them to establish the name of the type to use. It is doing this based on the conventions established in the reference policy. So files stored under /var/log should be labeled rwho_log_t, and files/directories stored under /var/spool should be labeled rwho_spool_t. In order to get the regular expression labeling correct, polgengui asks you to differentiate between files and directories. Files/directories do not have to exist before running the application. Most new paths added to this screen will result in a new type being generated.

Note: One common mistake people make when writing policy is the over-generation of types. File context types should be generated only for files/directories that are owned and written by the confined application. If the file/directory is only read by the application, you are better off leaving it as the default file context. So conf files in /etc/ should usually be left as etc_t. Files with sensitive security data are the exception. Something like a credit card database should not be labeled etc_t, even if the confined application treats it as read-only.

Generate policy in this directory

generate policy in this directory screenshot

This screen asks you where to put the tool's output. It will default to the current working directory, but often you are better off putting your policy files in a separate directory.

Generated policy files

generated policy files screenshot

The tool will confirm what you've asked it to do. Click Apply to generate the files, and polgengui will display the results:

results screenshot

Warning: The tool will continue running, and you can go back through the steps again, but it will overwrite these four files if you hit the apply button again.

Step 2 – Apply the new policy module

Now that you have the policy files, it's time to apply them to the current policy. My method is to use a terminal window, log in as root, and execute the shell script generated by polgengui.

# cd /home/devel/dwalsh/tmp
# sh rwho.sh
sh rwho.sh
Compiling targeted rwho module
/usr/bin/checkmodule:  loading policy configuration from tmp/rwho.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 6) to tmp/rwho.mod
Creating targeted rwho.pp policy package
rm tmp/rwho.mod tmp/rwho.mod.fc
/sbin/restorecon reset /usr/sbin/rwhod context system_u:object_r:bin_t:s0->system_u:object_r:rwho_exec_t:s0
/sbin/restorecon reset /var/log/rwhod context system_u:object_r:var_log_t:s0->system_u:object_r:rwho_log_t:s0
/sbin/restorecon reset /var/spool/rwho context system_u:object_r:var_spool_t:s0->system_u:object_r:rwho_spool_t:s0
/sbin/restorecon reset /var/spool/rwho/whod.patriots context system_u:object_r:var_spool_t:s0->system_u:object_r:rwho_spool_t:s0
/sbin/restorecon reset /var/spool/rwho/whod.dhcppc0 context system_u:object_r:var_spool_t:s0->system_u:object_r:rwho_spool_t:s0 

The shell script compiles the module that you just created, then loads it into the kernel. It then fixes the file context labels with the restorecon command.

Step 3 – Debug and complete the policy module

So far, so good. You created and applied the policy. What I always do next, and what you should do, too, is to verify the policy module. The best approach to use is to put the machine in permissive mode and allow the application to generate AVC messages, which you can in turn use to debug and fill in any gaps in the new policy module that you just created. This is an iterative process, but it's the safest way to ensure that the policy is complete and correct.

Note that you should not do this on a production machine, as you will lose your SELinux protection when you run in permissive mode. You could generate AVC messages one at a time in enforcing mode, but it would be very time consuming.

# setenforce 0
# service rwho restart
Stopping rwho services:                                  [FAILED]
Starting rwho services:                                    [  OK  ]
# >>> Run some commands to get rwho to execute.

Now I use audit2allow to generate policy.

# grep rwho /var/log/audit/audit.log | audit2allow -R
require {
        type initrc_var_run_t;
        type rwho_t;
        class capability sys_chroot;
        class file { read write getattr lock };
}

#============= rwho_t ==============
allow rwho_t initrc_var_run_t:file { read write getattr lock };
allow rwho_t self:capability sys_chroot;
kernel_read_system_state(rwho_t) 

It searches through the installed policy interfaces files on disk and attempts to find the best match for the AVC messages that were generated. These interface files are installed under the /usr/share/selinux/devel directory. In this case, it kernel_read_system_state(rwho_t). Note that any AVC that requires a domain to interact with itself will include the self qualifier.

The tool did not find an interface to match the AVCs that generated:

allow rwho_t initrc_var_run_t:file { read write getattr lock }; 

Looking at the AVCs that were generated in the /var/log/audit/audit.log, we see:

type=AVC msg=audit(1184874740.520:1685): avc:  denied  { read write } for  pid=18340 comm="rwhod" name="utmp" dev=dm-0 ino=3178503 scontext=system_u:system_r:rwho_t:s0 tcontext=system_u:object_r:initrc_var_run_t:s0 tclass=file

type=PATH msg=audit(1184874740.520:1685): item=0 name="/var/run/utmp" inode=3178503 dev=fd:00 mode=0100664 ouid=0 ogid=22 rdev=00:00 obj=system_u:object_r:initrc_var_run_t:s0 

These two things indicate that the rwho daemon is trying to read/write the /var/run/utmp file. (Note the read write getattr lock reference.) From personal experience, I know that the library for interacting with the utmp file always attempts to open it read/write and then falls back to read-only. You also don't want rwho to be able to write over the utmp file, because it could contains some important security data. If you search through the /usr/share/selinux/devel directories, you'll find two interface calls:

init_read_utmp(rwho_t)
init_dontaudit_write_utmp(rwho_t)

The first interface will allow rwho to read the utmp file, the second interface tells the kernel to stop generating AVC messages when rwho attempts to write to utmp.

Now you can execute the sh script again and set the machine back in enforcing mode. Watch for additional AVC messages. Rebooting the machine can often generate additional AVC messages that weren't generated from simply restarting the service. Repeat the process until there are no new AVC messages.

As soon as you're happy with the way the policy is working, add it to the policy rpm and release it to Rawhide. There, people will happily (maybe not that happily) report back any problems that SELinux is causing their application. (Eventually I also publish the policy to upstream and ask for critique.) Open Source people will then tell me the mistakes that I have made in my policy. It's just like Eric Raymond said in "The Cathedral and the Bazaar," “Given enough eyeballs, all bugs are shallow.”

What's next?

What's next for you is to try out this step-by-step process for building a new policy module. You'll no doubt see AVC messages unique to your own target applications, but if you take your time and make use of audit2allow, you'll be able to create your own new policy modules.

For me? The next article in this series will describe locking down userspace.

Acknowledgments

Thanks to Len DiMaggio for help editing and ideas for content.

Useful references

danwalsh.livejournal.com
SELinux by Example, Prentice Hall

About the author

Dan Walsh has over 20 years experience in the computer field. He has spent most of his career working on Security Applications and platforms. He spent several years working on the Athena Project while at Digital Equipment Corp. Dan was also involved in designing and developing the AltaVista Firewall and AltaVista Tunnel (VPN) Products. He has worked for Netect developing HackerShield, a Vulnerability Assessment Product. Netect was acquired by BindView, where he continued working on HackerShield and developed a new product, BVControl for UNIX. At Red Hat, Dan has led the SELinux project, concentrating mainly on the application space and policy development. Dan Graduated with a BA in Mathematics from the College of the Holy Cross and a MS in Computer Science from Worcester Polytechnic Institute.

15 responses to “A step-by-step guide to building a new SELinux policy module”

  1. Simon Farnsworth says:

    One thing that wasn’t made clear in this article (although it was included implicitly), but that all policy writers should be aware of: audit2allow output should not be trusted blindly.

    audit2allow tells you how to let the application do what it’s just tried to do; before you use its output, you *must* check that the application is meant to be able to do what it’s just tried to do. Otherwise, you may open up more privileges than the application needs.

    In the article, this is demonstrated when rwho tried to open utmp read/write; audit2allow suggested allowing this, but a quick check showed that allowing read-only access, and not auditing read/write access was what was required. Had the policy allowed rwho write access to utmp, a malicious user could exploit a flaw in rwho to remove entries from utmp.

  2. rhmag says:

    what happens if you run rwho.sh again? For example, what if you want to edit the current rwho policy, can you just run the new rwho.sh and that overwrites the current rwho policy?

  3. Dan Walsh says:

    Simon is correct. That the policy that is generated by audit2allow should be examined for correctness in a security sense. You should also use the open source method, or showing you policy to other more experienced policy writers. And getting there comments. Always watch out for policy that makes no sense such as a confined daemon trying to write to a file/directory that you know it shouldn’t. Leaked file descriptors can sometimes cause these avc messages.

    rwho.sh can be run repeatedly. It will replace the existing policy module with a new one and will run restorecon again.

    If you change or remove a file path, the shell script will not necessarily catch this.

  4. Gerwin says:

    What if you don’t run your servers in X mode? Will there be a -nox version too? I could not make that out of your (interesting) story.

    -Gerwin

  5. Dan Walsh says:

    This is just a tool for building the policy files, so you do not need to run this on servers. You would ship the policy files to the servers and install them, as a mater of fact you could copy the four files that you generate to a server and then run the shell script. So currently we do not plan to build a nox version, but building a script version of this tool would be fairly easy for a python hacker.

  6. Gerwin says:

    Oke thanks Dan, as I’m fairly new to SElinux I didn’t know it was that easy :)

    Cheers

  7. Torbjorn Lindahl says:

    I’m a bit puzzled..

    I am writing a new application that I want to tie down as strict as reasonable using SELinux.

    the gui worked nicely and set up the files needed to compile the selinx module. (i just had to add “type myapp_t;” since that did not already exist)

    However… it also granted the app
    corecmd_executable_file(myapp_exec_t)

    And trial and error concluded that this one single rule allowed the app to do whatever it pleased… All other selinux settings, network ports etc.. as generated by the GUI had no real effect as long as that one single rule was in the source.

    If i remove that one particular rule and run audi2dallow, it suggests:
    allow unconfined_t myapp_exec_t:file { ioctl relabelfrom execute read getattr execute_no_trans };

    .. which by my limited understanding seems way to broad, ie basically the let the app do anything. Wtih that one rule in the source the app was again allowed to do anything.

    where do I go from here..

  8. Dan Walsh says:

    You need to add a domain transition rule to the policy.
    Since you want to transition from the unconfined_t domain to the myapp_t.

    An interface should have been created in the if file called
    myapp_domtrans.

    So you could add to your te file,

    gen_require (`
    type unconfined_t;
    ‘)
    myapp_domtrans(unconfined_t)

    Of you could simply add these lines to your code.

    domain_auto_trans(unconfined_t,myapp_exec_t,myapp_t)

    allow unconfined_t myapp_t:fd use;
    allow myapp_t unconfined_t:fifo_file rw_file_perms;
    allow myapp_t unconfined_t:process sigchld;

  9. Torbjørn says:

    Hello, thank you for the answer. That was ofcourse the reason.

    I have been reading alot of your journals and howto’s on the web and fine them extremely usefull, thank you very much.

    I’ll address the mailing list for more questions, seems like a better fora for more basic issues like this.

  10. lgm says:

    I am struggling to get away from Windows and all the nice click options. I have just spent days building up a working Fedora 8 server with MySQL php and Apache, all working, all the chmods sorted and so on. Now I want to set up Zen Shopping cart which I have running fine on a Windows box. (I want to get into Apache POI java for an Excel application and windows is not the place to do this).

    BLAM, I am bu…red. SeLinux has decided to to stop ZenCart talking to MySQL. I can understand why but now I have to spend most of another day making a policy file.

    Will you guys who write this stuff please learn that normal people will never get seriously into Linux while you leave all these little exercises in masochism in the software. Bill Gates must be laughing all the way to the bank yet again.

    Fortunately I do not need SELinux at home so out it goes for now.

  11. Dan Walsh says:

    setsebool -P httpd_can_network_connect_db=1

    If you were running setroubleshoot, it would have told you this.

  12. Karanbir Singh says:

    Just want to point out that ( atleast on RHEL5 and clones ), the package you need to install in order to get system-config-selinux is policycoreutils-gui

  13. bhrugu says:

    i would like to work with new reference policy enhancement

  14. Tux Training » Blog Archive » A step-by-step guide to building a new SELinux policy module says:

    [...] Source [...]

  15. The Linux Guide - A must be ! - WHM/cPanel Support Platform says:

    [...] [...]