maandag 29 juni 2009

Multi Category Security

Multi Category Security or MCS is a discretionary implementation of the mandatory Multi Level Security or MLS Model. SELinux Policy MLS is a SELinux Policy model that is used in Department of Defense type environments. In a MLS environment processes are forced to operate on specified Security Levels. The s0 Security Level or SystemLow level is the lower end of the Security Level Range in a MLS environment. The s15 Security Level or SystemHigh level is the upper end of the Security Range in a MLS environment. Between the low and upper end there are fourteen levels to be used.

In MLS there is a rule that says: "no read up and no write down". This means that processes that are forced to operate on for example Security Level s14 can not read processes or files that operate on the s15 Security Level. Processes that are forced to operate on the s5 Security Level can not write to files or interact with processes on the s4 Security Level. The MLS model is used to enforce confidentiality.

MCS basically tries to use the MLS attributes: Security Levels and Security Compartments, in its own model in a way that may be useful in common environments. SELinux Policy Targeted can be build with the MCS functionality. Red Hat distributions have this MCS functionality enabled by default in its Targeted SELinux Policy model. Gentoo Hardened, as far as i know, does not have MCS functionality builtin by default.

MCS pretty much works like the DAC Extended attributes. Users are assigned categories and can apply these categories to content that they own to their discretion. You can easily recognise a MCS enabled system by looking at security contexts. A system that has SELinux Policy Targeted implemented without MCS enabled only has three fields in its Security Context tuples: user_u:role_r:type_t. Systems that have MCS implemented have one or more extra fields in their Security Context tuple: user_u:role_r:type_t:s0:c0. In MCS policy there is only one Security Level. This SystemLow level is s0. MCS does have 1024 categories that can be assigned to processes and files. Categories are the last field in the Security Context tuple. In a MCS environment s0:c0.c1023 is SystemHigh or the upper end of the MCS range. This means that if you are assigned the SystemHigh MCS range that you can access all categories. By default everything in a MCS environment has access to SystemLow or s0.


Create a new SELinux User that is based of off the user_u SELinux User. Call this SELinux User for example "mcsuser_u" and assign the full MCS range to this SELinux User:

sudo semanage user -a -L s0 -r s0-s0:c0.c1023 -R user_u -P user mcsuser_u
sudo cp /etc/selinux/targeted/contexts/users/user_u /etc/selinux/targeted/contexts/users/mcsuser_u

( to list the differences between SELinux User user_u and mcsuser_u simply: sudo semanage user -l | grep user_u )

Now create three Linux Users and map them to the mcsuser_u SELinux user. Give John access to the s0-s0:c122 MCS range. Give Jane access to the s0-s0:c123 MCS range, and give johnjane access to the s0-s0:c122,c123 MCS range.

sudo useradd john
sudo useradd jane
sudo useradd johnjane
sudo semanage login -a -s mcsuser_u -r s0-s0:c122 john
sudo semanage login -a -s mcsuser_u -r s0-s0:c123 jane
sudo semanage login -a -s mcsuser_u -r s0-s0:c122,c123 johnjane

Login as john, touch file with name test and list its attributes:

# touch test; ls -Z test;
mcsuser_u:object_r:user_home_t:s0 test

The file was created on the s0 SystemLow level which is accessible by everything. Now add the c122 category to the file with the chcat command, and list the SELinux attributes of file test:

# chcat -- +s0:c122 test
# ls -Z test

If you try to add for example category s0:c123 to the file you will be denied access to do so because your assigned MCS range does not include the s0:c123 category.

Try the same procedure for Jane but instead use the s0:c123 category.

Linux User johnjane has access to both s0:c122 and s0:c123 MCS categories. In a shared location where DAC permissions are sufficient johnjane would be able to access both files with s0:c122 as well as s0:c123 categories.

Johnjane can also assign both s0:c122 and s0:c123 to a single file, but then neither John nor Jane woould be able to access it.

If all these MCS category digits make you dizzy then you can install mcstrans. Mcstrans is a daemon that translates the MCS category numbers into strings of letters which are easier to work with. The mcstrans daemon has some problems though.

# sudo yum install mcstrans
# chkconfig mcstrans on
# service mcstrans start

Now one can add translations for the MCS category digits with the semanage command.

Translate s0:c122 to JohnsFriends, s0:c123 to JanesFriends, and s0:c122,c123 to JohnJanesFriends:

# sudo semanage translation -a -T JohnsFriends s0:c122
# sudo semanage translation -a -T JanesFriends s0:c123
# sudo semanage translation -a -T JohnsJanesFriend s0:c122,c123

( use sudo semanage translation -l to list current translation mappings )

Note: After this the mcstrans may have died. If required restart the mcstrans daemon.

# sudo service mcstrans restart

Users can use the chcat command to list which categories they have assigned:

# chcat -L john
john: JohnsFriends

User can also use the id command with the -Z option to view their security context. The context displays to categories.

# id -Z


MCS is a neat extra functionality that can be enabled on systems that have SELinux Targeted Policy implemented.
The functionality of MCS is similar to that of Extended Attributes.

man: chcat, man semanage, man id

zondag 28 juni 2009

Mystaff SELinux User Domain

The staff module provided by Refpolicy is pretty cool but i needed a user domain with less privileges that would be able to transition to privileged admin domains.

One of my requirements was that this User Domain should only be accessible via OpenSSH and that it should be identical to the guest_t User Domain or have even less privileges. The guest_t User Domain was designed to not User Domain transition ever.

Another requirement for my mystaff User Domain was that this user should not be able to change passwords. I did this because the provided staff_t User Domain by Tresys can change passwords. If i sudo to root in the staff_t User Domain than in theory i would be able to change the root password. This is something i wanted to prevent for mystaff user.

Generally i wanted mystaff to have as little privileges as possible. It would be a very restricted login environment only accessible with OpenSSH that would be able to transition to specified admin User domains like for example webadm_t.

I started by reverse engineering the staff_t User Domain:

The staff.te file is mostly one call to a interface that is defined in the userdom modules interface file: ( line 927 to line 1020)

So far so good, but this interface has calls to to included several other interfaces that are defined in the same interface files:


