Bookmark this page

Auditing the SELinux Policy

Objectives

  • Examine a system's SELinux policy to evaluate the access that it permits, and to troubleshoot and resolve issues.

Overview of the SELinux Policy

The SELinux policy is a set of rules that guides the SELinux security engine. It defines the SELinux users, Booleans, types for files and processes, and many other objects. Red Hat Enterprise Linux ships with a standardized policy that is adjustable for many use cases.

You can use the seinfo command, which is part of the setools-console package, to list all the objects in the policy.

[user@host ~]$ seinfo
Statistics for policy file: /sys/fs/selinux/policy
Policy Version:             33 (MLS enabled)
Target Policy:              selinux
Handle unknown classes:     allow
  Classes:             135    Permissions:         457
  Sensitivities:         1    Categories:         1024
  Types:              5066    Attributes:          253
  Users:                 8    Roles:                14
  Booleans:            348    Cond. Expr.:         378
  Allow:             63338    Neverallow:            0
  Auditallow:          163    Dontaudit:          8432
  Type_trans:       249598    Type_change:          87
  Type_member:          35    Range_trans:        6161
  Role allow:           37    Role_trans:          418
  Constraints:          70    Validatetrans:         0
  MLS Constrain:        72    MLS Val. Tran:         0
  Permissives:           4    Polcap:                6
  Defaults:              7    Typebounds:            0
  Allowxperm:            0    Neverallowxperm:       0
  Auditallowxperm:       0    Dontauditxperm:        0
  Ibendportcon:          0    Ibpkeycon:             0
  Initial SIDs:         27    Fs_use:               35
  Genfscon:            109    Portcon:             660
  Netifcon:              0    Nodecon:               0

[user@host ~]$ seinfo --type

Types: 5066
   NetworkManager_dispatcher_chronyc_script_t
   NetworkManager_dispatcher_chronyc_t
   NetworkManager_dispatcher_cloud_script_t
   NetworkManager_dispatcher_cloud_t
   NetworkManager_dispatcher_console_script_t
   NetworkManager_dispatcher_console_t
   NetworkManager_dispatcher_console_var_run_t
   NetworkManager_dispatcher_custom_t
...output omitted...

The policy also includes the rules that determine how each domain might access each type. Recall that a domain is what a type is called when it is applied to a process. These rules are discussed in more detail in a later section.

SELinux types have names that end in the _t suffix, and for clarity, they usually include the kind of object that they apply to. For example, types that are associated with service executable files end in the _exec_t suffix:

  • The httpd_exec_t type corresponds to the /usr/sbin/httpd binary.

  • The crond_exec_t type corresponds to the /usr/sbin/crond binary.

  • The postfix_master_exec_t type corresponds to the /sur/sbin/postfix binary.

Types for log files end in the _log_t suffix. The following examples are log files and their associated SELinux types:

  • The httpd_log_t type corresponds to log files in the /var/log/httpd/ directory.

  • The cron_log_t type corresponds to the /var/log/cron log file.

Types that are used to label ports end in the _port_t suffix, such as the http_port_t or smtp_port_t types.

For categorization and organization, SELinux associates attributes with types. Attributes are a way to group types. The following examples show some attributes and the types that they group:

  • The logfile attribute includes the httpd_log_t and cron_log_t types.

  • The exec_type attribute contains the httpd_exec_t, crond_exec_t, and postfix_master_exec_t types.

  • The domain attribute groups all the domains (types for processes), such as the httpd_t, sshd_t, or crond_t types.

Attributes help reduce and simplify the access rules. Because all processes need access to the libraries in the /usr/lib64/ directory, a single rule exists to allow the domain attribute to read directories with the lib_t type, which is the type that is associated with the /usr/lib64/ directory.

You can list all attributes with the seinfo --attribute command, and you can list the types in an attribute with the seinfo --attribute=attribute -x command.

[user@host ~]$ seinfo --attribute

Type Attributes: 253
   abrt_domain
   admindomain
   afs_domain
   antivirus_domain
   application_domain_type
   application_exec_type
   base_file_type
   base_ro_file_type
...output omitted...
[user@host ~]$ seinfo --attribute=exec_type -x

