The purpose of this tutorial is to teach the basic concepts and operations of administering SE Linux on a Red Hat Enterprise Linux or Fedora system. The tutorial is based around RHEL4 as it is the most recently released. SE Linux administration will be done in the same way in both Fedora and RHEL with only minor differences due to different versions (Fedora and RHEL releases are not synchronised and there are minor version differences of the packages that comprise them). What you learn today can be used on Fedora Core 4 when it is released (test releases should be available soon), and can be used on Fedora Core 3.

Please note that Fedora Core 3 has earlier versions of some programs and will have minor differences.


Learn how to use all the administrative commands that relate to SE Linux or which are modified for SE Linux.

Perform basic sys-admin operations such as adding a new user on a SE Linux system and learn about the extra steps that may be required to take full advantage of SE Linux features.

Learn how the interfaces between SE Linux aware applications and the kernel work. This will include some of the low level interfaces between applications and the kernel, the aim is that when strace and similar tools show system programs performing SE Linux operations you will have a rough understanding of what they are doing.

Learn the difference between targeted and strict policies. Most of the tutorial will be concerned with the strict policy even though it is not the default, this is because the strict policy is more technically demanding. Once you have learned about the strict policy the targeted policy will be much easier to understand.

Recover a system when the SE Linux configuration has been broken.

Learn the basics of writing SE Linux policy.

After completing this tutorial you should be able to install and administer a SE Linux machine without any assistance.


This tutorial is designed to take more than three hours. Some delegates may not complete all the material in the given time. This is not a problem, I have intentionally put more material than I expect most people to complete. The most important material is in the first half of the tutorial.


You should have already logged in to a RHEL4 machine running SE Linux by the time you read this. It was installed via a ghost image from a DVD, the image is of an everything install of RHEL4-AS.
If you break the configuration of your machine in a way that can not easily be fixed then it can be reinstalled in a small amount of time. Don't be afraid to experiment, if you are going to break a machine then this is a good one to break.

When I install such machines I use the Red Hat kickstart facility to do an automatic network install. Contact me after the tutorial if you want a copy of the kickstart configuration for installing such machines.

Go to the Applications menu, select System Tools and then select Terminal to open a terminal. Almost everything you do will be text based.

SE Linux basic concepts

SE Linux has at it's core a security model known as domain-type. Every process has a domain and every object that a process may access has a type. The domains are actually a sub-set of the type name-space (a process can be the target of an operation such as sending a signal).

Every process or object in the system has a security context that has three fields, an identity, a role and finally the domain or type . They are represented as a text string with the three fields separated by colons in the form identity:role:domain or identity:role:type.

The main part of the SE Linux policy is a set of rules determining what access each domain has to each type. The objects that may be accessed are divided into various classes, file, directory, etc. It is possible (and common) to grant a domain different access to the various object classes that may have a given type. For example writing to a file of a particular type may be permitted while writing to a directory of the same type is not.

A) System Information

  1. Run the command id and notice that the SE Linux context of the process is displayed along with the UID/GID information. The context is root:system_r:unconfined_t and is comprised of three parts, the identity of root, the role of system_r and the domain of unconfined_t.
    Now run the command id -Z and note that only the SE Linux context is displayed (suitable for scripts).
  2. Run the command ps axZ | less and note that the SE Linux context is displayed for each process. Note that most processes run in domain unconfined_t which means that SE Linux does not restrict any operations that they perform.
    The programs that are not running in unconfined_t are httpd, named, portmap, and syslogd. They have their own domains because they are daemons which accept data from the network and therefore are vulnerable to attack.
  3. The ls program has an option -Z to display the SE Linux security contexts of file system objects. Run the command ls -lZ / to see the contexts of the subdirectories of the root directory.
    Note that the directories /proc, /selinux, and /sys do not have a security context listed. The file systems mounted on those directories do not support the interface that is used by ls to query the context. See section C for details.
    Also you will notice that /lost+found has no label, to fix this run restorecon -v /lost+found , note that the -v option is to display verbose status information.

B) Domain transitions

Run the command ps axZ | grep portmap, then the command /etc/init.d/portmap restart and then run ps axZ | grep portmap again and note that the identity of the portmap process has changed from user_u to root.