These interfaces have calls to yet other interfaces defined in userdom.if file and so forth and so forth.


Which call:

userdom_manage_home_role($1_r, $1_t)
userdom_manage_tmp_role($1_r, $1_t)
userdom_manage_tmpfs_role($1_r, $1_t)

My goal was to expand all these userdomain interfaces so that i could remove parts of policy in there that i did not need.

Since i did not want mystaff to be able to change passwords i could for example remove the userdom_change_password_template altogether.

I also wanted mystaff to easily be customizable for different admin domain transitions and i started making different admin domains based of off webadm User domain. Basically i edited the name and declarations and replace the apache_admin interface calls by admin interface calls for other services. I also remove some policy specific to webadm.

For example: (line 46 to line 71)

The interface to call for administration of the aide domain.

Long story short. Below you will find the links to the mystaff user domain that is based of off the staff domain provided by upstream. In mystaff module i have expanded all userdom interface calls and i have removed policy that i did not require.

Basically the mystaff_u SELinux user is even more restricted then guest_u but unlike guest_u, mystaff_u can use sudo to user domain transition to admin domains. By default i have it call the webadm_role_change interface which allows mystaff_r access to the already by default installed webadm_r role. I also used the gen_user() macro to generate a mystaff_u SELinux User. However this SELinux user does not yet have the webadm_r role mapped. One could simply use semanage user -m <...> mystaff_u to add the webadm_r or edit the gen_user() macro to recflect mystaff_u SELinux Users access to the webadm_r role.

You will also notice plenty commented role_change template calls for other services. One can simple uncomment any of these calls to allow mystaff_r access to the role these provides. This requires that you install the corresponding admin User domains which can be found in the same modules directory.
It is then also required to map any of these roles to the mystaff_u SELinux User.


Other source policy modules:

By the way: the mystaff module can be easily modified to reflect a myguest modified guest User Domain. Just comment:
sudo_role_template(mystaff, mystaff_r, mystaff_t)

# Available privileged roles
# Test
In that case one may also want to rename the mystaff to something more appropriate. One can simply modify this policy to add or remove privileges.

Oh, by the way, do not forget to add default contexts for mystaff_u. You can base you mystaff_u default context file of off the staff_u file in /etc/selinux/targeted/contexts/users/. All you have to do is replace staff by mystaff in this file. (see my previous article on creating custom user domains)

Policy module source

In this article i will try to explain a bit more about the policy module source files.

A policy module source has three files. The first file is a Type Enforcement file. These files have the .te extension. The second file is a interface file. These files have a .if extension. The third file is called a file context file. This file has a .fc extension.

Type enforcement source policy file.

This file has policy and declarations that a personal or local to the policy module. Policy that is used by the (personal) types that are declared in this module.

Interface source policy file.

This file has policy and declarations that are shared with other types.

File context source policy file.

This file has personal file contexts for the personal types that are (usually) declared in the Type Enforcement file.

Some background information:

Example of a AVC denial:

avc: denied { read write } for pid=1864 comm="tor" path="/dev/console" dev=tmpfs ino=496 scontext=system_u:system_r:tor_t:s0 tcontext=system_u:object_r:console_device_t:s0 tclass=chr_file

Example of the AVC denial when processed by audit2allow:

allow tor_t console_device_t:chr_file { read write };

Example of the AVC denial processed by audit2allow -R:


The AVC denial has a lot of information about details of a denied rule in the Access Vector Cache. The basic audit2allow command translates this AVC denial into human readable policy language. The
audit2allow -R command goes looking for any available shared policy that may be available for us to use.

In this example the
tor_t domain wants to read and write to /dev/console which is a character device file that has type console_device_t. Tor interacts with terminal.

To make policy manageable and to keep the footprint of policy as small as possible the concept of shared policy is used. There is no need for duplicate policy. There should be a single point where a certain access is defined. This point is defined in the interface file of the target.

In this example
term_use_console is shared policy that is hosted by the module that declared(owns) type console_device_t. Interfaces (shared policy) are usually prefixed by the name of the module that own the type of the target.

tor_t domain tries to read and write to console_device_t which is a type that is declared in the terminal.te source policy file. The terminal.if interface file hosts policy to interact with this type properly: term_use_console. term is the abbreviation for terminal which is the name of the module that hosts the console_device_t file.

The type
console_device_t is declared here -- it is a declaration the is personal to the terminal policy module (terminal.te): (line 21)

The interface
term_use_console is defined here -- it is policy that is shared (hosted) by the terminal policy module (terminal.if):
(line 219 to line 237)

The terminal module own the
console_device_t type and shares all policy other types need to be able to interact with this type. If something changes in the way that interaction is done with this type then the terminal.if is a central point to modify policy for this. Other types simple call this shared policy and changes automatically get synchronized.

Shared policy usually have a commented header which explains the purpose of the interface and the parameters it expects when it is called.

For term_use_console the description is:

Read from and write to the console

And the parameter it expects is:

Domain allowed access.

If the tor_t domain wants to read and write to the terminal a device file with type
console_device_t then it can simple call the shared policy that is hosted in the terminal.if file, which is the file that shares policy and declaration with regard to types and domains that are personal to the terminal policy module.

Look at interfaces like BASH functions. You can source a external file that has BASH functions and then call any functions that are provided by the sources functions file.

The purpose of the commented headers is that it is easy to reference and call the interfaces. The source policy can be converted to html if you run "
make html" in the source root.

An example of this is hosted here:

The specific term_use_console interface is specified here:

Below the commented header for the term_use_console interface the actual shared policy can be found. This policy often has calls to shared policy for types that are external to the module. In this case the term_use_console as a interface call to
dev_list_all_dev_nodes($1) Which is shared policy that is hosted by the devices module (interfaces defined in device.if).

$1 is a variable of the parameter is expects. Look up the interface name here: and see what parameter is expects: Domain allowed to list device nodes.

If a domain type
tor_t wants to list device nodes then simple call dev_list_all_dev_nodes(tor_t)

$1, $2, $3 etc are variables that replace parameter 1, parameter 2, parameter 3 etc.