Type Attributes: 1
   attribute exec_type;
        NetworkManager_dispatcher_chronyc_script_t
        NetworkManager_dispatcher_cloud_script_t
        NetworkManager_dispatcher_console_script_t
        NetworkManager_dispatcher_ddclient_script_t
        NetworkManager_dispatcher_dhclient_script_t
        NetworkManager_dispatcher_dnssec_script_t
        NetworkManager_dispatcher_exec_t
...output omitted...

Analyzing the Targeted Policy

In Red Hat Enterprise Linux 9, Red Hat supports three policies: targeted, Multi-Level Security (MLS), and minimum.

The targeted policy is the default policy. This policy controls all the daemons that listen on the network, and the services that start at boot. Processes started by users, however, run in the unconfined_t domain. For processes in the unconfined_t domain, the SELinux policy still enforces restrictions, but the rules that are in effect allow almost complete access, so these processes are mostly restricted by standard permissions.

Note

Unconfined domains might still perform domain transitions (causing the child process to become confined), and some restrictions on executable memory might apply depending on Boolean settings.

The targeted policy is flexible enough to fit into enterprise infrastructures. The daemons that are part of the policy run in their own domains and SELinux controls every operation that they perform on the system. This way, daemons that are defective or exploited are limited in the damage that they can do.

The MLS (Multi-Level Security) policy contains the differentiation of security levels and categories. This policy is used in military applications, and allows for classifications such as unclassified, secret, or top secret.

The minimum policy is based on the targeted policy, but nearly everything runs under the unconfined_t domain. You can activate SELinux for a specific domain by loading its SELinux module.

Interpreting the Allow Rules

The targeted policy includes rules that determine how each domain might access each type. For example, the following rule allows the httpd service (httpd_t domain) to access and read its configuration files (httpd_config_t).

allow httpd_t httpd_config_t:file { getattr ioctl lock map open read };
  • The httpd_t element is a source or domain type.

  • The httpd_config_t element is the target type.

  • The file element is the class of the target type. For example, this element can be file, dir, lnk_file, or socket.

  • The { getattr ioctl lock map open read } element is the list of operations that SELinux allows. Because the write operation is not listed, SELinux does not allow the httpd process to write its configuration files.

To list the rules, use the sesearch command with the -A option. The sesearch command is part of the setools-console package.

[root@host ~]# sesearch -A
allow NetworkManager_dispatcher_chronyc_script_t NetworkManager_dispatcher_chronyc_script_t:filesystem associate;
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_script_t:file { entrypoint execute getattr ioctl lock map open read };
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:association sendto;
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:dir { getattr ioctl lock open read search watch };
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:fifo_file { append getattr ioctl lock open read write };
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:fifo_file { create link rename setattr unlink }; [ fips_mode ]:True
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:file { append getattr ioctl lock open read write };
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:lnk_file { getattr ioctl lock read };
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:peer recv;
allow NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:process { fork getcap getsched sigchld sigkill signal signull sigstop };
...output omitted...

The sesearch command accepts options to filter its output. The following command displays only the rule that allows the httpd_t source type (-s) to access files (-c file) with the httpd_config_t target type (-t).

[root@host ~]# sesearch -A -s httpd_t -t httpd_config_t -c file
allow domain file_type:file map; [ domain_can_mmap_files ]:True
allow httpd_t httpd_config_t:file { getattr ioctl lock map open read };

The following table lists some of the most useful options for the sesearch -A command.

OptionDescription
-s name Source domain type or attribute
-t name Target type or attribute
-c name Class of the target object. Use seinfo -c to get the full list. Use the -c option with the -x option to print a list of permissions for each displayed object class.

The -s and -t options also accept SELinux attributes. The following command lists the rules that allow domains to access directories with the lib_t type, which is the type of the /usr/lib64/ directory.

[root@host ~]# sesearch -A -s domain -t lib_t -c dir
allow NetworkManager_t lib_t:dir watch;
allow abrt_dump_oops_t non_security_file_type:dir { add_name create getattr ioctl link lock open read remove_name rename reparent rmdir search setattr unlink watch watch_reads write };
allow abrt_t file_type:dir { getattr open search };
allow aide_t file_type:dir { getattr ioctl lock open read search };
allow amanda_t file_type:dir { getattr ioctl lock open read search };
allow antivirus_domain file_type:dir { getattr ioctl lock open read search }; [ antivirus_can_scan_system ]:True
allow antivirus_domain file_type:dir { getattr ioctl lock open read search }; [ antivirus_can_scan_system ]:True
allow antivirus_domain file_type:dir { getattr open search }; [ antivirus_can_scan_system ]:True
allow antivirus_domain file_type:dir { getattr open search }; [ antivirus_can_scan_system ]:True
allow auditctl_t file_type:dir { getattr open search };
...output omitted...

