8.3. Writing New Policy for a Daemon

These section provides an overall methodology to follow for writing a new policy from scratch. Although this is more complex than adding a few rules to local.te, the concepts are the same. You bring the application under TE rules and work through the AVC denials, adding rules each time until all permissions are resolved.

New Policy Writing Procedure

  1. Work with a proper daemon under Red Hat Enterprise Linux. This means it has an initscript in /etc/init.d/ and can be managed using chkconfig. For example, this procedure assumes you are going to use the service command to control starting and stopping the daemon.

    For this procedure, you are writing policy for the fictional foo package and it's associated foo daemon. Use a real daemon name in this place when developing your own policy.

  2. Create a file at $SELINUX_SRC/domains/program/foo.te.

  3. Put the daemon domain macro call in the file:

    daemon_domain(foo)
  4. Create the file contexts file, $SELINUX_SRC/file_contexts/program/foo.fc.

  5. Put the first list of file contexts in file.fc. You may need to add to this later, depending on the needs of the foo daemon.

    /usr/bin/foo      --     system_u:object_r:foo_exec_t
    /var/run/foo.pid  --     system_u:object_r:foo_var_run_t
    /etc/foo.conf     --     system_u:object_r:foo_conf_t
  6. Load the new policy with make load.

  7. Label the foo files:

    restorecon /usr/bin/foo /var/run/foo.pid /etc/foo.conf
  8. Start the daemon, service foo start.

  9. Examine your audit log for denial messages:

    grep "avc:  denied" /var/log/messages > /tmp/avc_denials
    cat /tmp/avc_denials

    Familiarize yourself with the errors the daemon is generating. You are writing policy with the help of audit2allow, but you need to understand the nature of the denials. You can also use seaudit for viewing the log messages, as explained in Section 6.2 Using seaudit for Audit Log Analysis.

  10. Use audit2allow to start the first round of policy rules.

    audit2allow -l -i /var/log/messages -o \
      /etc/selinux/targeted/src/policy/domains/program/foo.te

    When looking at the generated rules, if you see a rule that gives the foo_t domain read access to a file or directory, change the permission to read { read gettatr }. The domain is likely to need that permission if it already wants to read a file.

  11. Look to see if the foo_t domain tries to create a network socket, that is, udp_socket or tcp_socket as the object class in the AVC denial:

    avc:  denied  { create } for  pid=7279 exe=/usr/bin/foo \
      scontext=root:system_r:foo_t tcontext=root:system_r:foo_t\
      tclass=udp_socket

    If this is the case, then add the can_network() macro to foo.te:

    can_network(foo_t)
  12. Continue to iterate through the basic steps to generate all the rules you need. Each set of rules added to the policy may reveal additional permission needs from the foo_t domain.

    1. Start the daemon.

    2. Read the AVC messages.

    3. Write policy from the AVC messages, using audit2allow and your own knowledge, looking for chances to use macros.

    4. Load new policy.

    5. Go back to beginning, starting the daemon ...

  13. If the domain tries to access port_t, which relates to tclass=tcp_socket or tclass=udp_socket in the AVC log message, you need to determine what port number foo needs to use. To diagnose, put these rules in foo.te:

    allow foo_t port_t:tcp_socket name_bind;
    auditallow foo_t port_t:tcp_socket name_bind;

    The auditallow rule helps you determine the nature of the port connection attempt.

  14. Iterate through the remaining AVC denials. When they are resolved with new policy, you can configure the unique port requirements for the foo_t domain.

  15. With the daemon started, determine which port foo is using. Look at the AVC allowed message and see what port the daemon is connected to:

    lsof | grep foo.*TCP
    foo  2283  root  3u  IPv6   3192    TCP *:4242 (LISTEN)

    The foo daemon is listening on port 4242.

  16. Remove the generic port_t rule, replacing it with a specific rule for a new port type based on the foo_t domain.

    type foo_port_t, port_type;
    allow foo_t foo_port_t:tcp_socket name_bind;

    Add this line to $SELINUX_SRC/file_contexts. This reserves the port 4242 for the domain foo_t:

    ifdef(`foo.te', `portcon tcp 4242 system_u:object_r:foo_port_t')