Another rule in the term_use_console interface in terminal.if is:

allow $1 console_device_t:chr_file rw_chr_file_perms;

If term_use_console(tor_t) is defined than the rule is translated:

allow tor_t console_device_t:chr_file rw_chr_file_perms;

Which basically allows the
tor_t domain type read and write access to character files with type console_device_t.

The last thing to mention about the term_use_console interface and interfaces in general is the
gen_require() blocks that can be seen.

Since type
console_device_t is declared in terminal.te and since term_use_console is called by tor_t domain in the tor.te source policy file, the tor_t files has to borrow the console_device_t type which is external to the tor.te file.

gen_require() and require{} block basically source types that are declared in other modules. After the types are sourced they can be used in personal policy.

another example:

If you encounter a AVC denial than consider that the target type (tcontext type) usually hosts policy to facilitate the access that the source type (scontext type) in the AVC denial needs. Run the AVC denial through audit2allow to hav it translated to raw policy language. Run the AVC denial through audit2allow -R to have it look for any shared policy that facilitates the access that is required.

Of course you can also do this yourself:

avc: denied { read } for pid=1842 comm="smartd" name="config" dev=dm-1 ino=39859 scontext=system_u:system_r:fsdaemon_t:s0 tcontext=system_u:object_r:selinux_config_t:s0 tclass=file

The smartd executable that runs in the
fsdaemon_t domain wants to read a file with name config and with type selinux_config_t.

allow fsdaemon_t selinux_config_t:file read;

The module that has declared
selinux_config_t should also provide shared policy that facilitates the access fsdaemon_t domain needs.

I would expect that type
selinux_config_t would be declared in selinux.te since types and interfaces are often prefixed by the name of the module that owns it. In this case it is not so straight forward. A little peruse of the policy shows that this type is declared in the selinuxutil.te file.

So shared policy related to this type should be in the selinuxutil.if file. On line 595 in the selinuxutil.if interface file here:

This interface called
seutil_read_config can be called by fsdaemon_t if we wish to give that domain this permissions.


However policy decisions are usually not that simple to make. A better idea may be to use this interface on line 575:


File context files define file contexts of types that are declared in the policy module. Have a look at some of the many examples Type Enforcement, Interface and File contexts files available in Tresys Reference policy.

zaterdag 27 juni 2009

SELinux screencasts

Here are some screen cast where i demonstrate some of the things that were discussed in the SELinux Lockdown series.

1. Create custom SELinux users.

Here i create a new SELinux User called new_u and map the staff_r, system_r and unconfined roles to this user. This SELinux User also has also has access to all available MCS categories.

Linux user joe is mapped to the new_u SELinux user. Default contexts for new_u SELinux were copied from those of staff_u since new_u is based of off staff_u with minor modifications (access to unconfined_r instead of sysadm_r)

Sudo is also set up to allow joe root access and to automatically Domain Transition to unconfined_t User Domain.

2. Quick demonstration of PAM SEPermit.

3. Quick demonstration of unconfined_login boolean.

4. How to extend staff_t User Domain to allow listing of /var (part1)

4.1 How to extend staff_t User Domain to allow listing of /var (part2)

5. Create a new unprivileged (secondary) User Domain.

6. The newrole command is useful for unprivileged User Domain transitions.

7. Demonstration of how to create a Application Domain to achieve listing of /var for staff_t (part1)

7.1 Demonstration of how to create a Application Domain to achieve listing of /var for staff_t (part2)

7.2 Demonstration of how to create a Application Domain to achieve listing of /var for staff_t (part3)

Looks like the last episode turned out a bit too long for YouTube. Heres a trimmed down version:

Excuse my bad english and funny dialect :)

SELinux Lockdown Part Ten: Conclusion

A lot was discussed in the previous nine articles. This is the last article in this series. In this article i will in short repeat the steps to maximize the security that SELinux provides. Details about any of these steps can be found in the previous articles.

1. SELinux User.

Map Linux Users by default to a confined SELinux User. Think least privilege. Use for example guest_u: sudo semanage login -m -s guest_u -r s0-s0 __default__

If you wish to override the default SELinux User for a new Linux User then you can do so with the useradd and -Z option.

Do not map Linux users to the unconfined_u SELinux user. An exception to this rule can be the root user. Root users should not be allowed to login except via TTY in emergencies.

2. PAM SEPermit.

Add entries for all your confined SELinux Users to /etc/security/sepermit.conf to block logins when SELinux is in permissive mode.

You can decide to create a unique SELinux User for yourself that is exempted from SEPermit.

3. Permissive mode Vs. Permissive Domains.

Always prefer the use of Permissive Domains over Permissive Mode.

4. Do not modify your default SELinux Users. If you need a custom SELinux User and or SELinux user Domain then create a new one. You can base them of off existing ones.

5. Use Role based Access control to confine root and user privileges.

6. Do not modify existing Roles/ User Domains.

If you need a custom role then base it of off an existing role and map the new role to a new SELinux user. You can however map existing roles to new SELinux users.

7. Use sudo. Not SU and newrole.

8. Remove the unconfined domain(s).

Be sure to sudo semodule -r unconfined to disable system services that have no policy defined. use sudo semodule -r unconfineduser to completely disable unconfined user environments (not recommended) If you do decide to remove unconfineduser, then make sure to configure your SELinux User mappings to reflect this. Use sysadm instead of unconfined

I prefer unconfined User Domain over sysadm User Domain for a secondary all purpose privileged user domain because:

a. unconfined_login boolean can be set to disable unconfined user logins.
b. ssh_sysadm_login, and xdm_sysadm_login booleans do not work.
c. sysadm is a drunken unconfined.
d. root logins are disabled.
e. i only use unconfined user domain as a secundary privileged user domain that can only be accessed via sudo
e.1. except for the root Linux User which can only login via TTY in emergencies.

9. Remove as much policy as possible by either disabling or enabling booleans.

set unconfined_login to off
set xserver_object_manager to on
set *_exec_content to off
consider the secure_mode_booleans

SELinux Lockdown Part Nine: Booleans