Remember that the domain attribute groups all the types that are associated with domains, such as the httpd_t type. Distinguishing attributes and types might be challenging at first, but remember that types always end with the _t suffix, which is never the case for attributes.

Selectively Disabling Audits

In addition to allow rules, the SELinux policy also contains dontaudit rules. When SELinux does not allow an action, but a dontaudit rule exists for that action, then SELinux does not log the denial.

A dontaudit rule is useful because some applications probe for files or directories: for example, to identify some features or devices that might be provided by the operating system. If those files or directory are missing, or the access is not allowed, then the application continues without problems. But these access attempts still might trigger SELinux violations, which would normally be added to the log.

By setting dontaudit rules, the log file does not store logs for access violations that are expected, and which do not have significant security implications.

You can list all the dontaudit rules on a system by using the --dontaudit option with the sesearch command.

[user@host ~]$ sesearch --dontaudit
dontaudit NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:capability { net_admin sys_module };
dontaudit NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:file create;
dontaudit NetworkManager_dispatcher_chronyc_t NetworkManager_dispatcher_chronyc_t:udp_socket listen;
dontaudit NetworkManager_dispatcher_cloud_t NetworkManager_dispatcher_cloud_t:capability { net_admin sys_module sys_ptrace };
dontaudit NetworkManager_dispatcher_cloud_t NetworkManager_dispatcher_cloud_t:file create;
dontaudit NetworkManager_dispatcher_cloud_t NetworkManager_dispatcher_cloud_t:process setrlimit;
dontaudit NetworkManager_dispatcher_cloud_t NetworkManager_dispatcher_cloud_t:udp_socket listen;
dontaudit NetworkManager_dispatcher_console_t NetworkManager_dispatcher_console_t:capability { net_admin sys_module };
dontaudit NetworkManager_dispatcher_console_t NetworkManager_dispatcher_console_t:file create;
dontaudit NetworkManager_dispatcher_console_t NetworkManager_dispatcher_console_t:udp_socket listen;
...output omitted...

Sometimes, for troubleshooting purposes, you might want to disable the dontaudit rules to record all SELinux denials in the log. Use the semodule -DB command to disable the dontaudit rules.

[root@host ~]# semodule -DB

To re-enable the dontaudit rules, use the semodule -B command.

[root@host ~]# semodule -B

Creating Custom Policy Modules

Developing a custom policy for your application is beyond the scope of this course. However, the audit2allow command can generate a policy module for you by analyzing the denials in the audit.log file.

For example, the audit2allow command can generate a custom rule based on the following message in the /var/log/audit/audit.log file:

[root@host ~]# grep denied /var/log/audit/audit.log
...output omitted...
type=AVC msg=audit(1697823181.181:205): avc:  denied  { getattr } for  pid=27012 comm="httpd" path="/var/www/html/index.html" dev="vda4" ino=25541492 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0
...output omitted...

Run audit2allow -a to print the rule to allow the access.

[root@host ~]# audit2allow -a

#============= httpd_t ==============
allow httpd_t admin_home_t:file getattr;
allow httpd_t self:capability net_admin;

Important

Use audit2allow with caution: it generates allow rules based on the denials in the log file. Ultimately, you must decide whether the rule is safe to add in the policy. Before changing the policy, consider whether the violation is being reported for a good reason, and why that access might be blocked by default.

In the previous example, a better solution might be to relabel the index.html file, rather than adding a new rule that gives the httpd daemon access to all the files that are labeled with the admin_home_t type.

To generate a new SELinux policy module, add the -M modulename option to the previous audit2allow command. Use the semodule command to persistently load the new module in SELinux.

[root@host ~]# audit2allow -a -M mymodule
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i mymodule.pp

[root@host ~]# semodule -i mymodule.pp