Run the command ls -lZ /sbin/portmap and observe that the type of the file is portmap_exec_t.

When a process is executed the default action is that all parts of the security context will remain the same. A key feature of the SE Linux is the domain transition, in this case there is a policy rule specifying that when the domain unconfined_t runs a program of type portmap_exec_t the process should transition to domain portmap_t. The original context was root:system_r:unconfined_t, so when the domain changes to portmap_t we get the context root:system_r:portmap_t.

The fact that the portmap program has a different identity when run by the administrator than when it is run from the system boot scripts makes no difference to the operation of the system, later sections of this tutorial will deal with this issue in more detail.

C) Interfaces to the SE Linux kernel code

  1. Run the command cd /proc/self/attr and then the command ls -l, the files that you see provide the main interface between an application and the SE Linux kernel code.
  2. Run the command cat current and the command id -Z, observe that the output of both commands is the same (apart from the fact that the cat output is missing a new-line character). The algorithm for id -Z is to check that SE Linux is enabled, if SE Linux is enabled it displays the contents of /proc/self/attr/current.
  3. Run the command runcon user_u:system_r:unconfined_t id -- -Z and observe that the identity that is reported is user_u as opposed to running id -Z from the command-line which gives an identity of root. The program runcon is used to run a program in a different security context. It does this by writing the desired new context to the file exec.

    Run the command echo -n user_u:system_r:unconfined_t > exec and then run the command id -Z, note that the identity is reported as being user_u now. The -n parameter to echo is very important, otherwise a newline character will be appended and the kernel will refuse the operation.
    Note that this setting is on a per-process basis, changing it has no affect on the rest of the system or other processes run in the same context.

    It is impossible to undo this operation because echo -n "" > file is optimised by bash to be a no-op, so you must now close your terminal window and open a new one to reverse this change.

    In normal operation you will never need to directly access files under /proc/self/attr, and even using runcon is not common.

    The most common use of this interface is to allow cron, login, sshd, and gdm to launch a session in the context of the user.

  4. Run the command echo -n > /tmp/test and the command ls -lZ /tmp/test, observe that the context of the created file is root:object_r:tmp_t. Files always have the role of object_r. When a file is created it defaults to having the identity of the creating process and the type of the directory that the file is being created in. This can be changed, run the command echo -n system_u:object_r:user_home_t > /proc/self/attr/fscreate to set the context of created files to system_u:object_r:user_home_t, then run echo -n > /tmp/test2. Run ls -lZ /tmp/test2 and observe that the newly created file has the context you set.

    Once again you have to exit the shell as bash will not let you undo the operation.

  5. The final entry in the /proc/self/attr directory is the file prev, this file has the context of the calling process. Run the command cat /proc/$$/attr/prev and observe that it has the same context as the shell (it will be the context of one of the GNOME processes run for your session).

    Use ssh localhost to start a new session and then run cat /proc/$$/attr/prev, observe that the reported context is user_u:system_r:unconfined_t, now run ps axZ | grep sshd and observe that sshd also has the context user_u:system_r:unconfined_t.

    Some programs need to check the context of their parent process to determine whether certain operations are permitted.

  6. The security contexts of files on regular file systems (Ext2, Ext3, XFS and tmpfs at the moment and ReiserFS in the near future) are stored using the XATTR interface. The XATTR interface allows associating a number of name:value pairs with a file system object. SE Linux uses the XATTR name security.selinux to store it's security contexts. To see how this is done compare the output of ls -lZ / with that of getfattr -n security.selinux /* .

    The devpts filesystem exports an XATTR interface to allow an SE Linux aware login program to change the context of the terminal device node for a remote login. Currently none of the other virtual file systems implement the XATTR interface. All file system objects are labeled, but the labels can not be queried by applications.