Booleans are blocks of policy that can be added or removed on the fly by toggling a boolean. The old NSA Example policy was based on a least privilege model. This means that as little as possible was allowed to successfully achieve a task. Almost each rule that gets added to SELinux policy adds new privileges. To maximize security that SELinux provide the mount of active rules should be kept to a minimum.

In Fedora 11 There is some policy enabled with booleans that your environment may not need. It is recommended that this policy is removed and that it is only added when it is needed.

Some booleans add policy when enabled, others add policy when disabled. A simple description of a booleans functionality can be displayed with the command: sudo semanage boolean -l. These descriptions are usually enough to understand its functionality but the descriptions are short. If you run a AVC denial through the audit2why command than audit2why will display which boolean to set in order to solve the issue if the problem can be solved by toggling a boolean.

Sometimes it is best to look to the contents of booleans to understand what they allow. Booleans are defined in Source Policy. You would have to have access to the Source Policy and you would have to know how to find the blocks of policy that are the content of the boolean.

There are three older tools that help you list, set and toggle SELinux booleans: getsebool, setsebool and togglesebool. In Fedora 11 the functionality that these tools provides has been built into the semanage command: semanage boolean.

The names of booleans should point to the Source Policy Module where the boolean is defined. Unfortunately it often is not that easy to find the location of the boolean definition in Source Policy. In a prefect scenario that name of the boolean has the name of the module where it is defined prepended.

Example: How to find the meaning and content of gpg_agent_env_file boolean:

# semanage boolean -l | grep gpg
gpg_agent_env_file -> off Allow usage of the gpg-agent --write-env-file option. This also allows gpg-agent to manage user files.

As the name of the boolean suggest, the boolean is defined somewhere in the gpg module:
On line 196 to line 203 the actually content of the boolean is defined.

This boolean allows programs that run in the gpg_agent_t SELinux Domain to write files in the user home space with the generic user_home_t type when the boolean is set to on.

I think there is actually a but in the block of policy as the gpg_agent_t may only type transition to user_home_t files and not to directories.

The boolean declaration can be found on line 9 to line 15. The declaration has a short description of the functionality of the boolean.

The reason why i showed you how to find a description and the actual content of a boolean is because i cannot discuss each and every boolean in this article. If it is decided to lock-down booleans one can look up whether it actually adds or removes policy when toggled on and what it actually does. Then the boolean can just be switched on and off to see if you need to policy it provides. If required that AVC denials can be fed to audit2why to see if there is a boolean available to allow the functionality.

There are a few booleans that i would like to highlight in this article:

The unconfined_login boolean:

unconfined_login -> off Allow a user to login as an unconfined domain

This boolean was discussed the Part Eight of this series. If set to on then users can login to the system in the unconfined_t User Domain.
Security can be improved much by setting this to off. The content of this boolean can be found in the unconfineduser.te file in Source Policy.

The ssh_sysadm_login and xdm_sysadm_login booleans:

ssh_sysadm_login -> off Allow ssh logins as sysadm_r:sysadm_t
xdm_sysadm_login -> off Allow xdm logins as sysadm

This boolean is similar to unconfined_login for the sysadm_t User Domain. This boolean currently does NOT work. Therefore it is not recommended to map any SELinux Users to the sysadm_r role. Users with access to this role can log into the system directly in this privileged domain.

The *_allow_exec_content_t booleans:

allow_sysadm_exec_content -> off allow_sysadm_exec_content
allow_xguest_exec_content -> off allow_xguest_exec_content
allow_user_exec_content -> off allow_user_exec_content
allow_staff_exec_content -> off allow_staff_exec_content
allow_guest_exec_content -> off allow_guest_exec_content

The description of this boolean is not very helpfull as you can see. When this boolean is set to on then users that operate in any of the mentioned User Domains can execute user content in their user space. This means files with type user_home_t, user_tmp_t or when nfs or samba home directories are enabled types nfs_t and cifs_t respectively. This boolean is Fedora specific and its contents can be found in the userdomain.if Source Policy file.

The secure_module boolean:

secure_mode -> off Enabling secure mode disallows programs, such as newrole, from transitioning to administrative user domains.

The secure_mode boolean can be enabled to disallow User Domain transitions to privileged User Domains. The content of this boolean can be found in the selinuxutil.te Source policy file:

The policy for this boolean starts on line 289 and ends on line 295. This boolean currently only works for the newrole command. It does NOT work for the sudo command. Therefore it is encouraged to not depend on this boolean.

The secure_mode_insmod boolean:

secure_mode_insmod -> off Disable transitions to insmod.

When this boolean is set to on then confined users will not be able to transition to the insmod Domain. Restricted user domain will not be able to insert Linux Kernel modules. This boolean is defined in the modutils.te Source Policy file:

The policy starts on line 120 and end on line 122. The declaration for this boolean can be found on line 4 to line 6.

The secure_mode_policyload boolean:

secure_mode_policyload -> off boolean to determine whether the system permits loading policy, setting enforcing mode, and changing boolean values. Set this to true and you have to reboot to set it back

The description of this boolean is quite good. When enabled there is no policy to permit the loading of policy, setting the enforcing mode and changing of booleans. This policy can be found in the selinux.te Source Policy file:

The content of this boolean starts on line 44 and ends on line 52.

The xserver_object_manager boolean:

xserver_object_manager -> off Support X userspace object manager

This boolean is quite powerful. By enabling this boolean the X Server access control extensions become enable. XACE allows the operator to define how processes can interact with X Server. The default policy still has rough edges. XACE is implemented for the SELinux Multi Level Security Policy Model which enforces confidentiality. By default it is disabled with SELinux Policy Targeted. If you feel brave, enable it and experience the power of Xace.
After you set this boolean to true you are required to restart the X server.

The content of this boolean can be found in the xserver.te Source Policy file:

The content of this boolean starts on line 749 and ends on line 766.

It is not always easy to find where booleans are defined unfortunately. There is room for improvement with the naming of booleans.


To achieve greater security minimize the amount of policy.
Sometimes policy is added by turning a boolean on and sometimes policy is added by turning a boolean off.
Content of booleans can be reviewed in Source Policy.