In the example, the first operation that the httpd daemon performs on the index.html file is to retrieve the file's attributes (getattr). Because SELinux is in enforcing mode, the operation is denied and the httpd daemon does not access the file further. The new rule that allows the getattr operation is not sufficient because the httpd daemon also needs to open and read the file, and those operations are still not allowed.

Therefore, before using the audit2allow command, put SELinux in permissive mode to collect all the denials in one operation.

[root@host ~]# setenforce 0
[root@host ~]# curl http://localhost/index.html
Hello, World!
[root@host ~]# setenforce 1
[root@host ~]# audit2allow -a


#============= httpd_t ==============
allow httpd_t admin_home_t:file { getattr open read };

#!!!! This avc can be allowed using the boolean 'domain_can_mmap_files'
allow httpd_t admin_home_t:file map;
allow httpd_t self:capability net_admin;

This time, the rule also includes the open and read operations.

Domain Transitions

New processes inherit the context type of their parent. The following example shows that the vim process with the 27393 PID has the same context as its parent.

[root@host ~]# pstree -Z 27393
bash(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
 └─vim(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')

Occasionally, processes must change their security contexts. This transition is fairly common with daemons that are started by the systemd daemon. When the systemd daemon starts a service, SELinux must confine the service in its own domain. For example, when the systemd daemon starts the httpd service, the httpd service transitions to the httpd_t domain.

[root@host ~]# pstree -Z  | grep -e ^systemd -e httpd
systemd(`system_u:system_r:init_t:s0')
 ├─httpd(`system_u:system_r:httpd_t:s0')
 │  ├─httpd(`system_u:system_r:httpd_t:s0')
 │  ├─httpd(`system_u:system_r:httpd_t:s0')
 │  │  └─64*[{httpd}(`system_u:system_r:httpd_t:s0')]
 │  ├─httpd(`system_u:system_r:httpd_t:s0')
 │  │  └─64*[{httpd}(`system_u:system_r:httpd_t:s0')]
 │  └─httpd(`system_u:system_r:httpd_t:s0')
 │     └─80*[{httpd}(`system_u:system_r:httpd_t:s0')]

The previous output shows that even though the systemd process is running in the init_t domain, SELinux confines the child httpd process in its own httpd_t domain.

These transition rules are part of the SELinux policy.

The following transition rule specifies that if a process with the init_t context type executes a binary file that has the httpd_exec_t context type, then the resulting child process has the httpd_t context type.

type_transition init_t httpd_exec_t : process httpd_t ;
  • The init_t element is the source or domain type of the parent process.

  • The httpd_exec_t element is the context type of the program file.

  • The httpd_t element is the domain of the resulting child process.

To list the transition rules, use the sesearch command with the -T option.

[root@host ~]# sesearch -T -s init_t -t httpd_exec_t
type_transition init_t httpd_exec_t:process httpd_t;

Note

Policies commonly set domain transition rules so that when an unconfined domain like the init_t domain runs an executable that has a type like the something_exec_t type, the resulting process ends up with a confined domain such as the example_t type.

The domain transition rules for the something_t domain are generally much more strict, which helps enforce confinement. If there is not a rule to transition the example_t domain back to an unconfined domain, then the executable remains confined.

As a complement to the sesearch -T command, the sepolicy transition command, from the policycoreutils-devel package, can analyze the policy and list all the intermediary types for the transition from one domain to another.

The following example lists all the paths of sequential transitions that can get from the httpd_t domain to the unconfined_t domain. In other words, the example shows whether and how a subprocess of the httpd service might be unconfined through domain transitions. The output does not indicate whether or not executables exist on the system to accomplish this set of transitions.

[root@host ~]# sepolicy transition -s httpd_t -t unconfined_t
httpd_t ... abrt_retrace_worker_t ... mock_t ... mount_t ... glusterd_t ... initrc_t ... pegasus_t ... rpm_t ... rpm_script_t ... openshift_initrc_t ... virt_qemu_ga_t ... system_cronjob_t ... logrotate_t ... cluster_t ... condor_schedd_t ... condor_startd_t ... inetd_t ... sshd_t @ shell_exec_t --> unconfined_t -- Allowed True [ ssh_sysadm_login=0 || unconfined_login=1 ]
...output omitted...

Run the sepolicy transition command on each transition to get more details.

[root@host ~]# sepolicy transition -s httpd_t -t abrt_retrace_worker_t
httpd_t @ abrt_retrace_worker_exec_t --> abrt_retrace_worker_t

[root@host ~]# sepolicy transition -s abrt_retrace_worker_t -t mock_t
abrt_retrace_worker_t @ mock_exec_t --> mock_t

[root@host ~]# sepolicy transition -s mock_t -t mount_t
mock_t @ fusermount_exec_t --> mount_t
mock_t @ mount_exec_t --> mount_t

Some domains have no way to transition to other domains under the domain transition policy, such as the postfix_master_t domain to the unconfined_t domain in the following example:

[root@host ~]# sepolicy transition -s postfix_master_t -t unconfined_t

[root@host ~]#

File Transitions

New files and directories inherit the context of their parent directory. Sometimes this inherited context is not correct and you must relabel the object.

You can get the expected context of an object with the matchpathcon command. The file or directory that you specify does not need to exist because the command uses the file context rules database, the same database that the restorecon command uses, to retrieve the context.

[root@host ~]# matchpathcon /var/www/html/myimage.png
/var/www/html/myimage.png	system_u:object_r:httpd_sys_content_t:s0

You can list the default file context rules with the semanage fcontext -l command and add new rules with the semanage fcontext -a command.

[root@host ~]# semanage fcontext -l
...output omitted...
/var/www(/.*)?      all files     system_u:object_r:httpd_sys_content_t:s0
...output omitted...
[root@host ~]# semanage fcontext -a -t httpd_sys_content_t '/virtual(/.*)?'

Sometimes, new files and directories must have a specific context at creation time. For example, the /var/log/cron file has a specific context that allows the crond daemon to write to it. The context of this file is not the same as its parent directory.

[root@host ~]# ls -Zd /var/log /var/log/cron
drwxr-xr-x. root root system_u:object_r:`var_log_t`:s0   /var/log
-rw-r--r--. root root system_u:object_r:`cron_log_t`:s0  /var/log/cron

The SELinux policy contains file transition rules that allow SELinux to automatically set the context of specific files and directories at creation time. For example, the following transition rule specifies that if a process in the crond_t domain (crond) creates a file in a directory with the var_log_t type, then the resulting file has the cron_log_t type.

type_transition crond_t var_log_t : file cron_log_t ;
  • The crond_t element is the source or domain type of the process.

  • The var_log_t element is the context type of the parent directory.

  • The file element is the type of the object that is being created.

  • The cron_log_t element is the resulting context type of the object.

To list the file transition rules, use the sesearch -T command.

[root@host ~]# sesearch -T -s crond_t -t var_log_t -c file
type_transition crond_t var_log_t:file NetworkManager_var_lib_t wpa_supplicant.log;
type_transition crond_t var_log_t:file cron_log_t;
type_transition crond_t var_log_t:file devicekit_var_log_t pm-powersave.log;
type_transition crond_t var_log_t:file devicekit_var_log_t pm-suspend.log;
type_transition crond_t var_log_t:file faillog_t btmp;
type_transition crond_t var_log_t:file faillog_t faillog;
type_transition crond_t var_log_t:file faillog_t tallylog;
type_transition crond_t var_log_t:file lastlog_t lastlog;
type_transition crond_t var_log_t:file plymouthd_var_log_t boot.log;
type_transition crond_t var_log_t:file rpm_log_t dnf.librepo.log;
...output omitted...

These file transition rules can include an extra parameter to specify the object name. The following rule sets the rpm_log_t type on the dnf.log file when an unconfined_t domain creates it in a directory with the var_log_t type.

[root@host ~]# sesearch -T -s unconfined_t -t var_log_t -c file | \
    grep dnf.log
type_transition unconfined_t var_log_t : file rpm_log_t "dnf.log";

References

The seinfo(1), sesearch(1), semodule(8), matchpathcon(8), sepolicy-transition(8), and audit2allow(1) man pages

For more information on troubleshooting SELinux denials, refer to the Troubleshooting Problems Related to SELinux chapter in the Using SELinux guide at https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/using_selinux/index#troubleshooting-problems-related-to-selinux_using-selinux

Revision: rh415-9.2-a821299