8.2. Minor Customizations of the Existing Policy

You may find it useful to resolve SELinux denials by using new policy rules to allow the behavior. The ramifications on security are impossible to predict. At the worst, you are back to standard Linux security. Your policy changes may be enough to effectively disable the confinement for one or more parts of a targeted daemon policy.

You must take these considerations into account when adjusting the policy. You can use apol to analyze the transitions and TE rules, looking for where your policy changes weaken security. For more information about working with apol, read Section 6.3 Using apol for Policy Analysis.

This procedure takes you through using audit2allow to add minor custom rules to the existing policy. It assumes that you have one or more avc: denied messages in $AUDIT_LOG.

TipTip
 

Put local policy changes in $SELINUX_SRC/domains/misc/local.te, and file context information in $SELINUX_SRC/file_contexts/misc/local.fc. Your files are picked up on compile.

Separating your local customizations from the main source is akin to maintaining a set of patches to pristine source code. When you update the policy source, your changes won't be clobbered or at risk.

If you package your own policy, follow a similar methodology. Use the maintained, upstream source for either the supported targeted or the unsupported strict policy. You can rebuild the policy packages, having your changes be in standalone files such as local.te or new files within $SELINUX_SRC/domains/program/.

If you change the existing TE files, you have to merge the patches with each new policy. Remember that policy.conf is all of the various source files concatenated together. It does not need to know where the content came from, that is a decision made in the Makefile. The files and directories in $SELINUX_SRC/ are a convenience for the policy writer. Following the methods prescribed by the SELinux developers helps you in your policy writing efforts.

WarningWarning
 

Just because audit2allow generates a rule does not make it a good or secure rule.

You must look through the rules generated by audit2allow for a sanity check. For example, an application generates an SELinux denial asking for read and write permission to /etc/passwd. A rule generated by audit2allow out of the audit log would allow that permission. You need to understand the underlying application and decide if it should have those permissions.

This is one of the ways SELinux can help find flaws in applications, where excessive and unnecessary levels of access are attempted.

Adding Rules to the Policy Using audit2allow

  1. Be sure you are in the policy source directory:

    cd $SELINUX_SRC/
  2. If you have an existing file at $SELINUX_SRC/domains/misc/local.te, make a backup before proceeding with the rule creation:

    # The directory domains/unused is ignored during the
    # policy build and is a safe place for archiving.
    
    cp domains/misc/local.te domains/unused/local.te.backup
  3. Tell audit2allow to look in dmesg for denial messages, only since the last load_policy ran, and write that to domains/misc/local.te:

    audit2allow -d -l -o domains/misc/local.te

    Look in local.te to be sure you don't have any duplicate rules. This is one reason for having audit2allow generate rules since the last load_policy, to keep from creating duplicates.

    Once you have a complete set of working rules, you may want to look for ways to rewrite and simplify the rules. One way to do this is to have audit2allow run one final time against the entire set of denial messages. It looks for ways to consolidate rules into single lines:

    # For example, two passes of audit2allow yield these rules:
    
    allow httpd_t user_home_t:dir getattr;
    allow httpd_t user_home_t:dir search;
    
    # Looking at all of denials in $AUDIT_LOG
    # may reveal some consolidation of rules, for example:
    
    audit2allow -d -o domains/misc/consolidated_local.te
    grep "user_home_t:dir" domains/misc/consolidated_local.te
    allow httpd_t user_home_t:dir { getattr search };
  4. Test your policy. Run make load and try your previously denied operation(s).

  5. At this point you may need to do multiple iterations of these steps. Each additional rule allows the operation to get one step further. You use the subsequent denial to write the next rule, and the process continues.

After multiple iterations, you have a set of rules. Now you want to analyze the rules to be sure they follow the principle of least privilege. This is where policy analysis with apol is useful, as described in Section 6.3 Using apol for Policy Analysis.

To make your policy more elegant and efficient, look for macros that provide you the permissions created by your new rules. You can then scrap one or more rules in favor of a macro, which simplifies code reuse. Ideally, your rules benefit from bug fixes and enhancements to the entire policy because your rules build on the policy as a privately maintained set of rules, relying upon the overall structure of the parent policy.

For example, you are running the targeted policy on a newly installed server that is using your in-house, custom-configured syslog-ng instead of sysklogd. You find that the policy for syslogd does not cover all of the non-standard logging operations that you perform. Some of the additional operational requirements for your syslog-ng implementation are to open non-standard files and UDP and TCP ports, as well as call non-standard routines.

The following are a sampling of the avc: denied messages you have received:

Jan 10 04:02:17 example kernel: audit(1009218137.102:0): \
  avc:  denied  { write } for  pid=6109 exe=/sbin/syslog-ng \
  name=kmsg dev=proc ino=-268435446 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:proc_kmsg_t tclass=file
Jan 10 04:02:17 example kernel: audit(1009218137.105:0): \
  avc:  denied  { read } for  pid=16202 exe=/bin/bash name=mtab \
  dev=dm-0 ino=7146016 scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:etc_runtime_t tclass=file