Refer: man getsebool, man setsebool, man togglesebool, man audit2why, man semanage

vrijdag 26 juni 2009

SELinux Lockdown Part Eight: Unconfined

The unconfined space is for processes that require almost unrestricted access. Almost because writable memory execution is not permitted. The following permissions are restricted for processes that operate in the unconfined space unless specified otherwise: Execmem Execstack Execheap Execmod.

Processes that in a default Fedora 11 installation operate in this Unconfined Domain are the Linux Kernel, RPM, init scripts, and unconfined users.
Unconfined processes usually run programs unconfined too unless other otherwise specified. It is expected that unconfined processes are unrestricted and that children inherit this unconfined environment. Exceptions to this rule as said should be specified by creating policy that transitions the Unconfined Domain to a restricted space when a unconfined process runs a program.

Security can be greatly improved by removing the unconfined space. This will cause all processes to be restricted by SELinux. In Fedora 11 the unconfined space was split into two parts. The first part is the unconfined space for programs, and the second part is the unconfined space for users. This second part was renamed to the unconfineduser domain.

The result of this split is that now either or both environments can be removed. If the unconfined domain which is the domain for unconfined programs is removed then the init scripts will be restricted. This means that system services that do not have SELinux policy defined will run in the restricted init script environment and will not run unrestricted. This is a great way to ensure that no unrestricted system services are implemented on the system. The kernel and RPM processes stay unconfined because these processes need many permissions in order to operate successfully.

The unconfineduser domain can be removed to ensure that all users operate in a restricted environment. If it is decided to remove the unconfineduser domain than you must reconfigure your SELinux mappings to reflect this change.

There should be no reason to not remove the unconfined domain if the operator feels comfortable with SELinux. The operator can implement SELinux policy for any system process that does not have policy defined already.

The unconfineduser domain is usually handy to keep, as the operator can mannually configure which Linux users are mapped to this User Domain. SELinux can be configured to not allow unconfined logins via OpenSSH or Grapical User Interface. This means that users that have access to the unconfineduser domain can only login using this environment on the TTY or access the unconfined user space via the sudo command or SU with newrole command.

Using the unconfined user domain as a secondary domain only improves security if unconfined logins are not enabled.

Example: Remove the unconfined domain, disallow unconfined user logins, map unconfined_r role to existing staff_u SELinux User, Remove the sysadm_r role for the staff_u SELinux User. Add Linux user John and map the Linux user to the staff_u SELinux user. Add an entry for John to /etc/sudoers.

sudo setsebool -P unconfined_login off
sudo semanage user -m -L s0 -r s0-s0:c0.c1023 -R "staff_r system_r unconfined_r" -P user staff_u
sudo semodule -r unconfined
sudo useradd -Z staff_u john
sudo visudo (john ALL=(ALL) TYPE=unconfined_t ROLE=unconfined_r ALL)

John logs into the system as the restricted Linux user staff_u. When John wants to acquire root privileges he simply runs the sudo command. John can open a root shell in the unconfined space by running the sudo command with the -s option.

This configuration should not change your ways of doing things too much since root logins are not encouraged and since the staff_t restricted user domain has all permissions that a unprivileged user requires. It is also possible to create a custom User Domain that is tailor made to your personal requirements and map its role to a custom SELinux Users together with the system_r and unconfined_r role. That way you can keep the staff_u SELinux User in its original state and can you modify your User Domain and SELinux user to your requirements.


Consider removing the Unconfined Domain and or the Unconfineduser Domain to improve security.

Refer: man semanage, man semodule, man visudo, man setsebool, man sudo

donderdag 25 juni 2009

SELinux Lockdown Part Seven: SU, Newrole, Sudo and Run_init.

Before Fedora 9 was released users with access to roles would execute the newrole command to Domain Transition. This command can be installed as policycoreutils-newrole. The user would for example run: newrole -r , and get prompted to enter his user password. The newrole command would than verify whether the users has all access that is required to make the requested domain transition and allow or deny it.

If the user Transitioned to a privileged User Domain and wanted to perform a privileged task, he would then execute the SU command to meet that Discretionary Access Control requirements. The SU command prompts for the root password.

In a strict environment to become a privileged user, one would run two programs and enter two seperate passwords. A privileged user would require access to roots password to perform any privileged task this way.

For a user such as this to run a system service the is another command to execute. The Run_init command performs a Domain transition to the Init Script which in turn starts a system service in its proper Domain. This program also prompts for a password.

That is a lot of passwords and commands just to restart Apache for example.

In Fedora 9 the sudo command was modified to support Domain Transitions. The use of the sudo command is recommended over the SU and Newrole command.
Two main advantages of the Sudo command are that this command allows you to change Linux uid and SElinux Domain Transition in one turn and that you do not need the root password to do so as long as you are added to the /etc/sudoers configuration file.

The destination Domain can be specified as a parameter of the -t option, and the destination role can be specified as a parameter of the -r option. Sudo can also be configured to Transition to a specified role and domain by default in its /etc/sudoers file.

For example: Linux user joe logs into the system as SELinux user joe_u. SELinux User joe_u has access to the joe_r role as well as the webadm_r role and the system_r role. The joe_r role maps to the joe_t User Domain which has all the permissions required for a restricted login user. The joe_t User Domain has access to the system_r role as well as the webadm_r role in policy. An entry for joe in /etc/sudoers is as follows:

joe ALL=(ALL) TYPE=webadm_t ROLE=webadm_r ALL

This entry allows Sudo to transition user joe to webadm_r role and webadm_t role automatically when joe run the sudo command.

Joe logs in and finds himself in the joe_t User Domain. Then when Joe wants to start the Apache service he simple runs: sudo service httpd start.

Sudo changes Joes Linux UID to 0 and transitions Joes' Domain and Role automatically to the specified Role and Domain webadm_t and webadm_r.

Joe also has access to the system_r role that is required to transition to the Init script domain which starts httpd in its proper Domain. The Run_init command is no longer required.

If Joe has access to several roles then Joe can override the default role and domain specified in the Sudo configuration file by specifying the -t and -r options with the destination role and domain parameters.