D) Introduction to Booleans

  1. Run the command system-config-securitylevel & (run it in the background so we can do other things with the terminal window). Notice that there are two tabs, one for Firewall Options and one for SE Linux. Select the SE Linux tab. The box at the bottom of the screen labeled Modify SE Linux Policy is used for changing the value of booleans.
  2. Booleans can be used to change the configuration of the SE Linux policy at run-time, for each boolean there are two sets of policy rules and the value of the boolean will determine which one is active.

    In the terminal window run the command getsebool -a to get a list of the state of all booleans. Note that the httpd_disable_trans boolean is inactive.

  3. Run the command cat /etc/selinux/targeted/booleans to see the state of each boolean that will be used on the next system boot. Note that httpd_disable_trans has the value 0 which corresponds to being inactive.
  4. Go back to the Security Level Configuration window, expand the HTTPD Service section and then select the check-box next to Disable SE Linux protection for httpd daemon. Now click on OK to apply the changes and close the window.
  5. Run the command getsebool httpd_disable_trans and note that it is now active.
  6. Run the command grep httpd_disable_trans /etc/selinux/targeted/booleans and note that it is now configured to be active on the next boot as it's assigned the value of 1.
  7. Run the command ps axZ | grep httpd and observe that the httpd processes are running in the domain httpd_t.
    Run the command /etc/init.d/httpd restart and then run ps axZ | grep httpd. Observe that the httpd processes are now running in the domain unconfined_t. This is the purpose of the httpd_disable_trans variable, to make Apache processes run in the unconfined_t domain to cater for the situation where the administrator can't write policy to permit Apache to perform the necessary actions. This means of course that a bug in Apache can do much more damage.
  8. Run the command setsebool httpd_disable_trans false and then run getsebool httpd_disable_trans, note that you have now changed the state of the boolean at the command line. But when you run grep httpd_disable_trans /etc/selinux/targeted/booleans you will see that if the machine was to be rebooted the boolean would be active.
  9. To apply a boolean setting that is to be active after the next boot use the -P option to setsebol.
    Run the command setsebool -P httpd_disable_trans false . Now run grep httpd_disable_trans /etc/selinux/targeted/booleans and observe that the boot setting has changed. Changing a boolean through the GUI tool gives the same result as setsebool -P.
  10. Run the command ps axZ | grep httpd and observe that the httpd processes are still running in the domain unconfined_t. This boolean affects the domain that is chosen at execution time. After the process has started running it has no affect. Run the command /etc/init.d/httpd restart to make this change take affect. Now run ps axZ | grep httpd and observe that the domain of the httpd processes is once again httpd_t.

E) Changing to the strict policy

The SE Linux policy is the most important part of SE Linux, it is the rules which determine what actions are permitted and what are denied. There are two policies for SE Linux in common use known as targeted and strict. The targeted policy is the default policy for Fedora and the only supported policy for Red Hat Enterprise Linux. The strict policy restricts the actions of more programs but requires more work to administer. There is always a trade-off between security and usability. The targeted policy was developed to be easier to use, but this means that it does not restrict the actions of programs as much as you may desire.

The standard support agreement for Red Hat Enterprise Linux does not cover the amount of work that is needed to support the strict policy. Red Hat support for the strict policy is only provided through a consulting contract, contact your sales representative for more information.