...
Jan 10 16:20:35 example kernel: audit(1009284205.210:0): \
  avc:  denied  { chown } for  pid=6109 exe=/sbin/syslog-ng \
  capability=0 scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:system_r:syslogd_t tclass=capability
Jan 10 16:20:35 example kernel: audit(1009284205.210:0): \
  avc:  denied  { fowner } for  pid=6109 exe=/sbin/syslog-ng \
  capability=3 scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:system_r:syslogd_t tclass=capability
Jan 10 16:20:35 example kernel: audit(1009284205.210:0): \
  avc:  denied  { fsetid } for  pid=6109 exe=/sbin/syslog-ng \
  capability=4 scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:system_r:syslogd_t tclass=capability
...
Jan 10 16:20:35 example kernel: audit(1009284205.422:0): \
  avc:  denied  { search } for  pid=1411 exe=/bin/bash \
  name=sbin dev=dm-0 ino=7356417 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:sbin_t tclass=dir
Jan 10 16:20:35 example kernel: audit(1009284205.422:0): \
  avc:  denied  { getattr } for  pid=1411 exe=/bin/bash \
  path=/bin/bash dev=dm-0 ino=1245248 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:shell_exec_t tclass=file
Jan 10 16:20:35 example kernel: audit(1009284205.423:0): \
  avc:  denied  { getattr } for  pid=1411 exe=/bin/bash \
  path=/bin/rm dev=dm-0 ino=1245243 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:bin_t tclass=file
Jan 10 16:20:35 example kernel: audit(1009284205.423:0): \
  avc:  denied  { execute_no_trans } for  pid=1411 \
  exe=/bin/bash path=/bin/rm dev=dm-0 ino=1245243 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:bin_t tclass=file
Jan 10 16:20:35 example kernel: audit(1009284205.423:0): \
  avc:  denied  { read } for  pid=1411 exe=/bin/bash \
  path=/bin/rm dev=dm-0 ino=1245243 \
  scontext=system_u:system_r:syslogd_t \
  tcontext=system_u:object_r:bin_t tclass=file

Running all of the audit messages through audit2allow generates a set of rules:

cd  /etc/selinux/targeted/src/policy/domains/misc/
audit2allow -i /var/log/messages -o ./local.te
cat local.te
allow syslogd_t bin_t:dir search;
allow syslogd_t bin_t:file { execute execute_no_trans getattr \
  read };
allow syslogd_t bin_t:lnk_file read; 
allow syslogd_t etc_runtime_t:file { getattr read };
allow syslogd_t proc_kmsg_t:file write;
allow syslogd_t proc_t:file { getattr read };
allow syslogd_t sbin_t:dir search;
allow syslogd_t shell_exec_t:file { execute execute_no_trans \
  getattr read };
allow syslogd_t self:capability { chown fowner fsetid sys_admin };
allow syslogd_t usr_t:dir { add_name remove_name write };
allow syslogd_t usr_t:file { append create getattr read setattr \
  unlink write };

Looking at the rules, you can see that there are two for execution permissions, and some other rules that are associated by having the same object, bin_t. There is also a permission to search directories of the type sbin_t, but no execution permissions.

From your reading of the available macros in $SELINUX_SRC/macros/, you know that can_exec() provides a common set of permissions for domains wishing to execute certain file types. This includes permissions that are likely to arise once the first set of basic rules are used. For example, after audit2allow generates a rule giving read permission to a process, the process often wants getattr permission. The can_exec() macro includes the permission rx_file_perms, which grants a common set of read and execute permissions to a file. Now you can make this substitution:

# these related rules ...
allow syslogd_t bin_t:dir search;
allow syslogd_t bin_t:file { execute execute_no_trans getattr \
  read };
allow syslogd_t shell_exec_t:file { execute execute_no_trans \
  getattr read };

# combine into this one rule
can_exec(syslog_t, { bin_t shell_exec_t } )

You also see in the rules that two of the rules are from syslog-ng attempting to use a directory in /usr/ of the type usr_t:

# These rules can be eliminated by properly labeling
# the files in the target location.
allow syslogd_t usr_t:dir { add_name remove_name write };
allow syslogd_t usr_t:file { append create getattr read setattr \
  unlink write };

Your configuration uses a directory in /usr/ to write log files to. Because this log data is not user data, it should have an appropriate label, var_log_t.

# If syslog-ng is configured to put logs in /usr/local/logs/,
# relabel that directory, and new files in the directory
# inherit the proper type.
chcon -R -t var_log_t /usr/local/logs/

Now you can trim the rules in $SELINUX_SRC/domains/misc/local.te to read:

can_exec(syslog_t, { bin_t shell_exec_t } )
allow syslogd_t etc_runtime_t:file { getattr read };
allow syslogd_t proc_kmsg_t:file write;
allow syslogd_t proc_t:file { getattr read };
allow syslogd_t bin_t:lnk_file read;
allow syslogd_t sbin_t:dir search;
allow syslogd_t self:capability { chown fowner fsetid sys_admin };

You also need to make an appropriate file contexts file so that the labeling is maintained during relabeling operations. Put the following context declaration in /etc/selinux/targeted/src/policy/file_contexts/misc/local.fc:

/usr/syslog(/.*)?   system_u:object_r:var_log_t