Prefer the Sudo command over the SU with Newrole commands.
Prefer access to the system_r role over the Run_init command.

Refer: man su, man sudo, man newrole, man run_init

woensdag 24 juni 2009

SELinux Lockdown Part Six: Customized SELinux Roles

In part four of this series i have demonstrated how to create and implement a customized SELinux User Domain. Creating Roles is done in very much the same way. As mentioned in my previous article about RBAC: Roles are mappings to User Domains. Some User Domains can be used by users to log into the system because those User Domains have permissions to access the user home directory for example. I refer those these User Domains as primary User Domains. Other User Domains are designed to be secondary. Users can Domain Transition using the sudo or su with newrole command to these secondary User Domains. Secondary User Domains can not be used by a user to log into the system.

In this Chapter i am going to demonstrate how such a secondary User Domain is create and implemented. The goal is to create a Privileged SELinux Role that provides the permissions required to manage a Bind DNS service. This role will be based of off the webadm_t User Domain that was discussed previously. I will also revisit some material from part four: Customized SELinux User Domains because the user primary User Domain must be modified to be able to access our new bindadm_r role.

As mentioned i will start with creating a new secondary User Domain called bindadm that is based of off the pre-defined webadm_t SELinux User Domain. The two SELinux Source Policy files i am going base my new User Domain of are:

SELinux Source Policy files that have a ".te" extension are called Type Enforcement files. Files with a ".if" extension are called Interface files. Type Enforcement files have Declarations and Policy that are personal or local to the Domain. Interface files have Declarations and Policy that are shared by the Domain with other Domains that may want to interact with our Domain.

mkdir ~/bindadm; cd ~/bindadm;
echo "policy_module(bindadm, 0.0.1)" > bindadm.te;
echo "role bindadm_r;" >> bindadm.te;
echo "userdom_base_user_template(bindadm)" >> bindadm.te;
echo "allow bindadm_t self:capability { dac_override dac_read_search kill sys_ptrace sys_nice };" >> bindadm.te;
echo "files_dontaudit_search_all_dirs(bindadm_t)" >> bindadm.te;
echo "files_manage_generic_locks(bindadm_t) >> bindadm.te;
echo "files_list_var(bindadm_t)" >> bindadm.te;
echo "selinux_get_enforce_mode(bindadm_t)" >> bindadm.te;
echo "seutil_domtrans_setfiles(bindadm_t)" >> bindadm.te;
echo "logging_send_syslog_msg(bindadm_t)" >> bindadm.te;
echo "userdom_dontaudit_search_user_home_dirs(bindadm_t)" >> bindadm.te;

This is the basic contents for a generic privileged secondary User Domain. I left out policy that is specific to managing the Apache service.
Now i will add policy that is specific to managing the Bind service. I am able to borrow this policy from the bind Source Policy. As i mentioned shared policy is located in Interface files. This means that if i want to include shared policy related to Bind i should be able to find this in the bind.te file if the policy is available:

Starting on line 252 in bind.if and ending on line 305 is policy shared by the Bind module to administrate the bind service. I just have to call this Interface in our bindadm Type Enforcement file to include this Policy:

echo "bind_admin(bindadm_t, bindadm_r)" >> bindadm.te;

This concludes my bindadm Type Enforcement file. Next my bindadm Source Policy should share policy that might be required by other Domains to be able to interact with our bindadm_t Domain. I will base this policy of off the webadm.if file.

echo "## Bind administrator role" > bindadm.if;

echo "########################################" >> bindadm.if;
echo "## " >> bindadm.if;
echo "## Change to the bind administrator role." >> bindadm.if;
echo "##
" >> bindadm.if;

echo '## ' >> bindadm.if;
echo "## " >> bindadm.if;
echo "## Role allowed access." >> bindadm.if;
echo "##
" >> bindadm.if;

echo "## " >> bindadm.if;
echo "## " >> bindadm.if;
echo "#" >> bindadm.if;
echo "interface(\`bindadm_role_change',\`" >> bindadm.if;
echo " gen_require(\`" >> bindadm.if;
echo " role bindadm_r;" >> bindadm.if;
echo " ')" >> bindadm.if;
echo " allow \$1 bindadm_r;" >> bindadm.if;
echo "')" >> bindadm.if;

The Bind Administrator Shared policy above will later be called by our user primary Customized SELinux User Domain. I am now finished with my bindadm_t User Domain.

My next step is to create a Customized SELinux User Domain for our Bind guy and allow that User Domain to transition to the bindadm_r role by calling the bindadm_role_change interface. This Customized SELinux User Domain will be based of off the staff_t User Domain:

echo "policy_module(bindguy, 0.0.1)" > bindguy.te;
echo "role bindguy_r;" >> bindguy.te;
echo "userdom_unpriv_user_template(bindguy)" >> bindguy.te;
echo "sudo_role_template(bindguy, bindguy_r, bindguy_t)" >> bindguy.te;
echo "ssh_role_template(bindguy, bindguy_r, bindguy_t)" >> bindguy.te;
echo "kernel_read_ring_buffer(bindguy_t)" >> bindguy.te;
echo "kernel_getattr_core_if(bindguy_t)" >> bindguy.te;
echo "kernel_getattr_message_if(bindguy_t)" >> bindguy.te;
echo "kernel_read_software_raid_state(bindguy_t)" >> bindguy.te;
echo "auth_domtrans_pam_console(bindguy_t)" >> bindguy.te;
echo "libs_manage_shared_libs(bindguy_t)" >> bindguy.te;
echo "seutil_run_newrole(bindguy_t, bindguy_r)" >> bindguy.te;
echo "netutils_run_ping(bindguy_t, bindguy_r)" >> bindguy.te;
echo "domain_read_all_domains_state(bindguy_t)" >> bindguy.te;
echo "domain_getattr_all_domains(bindguy_t)" >> bindguy.te;
echo "domain_obj_id_change_exemption(bindguy_t)" >> bindguy.te;
echo "files_read_kernel_modules(bindguy_t)" >> bindguy.te;
echo "kernel_read_fs_sysctls(bindguy_t)" >> bindguy.te;
echo "modutils_read_module_config(bindguy_t)" >> bindguy.te;
echo "modutils_read_module_deps(bindguy_t)" >> bindguy.te;
echo "miscfiles_read_hwdata(bindguy_t)" >> bindguy.te;
echo "term_use_unallocated_ttys(bindguy_t)" >> bindguy.te;