Understanding the strict policy is key to understanding SE Linux, so the next sections of this tutorial will be based around installing and using the strict policy. The targeted policy is a sub-set of the strict policy, so once you have learnt the strict policy the targeted policy will be easy.

  1. Run the command rpm -U /root/*.rpm to install the strict policy. The rpms that are on the system for strict policy are from rawhide, there are no strict policy packages built for RHEL4 at this time.
  2. Run cat /etc/selinux/config to see the boot time configuration of SE Linux. Init will use this file to determine which policy to load and whether to start the machine in enforcing or permissive mode. In enforcing mode SE Linux prevents actions that are not permitted by the security policy, in permissive mode the actions are allowed to proceed and SE Linux merely audits the fact that it is configured to deny them.
  3. Run the command system-config-securitylevel. Select the SE Linux tab again. Note that there is a drop-down list-box labeled Policy Type, select strict, the system will ask you if you really want to do this - select Yes. Click on OK to save the settings and exit system-config-securitylevel.
  4. Run cat /etc/selinux/config to verify that the policy type has been changed to strict.
  5. To change between strict and targeted policies the machine must be rebooted. This is because the context of every process must be changed and most of the files on the disk will also need to be relabeled.

    Run ls -al / and notice that a file named .autorelabel has appeared in the root directory. The system boot script /etc/rc.d/rc.sysinit will see this file and know that it has to relabel all files on the system before proceeding with a regular boot.

    The way it does this is to run the command fixfiles relabel. If you have something go drastically wrong with a SE Linux system you might want to run that command manually, but ideally you should never need to do so.

  6. Reboot the machine now. It will take a bit longer than usual for the next boot and will not be very exciting. Now might be a good time for a coffee break.

F) Using the strict policy

  1. Login as root and then try to run dmesg. Note that you are not permitted to run it. Run id -Z and note that your context is now root:staff_r:staff_t. The staff_t domain has only limited access to the system, it does not permit you to restart daemons or even see them in the output of ps, run ps axZ to verify that you can only see processes you own and not system proceses.

    Play with the system, run reboot, run rm -rf /usr try some other commands that might be expected to break a system. NB do not remove files under your own home directory, that is permitted by the SE Linux policy and will get in the way of the later exercises.

  2. The first thing you want to do is to get administrative access to the system. You do this by running the command newrole -r sysadm_r. This requests that a new shell be started on your behalf with role sysadm_r (the role for system administration). This command requires that you authenticate yourself with your password to prevent a user who gets a shell in context root:staff_r:staff_t from getting immediate administrative access. Enter your password to complete this operation.
  3. Run id -Z and note that your context is now root:sysadm_r:sysadm_t, when you selected the role of sysadm_r you were also given the default domain for that role which is sysadm_t.

    Run dmesg, note that the command now works as you have sufficient access. Also note that many audit messages are in the system message log describing the actions you attempted in the previous section but which were denied. Do not run any rm -rf commands, they will work and such actions will force a reinstall of your machine and delay your tutorial!

  4. Run ps axZ and notice that there are many more domains used for the daemons, almost every daemon gets it's own domain!

G) Configuring the strict policy

  1. Run system-config-securitylevel, select the SE Linux tab, then explore the booleans that are available in the Modify SE Linux Policy box. There is nothing that needs to be changed at this time.
  2. From the sysadm_r:sysadm_r session open from the previous section run the command useradd foo to create a new user and then run passwd foo to change the password. Do the same to add another user bar
  3. Run ssh foo@localhost to launch a shell for user foo, run id -Z and observe that the context is user_u:user_r:user_t.
    Now exit the ssh session.
  4. Change to the directory /etc/selinux/strict/src/policy. Edit the file users and add the following lines:
    user foo roles user_r;
    user bar roles { staff_r user_r sysadm_r system_r };
    When a user logs in the login program (/bin/login, sshd, or gdm) will query the kernel to discover whether there is a SE Linux identity that matches the account name. If there is then that identity will be used for the new session, otherwise the default identity of user_u will be used. The above lines are declaring identities for the users foo and bar which they will be compelled to use when they login. The identity has a list of roles associated with it, the login program and any other program that selects a role for the user can only select from the list of permitted roles. So in this case user foo is only permitted to enter the role user_r and user bar is permitted to enter the roles user_r, staff_r, sysadm_r, and system_r.
  5. Run the command make policy.conf, this concatenates all the files that comprise the SE Linux policy and runs the M4 macro processing system to expand macros in the policy source.
  6. Run make install, this uses checkpolicy to compile the policy.conf file into a binary form that the kernel understands and then copies it to the location on the file system that is used for policy load during the boot process. It then creates and installs the file_contexts file. This file has a series of regular expressions that match objects on the file-system along with the SE Linux contexts that should be assigned to those objects.
  7. Run make load to load the binary policy into the kernel. Note that make install depends on make policy.conf and make load depends on make install. In normal situations you would use make install to compile the policy and install it to be used at the next boot or make load to compile, install and load the policy. For this exercise we run the steps separately to see exactly what they do.
  8. Run dmesg | tail and observe the messages about the policy load. They tell you the number of users, roles, types, bools, classes, and rules.
  9. Open a new window and run newrole -r sysadm_r, then change to directory /etc/selinux/strict/src/policy.

    Run grep ^user policy.conf to see the declarations of the users.

    Run grep "^role " policy.conf to see role declaration statements, roles are defined implicitely when types (domains) are assigned to them. To see a list of unique role names run the command grep "^role " policy.conf | cut -f2 "-d " | sort -u. Note that the number of roles listed is one less than the count reported by the kernel when you loaded the policy. The reason for this is that the checkpolicy tool defines the role object_r for files on disk without it being declared in the policy source.

    This tutorial will not cover classes in any detail.

    The number of rules in the binary policy differs from the number of rules in the policy source as will be explained later.

  10. When you installed the new policy the build process generated a new file_contexts file that specifies the contexts for the users foo and bar. To apply this change you must relabel those home directories, before doing this run ls -alZ ~foo ~bar to see the current contexts.

    Run restorecon -R /home/foo /home/bar to relabel the home directories and recursively relabel the files and directories under them. Now run ls -alZ ~foo ~bar again to see the change.

  11. Open a new terminal window and run ssh foo@localhost, from that session run id to see the context.
  12. Open another terminal window and run ssh bar@localhost, run id and observe tht user bar is given the same role and domain as user foo by default.
  13. Exit the session as user bar and run the command ssh bar/staff_r@localhost, run id from this session and note that you have a different context, the role is staff_r and the domain is staff_t (the default for role staff_r). The SE Linux modifications to sshd allow specifying user/role@host to permit logging in as a non-default role.
  14. From account bar run the command ps axZ and observe that you can't see processes from user foo. We want to allow the staff_t domain to see processes from the user_t domain with ps, to do this we have to change the policy.
    From a session that runs as root:sysadm_r:sysadm_t change to directory /etc/selinux/strict/src/policy. For local additions to policy it's common to use a file named domains/misc/custom.te, this name is one that is reserved to local customisations, a future policy package will never install a file with that name. To allow programs in the staff_t domain to see the existance of processes in the user_t domain create the file domains/misc/custom.te and give it the contents can_ps(staff_t, user_t) . After making that change run the command make load to apply it.
    Now run ps axZ from the session that's logged in as user bar and you will see the processes of user foo that are running in domain user_t.

H) Interfaces to the SE Linux kernel code part 2

  1. Change to directory /selinux and run ls -l, the files and directories you see are used to control the global SE Linux state.
  2. The file enforce is used to switch SE Linux between permissive and enforcing modes. At the moment the machine will be in enforcing mode, run the command cat enforce and observe that it has the value 1. Run the command setenforce 0 and then run cat enforce, note that the value has changed to 0.
    Go to the window that has a session for user foo and run ps axZ, note that you can see all processes.
    From a root window run setenforce 1 and then run ps axZ again from the session for user foo, observe that the restrictions on the user_t domain are in place again. It is not required that you use the sysadm_t domain to run setenforce 1.
  3. The directory booleans contains a file for every boolean in the policy. Run cat booleans/user_dmesg and note that the value is 0 0, this means that currently the boolean has the value false, and the value to be applied is also false (IE no change). Run the command getsebool user_dmesg which gives the same information in a different way.
    Now run the command echo 1 > booleans/user_dmesg followed by cat booleans/user_dmesg and then getsebool user_dmesg. Note that the value 0 1 on the boolean means that the current value is false aka inactive and the value on the next update will be true aka active.
    Run the command echo 1 > commit_pending_bools, this commits the change you just requested, run cat booleans/user_dmesg and getsebool user_dmesg to see how this is reported. Now go to the window for the <foo session and run the dmesg command and observe that it now works.
  4. The avc directory has files that allow you to query the statistics of the Access Vector Cache. SE Linux caches the most common accesses to save time when it has to determine whether an operation should be permitted, run cat avc/cache_stats to see the statistics on this. Now run avcstat for the recommended way of doing this.
  5. The file mls contains 1 if the system is running MLS (not supported in RHEL so will always have the value 0.
  6. The file load is used for loading a new policy.
  7. The device node null is equivalent to /dev/null, it exists so that when a program inherits a file handle that it is not permitted to access then after the file handle is closed it can be replaced with a handle to /dev/null.
  8. Run cat policyvers and note that it has the value 18, this is the version of the policy that is accepted by the kernel. When the system boots init will read the file /etc/selinux/config and use the SELINUXTYPE environment variable to determine whether it's strict or targeted policy. The policy file to be loaded is in directory /ec/selinux/$SELINUXTYPE/policy and is named policy.XX where XX is the value contained in the policyvers file.