Categories

Donate

Advert

SE Linux Policy Writing Training

This training exercise is based on a Debian/Bullseye system running SE Linux in a default configuration in Enforcing mode. To set it up put the following in /usr/local/sbin/adaemon.sh and make it mode 0755:

#!/bin/bash

echo -100 > /proc/$$/oom_score_adj

while true ; do
  /sbin/runuser -u nobody -- /bin/nc -q0 -l -p 4000 < $0
  /bin/sleep 1
done

Then put the following in /etc/systemd/system/adaemon.service:

[Unit]
Description=a daemon

[Service]
RestartSec=20
Restart=always
ExecStart=/usr/local/sbin/adaemon.sh
StandardOutput=syslog
KillMode=mixed
Type=simple

[Install]
WantedBy=multi-user.target

To start it run the following commands:

systemctl enable adaemon.service
systemctl start adaemon.service
  1. Under the /root/pol directory create the file adaemon.fc:

    /usr/local/sbin/adaemon.sh -- gen_context(system_u:object_r:adaemon_exec_t,s0)

    Under the /root/pol directory create the file adaemon.te:

    policy_module(adaemon,1.0.0)
    
    #require {
    #}
    
    type adaemon_t;
    type adaemon_exec_t;
    init_daemon_domain(adaemon_t, adaemon_exec_t)
  2. After creating those files run the command "make load" (notice there is a symlink for the Makefile). That command compiles the policy module adaemon.pp and loads it into the running policy.

    After loading the policy run "restorecon -v /usr/local/sbin/adaemon.sh" to label the file, note that it should tell you that it has labelled it as adaemon_exec_t.

  3. The daemon is still running, run the command "nc localhost 4000" on your VM to observe it's output.

    Run the command "ps axZ|grep init_t" to see processes running as init_t (should only be process 1), in this case adaemon.sh is running in the wrong context.

  4. Try to restart the daemon (which will fail) and analyse the audit log for actions that were denied:

    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log
  5. To generate rules suitable for adding to policy the command "audit2allow -l < /var/log/audit/audit.log -R" should be used, the "-R" means to generate policy using macros. The first rule it will suggest adding is:

    corecmd_shell_entry_type(adaemon_t)

    Add the above rule to adaemon.te and run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    The above audit2allow gives the following output:

    corecmd_search_bin(adaemon_t)
    files_read_etc_files(adaemon_t)
    miscfiles_setattr_localization(adaemon_t)

    Setting the attributes of the localization files is not what we want the daemon to do. If you run the command "audit2allow -l < /var/log/audit/audit.log -R -v" it gives a list of all the possible macros that can be used, for the localization access one of the options is miscfiles_read_localization which is a good match for what you expect the daemon to need.

  6. corecmd_search_bin(adaemon_t)
    files_read_etc_files(adaemon_t)
    miscfiles_read_localization(adaemon_t)

    Add the above to adaemon.te and run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  7. After the previous change the audit2allow output is:

    corecmd_check_exec_bin_files(adaemon_t)

    This is something we want, but checking whether programs are executable isn't sufficient. Run the command "audit2allow -l < /var/log/audit/audit.log -R -v" and you will see that one of the options given is "corecmd_exec_bin(adaemon_t)". So add the following to adaemon.te:

    corecmd_exec_bin(adaemon_t)

    After adding that rule run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    Below is the output of audit2allow:

    allow adaemon_t self:capability { setgid sys_resource };
    allow adaemon_t self:netlink_audit_socket create;
    allow adaemon_t self:unix_dgram_socket create;
    kernel_read_system_state(adaemon_t)
    kernel_search_fs_sysctls(adaemon_t)
  8. Add the output of audit2allow from the previous step and run:
    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    That will give the following output:

    allow adaemon_t self:netlink_audit_socket write;
    allow adaemon_t self:unix_dgram_socket connect;
    kernel_read_kernel_sysctls(adaemon_t)

    Add the above to adaemon.te. You can remove the "kernel_search_fs_sysctls(adaemon_t)" line as that's included in the kernel_read_kernel_sysctls macro, or you could leave it as it doesn't do any harm. Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  9. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t self:netlink_audit_socket nlmsg_relay;

    The audit2allow command from the previous step gave the above output. Access to init_runtime_t shouldn't be needed and would need a require statement in the policy. So just add the following line to adaemon.te:

    allow adaemon_t self:netlink_audit_socket nlmsg_relay;

    Then run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  10. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t self:capability audit_write;
    allow adaemon_t self:netlink_audit_socket read;

    The audit2allow from the previous step gave the above. Now as the source of the daemon is visible it would be good to know how that maps to an audit socket. To discover that run the following command:

    grep netlink_audit_socket /var/log/audit/audit.log

    That shows that runuser is the responsible program. It makes sense that a program which is designed to change privileges of a child process will need audit access to log what it does, so this is correct.

    Add the following to adaemon.te:

    allow adaemon_t self:capability audit_write;
    allow adaemon_t self:netlink_audit_socket read;

    Then run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  11. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t init_t:dir search;
    allow adaemon_t self:capability setuid;
    allow adaemon_t self:process { setrlimit setsched };

    The output from audit2allow in the previous step is above. The init_t and init_runtime_t lines aren't needed (the macro init_daemon_domain gives all the init interaction the daemon needs), so add the following to the policy:

    allow adaemon_t self:capability setuid;
    allow adaemon_t self:process { setrlimit setsched };

    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  12. allow adaemon_t self:tcp_socket create;

    The above was the only relevant output of the previous audit2allow. Allowing the daemon to create a socket alone isn't enough, what we want is to allow it to listen, accept, and transfer data. Add the following line to the policy:

    allow adaemon_t self:tcp_socket create_stream_socket_perms;

    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  13. allow adaemon_t unreserved_port_t:tcp_socket name_bind;

    The previous step gave the above output. To address that add the following policy:

    corenet_tcp_bind_all_unreserved_ports(adaemon_t)
    corenet_tcp_bind_generic_node(adaemon_t)

    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  14. After the previous step you should get no relevant audit2allow messages and the following command should work:
    nc -q0 localhost 4000

Leave a Reply