Now i will call the bindadm_role_change interface that i have defined in the bindadm.if Interface file to allow the bindguy_t User Domain to transition to the bindadm_t SELinux restricted environment:

echo "bindadm_role_change(bindguy_r)" >> bindguy.te;

I can also automatically create a SELinux User Mapping called bindguy_u which is mapped to the bindguy_r, bindadm_r and system_r role. The system_r role is included so that bindadm_t can stop, start and restart the bind system service.

echo "gen_user(bindguy_u, user, bindguy_r system_r bindadm_r, s0, s0 - mls_systemhigh, mcs_allcats)" >> bindguy.te;

To let the login programs know that bindguy is a valid login user i will add default contexts for this user based of off staff_u default contexts:

cp /etc/selinux/targeted/contexts/users/staff_u /etc/selinux/targeted/contexts/users/bindguy_u
sed -i 's/staff/bindguy/g' /etc/selinux/targeted/contexts/users/bindguy_u

Both the bindguy and bindadm policy can now be build and installed:

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i bindguy.pp bindadm.pp

Now i can add a new Linux user with the useradd command and -Z option and bindguy_u parameter:

sudo useradd -Z bindguy_u bindguy

For bindguy to be able to use sudo to automatically transition to Linux user root and SELinux Domain bindadm_t i will add to /etc/sudoers:

echo "bindguy ALL=(ALL) ROLE=bindadm_r TYPE=bindadm_t ALL" >> /etc/sudoers;

When bindguy logs into the system he will find himself in the bindguy_t User Domain which is based of off the staff_t User Domain. The bindguy_t User Domain can domain transition to the bindadm_t User Domain which is a Domain to be used with root access, that can manage the Bind service.

Bindguy would be able to restart the bind service for example by executing:

sudo service named restart

Bindguy will also be able to edit the various Bind content and configuration files. Bindguy will not be able to change the root password for example.

Note: If you found any errors in this exercise or if you have any question or comments, please contact me. Thanks!

refer: man bind, man semodule, man sudo, man useradd

dinsdag 23 juni 2009

SELinux Lockdown Part Five: SELinux RBAC

It was explained in part one of this series that SELinux users are mapped to SELinux User Domains and Multi Category Security Components. These mappings can be configured by running the semanage command with the user option, and the SELinux User mappings can also automatically be configured by calling the gen_user() interface in the Type Enforcement Source Module file for your User Domain.

SELinux RBAC is used to be able to assign several SELinux restricted environments to a single SELinux user.

SELinux User Roles are User Domains. When i refer to roles i often mean a SELinux Users' secondary User Domain. Often roles that were designed to be secondary User Domains differ from primary User Domains. This is because Linux Users do not actually use the roles that were designed to be secondary User Domains to log into the system.

Instead Linux users Domain Transition to their secondary role by using the sudo command or a combination of the su and newrole command.

Keep in mind that some roles were designed to be primary User Domains and other roles only support secondary User Domain functionality. Primary User Domains let a user log into the system and secondary User Domains can only be used with the sudo and su with newrole command.

Sysadm Role - An example of a role that is designed to be a primary User Domain:

The sysadm_u SELinux User is mapped to the sysadm_r role. This mapping can be listed by running: sudo semanage user -l | grep sysadm_u

Linux Users that are mapped to the sysadm_u SELinux User operate in the sysadm_r role by default. In this case sysadm_r is a primary User Domain.

The staff_u SELinux User is also mapped to the sysadm_r role. The primary User Domain for the staff_u SELinux user is staff_r role. The default contexts configured in /etc/selinux/targeted/contexts/users/staff_u define what User Domains users should be logged in with by default, and what User Domains users can log in with by for example specifying a User Domain to log in with.

By default a staff_u SELinux User logs into the system in the staff_t User Domain. The sysadm_r role which was designed to be a primary User Domain can optionally be used to log in by using for example: ssh joe/sysadm_r@localhost.localdomain.tld

The sysadm_r role can also be used to Domain Transition by running the sudo and su with newrole command the way secondary User Domains as accessed.

The sysadm_t User Domain is the default environment of operation for the sysadm_u SELinux User and is designed to be the secondary environment of operation for the staff_u SELinux User, but even the staff_u SELinux User can force the pam_selinux Pluggable Authentication Module to use sysadm_t as its primary User Domain.

Webadm Role - An example of a role that is designed to be a secondary domain:

Unlike the sysadm_r role, the webadm_t User Domain cannot be used to log in to the system directly. Instead it must be accessed from another User Domain with the sudo or su with newrole command. The webadm_t User Domain does not have the permissions required to run a full user environment.

To use the webadm_r role, it should be mapped to for example the staff_u SELinux user: sudo semanage user -m -L s0 -r s0-s0:c0-c1023 -R "staff_r system_r webadm_r" -P user staff_u

Please note that you are not encouraged to modify existing SELinux Users. It is preferred that the default SELinux Users are left in their original state. Instead add new customized SELinux Users:

sudo semanage user -a -L s0 -r s0-s0:c0-c1023 -R "staff_r system_r webadm_r" -P user webadm_u
cp /etc/selinux/targeted/contexts/users/staff_u /etc/selinux/targeted/contexts/users/webadm_u

The webadm_r role can only be used as a secondary User Domain for two reason in this example. The first reason is the the webadm_t User Domain does not have enough permissions to support a full login environment. The second reason this that the we have based our webadm_u default context file in /etc/selinux/targeted/contexts/users of off the staff_u default contexts file. In this file the webadm_t User Domain is not a valid context to use for system logins.

Using Roles to confine Root:

Role Based Access Control can be used to create different User Domains with different permissions and assign those roles to SELinux Users. Although RBAC can be used for both unprivileged and privileged users, it is common to use it to restrict the privileged root user.

The webadm_u SELinux user i create above for example can be used to restrict a Linux User with root access to only be able to manage the Apache environment. The staff_t User Domain is unprivileged. This means that if i run in the staff_t User Domain as root, then i will not be able to use the root powers. The webadm_t User Domain is a limited privileged User Domain. If i operate in the webadm_t User Domain as root then i can access root resources that are permitted by the webadm_t restricted environment.

For example, i, dgrift am mapped to the webadm_u SELinux User. I am also allowed all root permissions in the sudoers configuration file in /etc/sudoers. When i log into the system by default i will find myself in the staff_t User Domain. If i run the sudo command as staff_t then i will for example not be able to restart the Apache service.

If i use the sudo command to Domain Transition to webadm_t before restarting the service i will be able to succeed:

sudo -r webadm_r -t webadm_t service httpd restart

If i try to use the sudo command to change root password i will fail because neither staff_t nor webam_t User Domains have access to this privilege.

This is a powerful feature. It means that it is not possible to delegate limited specified root privileges without having to share roots password.

Next i will explain some of the pre-defined roles available:

The guest_r role:
This is a primary User Domain only. See part one in this series for a explanation of the guest_u SELinux user.

The xguest_r role:
This is a primary User Domain only. See part one in this series for a explanation of the xguest_u SELinux user.

The user_r role:
This is a primary User Domain only. See part one in this series for a explanation of the user_u SELinux user.

The staff_r role:
This is a primary User Domain only. See part one in this series for a explanation of the staff_u SELinux user.

The sysadm_r role:
This User Domain can be used as both primary as well as secondary User Domain. The sysadm_u SELinux Users uses sysadm_r as its default role and the staff_u SELinux user uses sysadm_r as its optional role. The sysadm_r role has permissions sufficient for user logins. See part one in this series for a explanation of the sysadm_u user.

The system_r role:
This role is used by the system as a primary role. Users can use the system_r role as a secondary role to restart system services in their proper context.

The unconfined_r role:
This User Domain can be used both as primary as well as secondary User Domain. See part one in this series for a explanation of the unconfined_u user.

The webadm_r role:
This User Domain can be used as secondary User Domain only. This role has does not have access to user home directories and many other privileges required by login users. This User Domain has the permission required to manage the Apache environment.

The logadm_r role:
This User Domain can be used as secondary User Domain only. This role has does not have access to user home directories and many other privileges required by login users. This User Domain has the permission required to manage the logging environment.

Refer: man newrole, man su, man sudo, man semanage

maandag 22 juni 2009

SELinux Lockdown Part Four: Customized SELinux User Domains

In the first chapter of this series i discussed SELinux users and explained some of the properties of the pre-defined SELinux Users. These profiles should cover most common usage scenarios. In the event that non of the pre-defined SELinux Users really fit a Linux users' profile one can decide to introduce a new restricted environment tailor made to the requirements that are determined.

The easiest way to do this is to base a new custom SELinux User Domain of off a existing one rather then create a new restricted environment from scratch. In this article i will show you how the create a Custom SELinux User Domain that is based of off the user_t restricted user environment. I will also show you ways to map this User Domain to a custom SELinux User so that Linux users can be mapped to this environment as described in the chapter called SELinux Users.

The case is the following: I have to add a restricted user to this system with requirements that are almost identical to the SELinux user user_u. The exeption is the this user also must be able to list the /var mountpoint.

To accomplish this, a new SELinux restricted user environment based of off the user_t User Domain should be created and installed with a SELinux Policy Module. We can extend this Policy Module to allow the listing of /var, and we can create a map for the new User Domain to a new SELinux user. We can also use the semanage command to manually map our new User Domain to a new SELinux User instead of adding this map to the Policy Module itself.

To complete this task i will need access to the SELinux Source Policy for referencing purposes. In this example i will use SELinux Source Policy that is publicly available and browsable at Although that in this example using upstream Tresys Reference Policy will suffice, it should be noted that in most cases one should refer to distribution specific SELinux Source Policy instead.

First let's have a look that the user_t User Domain SELinux Source Policy Module in the Tresys Source Policy here:

Our new User Domain called myuser_t will be based of off this user_t Policy Module. There will be some minor modifications to target the Fedora Distro specific policy instead:

mkdir ~/myuser; cd ~/myuser;
echo "policy_module(myuser, 0.0.1)" > myuser.te;
echo "role myuser_r;" >> myuser.te;
echo "userdom_unpriv_user_template(myuser)" >> myuser.te;

The policy above will create a new SELinux restricted User Domain that is almost identical to that of the user_t user environment that is mapped to the user_u SELinux User in a default installation.

Now i am going to add the policy that allows this environment access to list the /var mountpoint:

echo "files_list_var(myuser_t)" >> myuser.te;

Next i will programmatically map our myuser_t SELinux User Domain to a new SELinux user called myuser_u:

echo "gen_user(myuser_u, user, myuser_r, s0, s0)" >> myuser.te;

Note that the above policy is similar to running the following semanage command manually:

sudo semanage user -a -L s0 -r s0-s0 -R myuser_r -P user myuser_u

Policy for the customized User Domain is now ready. A binary SELinux Policy Module should now be build from the Source Policy:

make -f /usr/share/selinux/devel/Makefile

Alternatively the following commands can be used to build a binary Policy Module:

checkmodule -M -m -o myuser.mod myuser.te
semodule_package -o myuser.pp -m myuser.mod

Install the new binary Policy Module that has been created:

sudo semodule -i myuser.pp

Finally we should define default SELinux Security Contexts for our new SELinux user in order to let the login programs know which User Domain to use:

cp /etc/selinux/targeted/contexts/users/user_u /etc/selinux/targeted/contexts/users/myuser_u
sed -i 's/user/myuser/g' /etc/selinux/targeted/contexts/users/myuser_u

Now we can simply add a new Linux User and map this user to our fresh myuser_u SELinux User with the useradd command and -Z option or with the semanage command with the -m option.

Instead of modifying default SELinux Users and SELinux User Domains create new ones.

refer: man semodule, man semanage, man checkmodule, man semodule_package