├── lab10-systemd ├── testprog.if ├── testprog.fc ├── testprog.te └── README.md ├── lab11-interfaces ├── testcat.if ├── testcat.fc ├── testcat.te └── README.md ├── .gitignore ├── lab09-putting-it-all-together ├── testprog.if ├── testprog.fc ├── testprog.te └── README.md ├── testprog ├── testprog.conf ├── testprog.service ├── Makefile └── testprog.c ├── lab05-file-contexts ├── testprog.fc ├── testprog.te └── README.md ├── lab12-interfaces-part-2 ├── testcat.fc ├── testprog.fc ├── testprog.if ├── testcat.te ├── testprog.te └── README.md ├── lab13-interfaces-part-3 ├── testcat.fc ├── testprog.fc ├── testprog.if ├── testcat.te ├── testprog.te └── README.md ├── testprog-net ├── testprog-net.conf ├── testprog-net.service ├── Makefile └── testprog-net.c ├── lab16-booleans ├── testprog.fc ├── testprog.if ├── testprog.te └── README.md ├── lab14-networking ├── testprog.fc ├── testprog.if ├── testprog.te └── README.md ├── lab15-allowing-networking ├── testprog.fc ├── testprog.if ├── testprog.te └── README.md ├── LICENSE ├── lab04-targeting-testprog ├── testprog.te └── README.md ├── lab02-the-testprog └── README.md ├── lab01-up-and-running └── README.md ├── README.md ├── CODE_OF_CONDUCT.md ├── lab08-about-macros └── README.md ├── lab07-finding-examples └── README.md ├── lab06-sealert-and-audit-log └── README.md └── lab03-running-testprog-for-the-first-time └── README.md /lab10-systemd/testprog.if: -------------------------------------------------------------------------------- 1 | ## 2 | -------------------------------------------------------------------------------- /lab11-interfaces/testcat.if: -------------------------------------------------------------------------------- 1 | ## 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | testprog/testprog 2 | testprog-net/testprog-net 3 | 4 | -------------------------------------------------------------------------------- /lab09-putting-it-all-together/testprog.if: -------------------------------------------------------------------------------- 1 | ## 2 | -------------------------------------------------------------------------------- /lab11-interfaces/testcat.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testcat -- system_u:object_r:testcat_exec_t:s0 2 | -------------------------------------------------------------------------------- /testprog/testprog.conf: -------------------------------------------------------------------------------- 1 | OUTPUTFILE=/var/testprog/testprg.txt 2 | LOOPCOUNT=-1 3 | 4 | -------------------------------------------------------------------------------- /lab05-file-contexts/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 2 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/testcat.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testcat -- system_u:object_r:testcat_exec_t:s0 2 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/testcat.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testcat -- system_u:object_r:testcat_exec_t:s0 2 | -------------------------------------------------------------------------------- /testprog-net/testprog-net.conf: -------------------------------------------------------------------------------- 1 | OUTPUTFILE=/var/testprog/testprg-net.txt 2 | LOOPCOUNT=-1 3 | NETWORKPORT=0 4 | -------------------------------------------------------------------------------- /lab10-systemd/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog.conf -- system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog.conf -- system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog.conf -- system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab09-putting-it-all-together/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog.conf -- system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab16-booleans/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog(.*)? system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog(.*)?.conf system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab14-networking/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog(.*)? system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog(.*)?.conf system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /lab15-allowing-networking/testprog.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/testprog(.*)? system_u:object_r:testprog_exec_t:s0 2 | /etc/testprog(.*)?.conf system_u:object_r:testprog_conf_t:s0 3 | /var/run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 4 | /run/testprog(.*).pid system_u:object_r:testprog_var_run_t:s0 5 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 6 | -------------------------------------------------------------------------------- /testprog/testprog.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=SELinux Test Program 3 | 4 | [Service] 5 | #Type=forking 6 | # The PID file is optional, but recommended in the manpage 7 | # "so that systemd can identify the main process of the daemon" 8 | PIDFile=/var/run/testprog.pid 9 | ExecStart=/usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /testprog-net/testprog-net.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=SELinux Test Program with Networking 3 | 4 | [Service] 5 | #Type=forking 6 | # The PID file is optional, but recommended in the manpage 7 | # "so that systemd can identify the main process of the daemon" 8 | PIDFile=/var/run/testprog-net.pid 9 | ExecStart=/usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /lab14-networking/testprog.if: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ### 3 | ### Read testprog data files. 4 | ### 5 | ### 6 | ### 7 | ### Domain allowed to read the data files. 8 | ### 9 | ### 10 | ## 11 | interface(`testprog_read_data',` 12 | gen_require(` 13 | type testprog_data_t; 14 | ') 15 | 16 | # Allow the domain passed as argument 1 to access the testprog data 17 | allow $1 testprog_data_t:dir { search add_name }; 18 | allow $1 testprog_data_t:file { open read getattr }; 19 | ') 20 | -------------------------------------------------------------------------------- /lab16-booleans/testprog.if: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ### 3 | ### Read testprog data files. 4 | ### 5 | ### 6 | ### 7 | ### Domain allowed to read the data files. 8 | ### 9 | ### 10 | ## 11 | interface(`testprog_read_data',` 12 | gen_require(` 13 | type testprog_data_t; 14 | ') 15 | 16 | # Allow the domain passed as argument 1 to access the testprog data 17 | allow $1 testprog_data_t:dir { search add_name }; 18 | allow $1 testprog_data_t:file { open read getattr }; 19 | ') 20 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/testprog.if: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ### 3 | ### Read testprog data files. 4 | ### 5 | ### 6 | ### 7 | ### Domain allowed to read the data files. 8 | ### 9 | ### 10 | ## 11 | interface(`testprog_read_data',` 12 | gen_require(` 13 | type testprog_data_t; 14 | ') 15 | 16 | # Allow the domain passed as argument 1 to access the testprog data 17 | allow $1 testprog_data_t:dir { search add_name }; 18 | allow $1 testprog_data_t:file { open read getattr }; 19 | ') 20 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/testprog.if: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ### 3 | ### Read testprog data files. 4 | ### 5 | ### 6 | ### 7 | ### Domain allowed to read the data files. 8 | ### 9 | ### 10 | ## 11 | interface(`testprog_read_data',` 12 | gen_require(` 13 | type testprog_data_t; 14 | ') 15 | 16 | # Allow the domain passed as argument 1 to access the testprog data 17 | allow $1 testprog_data_t:dir { search add_name }; 18 | allow $1 testprog_data_t:file { open read getattr }; 19 | ') 20 | -------------------------------------------------------------------------------- /lab15-allowing-networking/testprog.if: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ### 3 | ### Read testprog data files. 4 | ### 5 | ### 6 | ### 7 | ### Domain allowed to read the data files. 8 | ### 9 | ### 10 | ## 11 | interface(`testprog_read_data',` 12 | gen_require(` 13 | type testprog_data_t; 14 | ') 15 | 16 | # Allow the domain passed as argument 1 to access the testprog data 17 | allow $1 testprog_data_t:dir { search add_name }; 18 | allow $1 testprog_data_t:file { open read getattr }; 19 | ') 20 | -------------------------------------------------------------------------------- /testprog/Makefile: -------------------------------------------------------------------------------- 1 | # References: 2 | # http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 3 | # http://nuclear.mutantstargoat.com/articles/make/ 4 | 5 | CC=gcc 6 | PREFIX = /usr 7 | 8 | testprog: testprog.c 9 | $(CC) -o testprog testprog.c 10 | 11 | clean: 12 | rm -f testprog 13 | 14 | install: 15 | mkdir -p $(DESTDIR)$(PREFIX)/bin 16 | cp testprog $(DESTDIR)$(PREFIX)/bin/testprog 17 | cp testprog.conf /etc/testprog.conf 18 | mkdir -p /var/testprog 19 | cp testprog.service /etc/systemd/system/testprog.service 20 | 21 | uninstall: 22 | rm -f $(DESTDIR)$(PREFIX)/bin/testprog 23 | rm -f /etc/testprog.conf 24 | rm -f /etc/systemd/system/testprog.service 25 | rm -rf /var/testprog 26 | 27 | -------------------------------------------------------------------------------- /testprog-net/Makefile: -------------------------------------------------------------------------------- 1 | # References: 2 | # http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 3 | # http://nuclear.mutantstargoat.com/articles/make/ 4 | 5 | CC=gcc 6 | PREFIX = /usr 7 | 8 | testprog-net: testprog-net.c 9 | $(CC) -o testprog-net testprog-net.c 10 | 11 | clean: 12 | rm -f testprog-net 13 | 14 | install: 15 | mkdir -p $(DESTDIR)$(PREFIX)/bin 16 | cp testprog-net $(DESTDIR)$(PREFIX)/bin/testprog-net 17 | cp testprog-net.conf /etc/testprog-net.conf 18 | mkdir -p /var/testprog-net 19 | cp testprog-net.service /etc/systemd/system/testprog-net.service 20 | 21 | uninstall: 22 | rm -f $(DESTDIR)$(PREFIX)/bin/testprog-net 23 | rm -f /etc/testprog-net.conf 24 | rm -f /etc/systemd/system/testprog-net.service 25 | rm -rf /var/testprog-net 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 James Freeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lab11-interfaces/testcat.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testcat, 0.1) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class process transition; 16 | } 17 | 18 | # Define our new types that testcat will use, and ensure that we tell the policy that testcat_exec_t is a file 19 | type testcat_t; 20 | domain_type(testcat_t); 21 | type testcat_exec_t; 22 | files_type(testcat_exec_t); 23 | 24 | # Allow the testcat_t type under the unconfined_r role 25 | role unconfined_r types testcat_t; 26 | 27 | # Tell SELinux that testcat_exec_t is an entrypoint to the testprog_t domain 28 | allow testcat_t testcat_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; 29 | # Make the type transition from unconfined_t (i.e. user shell) to testcat_t 30 | type_transition unconfined_t testcat_exec_t : process testcat_t; 31 | # Explicitly allow the type transition we have just created 32 | allow unconfined_t testcat_t : process transition ; 33 | 34 | -------------------------------------------------------------------------------- /lab05-file-contexts/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class process transition; 16 | } 17 | 18 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 19 | type testprog_t; 20 | domain_type(testprog_t); 21 | type testprog_exec_t; 22 | files_type(testprog_exec_t); 23 | 24 | # Allow the testprog_t type under the unconfined_r role 25 | role unconfined_r types testprog_t; 26 | 27 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 28 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; 29 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 30 | type_transition unconfined_t testprog_exec_t : process testprog_t; 31 | # Explicitly allow the type transition we have just created 32 | allow unconfined_t testprog_t : process transition ; 33 | 34 | -------------------------------------------------------------------------------- /lab04-targeting-testprog/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class process transition; 16 | } 17 | 18 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 19 | type testprog_t; 20 | domain_type(testprog_t); 21 | type testprog_exec_t; 22 | files_type(testprog_exec_t); 23 | 24 | # Allow the testprog_t type under the unconfined_r role 25 | role unconfined_r types testprog_t; 26 | 27 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 28 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; 29 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 30 | type_transition unconfined_t testprog_exec_t : process testprog_t; 31 | # Explicitly allow the type transition we have just created 32 | allow unconfined_t testprog_t : process transition ; 33 | 34 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/testcat.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testcat, 0.1) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class process transition; 16 | type console_device_t; 17 | type user_devpts_t; 18 | class unix_dgram_socket { create connect sendto }; 19 | class chr_file { append read write open getattr ioctl }; 20 | class capability sys_tty_config; 21 | } 22 | 23 | # Define our new types that testcat will use, and ensure that we tell the policy that testcat_exec_t is a file 24 | type testcat_t; 25 | domain_type(testcat_t); 26 | type testcat_exec_t; 27 | files_type(testcat_exec_t); 28 | 29 | # Allow the testcat_t type under the unconfined_r role 30 | role unconfined_r types testcat_t; 31 | 32 | # Tell SELinux that testcat_exec_t is an entrypoint to the testprog_t domain 33 | allow testcat_t testcat_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 34 | # Make the type transition from unconfined_t (i.e. user shell) to testcat_t 35 | type_transition unconfined_t testcat_exec_t : process testcat_t; 36 | # Explicitly allow the type transition we have just created 37 | allow unconfined_t testcat_t : process transition ; 38 | 39 | allow testcat_t console_device_t:chr_file { open write getattr ioctl }; 40 | allow testcat_t self:capability sys_tty_config; 41 | allow testcat_t user_devpts_t:chr_file { append read write getattr }; 42 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/testcat.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testcat, 0.1) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class process transition; 16 | type console_device_t; 17 | type user_devpts_t; 18 | class unix_dgram_socket { create connect sendto }; 19 | class chr_file { append read write open getattr ioctl }; 20 | class capability sys_tty_config; 21 | } 22 | 23 | # Define our new types that testcat will use, and ensure that we tell the policy that testcat_exec_t is a file 24 | type testcat_t; 25 | domain_type(testcat_t); 26 | type testcat_exec_t; 27 | files_type(testcat_exec_t); 28 | 29 | # Allow the testcat_t type under the unconfined_r role 30 | role unconfined_r types testcat_t; 31 | 32 | # Tell SELinux that testcat_exec_t is an entrypoint to the testprog_t domain 33 | allow testcat_t testcat_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 34 | # Make the type transition from unconfined_t (i.e. user shell) to testcat_t 35 | type_transition unconfined_t testcat_exec_t : process testcat_t; 36 | # Explicitly allow the type transition we have just created 37 | allow unconfined_t testcat_t : process transition ; 38 | 39 | allow testcat_t console_device_t:chr_file { open write getattr ioctl }; 40 | allow testcat_t self:capability sys_tty_config; 41 | allow testcat_t user_devpts_t:chr_file { append read write getattr }; 42 | 43 | # Use the interface defined for testprog data to allow us to access it's files from our confined domain 44 | testprog_read_data(testcat_t); 45 | -------------------------------------------------------------------------------- /lab02-the-testprog/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to testprog! This is a little C program that was designed to perform a series of common functions on an EL9/Fedora 41 system to help you learn SELinux. It is by it's nature very simple and also at this stage quite crude. If anyone wants to help improve the code I would be very grateful - however for now it meets my original goals: 4 | 5 | * A native binary (as opposed to a script requiring another binary to run it) 6 | * Can be controlled by systemd 7 | * Write to syslog and the terminal 8 | * Writes output data to a known unique data directory on the system 9 | * Reads its configuration data from a unique config file in /etc 10 | 11 | In time additional functionality may be added, especially network connectivity - however at this stage it touches enough parts of the system to learn the fundamentals of SELinux on an EL9/Fedora 41 system operating the `targeted` policy in `enforcing` mode. 12 | 13 | If you get a **Segmentation Fault**, this is almost certainly because of one of these things: 14 | 15 | * The configuration isn't correct (the default config should work though as it has been tested on a plain EL9 and Fedora 41 system) 16 | * The configuration is correct but the binary cannot perform an operation it needs to 17 | 18 | Error handling is currently limited resulting in **Segmentation Faults** - this may be improved if time allows or someone can contribute to the code. 19 | 20 | # Build and install 21 | 22 | To build and install testprog exactly as it is, run the following commands: 23 | 24 | ``` 25 | [james@selinux-dev testprog]$ cd ~/selinux-hands-on-labs/testprog 26 | [james@selinux-dev testprog]$ sudo dnf -y install gcc make 27 | ... 28 | 29 | ... 30 | [james@selinux-dev testprog]$ make 31 | gcc -o testprog testprog.c 32 | [james@selinux-dev2 testprog]$ sudo make install 33 | mkdir -p /usr/bin 34 | cp testprog /usr/bin/testprog 35 | cp testprog.conf /etc/testprog.conf 36 | mkdir -p /var/testprog 37 | cp testprog.service /etc/systemd/system/testprog.service 38 | ``` 39 | 40 | And optionally: 41 | 42 | ``` 43 | [james@selinux-dev testprog]$ sudo systemctl enable testprog 44 | Created symlink from /etc/systemd/system/multi-user.target.wants/testprog.service to /etc/systemd/system/testprog.service. 45 | ``` 46 | 47 | If that completed without error, congratulations, you can move on to the next lab! If it doesn't please raise an Issue against this project and I'll help you out. 48 | 49 | # Uninstall 50 | 51 | As you can see from the above output, testprog doesn't install much. If you want to tidy it up, simply remove the items you see resulting from the `make install` stage. You can also try: 52 | 53 | ``` 54 | [james@selinux-dev testprog]$ sudo make uninstall 55 | rm -f /usr/bin/testprog 56 | rm -f /etc/testprog.conf 57 | rm -f /etc/systemd/system/testprog.service 58 | rm -rf /var/testprog 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /lab09-putting-it-all-together/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1.10) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | } 29 | 30 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 31 | type testprog_t; 32 | domain_type(testprog_t); 33 | type testprog_exec_t; 34 | files_type(testprog_exec_t); 35 | type testprog_conf_t; 36 | files_config_file(testprog_conf_t); 37 | type testprog_var_run_t; 38 | files_pid_file(testprog_var_run_t); 39 | type testprog_data_t; 40 | files_type(testprog_data_t); 41 | #typeattribute testprog_exec_t file_type, non_security_file_type; 42 | 43 | # Allow the testprog_t type under the unconfined_r role 44 | role unconfined_r types testprog_t; 45 | 46 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 47 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 48 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 49 | type_transition unconfined_t testprog_exec_t : process testprog_t; 50 | # Explicitly allow the type transition we have just created 51 | allow unconfined_t testprog_t : process transition ; 52 | 53 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 54 | allow testprog_t testprog_conf_t:file read_file_perms; 55 | 56 | # We know from sealert that these allow rules are required, largely for writing to the console 57 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 58 | allow testprog_t self:capability sys_tty_config; 59 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 60 | 61 | # Allow testprog access to it's data directory 62 | allow testprog_t testprog_data_t:dir { search write add_name }; 63 | allow testprog_t testprog_data_t:file { create open write append getattr }; 64 | 65 | # Allow syslog access based on Red Hat's code for ntpd 66 | logging_send_syslog_msg(testprog_t) 67 | 68 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 69 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 70 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 71 | 72 | -------------------------------------------------------------------------------- /lab10-systemd/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1.10) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | } 29 | 30 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 31 | type testprog_t; 32 | domain_type(testprog_t); 33 | type testprog_exec_t; 34 | files_type(testprog_exec_t); 35 | type testprog_conf_t; 36 | files_config_file(testprog_conf_t); 37 | type testprog_var_run_t; 38 | files_pid_file(testprog_var_run_t); 39 | type testprog_data_t; 40 | files_type(testprog_data_t); 41 | #typeattribute testprog_exec_t file_type, non_security_file_type; 42 | 43 | # Allow the testprog_t type under the unconfined_r role 44 | role unconfined_r types testprog_t; 45 | 46 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 47 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 48 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 49 | type_transition unconfined_t testprog_exec_t : process testprog_t; 50 | # Explicitly allow the type transition we have just created 51 | allow unconfined_t testprog_t : process transition ; 52 | 53 | init_daemon_domain(testprog_t, testprog_exec_t) 54 | 55 | 56 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 57 | allow testprog_t testprog_conf_t:file read_file_perms; 58 | 59 | # We know from sealert that these allow rules are required, largely for writing to the console 60 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 61 | allow testprog_t self:capability sys_tty_config; 62 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 63 | 64 | # Allow testprog access to it's data directory 65 | allow testprog_t testprog_data_t:dir { search write add_name }; 66 | allow testprog_t testprog_data_t:file { create open write append getattr }; 67 | 68 | # Allow syslog access based on Red Hat's code for ntpd 69 | logging_send_syslog_msg(testprog_t) 70 | 71 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 72 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 73 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 74 | 75 | -------------------------------------------------------------------------------- /lab14-networking/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.2.0) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | } 29 | 30 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 31 | type testprog_t; 32 | domain_type(testprog_t); 33 | type testprog_exec_t; 34 | files_type(testprog_exec_t); 35 | type testprog_conf_t; 36 | files_config_file(testprog_conf_t); 37 | type testprog_var_run_t; 38 | files_pid_file(testprog_var_run_t); 39 | type testprog_data_t; 40 | files_type(testprog_data_t); 41 | #typeattribute testprog_exec_t file_type, non_security_file_type; 42 | 43 | # Allow the testprog_t type under the unconfined_r role 44 | role unconfined_r types testprog_t; 45 | 46 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 47 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 48 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 49 | type_transition unconfined_t testprog_exec_t : process testprog_t; 50 | # Explicitly allow the type transition we have just created 51 | allow unconfined_t testprog_t : process transition ; 52 | 53 | init_daemon_domain(testprog_t, testprog_exec_t) 54 | 55 | 56 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 57 | allow testprog_t testprog_conf_t:file read_file_perms; 58 | 59 | # We know from sealert that these allow rules are required, largely for writing to the console 60 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 61 | allow testprog_t self:capability sys_tty_config; 62 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 63 | 64 | # Allow testprog access to it's data directory 65 | allow testprog_t testprog_data_t:dir { search write add_name }; 66 | allow testprog_t testprog_data_t:file { create open write append getattr }; 67 | 68 | # Allow syslog access based on Red Hat's code for ntpd 69 | logging_send_syslog_msg(testprog_t) 70 | 71 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 72 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 73 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 74 | 75 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1.10) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | } 29 | 30 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 31 | type testprog_t; 32 | domain_type(testprog_t); 33 | type testprog_exec_t; 34 | files_type(testprog_exec_t); 35 | type testprog_conf_t; 36 | files_config_file(testprog_conf_t); 37 | type testprog_var_run_t; 38 | files_pid_file(testprog_var_run_t); 39 | type testprog_data_t; 40 | files_type(testprog_data_t); 41 | #typeattribute testprog_exec_t file_type, non_security_file_type; 42 | 43 | # Allow the testprog_t type under the unconfined_r role 44 | role unconfined_r types testprog_t; 45 | 46 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 47 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 48 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 49 | type_transition unconfined_t testprog_exec_t : process testprog_t; 50 | # Explicitly allow the type transition we have just created 51 | allow unconfined_t testprog_t : process transition ; 52 | 53 | init_daemon_domain(testprog_t, testprog_exec_t) 54 | 55 | 56 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 57 | allow testprog_t testprog_conf_t:file read_file_perms; 58 | 59 | # We know from sealert that these allow rules are required, largely for writing to the console 60 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 61 | allow testprog_t self:capability sys_tty_config; 62 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 63 | 64 | # Allow testprog access to it's data directory 65 | allow testprog_t testprog_data_t:dir { search write add_name }; 66 | allow testprog_t testprog_data_t:file { create open write append getattr }; 67 | 68 | # Allow syslog access based on Red Hat's code for ntpd 69 | logging_send_syslog_msg(testprog_t) 70 | 71 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 72 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 73 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 74 | 75 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.1.10) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | } 29 | 30 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 31 | type testprog_t; 32 | domain_type(testprog_t); 33 | type testprog_exec_t; 34 | files_type(testprog_exec_t); 35 | type testprog_conf_t; 36 | files_config_file(testprog_conf_t); 37 | type testprog_var_run_t; 38 | files_pid_file(testprog_var_run_t); 39 | type testprog_data_t; 40 | files_type(testprog_data_t); 41 | #typeattribute testprog_exec_t file_type, non_security_file_type; 42 | 43 | # Allow the testprog_t type under the unconfined_r role 44 | role unconfined_r types testprog_t; 45 | 46 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 47 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 48 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 49 | type_transition unconfined_t testprog_exec_t : process testprog_t; 50 | # Explicitly allow the type transition we have just created 51 | allow unconfined_t testprog_t : process transition ; 52 | 53 | init_daemon_domain(testprog_t, testprog_exec_t) 54 | 55 | 56 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 57 | allow testprog_t testprog_conf_t:file read_file_perms; 58 | 59 | # We know from sealert that these allow rules are required, largely for writing to the console 60 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 61 | allow testprog_t self:capability sys_tty_config; 62 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 63 | 64 | # Allow testprog access to it's data directory 65 | allow testprog_t testprog_data_t:dir { search write add_name }; 66 | allow testprog_t testprog_data_t:file { create open write append getattr }; 67 | 68 | # Allow syslog access based on Red Hat's code for ntpd 69 | logging_send_syslog_msg(testprog_t) 70 | 71 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 72 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 73 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 74 | 75 | -------------------------------------------------------------------------------- /lab01-up-and-running/README.md: -------------------------------------------------------------------------------- 1 | # LAB 1 - Up and Running 2 | 3 | ## Introduction 4 | 5 | Welcome! Let's get you up and running so that you can start to experience SELinux development! This lab will ensure you have all pre-requisites in place to run the other labs. 6 | 7 | ## Pre-requisites 8 | 9 | These labs are designed to be run on a Red Hat Enterprise Linux or CentOS 7 system. They have been developed and tested on RHEL 7.4 x86_64 but should work well on other EL7 systems. The basic premises and even some of the commands should work on other SELinux enabled systems but I know some commands have changed since RHEL 6 so YMMV. 10 | 11 | It is recommended you run these labs on a system that is not mission critical, as you will be playing with SELinux policies and worst case scenario you will break something you didn't intend to! Not a good idea on a production system! 12 | 13 | All labs were created and tested on a system resulting from a **Minimal Install** of RHEL 7 so depending on your installation you may find you already have packages installed that you are asked to install in these labs. That is fine. Just ignore the **yum install** steps in those cases. 14 | 15 | Your test system must be capable of running in SELinux enforcing mode. The labs at the time of writing are designed to demonstrate the **targeted** SELinux policy that comes as default on most EL7 systems. To check this, run the following command and check that the output is similar to that shown below: 16 | 17 | ``` 18 | [james@selinux-dev ~]$ sestatus 19 | SELinux status: enabled 20 | SELinuxfs mount: /sys/fs/selinux 21 | SELinux root directory: /etc/selinux 22 | Loaded policy name: targeted 23 | Current mode: enforcing 24 | Mode from config file: enforcing 25 | Policy MLS status: enabled 26 | Policy deny_unknown status: allowed 27 | Memory protection checking: actual (secure) 28 | Max kernel policy version: 33 29 | ``` 30 | Specifically, of the output shown above, the following aspects are of interest to us: 31 | 32 | * The **SELinux status** which is **enabled** 33 | * The **Loaded policy name** which is **targeted** 34 | * The **Current mode** which is **enforcing** 35 | 36 | ## Additional packages 37 | 38 | As we go through the labs, we will install additional packages as required so you will want to make sure you have a working yum repository for your system to hand, or another source for the packages. Rather than install these all up front, we will install them as they become necessary so that you know which package performs which function. 39 | 40 | ## Get the code 41 | 42 | Finally, if you're reading this on github.com, clone the repository to ensure you have all the code locally: 43 | 44 | ``` 45 | [james@selinux-dev ~]$ sudo dnf -y install git 46 | ... 47 | 48 | ... 49 | [james@selinux-dev2 ~]$ git clone https://github.com/jamesfreeman959/selinux-hands-on-labs.git 50 | Cloning into 'selinux-hands-on-labs'... 51 | ... 52 | 53 | ... 54 | ``` 55 | ## Now proceed to lab 2! 56 | 57 | Congratulations - you have completed the first lab! That's all that's required here so please move on to lab 2. 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | During my career as a consultant at a Red Hat Premier Partner, it became very clear to me that very few people actually understand SELinux, let alone implement it in their environments. Many companies I have worked with simply turn it off as a matter of course. I have even seen commercial software distributed for Enterprise Linux distributions which lists disabling SELinux as a mandatory installation step. As a result I felt it prudent that I get to grips with this technology both for my own understanding, and to help others. 4 | 5 | With the advent of the GDPR simply turning off SELinux is no longer acceptable as a solution. Whilst this project does not intend to get into any debate on the meaning or implementation of the GDPR, it is based on the premise that in the event of an attack on a system (whether that attack was successful or not), it would have been better to have taken advantage of this additonal security layer than to have simply turned it off because it was deemed too complex or difficult to get working. 6 | 7 | # Scope 8 | 9 | At this stage, this project does not aim to provide a comprehensive coverage of SELinux - it is a huge and powerful security layer and there are many excellent references texts on it. I have always learned better by doing than by learning through theory, so I decided to come up with a set of labs where you can safely learn some of the more common SELinux fundamentals and hopefully demystify it. 10 | 11 | As such the scope of this project is a very common scenario that I have come up against many times in my career: 12 | 13 | * The application to be secured is not SELinux aware and has no specific coding to work with or alongside SELinux 14 | * The hosting machine is running Red Hat Enterprise Linux or a derivative (e.g. Rocky or CentOS Stream) 15 | * (new for 2025) All labs have been tested on Fedora 41 Server also 16 | * The host machine has SELinux enabled and in `enforcing` mode 17 | * The host machine is using the `targeted` policy 18 | 19 | MLS (Multi-Level Security) is beyond the scope of this project at this stage but may be added if there is a requirement for it. 20 | 21 | # Getting started 22 | 23 | I have endeavoured to provide all the information you need to get started and run these labs, and more information can be found in Lab 1 which I recommend you proceed to straight away. I do recommend working through these labs on a VM set aside for this purpose as although the labs are designed to be self contained and not affect the other part of the host system, any tinkering or testing things outside the bounds of the lab (which is highly recommended if it helps you learn more!) could have an undesirable effect on the system. 24 | 25 | # Credits 26 | 27 | There have been many many sources that have helped me put this project together and I have tried to include them in the comments section of each file where relevant. Special mention is deserved to: 28 | 29 | * Red Hat Enterprise Linux documentation: https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/using_selinux/index 30 | * Gentoo Linux for their excellent SELinux tutorials - found at: https://wiki.gentoo.org/wiki/SELinux/Tutorials 31 | * Sven Vermeulen for his SELinux Cookbook 32 | 33 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at sysadmin@hkskies.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /lab13-interfaces-part-3/README.md: -------------------------------------------------------------------------------- 1 | # Interfaces - part 3 2 | 3 | Looking at the new policy file in this directory for `testcat`, you will see that it has a new rule in it: 4 | 5 | ``` 6 | testprog_read_data(testcat_t); 7 | ``` 8 | 9 | This references the macro we created in the `testprog` interface previously. Now we have to build the policy and test it. Note that when you run the `make` command, a number of known locations are searched for interface files to pull into the module. By default on EL9 and Fedora 41, `/usr/share/selinux/devel/include/` is searched, and if you look in here you will find all the interface files for the base policy. However the actual source for the policy definitions and file contexts are missing, hence we had to extract them manually in an earlier lab to analyze them. 10 | 11 | We have not put our new interface file for `testprog` into this central directory, so we have to make sure it's available to the build system. If the interface definition isn't in the aforementioned place, `make` searches the current directory. This is why we have a full copy of the `testprog` policy source code in here, even though we won't be using it directly in this lab - it is identical to the previous lab's version and is simply needed so that the interface definitions can be found to build the policy for testcat. 12 | 13 | Let's build the new policy for testcat and get it loaded: 14 | 15 | ``` 16 | mes@selinux-dev lab13-interfaces-part-3]$ make -f /usr/share/selinux/devel/Makefile testcat.pp 17 | Compiling targeted testcat module 18 | Creating targeted testcat.pp policy package 19 | rm tmp/testcat.mod.fc tmp/testcat.mod 20 | [james@selinux-dev lab13-interfaces-part-3]$ sudo semodule -r testcat 21 | libsemanage.semanage_direct_remove_key: Removing last testcat module (no other testcat module exists at another priority). 22 | [james@selinux-dev lab13-interfaces-part-3]$ sudo semodule -i testcat.pp 23 | ``` 24 | 25 | Let's now see the effects of this: 26 | 27 | ``` 28 | [james@selinux-dev lab13-interfaces-part-3]$ sudo testcat /var/testprog/testprg.txt 29 | Hello World 30 | abcdefghij 31 | Hello World 32 | abcdefghij 33 | Hello World 34 | abcdefghij 35 | Hello World 36 | abcdefghij 37 | Hello World 38 | ``` 39 | 40 | Excellent - we have access to our data. However to show the power of confining a binary to a domain: 41 | 42 | ``` 43 | [james@selinux-dev lab13-interfaces-part-3]$ sudo testcat /etc/shadow 44 | testcat: /etc/shadow: Permission denied 45 | ``` 46 | 47 | This might seem a trivial example from the shell, but suppose `testcat` was actually a system service with network ports open to the internet. Even if not designed to read the `shadow` password file, suppose some attacker found a vulnerability in the code that enabled us to point `testcat` at the shadow file - SELinux would deny their permission even running with root privileges - hopefully this shows how powerful this is, and how important it is to enable SELinux. 48 | 49 | We can also show that we didn't give write access to `testcat`, so when giving out access to our data we maintain control over who can write to it: 50 | 51 | ``` 52 | [james@selinux-dev lab13-interfaces-part-3]$ echo foo | sudo testcat > /var/testprog/testprg.txt 53 | -bash: /var/testprog/testprg.txt: Permission denied 54 | ``` 55 | 56 | With the normal `cat` command this would have overwritten everything in our testprog data file with the word `foo`. 57 | 58 | -------------------------------------------------------------------------------- /lab15-allowing-networking/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.2.26) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | 29 | class tcp_socket { create accept listen }; 30 | attribute port_type; 31 | } 32 | 33 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 34 | type testprog_t; 35 | domain_type(testprog_t); 36 | type testprog_exec_t; 37 | files_type(testprog_exec_t); 38 | type testprog_conf_t; 39 | files_config_file(testprog_conf_t); 40 | type testprog_var_run_t; 41 | files_pid_file(testprog_var_run_t); 42 | type testprog_data_t; 43 | files_type(testprog_data_t); 44 | 45 | type testprog_port_t; 46 | typeattribute testprog_port_t port_type; 47 | 48 | # Allow the testprog_t type under the unconfined_r role 49 | role unconfined_r types testprog_t; 50 | 51 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 52 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 53 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 54 | type_transition unconfined_t testprog_exec_t : process testprog_t; 55 | # Explicitly allow the type transition we have just created 56 | allow unconfined_t testprog_t : process transition ; 57 | 58 | init_daemon_domain(testprog_t, testprog_exec_t) 59 | 60 | 61 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 62 | allow testprog_t testprog_conf_t:file read_file_perms; 63 | 64 | # We know from sealert that these allow rules are required, largely for writing to the console 65 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 66 | allow testprog_t self:capability sys_tty_config; 67 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 68 | 69 | # Allow testprog access to it's data directory 70 | allow testprog_t testprog_data_t:dir { search write add_name }; 71 | allow testprog_t testprog_data_t:file { create open write append getattr }; 72 | 73 | # Allow syslog access based on Red Hat's code for ntpd 74 | logging_send_syslog_msg(testprog_t) 75 | 76 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 77 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 78 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 79 | 80 | # Allow our new network capabilities 81 | allow testprog_t self:tcp_socket create_stream_socket_perms; 82 | corenet_tcp_sendrecv_generic_node(testprog_t) 83 | corenet_tcp_bind_generic_node(testprog_t) 84 | allow testprog_t testprog_port_t:tcp_socket { name_bind }; 85 | -------------------------------------------------------------------------------- /lab12-interfaces-part-2/README.md: -------------------------------------------------------------------------------- 1 | # Interfaces - part 2 2 | 3 | Let's build the policy in this directory and use it to replace the one we built in the last lab - this will enable our `testcat` tool to write to the console and let us focus on the task of reading the testprog data file through an interface: 4 | 5 | ``` 6 | [james@selinux-dev lab12-interfaces-part-2]$ make -f /usr/share/selinux/devel/Makefile testcat.pp 7 | Compiling targeted testcat module 8 | Creating targeted testcat.pp policy package 9 | rm tmp/testcat.mod.fc tmp/testcat.mod 10 | [james@selinux-dev lab12-interfaces-part-2]$ sudo semodule -r testcat 11 | libsemanage.semanage_direct_remove_key: Removing last testcat module (no other testcat module exists at another priority). 12 | [james@selinux-dev lab12-interfaces-part-2]$ sudo semodule -i testcat.pp 13 | [james@selinux-dev lab12-interfaces-part-2]$ sudo testcat /var/testprog/testprg.txt 14 | Segmentation fault (core dumped) 15 | ``` 16 | 17 | This might not look any better - we're still getting a segmentation fault - however if you query the audit log again, you'll see a reduced number of AVC denials for `testcat`: 18 | 19 | ``` 20 | [james@selinux-dev lab12-interfaces-part-2]$ sudo grep testcat /var/log/audit/audit.log | grep AVC 21 | ... 22 | type=AVC msg=audit(1736176260.532:844): avc: denied { map } for pid=16049 comm="testcat" path="/usr/bin/testcat" dev="vda4" ino=25196736 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:testcat_exec_t:s0 tclass=file permissive=0 23 | ``` 24 | 25 | Great - that means the console access must be working! So how do we allow `testcat` to access `testprog` data? For security reasons we don't want `testcat` to use the `testprog` domain, and remember in reality this would be a more complex application with access to lots of other things. Whilst we could specifically allow access to the data type of `testprog`, the best way is to present an interface in the `testprog` policy that other applications (such as `testcat`) can use. 26 | 27 | Look at the new policy for `testprog` in this directory - only the `testprog.if` file is changed from before - however note it might look familiar to the output seen when we ran our `seshowif` commands previously. You will also note that the two allow rules look almost identical to the ones in the main `testprog` policy, except that in place of `testprog_t` there is a **$1** - this tells the macro to substitute the first argument it is passed with this value. The rules are also write centric in the main `testprog` policy but here you will notice they are read centric - this is only to allow others to read our data, not to modify it. In this way we can allow any arbitrary domain to access this type. Let's build and install the new policy module for `testprog`: 28 | 29 | ``` 30 | [james@selinux-dev lab12-interfaces-part-2]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 31 | Compiling targeted testprog module 32 | Creating targeted testprog.pp policy package 33 | rm tmp/testprog.mod tmp/testprog.mod.fc 34 | [james@selinux-dev lab12-interfaces-part-2]$ sudo semodule -r testprog 35 | [sudo] password for james: 36 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority). 37 | [james@selinux-dev lab12-interfaces-part-2]$ sudo semodule -i testprog.pp 38 | ``` 39 | 40 | There won't be much to see at this stage - however we should now generate a new version of the policy module for `testcat` to take advantage of our newly defined interface - proceed to lab13 to do this... 41 | 42 | -------------------------------------------------------------------------------- /lab16-booleans/testprog.te: -------------------------------------------------------------------------------- 1 | # 2 | # Useful references for building this policy: 3 | # 4 | # https://www.redhat.com/archives/fedora-selinux-list/2008-June/msg00076.html 5 | # https://serverfault.com/questions/628344/selinux-cannot-confine-firefox-process-to-mozilla-t-domain 6 | # 7 | # 8 | policy_module(testprog, 0.2.27) 9 | 10 | # Require all the types, attributes and classes we reference in this policy 11 | require { 12 | type unconfined_t; 13 | role unconfined_r; 14 | class file { ioctl getattr setattr create read write unlink open relabelto }; 15 | class dir { search write add_name }; 16 | class process transition; 17 | type fs_t; 18 | class filesystem getattr; 19 | type console_device_t; 20 | type user_devpts_t; 21 | class unix_dgram_socket { create connect sendto }; 22 | class chr_file { append read write open getattr ioctl }; 23 | type devlog_t; 24 | class sock_file write; 25 | type kernel_t; 26 | type var_run_t; 27 | class capability sys_tty_config; 28 | 29 | class tcp_socket { create accept listen }; 30 | attribute port_type; 31 | } 32 | 33 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 34 | type testprog_t; 35 | domain_type(testprog_t); 36 | type testprog_exec_t; 37 | files_type(testprog_exec_t); 38 | type testprog_conf_t; 39 | files_config_file(testprog_conf_t); 40 | type testprog_var_run_t; 41 | files_pid_file(testprog_var_run_t); 42 | type testprog_data_t; 43 | files_type(testprog_data_t); 44 | 45 | type testprog_port_t; 46 | typeattribute testprog_port_t port_type; 47 | 48 | bool allow_testprog_use_network true; 49 | 50 | # Allow the testprog_t type under the unconfined_r role 51 | role unconfined_r types testprog_t; 52 | 53 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 54 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open map } ; 55 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 56 | type_transition unconfined_t testprog_exec_t : process testprog_t; 57 | # Explicitly allow the type transition we have just created 58 | allow unconfined_t testprog_t : process transition ; 59 | 60 | init_daemon_domain(testprog_t, testprog_exec_t) 61 | 62 | 63 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 64 | allow testprog_t testprog_conf_t:file read_file_perms; 65 | 66 | # We know from sealert that these allow rules are required, largely for writing to the console 67 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 68 | allow testprog_t self:capability sys_tty_config; 69 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 70 | 71 | # Allow testprog access to it's data directory 72 | allow testprog_t testprog_data_t:dir { search write add_name }; 73 | allow testprog_t testprog_data_t:file { create open write append getattr }; 74 | 75 | # Allow syslog access based on Red Hat's code for ntpd 76 | logging_send_syslog_msg(testprog_t) 77 | 78 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 79 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 80 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 81 | 82 | # Allow our new network capabilities 83 | tunable_policy(`allow_testprog_use_network',` 84 | allow testprog_t self:tcp_socket create_stream_socket_perms; 85 | corenet_tcp_sendrecv_generic_node(testprog_t) 86 | corenet_tcp_bind_generic_node(testprog_t) 87 | allow testprog_t testprog_port_t:tcp_socket { name_bind }; 88 | ') 89 | -------------------------------------------------------------------------------- /lab11-interfaces/README.md: -------------------------------------------------------------------------------- 1 | # Interfaces 2 | 3 | So far we have largely ignored the `testprog.if` file that was created as part of the template for our policy. We know that the `.fc` file sets the default file contexts, and the `.te` file is the actual policy. What about the `.if` file? This file denotes the interfaces for the policy, and in short creates macros that other modules will use to access our resources. So far we have treated `testprog` as totally self contained, but what if we wanted another process to access it's data from a different domain? 4 | 5 | To do this, we'll take a copy of the standard `cat` command and set up a special policy called `testcat` that will confine it to its own domain. In real life this would be another daemon or service, but making use of this existing command keeps our lives simple for these labs. 6 | 7 | To get started, let's take a copy of the `cat` command, and then set up a special policy module for it that causes it to switch to its own confined context: 8 | 9 | ``` 10 | [james@selinux-dev2 lab11-interfaces]$ sudo cp /usr/bin/cat /usr/bin/testcat 11 | [james@selinux-dev2 lab11-interfaces]$ make -f /usr/share/selinux/devel/Makefile testcat.pp 12 | Compiling targeted testcat module 13 | Creating targeted testcat.pp policy package 14 | rm tmp/testcat.mod.fc tmp/testcat.mod 15 | 16 | [james@selinux-dev2 lab11-interfaces]$ sudo semodule -i testcat.pp 17 | 18 | [james@selinux-dev2 lab11-interfaces]$ sudo restorecon -v /usr/bin/testcat 19 | restorecon reset /usr/bin/testcat context unconfined_u:object_r:bin_t:s0->unconfined_u:object_r:testcat_exec_t:s0 20 | ``` 21 | 22 | Now let's see what happens when it runs: 23 | 24 | ``` 25 | [james@selinux-dev2 lab11-interfaces]$ sudo testcat /var/testprog/testprg.txt 26 | Segmentation fault (core dumped) 27 | [james@selinux-dev2 lab11-interfaces]$ sudo grep testcat /var/log/audit/audit.log | grep AVC 28 | type=AVC msg=audit(1736175971.335:803): avc: denied { read write } for pid=15978 comm="testcat" path="/dev/pts/0" dev="devpts" ino=3 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_devpts_t:s0 tclass=chr_file permissive=0 29 | type=AVC msg=audit(1736175971.335:803): avc: denied { read append } for pid=15978 comm="testcat" path="/dev/pts/0" dev="devpts" ino=3 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_devpts_t:s0 tclass=chr_file permissive=0 30 | type=AVC msg=audit(1736175971.335:803): avc: denied { read append } for pid=15978 comm="testcat" path="/dev/pts/0" dev="devpts" ino=3 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_devpts_t:s0 tclass=chr_file permissive=0 31 | type=AVC msg=audit(1736175971.335:803): avc: denied { read append } for pid=15978 comm="testcat" path="/dev/pts/0" dev="devpts" ino=3 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_devpts_t:s0 tclass=chr_file permissive=0 32 | type=AVC msg=audit(1736175971.335:803): avc: denied { map } for pid=15978 comm="testcat" path="/usr/bin/testcat" dev="vda4" ino=25196736 scontext=unconfined_u:unconfined_r:testcat_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:testcat_exec_t:s0 tclass=file permissive=0 33 | ``` 34 | 35 | We can see that the program exited immediately with a segmentation fault, and returned no actual data even though we generated test data in a previous lab. A quick look at the audit log shows us that `testcat` was successfully confined and that it could neither `write` to the terminal, nor could it perform file operations on our `testprog` data directory. Let's first of all fix the terminal errors so that we can focus on interface generation. We know how to do this from our previous labs so move on to the next lab to put this in place... 36 | 37 | -------------------------------------------------------------------------------- /lab05-file-contexts/README.md: -------------------------------------------------------------------------------- 1 | # Filesystem contexts 2 | 3 | Our application never switched contexts on execution because we didn't actually change the context on the file itself. Now there are two ways to do this. 4 | 5 | The first is to use the **chcon** command - you would set the context of our binary like this: 6 | 7 | ``` 8 | [james@selinux-dev selinux-hands-on-labs]$ sudo chcon -t testprog_exec_t /usr/bin/testprog 9 | [james@selinux-dev selinux-hands-on-labs]$ ls -lZ /usr/bin/testprog 10 | -rwxr-xr-x. root root unconfined_u:object_r:testprog_exec_t:s0 /usr/bin/testprog 11 | ``` 12 | 13 | Initially this looks good - however there is something to be aware of, and that is filesystem relabelling, or context restoring. A full relabel of a filesystem should be a rare occurrence, but you cannot prevent another system administrator unknowingly coming along and restoring the context of all the files in a directory to their defaults. They might do this in a well-meaning effort to fix another problem, or as part of a policy enforcement - either way we have seen in previous labs that the default context of the files in `/usr/bin` is not `testprog_exec_t`: 14 | 15 | ``` 16 | [james@selinux-dev selinux-hands-on-labs]$ ls -ldZ /usr/bin 17 | dr-xr-xr-x. root root system_u:object_r:bin_t:s0 /usr/bin 18 | [james@selinux-dev selinux-hands-on-labs]$ ls -lZ /usr/bin/testprog 19 | -rwxr-xr-x. root root unconfined_u:object_r:testprog_exec_t:s0 /usr/bin/testprog 20 | [james@selinux-dev selinux-hands-on-labs]$ sudo restorecon -v /usr/bin/* 21 | restorecon reset /usr/bin/testprog context unconfined_u:object_r:testprog_exec_t:s0->unconfined_u:object_r:bin_t:s0 22 | ``` 23 | 24 | Obviously if this were to happen on a live server it would break your application the next time it was launched - clearly not good. As a result, we must declare the file context as part of the policy we have created. In addition to the `testprog.te` file which remains unchanged in this lab, we now introduce `testprog.fc`: 25 | 26 | ``` 27 | [james@selinux-dev lab05-file-contexts]$ cat testprog.fc 28 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 29 | ``` 30 | 31 | Now we rebuild the policy as in the previous lab and install it: 32 | 33 | ``` 34 | [james@selinux-dev lab05-file-contexts]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 35 | Compiling targeted testprog module 36 | /usr/bin/checkmodule: loading policy configuration from tmp/testprog.tmp 37 | /usr/bin/checkmodule: policy configuration loaded 38 | /usr/bin/checkmodule: writing binary representation (version 17) to tmp/testprog.mod 39 | Creating targeted testprog.pp policy package 40 | rm tmp/testprog.mod tmp/testprog.mod.fc 41 | [james@selinux-dev lab05-file-contexts]$ sudo semodule -r testprog 42 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority). 43 | [james@selinux-dev lab05-file-contexts]$ sudo semodule -i testprog.pp 44 | ``` 45 | 46 | Normally you wouldn't need to remove the old module first, but we are being lazy here and haven't bumped up the version number in the head of the policy file, so we are taking this opportunity to demonstrate the removal and installation of a module. 47 | 48 | We should now have a default context installed for our binary file - let's test it: 49 | 50 | ``` 51 | [james@selinux-dev lab05-file-contexts]$ sudo restorecon -v /usr/bin/* 52 | restorecon reset /usr/bin/testprog context unconfined_u:object_r:bin_t:s0->unconfined_u:object_r:testprog_exec_t:s0 53 | ``` 54 | 55 | Excellent! What happens now if we run the binary from the shell? 56 | 57 | ``` 58 | [james@selinux-dev lab05-file-contexts]$ sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid & 59 | [1] 12441 60 | [1]+ Segmentation fault sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 61 | ``` 62 | 63 | Excellent!! You may be wondering why it is excellent that we have broken our application and received a `Segmentation Fault`, but this actually is an indication that we have switched contexts and are no longer operating in the **unconfined** context but instead one that is totally restricted. 64 | 65 | The next lab will focus on confirming this so we can do something about it. 66 | 67 | -------------------------------------------------------------------------------- /lab04-targeting-testprog/README.md: -------------------------------------------------------------------------------- 1 | # Targeting testprog 2 | 3 | By this stage we've established that we can run testprog on our lab system, either from the shell or through systemd. This is all well and good but remember the binary is running in the **unconfined** space which is largely unrestricted - we don't actually want that - we want to know that our application is secured by SELinux and that even if an attacker compromised it, SELinux would contain the attack from further compromise of the system. 4 | 5 | Now you might think, "yes but we have the source code so we can fix it or make more secure". Remember that in many cases software vendors only provide pre-compiled binaries and as such you won't have this privilege. Hence we want to target `testprog` and confine it, especially in case someone were to (hypothetically) compromise the binary itself. 6 | 7 | To do this we need to write a simple SELinux policy. I will leave reading the documentation about how this policy is created as an exercise for the reader (hint: https://selinuxproject.org/page/TypeRules) - however the files you need are provided in this lab subdirectory. To build and test the policy do the following: 8 | 9 | ``` 10 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install selinux-policy-devel 11 | ... 12 | [james@selinux-dev selinux-hands-on-labs]$ cd lab04-targeting-testprog/ 13 | [james@selinux-dev lab04-targeting-testprog]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 14 | Compiling targeted testprog module 15 | /usr/bin/checkmodule: loading policy configuration from tmp/testprog.tmp 16 | /usr/bin/checkmodule: policy configuration loaded 17 | /usr/bin/checkmodule: writing binary representation (version 17) to tmp/testprog.mod 18 | Creating targeted testprog.pp policy package 19 | rm tmp/testprog.mod tmp/testprog.mod.fc 20 | [james@selinux-dev lab04-targeting-testprog]$ sudo semodule -i testprog.pp 21 | ``` 22 | 23 | We can check that the policy module is now loaded: 24 | 25 | ``` 26 | [james@selinux-dev lab04-targeting-testprog]$ sudo semodule -l | grep testprog 27 | testprog 28 | ``` 29 | 30 | Good - now what does the module do? Let's take a look at it: 31 | 32 | ``` 33 | [james@selinux-dev lab04-targeting-testprog]$ grep -v -e ^# testprog.te 34 | policy_module(testprog, 0.1) 35 | 36 | require { 37 | type unconfined_t; 38 | role unconfined_r; 39 | class file { ioctl getattr setattr create read write unlink open relabelto }; 40 | class process transition; 41 | } 42 | 43 | type testprog_t; 44 | domain_type(testprog_t); 45 | type testprog_exec_t; 46 | files_type(testprog_exec_t); 47 | 48 | role unconfined_r types testprog_t; 49 | 50 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; 51 | type_transition unconfined_t testprog_exec_t : process testprog_t; 52 | allow unconfined_t testprog_t : process transition ; 53 | ``` 54 | 55 | First off we're giving our module a name and version number so that it is easily identified. Then we declare all the objects we require that are already found elsewhere in the existing policy. We then define two new types just for our application - `testprog_t` which is the domain under which the process will run, and `testprog_exec_t` which is the type that our binary file will possess. Finally we have 4 rules: 56 | 57 | 1. Allow the `unconfined_r` role access to the `testprog_t` domain (remember RBAC?). For now we'll focus on launching from the shell which we know from Lab 3 we'll be launching under. 58 | 2. Allow the `testprog` binary (identified by it's SELinux type of `testprog_exec_t`) to be an entrypoint (amongst other things) to the `testprog_t` domain we have defined for the process. 59 | 3. Define a type transition to tell SELinux to transition out process to the `testprog_t` domain whenever it is launched from the `unconfined_t` domain and the binary has the `testprog_exec_t` type. 60 | 4. Allow the process transition we specified previously to actually happen (this is not implicit - SELinux expects you to be very explicit in your policy). 61 | 62 | Now our policy is loaded so let's see what happens when we run from the shell: 63 | 64 | ``` 65 | [james@selinux-dev lab04-targeting-testprog]$ sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid & 66 | [1] 12090 67 | Using configuration file: /etc/testprog.conf 68 | Wrote PID to /var/run/testprog.pid 69 | Writing output to: /var/testprog/testprg.txt 70 | Iteration count: -1 71 | [james@selinux-dev lab04-targeting-testprog]$ ps -fZwwp $(cat /var/run/testprog.pid) 72 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 12094 12090 0 17:31 pts/0 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 73 | ``` 74 | 75 | **Something is wrong** - it's still working in the unconfined space. Proceed to Lab 5 to find out why and what to do... 76 | 77 | 78 | -------------------------------------------------------------------------------- /lab16-booleans/README.md: -------------------------------------------------------------------------------- 1 | # Booleans 2 | 3 | Following on from the previous lab, we have decided that we want the network functionality to be an option within the SELinux policy that we can turn on or off as required. How do we modify the policy to do that? 4 | 5 | First of all, at the top of the policy, we need to declare our new boolean in the same way that we have declared all the other types before. The following line will go somewhere with the other type declarations just below the `require` section: 6 | 7 | ``` 8 | bool allow_testprog_use_network true; 9 | ``` 10 | 11 | This declares a new boolean, called `allow_testprog_use_network`, which is `true` by default. Then we simply find the block of code that we wish to be optional - in this case it will be the 4 lines at the end that we added previously, and ensure it is in a block marked as a `tunable_policy`: 12 | 13 | ``` 14 | # Allow our new network capabilities 15 | tunable_policy(`allow_testprog_use_network',` 16 | allow testprog_t self:tcp_socket create_stream_socket_perms; 17 | corenet_tcp_sendrecv_generic_node(testprog_t) 18 | corenet_tcp_bind_generic_node(testprog_t) 19 | allow testprog_t testprog_port_t:tcp_socket { name_bind }; 20 | ') 21 | ``` 22 | 23 | Note the 4 lines in the middle are exactly the same as before, we have just decided to allow them to be all on, or all off as required. Now we can test out our new functionality: 24 | 25 | ``` 26 | [james@selinux-dev lab16-booleans]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 27 | Compiling targeted testprog module 28 | Creating targeted testprog.pp policy package 29 | rm tmp/testprog.mod tmp/testprog.mod.fc 30 | [james@selinux-dev lab16-booleans]$ sudo semodule -i testprog.pp 31 | ``` 32 | 33 | Booleans can be managed through `semanage`, or `setsebool` and `getsebool`. Note that if you are using `setsebool`, changes do not persist across reboots unless you specify the `-P` flag in the command. Change made is `semanage` persist by default. 34 | 35 | ``` 36 | [james@selinux-dev lab16-booleans]$ getsebool -a | grep test 37 | allow_testprog_use_network --> on 38 | [james@selinux-dev lab16-booleans]$ sudo sed -i 's/NETWORKPORT=.*/NETWORKPORT=8234/g' /etc/testprog-net.conf 39 | [james@selinux-dev lab16-booleans]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 40 | [1] 60733 41 | [james@selinux-dev lab16-booleans]$ Using configuration file: /etc/testprog-net.conf 42 | Wrote PID to /var/run/testprog-net.pid 43 | Writing output to: /var/testprog/testprg-net.txt 44 | Iteration count: -1 45 | Network functionality enabled on port 8234 46 | Socket created 47 | bind done 48 | Waiting for incoming connections... 49 | ``` 50 | 51 | Se we can see above that our new boolean was created, and it is set to `on` as per our default setting. Our network enabled version of `testprog` runs as normal. Now what happens if we turn off this boolean: 52 | 53 | ``` 54 | [james@selinux-dev lab16-booleans]$ fg 55 | sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 56 | ^C 57 | [james@selinux-dev lab16-booleans]$ sudo setsebool allow_testprog_use_network=off 58 | [james@selinux-dev lab16-booleans]$ getsebool -a | grep test 59 | allow_testprog_use_network --> off 60 | [james@selinux-dev lab16-booleans]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 61 | [2] 60746 62 | [james@selinux-dev lab16-booleans]$ Using configuration file: /etc/testprog-net.conf 63 | Wrote PID to /var/run/testprog-net.pid 64 | Writing output to: /var/testprog/testprg-net.txt 65 | Iteration count: -1 66 | Network functionality enabled on port 8234 67 | Could not create socket 68 | Socket created 69 | bind failed. Error 70 | ``` 71 | 72 | Notice how as soon as we disable the boolean we stop being able to run our program, as it now lacks the permissions to create sockets. You can verify this through the audit log if you wish. However if we disable the networking function in the configuration file: 73 | 74 | ``` 75 | [james@selinux-dev lab16-booleans]$ sudo sed -i 's/NETWORKPORT=.*/NETWORKPORT=0/g' /etc/testprog-net.conf 76 | [james@selinux-dev lab16-booleans]$ getsebool -a | grep test 77 | allow_testprog_use_network --> off 78 | [james@selinux-dev lab16-booleans]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 79 | Using configuration file: /etc/testprog-net.conf 80 | Wrote PID to /var/run/testprog-net.pid 81 | Writing output to: /var/testprog/testprg-net.txt 82 | Iteration count: -1 83 | Network functionality disabled 84 | 85 | [james@selinux-dev lab16-booleans]$ tail -f /var/testprog/testprg-net.txt 86 | Hello World 87 | abcdefghij 88 | Hello World 89 | abcdefghij 90 | ``` 91 | 92 | The rest of our application works just fine, as the rest of our loadable policy remains untouched. 93 | 94 | # The End! 95 | 96 | I hope you have found running through these labs useful - if you have any feedback please do let me know! 97 | -------------------------------------------------------------------------------- /testprog/testprog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A test program for playing with SELinux contexts and permissions 3 | * 4 | * Based on a simple premise of a native C binary, that reads one config file and writes an output file 5 | * 6 | * Credits for code: 7 | * https://gsamaras.wordpress.com/code/eat-spaces-and-newline-c/ 8 | * https://www.pacificsimplicity.ca/blog/simple-read-configuration-file-struct-example 9 | * http://www.cprogramming.com/tutorial/c/lesson14.html 10 | * https://cboard.cprogramming.com/c-programming/155124-using-c-program-append-text-txt-file.html 11 | * https://stackoverflow.com/questions/24249369/how-to-write-pid-to-file-on-unix 12 | * https://www.gnu.org/software/libc/manual/html_node/Syslog-Example.html 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | char s[11] = {"\nabcdefghij"}; 21 | 22 | #define FILENAME "testprog.conf" 23 | #define MAXBUF 1024 24 | #define DELIM "=" 25 | #define PIDFILE "/var/run/testprog.pid" 26 | 27 | struct config 28 | { 29 | char outputfile[MAXBUF]; 30 | char loopcount[MAXBUF]; 31 | }; 32 | 33 | 34 | struct config get_config(char *filename) 35 | { 36 | struct config configstruct; 37 | FILE *file = fopen (filename, "r"); 38 | 39 | if (file != NULL) 40 | { 41 | char line[MAXBUF]; 42 | int i = 0; 43 | 44 | while(fgets(line, sizeof(line), file) != NULL) 45 | { 46 | char *cfline; 47 | cfline = strstr((char *)line,DELIM); 48 | cfline = cfline + strlen(DELIM); 49 | 50 | if (i == 0){ 51 | memcpy(configstruct.outputfile,cfline,strlen(cfline)); 52 | //printf("%s",configstruct.outputfile); 53 | } else if (i == 1){ 54 | memcpy(configstruct.loopcount,cfline,strlen(cfline)); 55 | //printf("%s",configstruct.loopcount); 56 | } 57 | 58 | i++; 59 | } // End while 60 | } // End if file 61 | 62 | 63 | fclose(file); 64 | 65 | return configstruct; 66 | 67 | } 68 | 69 | int main(int argc, char *argv[] ) 70 | { 71 | struct config configstruct; 72 | 73 | setlogmask (LOG_UPTO (LOG_NOTICE)); 74 | openlog ("testprog", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 75 | 76 | if (argc < 2) { 77 | // No arguments were passed 78 | configstruct = get_config(FILENAME); 79 | printf("Using configuration file: %s\n", FILENAME); 80 | syslog (LOG_NOTICE, "Using configuration file: %s\n", FILENAME); 81 | // We assume argv[2] is a pidfile to open 82 | FILE *file = fopen( PIDFILE, "wb" ); 83 | fprintf(file, "%d\n", getpid()); 84 | fclose(file); 85 | printf("Wrote PID to %s\n", PIDFILE ); 86 | syslog (LOG_NOTICE, "Wrote PID to %s\n", PIDFILE ); 87 | } else { 88 | // We assume argv[1] is a config file to open 89 | FILE *file = fopen( argv[1], "r" ); 90 | 91 | /* fopen returns 0, the NULL pointer, on failure */ 92 | if ( file == 0 ) 93 | { 94 | printf( "Could not open file\n" ); 95 | syslog (LOG_CRIT, "Could not open file\n" ); 96 | } 97 | else 98 | { 99 | configstruct = get_config(argv[1]); 100 | printf("Using configuration file: %s\n", argv[1]); 101 | syslog (LOG_NOTICE, "Using configuration file: %s\n", argv[1]); 102 | } 103 | fclose(file); 104 | 105 | // We assume argv[2] is a pidfile to open 106 | FILE *pidfile = fopen( argv[2], "wb" ); 107 | fprintf(pidfile, "%d\n", getpid()); 108 | fclose(pidfile); 109 | printf("Wrote PID to %s\n", argv[2] ); 110 | syslog (LOG_NOTICE, "Wrote PID to %s\n", argv[2] ); 111 | } 112 | 113 | /* Code to trim our string and prevent bad chars in filename */ 114 | char* outputfilePtr = configstruct.outputfile; 115 | size_t len; 116 | outputfilePtr += strspn(outputfilePtr, "\t\n\v\f\r "); 117 | len = strcspn(configstruct.outputfile, "\r\n"); 118 | configstruct.outputfile[len] = '\0'; 119 | 120 | printf("Writing output to: %s\n",configstruct.outputfile); 121 | syslog (LOG_NOTICE, "Writing output to: %s\n",configstruct.outputfile); 122 | 123 | 124 | /* Cast port as int */ 125 | int x; 126 | x = atoi(configstruct.loopcount); 127 | printf("Iteration count: %d\n",x); 128 | syslog (LOG_NOTICE, "Iteration count: %d\n",x); 129 | 130 | int i = 0; 131 | while(i < x || x < 0) 132 | { 133 | FILE *file = fopen(configstruct.outputfile, "a"); 134 | fputs("\nHello World",file); 135 | fputs(s,file); 136 | 137 | fclose(file); 138 | sleep(1); 139 | 140 | i++; 141 | 142 | } 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /lab08-about-macros/README.md: -------------------------------------------------------------------------------- 1 | # About macros 2 | 3 | Let's proceed with understanding how the `ntp` service, which runs in a confined domain, writes its own PID file into `/var/run` - we can hopefully make use of this to create our own policy. 4 | 5 | Here's a disection of the ntp policy file, showing the parts that are relevant to the PID file. Once you get used to the nomenclature used in SELinux and the policies, this will get easier to find, and I recommend you stick to the same standards so that others can understand your policy files when you hand them over. 6 | 7 | First off, is there a file context specifically for the PID file so that, per our previous desire, we can isolate this file and allow our confined application to write this file, but not everything else in that directory (remember our .fc file from before?)? Remember to change into the directory where we found the `ntp` policy files in the previous lab! 8 | 9 | ``` 10 | [james@selinux-dev selinux-hands-on-labs ]$ cd refpolicy/selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c/policy/modules/contrib/ 11 | [james@selinux-dev contrib]$ grep pid ntp.fc 12 | /var/run/ntpd\.pid -- gen_context(system_u:object_r:ntpd_var_run_t,s0) 13 | ``` 14 | 15 | Excellent! Now we know what to look for in the policy file: 16 | 17 | ``` 18 | [james@selinux-dev serefpolicy-contrib-3.13.1]$ cat ntp.te 19 | policy_module(ntp, 1.11.0) 20 | 21 | ######################################## 22 | # 23 | # Declarations 24 | # 25 | ... 26 | 27 | type ntpd_var_run_t; 28 | files_pid_file(ntpd_var_run_t) 29 | ... 30 | 31 | ######################################## 32 | # 33 | # Local policy 34 | # 35 | ... 36 | manage_files_pattern(ntpd_t, ntpd_var_run_t, ntpd_var_run_t) 37 | files_pid_filetrans(ntpd_t, ntpd_var_run_t, file) 38 | ... 39 | ``` 40 | 41 | We found all the pieces that fit together, simply by matching up names. The type declaration we have seen before - this is simply declaring a new type, but what of the other lines? These look a bit different to the kind of allow rules we created when we built our `testprog` domain transition policy. 42 | 43 | Where you see lines in a policy like `macro_name(parameters...)`, these are **macros**, and are used to make the policy files more readable, and save developers from writing the same chunks of code over and over again. Essentially this follows the usual best practises concerning code reuse. If you're curious though - and you might prefer to be than to simply take a macro someone else created without understanding what they do, then how do you understand the macros? Thankfully we have the source code for these too - we just have to find the macros. 44 | 45 | You can do this by hand, but to make your life easier I thoroughly recommend the SELinux functions developed by Sven Vermeulen. Sadly these don't seem to be available to download any more, but I have saved a slightly modified copy that I have tested on EL9 and Fedora 41 here: https://gist.githubusercontent.com/jamesfreeman959/40d41810beccc4ded23dc049d6ed570d/raw/5da8c9b2aae21e38777d0d2c0e4ac615cc2a2455/selinux-funcs-el9.txt 46 | Thus you can download and use these as follows: 47 | 48 | ``` 49 | [james@selinux-dev selinux-hands-on-labs]$ curl -O https://gist.githubusercontent.com/jamesfreeman959/40d41810beccc4ded23dc049d6ed570d/raw/5da8c9b2aae21e38777d0d2c0e4ac615cc2a2455/selinux-funcs-el9.txt 50 | % Total % Received % Xferd Average Speed Time Time Time Current 51 | Dload Upload Total Spent Left Speed 52 | 100 2228 100 2228 0 0 14191 0 --:--:-- --:--:-- --:--:-- 14191 53 | 54 | [james@selinux-dev selinux-hands-on-labs]$ source selinux-funcs-el9.txt 55 | [james@selinux-dev selinux-hands-on-labs]$ export POLICY_LOCATION=$HOME/selinux-hands-on-labs/refpolicy/selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c/ 56 | [james@selinux-dev selinux-hands-on-labs]$ seshowif files_pid_filetrans 57 | interface(`files_pid_filetrans',` 58 | gen_require(` 59 | type var_t, var_run_t; 60 | ') 61 | 62 | allow $1 var_t:dir search_dir_perms; 63 | filetrans_pattern($1, var_run_t, $2, $3, $4) 64 | ') 65 | ``` 66 | 67 | This macro saves developers from writing 4 lines of code every time they want to allow a confined application to write a PID file into `/var/run` by: 68 | 69 | 1. Declaring the types required 70 | 2. Allowing the first passed argument to access the `var_t` type (i.e. `/var`) to search directories (i.e. to find `/var/run`) 71 | 3. Allowing the first passed argument when the macro is called (`ntp_t` in our example) to access the `var_run_t` type (i.e. `/var/run`) with permission to read link files (on EL9/Fedora 41, `/var/run` is a symbolic link to `/run`). 72 | 4. Another macro is then called allow access to write files into the `var_run_t` type (so that we can create our PID file in /var/run) 73 | 74 | If we so chose we could expand this second macro, and so on until we have a complete picture of all the policies that make up a particular macro: 75 | 76 | ``` 77 | [james@selinux-dev selinux-hands-on-labs]$ seshowdef filetrans_pattern 78 | define(`filetrans_pattern',` 79 | allow $1 $2:dir rw_dir_perms; 80 | type_transition $1 $2:$4 $3 $5; 81 | ') 82 | ``` 83 | 84 | Briefly, we can see this is allowing `ntp_t` (`$1`) access to `var_run_t` with permissions to read and write the directory. It also specifies a type transition to ensure we can write into this context. Note that even the permissions themselves are macros to save on coding: 85 | 86 | ``` 87 | [james@selinux-dev selinux-hands-on-labs]$ seshowdef rw_dir_perms 88 | define(`rw_dir_perms', `{ open read getattr lock search ioctl add_name remove_name write }') 89 | ``` 90 | 91 | Once you are happy with what macros are, and how they work, you can start to use them to build your own policy. Note that you don't have to use macros - however you can see how much that one line we have explored here has expanded to as we have explored the macros, so it makes sense to use them if you feel they will achieve your aims. 92 | 93 | We'll put all this together in the next lab and actually get `testprog` running from the shell in a confined domain! 94 | 95 | -------------------------------------------------------------------------------- /lab07-finding-examples/README.md: -------------------------------------------------------------------------------- 1 | # Finding examples 2 | 3 | We know now that we want to create our own bespoke policy for our application, and that the output from `sealert`, whilst helpful, isn't going to achieve for us the level of security we really want. However SELinux is complex and there are many many permissions to have to deal with, so how can we make our lives easier? 4 | 5 | Well first of all, we know that good SELinux policies already exist, and are backed by Red Hat (or whatever distribution you are using). Whilst no-one is infallible, not even big corporates, it does make sense to start by looking at how other confined applications are secured with SELinux rather than reinventing the wheel, or spending hours iterating through `audit.log`, fixing denials to see what the next one is (believe me you'll end up doing this anyway!). 6 | 7 | Many applications have common features and so it's a matter of choosing one that isn't too complex to analyze, but that helps us get our application working in a confined context. We know that (and even if we didn't have the source code we could see the behaviour when we ran it as unconfined) `testprog` needs: 8 | 9 | * To display text on the terminal 10 | * To read its config file from `/etc` 11 | * To write its PID file to `/var/run` 12 | * To write its data to `/var/testprog` 13 | * To write to `syslog` 14 | 15 | There are many suitable examples, but I have picked for these labs the **ntp** package - this does all these things, and more, but not so much as to be overly complex for us to understand and analyze. 16 | 17 | # A note on disassembly 18 | 19 | The beauty of open-source software is that, even in the case of the loaded SELinux policies, you can find the source code and see how they were written. You can actually extract and disassemble the loaded polcies without the source code, but doing so will give you details of the policy without any comments or the macros that make writing them easier (more on macros later). If you wish to to disassemble the existing policy, you can do so as follows: 20 | 21 | ``` 22 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install policycoreutils-devel checkpolicy 23 | ... 24 | [james@selinux-dev selinux-hands-on-labs]$ sudo semodule -E ntp 25 | Module 'ntp' does not exist at the default priority '400'. Extracting at highest existing priority '100'. 26 | [james@selinux-dev selinux-hands-on-labs]$ sedismod ntp.pp 27 | Reading policy... 28 | libsepol.policydb_index_others: security: 0 users, 3 roles, 96 types, 2 bools 29 | libsepol.policydb_index_others: security: 1 sens, 1024 cats 30 | libsepol.policydb_index_others: security: 103 classes, 0 rules, 0 cond rules 31 | libsepol.policydb_index_others: security: 0 users, 3 roles, 96 types, 2 bools 32 | libsepol.policydb_index_others: security: 1 sens, 1024 cats 33 | libsepol.policydb_index_others: security: 103 classes, 0 rules, 0 cond rules 34 | Binary policy module file loaded. 35 | Module name: ntp 36 | Module version: 1.11.0 37 | Policy version: 21 38 | 39 | 40 | Select a command: 41 | 1) display unconditional AVTAB 42 | 2) display conditional AVTAB 43 | 3) display users 44 | 4) display bools 45 | 5) display roles 46 | 6) display types, attributes, and aliases 47 | 7) display role transitions 48 | 8) display role allows 49 | 9) Display policycon 50 | 0) Display initial SIDs 51 | 52 | a) Display avrule requirements 53 | b) Display avrule declarations 54 | c) Display policy capabilities 55 | l) Link in a module 56 | u) Display the unknown handling setting 57 | F) Display filename_trans rules 58 | v) display the version of policy and/or module 59 | 60 | f) set output file 61 | m) display menu 62 | q) quit 63 | 64 | Command ('m' for menu): 65 | ``` 66 | 67 | Through the menu system you can explore the policy, but I personally found it non-intuitive. Rather than do that, let's take advantage of open-source and get hold of the source code. 68 | 69 | # Finding the source code 70 | 71 | On EL9/Fedora 41, you need to file and download a copy of the **source** rpm for `selinux-policy`. At the time of writing this was `selinux-policy-38.1.45-3.el9_5.src.rpm ` for Rocky Linux 9 and `selinux-policy-41.27-1.fc41.src.rpm` for Fedora 41, or which can be downloaded as follows: 72 | 73 | ``` 74 | [james@selinux-dev selinux-hands-on-labs]$ curl -O https://download.rockylinux.org/pub/rocky/9/AppStream/source/tree/Packages/s/selinux-policy-38.1.45-3.el9_5.src.rpm # Rocky Linux 9 75 | [james@selinux-dev selinux-hands-on-labs]$ curl -O https://kojipkgs.fedoraproject.org//packages/selinux-policy/41.27/1.fc41/src/selinux-policy-41.27-1.fc41.src.rpm # Fedora 41 76 | ``` 77 | 78 | Once you have a copy of the rpm extract the contents or install it as you prefer, also remembering to extract the tarballs of source code within the rpm. The below examples are for Rocky Linux 9 but the process will work the same on Fedora - just substitute the version number for your package as required: 79 | 80 | ``` 81 | [james@selinux-dev selinux-hands-on-labs]$ mkdir refpolicy 82 | [james@selinux-dev selinux-hands-on-labs]$ cd refpolicy/ 83 | [james@selinux-dev refpolicy]$ rpm2cpio ../selinux-policy-38.1.45-3.el9_5.src.rpm | cpio -idm 84 | 2456 blocks 85 | [james@selinux-dev refpolicy]$ sudo dnf -y install tar 86 | ..output truncated.. 87 | [james@selinux-dev refpolicy]$ for tarball in *.tgz *.tar.gz; do tar -xzf $tarball; done 88 | [james@selinux-dev refpolicy]$ cd selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c 89 | [james@selinux-dev selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c]$ find . -type f -name 'ntp.*' 90 | ./policy/modules/contrib/ntp.fc 91 | ./policy/modules/contrib/ntp.if 92 | ./policy/modules/contrib/ntp.te 93 | ``` 94 | 95 | Fantastic! There's our raw policy files for ntp just as we wanted. In the next lab we'll start to understand this policy. 96 | 97 | # Finding documentation 98 | 99 | As well as accessing the source, all distribution provided modules usually come with good documentation. This won't show you the source code for the policy, but it will tell you how the policy is intended to be used, what booleans and other aspects are available, and so on. To ensure the documentation is on your system: 100 | 101 | ``` 102 | [james@selinux-dev ~]# sudo dnf -y install selinux-policy-doc lynx 103 | ``` 104 | 105 | You can search for selinux related man pages as follows: 106 | 107 | ``` 108 | [root@selinux-dev ~]# man -k _selinux 109 | ``` 110 | 111 | Or view the policy documentation: 112 | 113 | ``` 114 | [root@selinux-dev ~]# lynx /usr/share/doc/selinux-policy/html/index.html 115 | ``` 116 | -------------------------------------------------------------------------------- /lab10-systemd/README.md: -------------------------------------------------------------------------------- 1 | # systemd 2 | 3 | It's great that our application runs, but right now we are starting it using the shell. We know ultimately we want this to run through `systemd`, and we saw earlier that `systemd` processes run with a different context, as `systemd` itself runs in a separate context that it's children inherit unless a policy says otherwise. 4 | 5 | From our policy that got us this far, we know that we defined: 6 | 7 | ``` 8 | type_transition unconfined_t testprog_exec_t : process testprog_t; 9 | ``` 10 | 11 | However that won't have any effect if we start the process with `systemd`. Let's take a look: 12 | 13 | ``` 14 | [james@selinux-dev lab10-systemd]$ sudo kill $(cat /var/run/testprog.pid) 15 | 16 | [james@selinux-dev lab10-systemd]$ sudo systemctl start testprog 17 | [james@selinux-dev lab10-systemd]$ ps -efZ | grep testprog 18 | system_u:system_r:init_t:s0 root 26390 1 0 15:20 ? 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 19 | 20 | [james@selinux-dev lab10-systemd]$ sudo systemctl status testprog 21 | × testprog.service - SELinux Test Program 22 | Loaded: loaded (/etc/systemd/system/testprog.service; enabled; preset: disabled) 23 | Active: failed (Result: exit-code) since Sun 2025-01-05 22:12:14 UTC; 14s ago 24 | Duration: 4ms 25 | Process: 13138 ExecStart=/usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid (code=exited, status=203/EXEC) 26 | Main PID: 13138 (code=exited, status=203/EXEC) 27 | CPU: 2ms 28 | 29 | Jan 05 22:12:14 selinux-rocky9 systemd[13138]: testprog.service: Failed to locate executable /usr/bin/testprog: Permission denied 30 | Jan 05 22:12:14 selinux-rocky9 systemd[13138]: testprog.service: Failed at step EXEC spawning /usr/bin/testprog: Permission denied 31 | Jan 05 22:12:14 selinux-rocky9 systemd[1]: Started SELinux Test Program. 32 | Jan 05 22:12:14 selinux-rocky9 systemd[1]: testprog.service: Main process exited, code=exited, status=203/EXEC 33 | Jan 05 22:12:14 selinux-rocky9 systemd[1]: testprog.service: Failed with result 'exit-code'. 34 | ``` 35 | 36 | We can see that `testprog` is not running - interestingly if you had attempted the previous version of this lab which was written for and tested on EL7, you would have seen `testprog` running successfully, albeit in the wrong context! However times change policies get more secure, and so it's clear we'll need to fix our policy to enable `testprog` to be run from `systemd`. 37 | 38 | We already know from earlier labs that `systemd` launches processes in a different context, so clearly what we need to do now is to create a type transition that will make `testprog` switch into the `testprog_t` domain when it is launched from `systemd`. 39 | 40 | Now we have already seen from our earlier lab that we can create a manual transition from one domain to another. We have also seen, however, that most common SELinux permissions have already been defined for us in the reference policy. Rather than adding multiple lines of code to create a second type transition, it seems fair to surmise that someone has already done this for us. As before, let's pick on an example SELinux policy that we know does what we want already (again I will use `ntp` here). If we examine the policy definition we can see: 41 | 42 | ``` 43 | [james@selinux-dev lab10-systemd]$ grep -A2 '^type ntpd_t;' ../refpolicy/selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c/policy/modules/contrib/ntp.te 44 | type ntpd_t; 45 | type ntpd_exec_t; 46 | init_daemon_domain(ntpd_t, ntpd_exec_t) 47 | ``` 48 | 49 | Just from the name, that piece of code looks useful, so let's use our SELinux functions we downloaded previously to examine it: 50 | 51 | ``` 52 | [james@selinux-dev lab10-systemd]$ seshowif init_daemon_domain 53 | interface(`init_daemon_domain',` 54 | gen_require(` 55 | attribute direct_run_init, direct_init, direct_init_entry; 56 | type init_t; 57 | role system_r; 58 | attribute daemon; 59 | attribute initrc_transition_domain; 60 | attribute initrc_domain; 61 | ') 62 | 63 | typeattribute $1 daemon; 64 | typeattribute $2 direct_init_entry; 65 | 66 | domain_type($1) 67 | domain_entry_file($1, $2) 68 | 69 | type_transition initrc_domain $2:process $1; 70 | 71 | ifdef(`direct_sysadm_daemon',` 72 | type_transition direct_run_init $2:process $1; 73 | typeattribute $1 direct_init; 74 | ') 75 | 76 | optional_policy(` 77 | systemd_connectto_socket_proxyd_unix_sockets($1) 78 | ') 79 | ') 80 | 81 | ``` 82 | 83 | I will leave full expansion of this to the reader, but suffice to say this does everything we need to do, and lots more besides. In short it's perfect, so we just need to add one single line to our testprog policy: 84 | 85 | ``` 86 | [james@selinux-dev lab10-systemd]$ diff testprog.te ../lab9-putting-it-all-together/testprog.te 87 | 53,55d52 88 | < init_daemon_domain(testprog_t, testprog_exec_t) 89 | < 90 | < 91 | ``` 92 | 93 | It should be that simple! Let's build and install the policy, then we can test: 94 | 95 | ``` 96 | [james@selinux-dev lab10-systemd]$ sudo systemctl stop testprog 97 | [james@selinux-dev lab10-systemd]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 98 | Compiling targeted testprog module 99 | /usr/bin/checkmodule: loading policy configuration from tmp/testprog.tmp 100 | /usr/bin/checkmodule: policy configuration loaded 101 | /usr/bin/checkmodule: writing binary representation (version 17) to tmp/testprog.mod 102 | Creating targeted testprog.pp policy package 103 | rm tmp/testprog.mod tmp/testprog.mod.fc 104 | [james@selinux-dev lab10-systemd]$ sudo semodule -r testprog 105 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority). 106 | [james@selinux-dev lab10-systemd]$ sudo semodule -i testprog.pp 107 | 108 | [james@selinux-dev lab10-systemd]$ sudo systemctl start testprog 109 | [james@selinux-dev lab10-systemd]$ systemctl status testprog 110 | ● testprog.service - SELinux Test Program 111 | Loaded: loaded (/etc/systemd/system/testprog.service; enabled; preset: disabled) 112 | Active: active (running) since Sun 2025-01-05 22:21:20 UTC; 1s ago 113 | Main PID: 15323 (testprog) 114 | Tasks: 1 (limit: 22920) 115 | Memory: 200.0K 116 | CPU: 3ms 117 | CGroup: /system.slice/testprog.service 118 | └─15323 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 119 | 120 | Jan 05 22:21:20 selinux-rocky9 systemd[1]: Started SELinux Test Program. 121 | Jan 05 22:21:20 selinux-rocky9 testprog[15323]: Using configuration file: /etc/testprog.conf 122 | Jan 05 22:21:20 selinux-rocky9 testprog[15323]: Wrote PID to /var/run/testprog.pid 123 | Jan 05 22:21:20 selinux-rocky9 testprog[15323]: Writing output to: /var/testprog/testprg.txt 124 | Jan 05 22:21:20 selinux-rocky9 testprog[15323]: Iteration count: -1 125 | 126 | [james@selinux-dev lab10-systemd]$ ps -fZwwp $(cat /var/run/testprog.pid) 127 | system_u:system_r:testprog_t:s0 root 26513 1 0 15:34 ? 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 128 | 129 | ``` 130 | 131 | Excellent result! Our policy is more or less complete... for now. 132 | -------------------------------------------------------------------------------- /lab14-networking/README.md: -------------------------------------------------------------------------------- 1 | # Adding networking 2 | 3 | Let's suppose now that someone releases a new version of `testprog`. This version is identical to the previous in all respects, except that an optional networking feature has now been aded. When turned off (signified by setting `NETWORKPORT=0` in the config file) the program functions exactly as it always did. However when a positive integer is specified in this field, the program opens a TCP socket and listens for traffic on that port. Any data written to the port is echoed back to the client, and also written to the data file in place of our test strings. 4 | 5 | For simplicity the new program is called `testprog-net` - this enables us to keep and play with the old version of `testprog` if required. However in real life this might be a version 2 of the code, and simply installed over the top of the existing code. 6 | 7 | Let's make a start by building and installing the code itself: 8 | 9 | ``` 10 | [james@selinux-dev selinux-hands-on-labs]$ cd testprog-net 11 | [james@selinux-dev testprog-net]$ make 12 | gcc -o testprog-net testprog-net.c 13 | [james@selinux-dev testprog-net]$ sudo make install 14 | mkdir -p /usr/bin 15 | cp testprog-net /usr/bin/testprog-net 16 | cp testprog-net.conf /etc/testprog-net.conf 17 | mkdir -p /var/testprog-net 18 | cp testprog-net.service /etc/systemd/system/testprog-net.service 19 | [james@selinux-dev testprog-net]$ sudo systemctl enable testprog-net 20 | Created symlink from /etc/systemd/system/multi-user.target.wants/testprog-net.service to /etc/systemd/system/testprog-net.service. 21 | ``` 22 | 23 | By default networking is disabled in the configuration file, so our existing SELinux policy should work (if you look at the C code, the network section is completely conditional and will not run unless `NETWORKPORT` is > 0). However our filenames have changed so we need to update `testprog.fc` to cover our new files such as `/usr/bin/testprog-net`. Now note before we have been tidying up after ourselves, removing the old `testprog.pp` policy using `sudo semodule -r testprog` before installing a new version. Before we go any further let's try that now: 24 | 25 | ``` 26 | [james@selinux-dev testprog-net]$ sudo semodule -r testprog 27 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority). 28 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority). 29 | Failed to resolve typeattributeset statement at /var/lib/selinux/targeted/tmp/modules/400/testcat/cil:21 30 | Failed to resolve AST 31 | semodule: Failed! 32 | ``` 33 | 34 | We haven't seen that before! The issue here is that we create a policy called `testcat` that depends on an interface provided by `testprog`. As a result we can no longer remove the policy and reload it - instead we have to start working with the version number field at the top of the `testprog.te` file. In previous labs this was set to `0.1.10` consistently - clearly this is incorrect behaviour and we need to start bumping up this version number. If you look at the new `testprog.te` that comes with this lab, you'll see the version number has been bumped to `0.2.0`. From now on we'll have to increment it every time we make a change, which is the practise we should be undertaking consistently. 35 | 36 | Also take a look inside `testprog.fc` - previously we specified only one file in each of the first 3 lines - however now we've added wildcards to pick up all variants of `testprog`. Of course this could pick up too many files if someone else out there wants to create say `testprog3` which needs a different policy - some care and planning is required when defining your file contexts not to make them too broad - however for this example are demonstrating the transition from specifying individual files to wildcard matching. 37 | 38 | Let's go ahead and build and install the new policy, then reset file contexts: 39 | 40 | ``` 41 | [james@selinux-dev lab14-networking]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 42 | Compiling targeted testprog module 43 | Creating targeted testprog.pp policy package 44 | rm tmp/testprog.mod tmp/testprog.mod.fc 45 | [james@selinux-dev lab14-networking]$ sudo semodule -i testprog.pp 46 | [james@selinux-dev lab14-networking]$ sudo restorecon -v /usr/bin/testprog* 47 | restorecon reset /usr/bin/testprog-net context unconfined_u:object_r:bin_t:s0->unconfined_u:object_r:testprog_exec_t:s0 48 | [james@selinux-dev lab14-networking]$ sudo restorecon -v /etc/testprog* 49 | restorecon reset /etc/testprog-net.conf context unconfined_u:object_r:etc_t:s0->unconfined_u:object_r:testprog_conf_t:s0 50 | [james@selinux-dev lab14-networking]$ sudo restorecon -v /var/run/testprog* 51 | [james@selinux-dev lab14-networking]$ sudo restorecon -rv /var/testprog 52 | [james@selinux-dev lab14-networking]$ 53 | ``` 54 | 55 | We expect our initial run of `testprog-net` to proceed unhindered as networking is disabled in the config file - verify this: 56 | 57 | ``` 58 | [james@selinux-dev lab14-networking]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 59 | [1] 28449 60 | [james@selinux-dev lab14-networking]$ Using configuration file: /etc/testprog-net.conf 61 | Wrote PID to /var/run/testprog-net.pid 62 | Writing output to: /var/testprog/testprg-net.txt 63 | Iteration count: -1 64 | Network functionality disabled 65 | 66 | [james@selinux-dev lab14-networking]$ tail -f /var/testprog/testprg-net.txt 67 | Hello World 68 | abcdefghij 69 | Hello World 70 | abcdefghij 71 | Hello World 72 | abcdefghij 73 | Hello World 74 | abcdefghij 75 | ^C 76 | [james@selinux-dev lab14-networking]$ ps -efZ | grep testprog-net 77 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 28449 26149 0 10:57 pts/0 00:00:00 sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 78 | unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 root 28450 28449 0 10:57 pts/0 00:00:00 /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 79 | ``` 80 | 81 | Excellent - we're up and running, and in our confined domain! 82 | 83 | Now enable networking - change the port in `/etc/testprog-net.conf` from `0` to `8123` and try running the program again: 84 | 85 | ``` 86 | [james@selinux-dev lab14-networking]$ sudo sed -i 's/NETWORKPORT=0/NETWORKPORT=8123/g' /etc/testprog-net.conf 87 | [james@selinux-dev lab14-networking]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 88 | [2] 28512 89 | [james@selinux-dev lab14-networking]$ Using configuration file: /etc/testprog-net.conf 90 | Wrote PID to /var/run/testprog-net.pid 91 | Writing output to: /var/testprog/testprg-net.txt 92 | Iteration count: -1 93 | Network functionality enabled on port 8123 94 | Could not create socket 95 | Socket created 96 | bind failed. Error 97 | 98 | [2]+ Exit 1 sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 99 | ``` 100 | 101 | Oh dear - we've failed. By now you will have guessed that SELinux also restricts access to network ports as well as files, `tty` devices and so on. This is over and above any firewall that you may or may not have running on your server - as with everything else we have explored so far it is an additional layer to the usual protections provided on a Linux system. Very that SELinux did indeed cause this: 102 | 103 | ``` 104 | [james@selinux-dev lab14-networking]$ sudo grep testprog-net /var/log/audit/audit.log | grep AVC 105 | type=AVC msg=audit(1736177507.491:1052): avc: denied { create } for pid=16360 comm="testprog-net" scontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tclass=tcp_socket permissive=0 106 | ``` 107 | 108 | As suspected! We were denied from creating a TCP socket. As with our previous labs, we can either manually add allow rules for each socket action required to allow our new program to communicate on the network, or we can see what macros are available to us, and how other reference policies do this... 109 | 110 | -------------------------------------------------------------------------------- /testprog-net/testprog-net.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A test program for playing with SELinux contexts and permissions 3 | * 4 | * Based on a simple premise of a native C binary, that reads one config file and writes an output file 5 | * 6 | * Credits for code: 7 | * https://gsamaras.wordpress.com/code/eat-spaces-and-newline-c/ 8 | * https://www.pacificsimplicity.ca/blog/simple-read-configuration-file-struct-example 9 | * http://www.cprogramming.com/tutorial/c/lesson14.html 10 | * https://cboard.cprogramming.com/c-programming/155124-using-c-program-append-text-txt-file.html 11 | * https://stackoverflow.com/questions/24249369/how-to-write-pid-to-file-on-unix 12 | * https://www.gnu.org/software/libc/manual/html_node/Syslog-Example.html 13 | * 14 | * Thanks to http://www.binarytides.com/server-client-example-c-sockets-linux/ for the network socket example 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | char s[11] = {"\nabcdefghij"}; 25 | 26 | #define FILENAME "testprog-net.conf" 27 | #define MAXBUF 1024 28 | #define DELIM "=" 29 | #define PIDFILE "/var/run/testprog-net.pid" 30 | 31 | struct config 32 | { 33 | char outputfile[MAXBUF]; 34 | char loopcount[MAXBUF]; 35 | char networkport[MAXBUF]; 36 | }; 37 | 38 | 39 | struct config get_config(char *filename) 40 | { 41 | struct config configstruct; 42 | FILE *file = fopen (filename, "r"); 43 | 44 | if (file != NULL) 45 | { 46 | char line[MAXBUF]; 47 | int i = 0; 48 | 49 | while(fgets(line, sizeof(line), file) != NULL) 50 | { 51 | char *cfline; 52 | cfline = strstr((char *)line,DELIM); 53 | cfline = cfline + strlen(DELIM); 54 | 55 | if (i == 0){ 56 | memcpy(configstruct.outputfile,cfline,strlen(cfline)); 57 | //printf("%s",configstruct.outputfile); 58 | } else if (i == 1){ 59 | memcpy(configstruct.loopcount,cfline,strlen(cfline)); 60 | //printf("%s",configstruct.loopcount); 61 | } else if (i == 2){ 62 | memcpy(configstruct.networkport,cfline,strlen(cfline)); 63 | } 64 | 65 | i++; 66 | } // End while 67 | } // End if file 68 | 69 | 70 | fclose(file); 71 | 72 | return configstruct; 73 | 74 | } 75 | 76 | int main(int argc, char *argv[] ) 77 | { 78 | struct config configstruct; 79 | 80 | setlogmask (LOG_UPTO (LOG_NOTICE)); 81 | openlog ("testprog", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 82 | 83 | if (argc < 2) { 84 | // No arguments were passed 85 | configstruct = get_config(FILENAME); 86 | printf("Using configuration file: %s\n", FILENAME); 87 | syslog (LOG_NOTICE, "Using configuration file: %s\n", FILENAME); 88 | // We assume argv[2] is a pidfile to open 89 | FILE *file = fopen( PIDFILE, "wb" ); 90 | fprintf(file, "%d\n", getpid()); 91 | fclose(file); 92 | printf("Wrote PID to %s\n", PIDFILE ); 93 | syslog (LOG_NOTICE, "Wrote PID to %s\n", PIDFILE ); 94 | } else { 95 | // We assume argv[1] is a config file to open 96 | FILE *file = fopen( argv[1], "r" ); 97 | 98 | /* fopen returns 0, the NULL pointer, on failure */ 99 | if ( file == 0 ) 100 | { 101 | printf( "Could not open file\n" ); 102 | syslog (LOG_CRIT, "Could not open file\n" ); 103 | } 104 | else 105 | { 106 | configstruct = get_config(argv[1]); 107 | printf("Using configuration file: %s\n", argv[1]); 108 | syslog (LOG_NOTICE, "Using configuration file: %s\n", argv[1]); 109 | } 110 | fclose(file); 111 | 112 | // We assume argv[2] is a pidfile to open 113 | FILE *pidfile = fopen( argv[2], "wb" ); 114 | fprintf(pidfile, "%d\n", getpid()); 115 | fclose(pidfile); 116 | printf("Wrote PID to %s\n", argv[2] ); 117 | syslog (LOG_NOTICE, "Wrote PID to %s\n", argv[2] ); 118 | } 119 | 120 | /* Code to trim our string and prevent bad chars in filename */ 121 | char* outputfilePtr = configstruct.outputfile; 122 | size_t len; 123 | outputfilePtr += strspn(outputfilePtr, "\t\n\v\f\r "); 124 | len = strcspn(configstruct.outputfile, "\r\n"); 125 | configstruct.outputfile[len] = '\0'; 126 | 127 | printf("Writing output to: %s\n",configstruct.outputfile); 128 | syslog (LOG_NOTICE, "Writing output to: %s\n",configstruct.outputfile); 129 | 130 | 131 | /* Cast loopcount as int */ 132 | int x; 133 | x = atoi(configstruct.loopcount); 134 | printf("Iteration count: %d\n",x); 135 | syslog (LOG_NOTICE, "Iteration count: %d\n",x); 136 | 137 | /* Cast networkport at int */ 138 | int networkport; 139 | networkport = atoi(configstruct.networkport); 140 | if ( networkport == 0 ) 141 | { 142 | printf("Network functionality disabled\n"); 143 | syslog (LOG_NOTICE, "Network functionality disabled\n"); 144 | 145 | int i = 0; 146 | while(i < x || x < 0) 147 | { 148 | FILE *file = fopen(configstruct.outputfile, "a"); 149 | fputs("\nHello World",file); 150 | fputs(s,file); 151 | 152 | fclose(file); 153 | sleep(1); 154 | 155 | i++; 156 | } 157 | } else if ( networkport > 0 ) { 158 | printf("Network functionality enabled on port %d\n",networkport); 159 | syslog (LOG_NOTICE, "Network functionality enabled on port %d\n",networkport); 160 | 161 | int socket_desc , client_sock , c , read_size; 162 | struct sockaddr_in server , client; 163 | char client_message[2000]; 164 | 165 | //Create socket 166 | socket_desc = socket(AF_INET , SOCK_STREAM , 0); 167 | if (socket_desc == -1) 168 | { 169 | printf("Could not create socket\n"); 170 | syslog (LOG_CRIT, "Could not create socket\n"); 171 | } 172 | printf("Socket created\n"); 173 | syslog (LOG_NOTICE, "Socket created\n"); 174 | 175 | //Prepare the sockaddr_in structure 176 | server.sin_family = AF_INET; 177 | server.sin_addr.s_addr = INADDR_ANY; 178 | server.sin_port = htons( networkport ); 179 | 180 | //Bind 181 | if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) 182 | { 183 | //print the error message 184 | printf("bind failed. Error\n"); 185 | syslog (LOG_CRIT, "bind failed. Error\n"); 186 | return 1; 187 | } 188 | printf("bind done\n"); 189 | syslog (LOG_NOTICE, "bind done\n"); 190 | 191 | //Listen 192 | listen(socket_desc , 3); 193 | while (1) { 194 | //Accept and incoming connection 195 | printf("Waiting for incoming connections...\n"); 196 | syslog (LOG_NOTICE, "Waiting for incoming connections...\n"); 197 | c = sizeof(struct sockaddr_in); 198 | 199 | //accept connection from an incoming client 200 | client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); 201 | if (client_sock < 0) 202 | { 203 | printf("accept failed\n"); 204 | syslog (LOG_CRIT, "accept failed\n"); 205 | return 1; 206 | } 207 | printf("Connection accepted\n"); 208 | syslog (LOG_NOTICE, "Connection accepted\n"); 209 | 210 | //Receive a message from client 211 | while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 ) 212 | { 213 | //Send the message back to client 214 | write(client_sock , client_message , strlen(client_message)); 215 | //Write the message to our data file also 216 | FILE *file = fopen(configstruct.outputfile, "a"); 217 | fputs(client_message,file); 218 | 219 | fclose(file); 220 | } 221 | 222 | if(read_size == 0) 223 | { 224 | printf("Client disconnected\n"); 225 | syslog(LOG_NOTICE, "Client disconnected\n"); 226 | fflush(stdout); 227 | } 228 | else if(read_size == -1) 229 | { 230 | printf("recv failed\n"); 231 | syslog(LOG_CRIT, "recv failed\n"); 232 | } 233 | } 234 | 235 | } 236 | 237 | return 0; 238 | } 239 | -------------------------------------------------------------------------------- /lab09-putting-it-all-together/README.md: -------------------------------------------------------------------------------- 1 | # Putting It All Together 2 | 3 | Now it's time to put all this together and build a working policy to enable us to run `testprog` from the shell. First of all we shall decide what contexts to put the files and directories into as we shall build the policy around that. To follow the practise used for other applications on EL7 (including our reference policy `ntp`) our file contexts will look like this: 4 | 5 | ``` 6 | [root@selinux-dev lab09-putting-it-all-together]# cat testprog.fc 7 | /usr/bin/testprog -- system_u:object_r:testprog_exec_t:s0 8 | /etc/testprog.conf -- system_u:object_r:testprog_conf_t:s0 9 | /var/run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 10 | /run/testprog.pid -- system_u:object_r:testprog_var_run_t:s0 11 | /var/testprog(/.*)? system_u:object_r:testprog_data_t:s0 12 | 13 | ``` 14 | 15 | In this way we have defined a type for the executable, a configuration file type to ensure we can read the configuration file but in isolation from the test of the `/etc` directory, a type for the pid file as discussed in previous labs, and a type for the data directory. Note we have used a wildcard for the data directory to cover any file in there - this is an example here to show the alternative syntax and the regular expression style syntax that does in the `fc` file. 16 | 17 | Now that we have this created we can build the policy. Building on our previous policy from earlier, and the reference code we have taken from the `ntp` policy, `testprog`'s policy should look something like this: 18 | 19 | ``` 20 | [james@selinux-dev2 selinux-testprog]$ cat testprog.te 21 | policy_module(testprog, 0.2) 22 | 23 | # Require all the types, attributes and classes we reference in this policy 24 | require { 25 | type unconfined_t; 26 | role unconfined_r; 27 | class file { ioctl getattr setattr create read write unlink open relabelto }; 28 | class dir { search write add_name }; 29 | class process transition; 30 | type fs_t; 31 | class filesystem getattr; 32 | type console_device_t; 33 | type user_devpts_t; 34 | class unix_dgram_socket { create connect sendto }; 35 | class chr_file { append read write open getattr ioctl }; 36 | type devlog_t; 37 | class sock_file write; 38 | type kernel_t; 39 | type var_run_t; 40 | class capability sys_tty_config; 41 | } 42 | 43 | # Define our new types that testprog will use, and ensure that we tell the policy that testprog_exec_t is a file 44 | type testprog_t; 45 | domain_type(testprog_t); 46 | type testprog_exec_t; 47 | files_type(testprog_exec_t); 48 | type testprog_conf_t; 49 | files_config_file(testprog_conf_t); 50 | type testprog_var_run_t; 51 | files_pid_file(testprog_var_run_t); 52 | type testprog_data_t; 53 | files_type(testprog_data_t); 54 | 55 | # Allow the testprog_t type under the unconfined_r role 56 | role unconfined_r types testprog_t; 57 | 58 | # Tell SELinux that testprog_exec_t is an entrypoint to the testprog_t domain 59 | allow testprog_t testprog_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ; 60 | # Make the type transition from unconfined_t (i.e. user shell) to testprog_t 61 | type_transition unconfined_t testprog_exec_t : process testprog_t; 62 | # Explicitly allow the type transition we have just created 63 | allow unconfined_t testprog_t : process transition ; 64 | 65 | # Allow testprog to read it's config file using Red Hat's code for ntpd as example 66 | allow testprog_t testprog_conf_t:file read_file_perms; 67 | 68 | # We know from sealert that these allow rules are required, largely for writing to the console 69 | allow testprog_t console_device_t:chr_file { open write getattr ioctl }; 70 | allow testprog_t self:capability sys_tty_config; 71 | allow testprog_t user_devpts_t:chr_file { append read write getattr }; 72 | 73 | # Allow testprog access to it's data directory 74 | allow testprog_t testprog_data_t:dir { search write add_name }; 75 | allow testprog_t testprog_data_t:file { create open write append getattr }; 76 | 77 | # Allow syslog access based on Red Hat's code for ntpd 78 | logging_send_syslog_msg(testprog_t) 79 | 80 | # Allow testprog to create and write it's PID file - based on code from Red Hat for ntpd 81 | manage_files_pattern(testprog_t, testprog_var_run_t, testprog_var_run_t) 82 | files_pid_filetrans(testprog_t, testprog_var_run_t, file) 83 | ``` 84 | 85 | This as you'll see is an amalgamation of our previous context transition policy, rules from the `ntp.te` policy file, and a set of individually created rules that were derived from the audit log and `sealert`. In cases where rules were taken from the audit log the process of development was sometimes iterative, allowing one permission only to find a subsequent one was missing. 86 | 87 | Take for example the `testprog_data_t:file` rule - the initial AVC denial was for the `open` operation on our data file. This results in a rule like: 88 | 89 | ``` 90 | allow testprog_t testprog_data_t:file open; 91 | ``` 92 | 93 | However once `open` was allowed and the new policy installed and `testprog` run again, it was found that `testprog` could not append to the open file. Thus over several iterations of testing we ended up with: 94 | 95 | ``` 96 | allow testprog_t testprog_data_t:file { create open write append getattr }; 97 | ``` 98 | 99 | This includes both the case where the data file does not exist so has to have the `create` and `write` permissions, as well as when it does and so needs the `open` and `append` permissions. The `getattr` permission is needed to look up the file when it exists before it is opened and written to. 100 | 101 | Let's try building, installing and then testing the policy: 102 | 103 | ``` 104 | [james@selinux-dev2 lab09-putting-it-all-together]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 105 | Compiling targeted testprog module 106 | /usr/bin/checkmodule: loading policy configuration from tmp/testprog.tmp 107 | /usr/bin/checkmodule: policy configuration loaded 108 | /usr/bin/checkmodule: writing binary representation (version 17) to tmp/testprog.mod 109 | Creating targeted testprog.pp policy package 110 | rm tmp/testprog.mod tmp/testprog.mod.fc 111 | 112 | [james@selinux-dev2 lab09-putting-it-all-together]$ sudo semodule -r testprog 113 | libsemanage.semanage_direct_remove_key: Removing last testprog module (no other testprog module exists at another priority) 114 | 115 | [james@selinux-dev2 lab09-putting-it-all-together]$ sudo semodule -i testprog.pp 116 | 117 | [james@selinux-dev2 lab09-putting-it-all-together]$ sudo restorecon -v /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 118 | restorecon reset /etc/testprog.conf context unconfined_u:object_r:etc_t:s0->unconfined_u:object_r:testprog_conf_t:s0 119 | restorecon reset /run/testprog.pid context unconfined_u:object_r:var_run_t:s0->unconfined_u:object_r:testprog_var_run_t:s0 120 | 121 | [james@selinux-dev2 lab09-putting-it-all-together]$ sudo restorecon -rv /var/testprog 122 | restorecon reset /var/testprog context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:testprog_data_t:s0 123 | restorecon reset /var/testprog/testprg.txt context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:testprog_data_t:s0 124 | 125 | [james@selinux-dev2 lab09-putting-it-all-together]$ sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid & 126 | [1] 26348 127 | Using configuration file: /etc/testprog.conf 128 | Wrote PID to /var/run/testprog.pid 129 | Writing output to: /var/testprog/testprg.txt 130 | Iteration count: -1 131 | 132 | [james@selinux-dev2 lab09-putting-it-all-together]$ ps -fZwwp $(cat /var/run/testprog.pid) 133 | unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 root 26349 26348 0 15:08 pts/0 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 134 | 135 | [james@selinux-dev2 lab09-putting-it-all-together]$ tail -f /var/testprog/testprg.txt 136 | Hello World 137 | abcdefghij 138 | Hello World 139 | abcdefghij 140 | Hello World 141 | abcdefghij 142 | ``` 143 | 144 | Note that we had to run `restorecon` against all the files that `testprog` will access as we added new file contexts and definitions in this lab. Without this we would have the application fail, as a result of the following: 145 | 146 | * The files were created from our previous labs, only in the wrong context as we hadn't defined one for them at that time. 147 | * Our policy of confining the application stops it from accessing files except in the very specific contexts we allowed 148 | * This applies even if the files were created in one of the `unconfined` contexts as our application is now confined and cannot access anything unless we specifically allow it. 149 | 150 | Now we've made huge progress - our application is working again, only this time it's running in a confined domain and cannot access anything we haven't told it to. Feel free to play around with this - for example: 151 | 152 | 1. Move `/usr/bin/testprog` to a new location or place, check the file context (hint: it should inherit from the parent directory) and try running it. See if it runs and check `audit.log`. 153 | 2. Change one of the config files, or the output directory in the config file and see what happens. Play about with new contexts using `chcon` as discussed previously. 154 | -------------------------------------------------------------------------------- /lab06-sealert-and-audit-log/README.md: -------------------------------------------------------------------------------- 1 | # Lab 6 - sealert and audit.log 2 | 3 | We have successfully broken our application by making it switch to a confined context - we now need to understand why it is broken and how to fix it. This lab will focus on diagnosing the failures - something that will be useful as you start to debug SELinux issues and create your own policies. 4 | 5 | When SELinux is enabled, it's actions are logged into `/var/log/audit/audit.log` - initially this file might be hard to deciper but with a little practise it becomes easier. There are also tools to help you. Let's take a little look at our failed run of `testprog` to start with - we can grep that as a keyword as we know with almost certainty that there won't be any other applications called testprog on our system: 6 | 7 | ``` 8 | [james@selinux-dev selinux-hands-on-labs]$ sudo grep testprog /var/log/audit/audit.log 9 | type=USER_CMD msg=audit(1504795300.550:139): pid=11516 uid=1000 auid=0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='cwd="/home/james/selinux-hands-on-labs/testprog" cmd=79756D202D7920696E7374616C6C20676363206D616B65 terminal=pts/0 res=success' 10 | type=USER_CMD msg=audit(1504795720.854:146): pid=11547 uid=1000 auid=0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='cwd="/home/james/selinux-hands-on-labs/testprog" cmd=6D616B6520696E7374616C6C terminal=pts/0 res=success' 11 | type=USER_CMD msg=audit(1504795841.993:151): pid=11557 uid=1000 auid=0 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='cwd="/home/james/selinux-hands-on-labs/testprog" cmd=73797374656D63746C2072656C6F6164202D2D616C6C terminal=pts/0 res=success' 12 | ... 13 | 14 | ... 15 | ``` 16 | 17 | There's a huge amount of output including success messages so let's be a little more specific: 18 | 19 | ``` 20 | [james@selinux-dev selinux-hands-on-labs]$ sudo grep AVC /var/log/audit/audit.log | grep testprog 21 | ... 22 | type=AVC msg=audit(1504818384.560:460): avc: denied { write } for pid=12442 comm="testprog" name="testprog.pid" dev="tmpfs" ino=34664 scontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:var_run_t:s0 tclass=file 23 | ... 24 | ``` 25 | 26 | There are still lots of lines of output, which really show how many parts of the system even our simple little testprog tries to access. It also shows you how finely grained SELinux's access control is - it goes beyond filesystem restrictions to controlling socket creation, `chr` file access, console output and so much more. I have cherry picked a simple example from the output on my development system to share above - what it's saying is: 27 | 28 | The process with PID `12442` (named `testprog`) tried to `write` to a `file` object which had context `unconfined_u:object_r:var_run_t:s0` and was called `testprog.pid`, with inode number `34663` on the `tmpfs` device (`/var/run` is on `tmpfs`). Most importantly the source context of the process was `unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023`, which confirms our new policy did indeed switch successfully to the new `testprog_t` context we created. 29 | 30 | Given this log we can also see how we could go about creating a full policy for our application so that it works like it used to, only confined to it's own context. We also see the power of SELinux - even though we ran `testprog` as root, it cannot write it's own PID file in `/var/run`, which is not what you would normally expect! 31 | 32 | # sealert 33 | 34 | Of course whilst this log file can be quite useful once you know how to interpret it, it's not always easy to decipher. The `sealert` tool can help you create new policies and fix issues like this, but it should be used with caution and awareness to create a truly secure application. 35 | 36 | ``` 37 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install setroubleshoot-server 38 | ... 39 | ``` 40 | 41 | Having installed the package containing sealert, let's have a look at it's interpretation of the audit log: 42 | 43 | ``` 44 | [james@selinux-dev selinux-hands-on-labs]$ sudo sealert -a /var/log/audit/audit.log > sealert.txt 45 | [james@selinux-dev selinux-hands-on-labs]$ cat sealert.txt 46 | -------------------------------------------------------------------------------- 47 | 48 | SELinux is preventing /usr/bin/testprog from write access on the file testprog.pid. 49 | 50 | ***** Plugin catchall (100. confidence) suggests ************************** 51 | 52 | If you believe that testprog should be allowed write access on the testprog.pid file by default. 53 | Then you should report this as a bug. 54 | You can generate a local policy module to allow this access. 55 | Do 56 | allow this access for now by executing: 57 | # ausearch -c 'testprog' --raw | audit2allow -M my-testprog 58 | # semodule -i my-testprog.pp 59 | 60 | 61 | Additional Information: 62 | Source Context unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c102 63 | 3 64 | Target Context unconfined_u:object_r:var_run_t:s0 65 | Target Objects testprog.pid [ file ] 66 | Source testprog 67 | Source Path /usr/bin/testprog 68 | Port 69 | Host 70 | Source RPM Packages 71 | Target RPM Packages 72 | Policy RPM selinux-policy-3.13.1-166.el7_4.4.noarch 73 | Selinux Enabled True 74 | Policy Type targeted 75 | Enforcing Mode Enforcing 76 | Host Name selinux-dev.lab.quru.com 77 | Platform Linux selinux-dev.lab.quru.com 78 | 3.10.0-693.2.1.el7.x86_64 #1 SMP Fri Aug 11 79 | 04:58:43 EDT 2017 x86_64 x86_64 80 | Alert Count 2 81 | First Seen 2017-09-07 22:06:11 BST 82 | Last Seen 2017-09-07 22:06:24 BST 83 | Local ID 05861116-def2-4893-a07c-03f096b185c7 84 | 85 | Raw Audit Messages 86 | type=AVC msg=audit(1504818384.560:460): avc: denied { write } for pid=12442 comm="testprog" name="testprog.pid" dev="tmpfs" ino=34664 scontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:var_run_t:s0 tclass=file 87 | 88 | type=SYSCALL msg=audit(1504818384.560:460): arch=x86_64 syscall=open success=no exit=EACCES a0=7fff3eca57d0 a1=241 a2=1b6 a3=24 items=0 ppid=12441 pid=12442 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=1 comm=testprog exe=/usr/bin/testprog subj=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 key=(null) 89 | 90 | Hash: testprog,testprog_t,var_run_t,file,write 91 | ``` 92 | 93 | This again is just focussing on our PID file example for simplicity. The output from `sealert` is full of other alerts. However we can see that from the output above, `sealert` is 100% confident that it's found a fix for our inability to write our pid file and it even tells us how to automatically create a new policy to fix it whilst allowing `testprog` to run in it's confined context. How helpful! Let's run the first command it suggests and have a look at the results... 94 | 95 | ``` 96 | [james@selinux-dev selinux-hands-on-labs]$ sudo ausearch -c 'testprog' --raw | audit2allow -M my-testprog 97 | ******************** IMPORTANT *********************** 98 | To make this policy package active, execute: 99 | 100 | semodule -i my-testprog.pp 101 | 102 | [james@selinux-dev selinux-hands-on-labs]$ cat my-testprog.te 103 | 104 | module my-testprog 1.0; 105 | 106 | require { 107 | type var_run_t; 108 | type testprog_t; 109 | class file write; 110 | } 111 | 112 | #============= testprog_t ============== 113 | 114 | #!!!! WARNING: 'var_run_t' is a base type. 115 | allow testprog_t var_run_t:file write; 116 | 117 | ``` 118 | 119 | Now again I have sanitized the output so that it's only relevant to our PID file for simplicity. The output above is what `sealert` suggests we install as a new policy - but let's take a step back - what would be the consequences of doing that? 120 | 121 | 1. If you run this as is, you will end up with two policies governing `testprog` - one for the transition and one for the writing of the PID file. Given that we know that we have more to fix to make the application work, we could end up with a whole litter of policies just for one application which is an administrative headache. We could of course copy the output above and merge it with the policy we created in the previous lab - that would be better, but.... 122 | 123 | 2. What the above policy is saying is **allow write access to files in the `var_run_t` type from the `testprog_t` domain**. We know (and can easily verify) that the directory `/var/run` itself has this type, and as a result we are giving `testprog` write access to anything in this directory. This may not sound terrible in the case of a PID file residing on `tmpfs`, but remember we also have a config file to deal with that is in `/etc`. Would we really want our application to have read access (as will be required) to the entire `/etc` directory? Including our shadow password file, `sudoers`, and such (which as the application is running at root it will be able to do)? The whole idea is to block this kind of access to that in the event of an unexpected compromise, an attacker is contained and can read the `testprog` configuration file, and write the `testprog` PID file, but nothing more. 124 | 125 | Make no mistake, using the above policy is better than just turning off SELinux to make the application work. It will be more contained if we do this than it would in the `unconfined` domain, or without SELinux at all. Simply we can do better. So let's discard this suggestion and write our own policy. 126 | 127 | # Recommended reading 128 | 129 | https://wiki.gentoo.org/wiki/SELinux/Tutorials/Where_to_find_SELinux_permission_denial_details 130 | 131 | -------------------------------------------------------------------------------- /lab15-allowing-networking/README.md: -------------------------------------------------------------------------------- 1 | # Allowing networking 2 | 3 | We know from before that looking at the policy for the NTP daemon helped us immensely in moving forwards with our policy for `testprog`. However we also know that `ntpd` relies primarily on UDP for its communications, and our appliation is TCP based. As a result we'll focus on another example - here I have picked the ssh daemon policy module (aptly named `ssh.te`). If you read through the policy file, you'll come across this section somewhere near the middle of the file: 4 | 5 | ``` 6 | [james@selinux-dev selinux-hands-on-labs]$ find . -name ssh.te 7 | ./refpolicy/selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c/policy/modules/services/ssh.te 8 | [james@selinux-dev selinux-hands-on-labs]$ less ./refpolicy/selinux-policy-0113b35519369e628e7fcd87af000cfcd4b1fa6c/policy/modules/services/ssh.te 9 | ... 10 | allow ssh_t self:tcp_socket create_stream_socket_perms; 11 | ... 12 | corenet_all_recvfrom_netlabel(ssh_t) 13 | corenet_tcp_sendrecv_generic_if(ssh_t) 14 | corenet_tcp_sendrecv_generic_node(ssh_t) 15 | corenet_tcp_sendrecv_all_ports(ssh_t) 16 | corenet_tcp_connect_ssh_port(ssh_t) 17 | corenet_tcp_connect_all_unreserved_ports(ssh_t) 18 | corenet_sendrecv_ssh_client_packets(ssh_t) 19 | corenet_tcp_bind_generic_node(ssh_t) 20 | #corenet_tcp_bind_all_unreserved_ports(ssh_t) 21 | corenet_rw_tun_tap_dev(ssh_t) 22 | ... 23 | corenet_tcp_bind_ssh_port(ssh_t) 24 | corenet_tcp_bind_generic_node(ssh_t) 25 | corenet_tcp_bind_all_unreserved_ports(ssh_t) 26 | ... 27 | corenet_tcp_bind_xserver_port(sshd_t) 28 | corenet_tcp_bind_vnc_port(sshd_t) 29 | corenet_sendrecv_xserver_server_packets(sshd_t) 30 | ... 31 | ``` 32 | 33 | There's a pretty massive hint there! Much of the networking policy rules is performed using a set of macros that all begin with `corenet`. The exception is the `tcp_socket` line above them all - however this sets up the most basic level of permissions to allow the program to set up a TCP socket before any other work relating to connections or traffic is allowed. SELinux with networking is very powerful, and there are ways to restrict communications by address and many other things. However, the most common task you will face in an EL9/Fedora 41 environment is to define a policy to allow access to a given port, and possibly customize that port. 34 | 35 | As previously, it would help us to expand the above macros and see what they do so that we can pick the appropriate one. In an earlier lab we installed the `selinux-policy-devel` package which gave us our Makefile amongst other things. If we look into the files that this package installed, we can see: 36 | 37 | ``` 38 | [james@selinux-dev selinux-hands-on-labs]$ rpm -ql selinux-policy-devel | grep core 39 | /usr/share/selinux/devel/html/abrt_retrace_coredump.html 40 | /usr/share/selinux/devel/include/kernel/corecommands.if 41 | /usr/share/selinux/devel/include/kernel/corenetwork.if 42 | ``` 43 | 44 | It turns out that some of the interfaces for the policy are built as part of the actual build process of the source RPM and as a result we can't just go into the source tree of the `refpolicy` directory we created earlier and find them - we have to actually look on the host itself. Thankfully the `selinux-funcs-el9.txt` has been modified from the author's original version to take account of this and search these additional directories. 45 | 46 | Now, let's say that we have decided on a port of `8123/tcp` for `testprog-net`. How do we make this work? From the previous lab we saw that the app was denied the chance to create a `tcp_socket` and the policy for sshd creates this manually, so let's add the following line to our policy (along with the appropriate require declarations at the top): 47 | 48 | ``` 49 | allow testprog_t self:tcp_socket create_stream_socket_perms; 50 | ``` 51 | 52 | If you add the above and try running `sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid &` again, you'll see it still fails to run, only now with a different error in the audit logs: 53 | 54 | ``` 55 | type=AVC msg=audit(1736178175.452:1322): avc: denied { name_bind } for pid=16747 comm="testprog-net" src=8123 scontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket permissive=0 56 | ``` 57 | 58 | Whilst we could just allow a straightforward bind to our socket, we actually want to restrict it to a specific port as we know the one that "belongs" to our application. This is where it gets interesting. We have already seen that we can generate new file contexts, load them through a policy module, and then apply or restore them. However the same is not true of ports - these must (at the time of writing) be labelled as part of the base policy, and updating this is a job for the distribution maintainer. You could rebuild it, but on EL9/Fedora 41 a simple rpm update would remove your custom changes so this is not recommended. Instead we need to create a type for our port, then add a label for it on the command line. First off add the following to the testprog.te policy: 59 | 60 | ``` 61 | ... 62 | require 63 | { 64 | ... 65 | attribute port_type; 66 | } 67 | ... 68 | type testprog_port_t; 69 | typeattribute testprog_port_t port_type; 70 | 71 | # Allow our new network capabilities 72 | allow testprog_t self:tcp_socket create_stream_socket_perms; 73 | corenet_tcp_sendrecv_generic_node(testprog_t) 74 | corenet_tcp_bind_generic_node(testprog_t) 75 | allow testprog_t testprog_port_t:tcp_socket { name_bind }; 76 | ``` 77 | 78 | Now we have an SELinux port type defined just for `testprog`. Build and install the policy module in the usual way, then let's create a port type definition for `testprog_t`... 79 | 80 | ``` 81 | [james@selinux-dev lab15-allowing-networking]$ make -f /usr/share/selinux/devel/Makefile testprog.pp 82 | Compiling targeted testprog module 83 | Creating targeted testprog.pp policy package 84 | rm tmp/testprog.mod tmp/testprog.mod.fc 85 | [james@selinux-dev lab15-allowing-networking]$ sudo semodule -i testprog.pp 86 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -a -t testprog_port_t -p tcp 8123 87 | Port tcp/8123 already defined, modifying instead 88 | ``` 89 | 90 | Oops! Now we see why port definitions can only be managed at the base policy level - it would be too easy to overlap ports or re-assign something. Let's instead find a free port and reconfigure `testprog` to use it - we'll also clean up that duplicate mapping we've created... 91 | 92 | ``` 93 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -l | grep 8123 94 | http_cache_port_t tcp 8080, 8118, 8123, 10001-10010 95 | testprog_port_t tcp 8123 96 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -d -t testprog_port_t -p tcp 8123 97 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -l | grep 8234 98 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -a -t testprog_port_t -p tcp 8234 99 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -l | grep test 100 | testprog_port_t tcp 8234 101 | [james@selinux-dev lab15-allowing-networking]$ sudo sed -i 's/NETWORKPORT=.*/NETWORKPORT=8234/g' /etc/testprog-net.conf 102 | ``` 103 | 104 | That literally is all that is required to get our network enabled version of `testprog` working - much simpler than SSH, but then the SSH daemon is a lot more feature rich that our little test program. Build and install the policy if you haven't already, and try running `testprog` one more time... 105 | 106 | ``` 107 | [james@selinux-dev lab15-allowing-networking]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 108 | [1] 60296 109 | [james@selinux-dev lab15-allowing-networking]$ Using configuration file: /etc/testprog-net.conf 110 | Wrote PID to /var/run/testprog-net.pid 111 | Writing output to: /var/testprog/testprg-net.txt 112 | Iteration count: -1 113 | Network functionality enabled on port 8234 114 | Socket created 115 | bind done 116 | Waiting for incoming connections... 117 | ``` 118 | 119 | Success! Try out the program and make sure it works - you can use `telnet` or `nc` to connect to it on port 8234, and anything you type in should get echoed back to you, and also written to the data file. 120 | 121 | Now one final question remains. We allocated and labelled a specific port for `testprog-net`. But what if we need to change it? Try the following: 122 | 123 | ``` 124 | [james@selinux-dev lab15-allowing-networking]$ sudo sed -i 's/NETWORKPORT=.*/NETWORKPORT=8235/g' /etc/testprog-net.conf 125 | [james@selinux-dev lab15-allowing-networking]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 126 | [1] 60301 127 | [james@selinux-dev lab15-allowing-networking]$ Using configuration file: /etc/testprog-net.conf 128 | Wrote PID to /var/run/testprog-net.pid 129 | Writing output to: /var/testprog/testprg-net.txt 130 | Iteration count: -1 131 | Network functionality enabled on port 8235 132 | Socket created 133 | bind failed. Error 134 | 135 | [1]+ Exit 1 sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid 136 | ``` 137 | 138 | This fails because our program tried to access a port that it didn't have permission to bind to. We can verify this from the audit logs: 139 | 140 | ``` 141 | [james@selinux-dev lab15-allowing-networking]$ sudo grep AVC /var/log/audit/audit.log | grep testprog 142 | type=AVC msg=audit(1736178782.210:1394): avc: denied { name_bind } for pid=16914 comm="testprog-net" src=8235 scontext=unconfined_u:unconfined_r:testprog_t:s0-s0:c0.c1023 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0 143 | ``` 144 | 145 | As you can see, we've falled into a different type because port 8235 is labelled at `unreserved_port_t`. We can fix this quite simply by relabelling this port: 146 | 147 | ``` 148 | [james@selinux-dev lab15-allowing-networking]$ sudo semanage port -a -t testprog_port_t -p tcp 8235 149 | [james@selinux-dev lab15-allowing-networking]$ sudo /usr/bin/testprog-net /etc/testprog-net.conf /var/run/testprog-net.pid & 150 | [1] 60312 151 | [james@selinux-dev lab15-allowing-networking]$ Using configuration file: /etc/testprog-net.conf 152 | Wrote PID to /var/run/testprog-net.pid 153 | Writing output to: /var/testprog/testprg-net.txt 154 | Iteration count: -1 155 | Network functionality enabled on port 8235 156 | Socket created 157 | bind done 158 | Waiting for incoming connections... 159 | ``` 160 | 161 | Job done! One thing remains though - our network functionality is optional, and can be turned off by setting the `NETWORKPORT` paramter to `0`. If we are running with the networking off, surely we want to disable this SELinux access to ensure everything is locked down as tight as it can be! To do that we could create a separate policy and load that (after first removing the original policy and all it's dependencies such as `testprog_port_t` and `testcat`), but that is a lot of work for a simple parameter change. There must be a better way... 162 | 163 | -------------------------------------------------------------------------------- /lab03-running-testprog-for-the-first-time/README.md: -------------------------------------------------------------------------------- 1 | # Running testprog for the first time 2 | 3 | Let's get stuck in and try running testprog for the first time. First of all let's double check that we're in SELinux Enforcing mode (if you just completed lab 2, you can skip this step): 4 | 5 | ``` 6 | [james@selinux-dev selinux-hands-on-labs]$ sestatus 7 | SELinux status: enabled 8 | SELinuxfs mount: /sys/fs/selinux 9 | SELinux root directory: /etc/selinux 10 | Loaded policy name: targeted 11 | Current mode: enforcing 12 | Mode from config file: enforcing 13 | Policy MLS status: enabled 14 | Policy deny_unknown status: allowed 15 | Memory protection checking: actual (secure) 16 | Max kernel policy version: 33 17 | ``` 18 | 19 | Ok good. Now what happens first of all if we run the binary from the shell? We're in **Enforcing** mode so we would expect the application to fail.... 20 | 21 | ``` 22 | [james@selinux-dev selinux-hands-on-labs]$ sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid & 23 | [1] 11665 24 | Using configuration file: /etc/testprog.conf 25 | Wrote PID to /var/run/testprog.pid 26 | Writing output to: /var/testprog/testprg.txt 27 | Iteration count: -1 28 | [james@selinux-dev selinux-hands-on-labs]$ 29 | ``` 30 | 31 | It's working, and if you want to double check: 32 | 33 | ``` 34 | [james@selinux-dev selinux-hands-on-labs]$ tail -f /var/testprog/testprg.txt 35 | Hello World 36 | abcdefghij 37 | Hello World 38 | abcdefghij^C 39 | [james@selinux-dev selinux-hands-on-labs]$ cat /var/run/testprog.pid 40 | 11666 41 | ``` 42 | 43 | Normally SELinux Enforcing mode strikes fear into people - otherwise they wouldn't turn it off when it comes to running bespoke applications. So the natural question here is, if SELinux is in **Enforcing** mode, Why did this work? Let's look at the process table: 44 | 45 | ``` 46 | [james@selinux-dev selinux-hands-on-labs]$ ps -fZp $(cat /var/run/testprog.pid) 47 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 11666 11665 0 16:04 pts/0 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 48 | ``` 49 | 50 | Note the output in the first column - this is the SELinux **context** that the process is running in. This gives us our clue as to why the application is running perfectly, and the clue is in the word `unconfined`. 51 | 52 | Although it might look scary, let's break it down so you can understand it a bit better: 53 | 54 | * `unconfined_u`: The SELinux user associated with this process. `unconfined_u` typically indicates a user that isn't restricted by SELinux policies. 55 | * `unconfined_r`: The SELinux role. `unconfined_r` suggests this process is running with an unrestricted role. 56 | * `unconfined_t`: The SELinux type (domain). `unconfined_t` means the process is in an unrestricted domain. 57 | * `s0-s0:c0.c1023`: The SELinux sensitivity and category range. This defines the security clearance level and categories, and is only applicable in MLS (Multi-Level Security). `s0-s0` means the lowest sensitivity level, and "c0.c1023" represents a category range. We won't be working with these during this course. 58 | 59 | Putting aside the sensitivity and category ranges, the `user`, `role` and `type` form a hierarchical permissions set. Specifically: 60 | * The `user` defines "who is trying to access something". It is separate from the normal Linux user accounts and SELinux `users` are managed separately. Each SELinux user has a specified set of `roles` they are allowed to access. 61 | * The `role` is the next level of security, and controls what the `user` is allowed to do. 62 | * The `type` is the core of SELinux's policy definition, and defines the fine-grained permissions such as "can this process read this specific file?". 63 | 64 | There are a number of `users`, `roles` and `types` built-in to all SELinux enabled Linux distributions, and you can query them with the `seinfo` tool. If it isn't already installed, you can install it via: 65 | ``` 66 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install setools-console 67 | ``` 68 | 69 | You can they list the `users`, `roles` and `types` respectively using the following 3 commands: 70 | ``` 71 | [james@selinux-dev selinux-hands-on-labs]$ seinfo -u 72 | [james@selinux-dev selinux-hands-on-labs]$ seinfo -r 73 | [james@selinux-dev selinux-hands-on-labs]$ seinfo -t 74 | ``` 75 | 76 | We'll explore more detail on the SELinux `users`, `roles` and `types`! 77 | 78 | # Targeted policies 79 | 80 | Targeted mode on SELinux does more or less what it says on the tin - it **targets** specific applications on the system and applies policy to them. Everything else that is unknown to it has to come under the default `unconfined` policy as explored above. This is rather like a firewall. Typically a firewall denies all incoming connections and allows known ones through (i.e. targeted ones in SELinux parlance). SELinux takes the opposite approach - denying all applications from running if there is no known SELinux policy for them would make the system incredibly secure, but also painful to work with. As a result the default targetted policy is to run unknown applications in an `unconfined` context - that is to say that they run as if SELinux was **disabled**! 81 | 82 | This obviously makes it very easy to get new applications running, but it's not very secure - our `testprog` application has access to the entire system, as root (we ran it with `sudo`) without restriction. If compromised, it could do untold damage. This absolutely negates the idea of running SELinux, especially if this was an internet facing service accepting connections. 83 | 84 | # What about systemd? 85 | 86 | Now you may ask, is it more secure if you run it as a service? Let's try (don't forget to kill the process we backgrounded earlier first).... 87 | 88 | ``` 89 | [james@selinux-dev selinux-hands-on-labs]$ fg 90 | sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 91 | ^C 92 | [james@selinux-dev selinux-hands-on-labs]$ sudo systemctl start testprog 93 | [james@selinux-dev selinux-hands-on-labs]$ sudo systemctl status testprog 94 | ● testprog.service - SELinux Test Program 95 | Loaded: loaded (/etc/systemd/system/testprog.service; enabled; vendor preset: disabled) 96 | Active: active (running) since Thu 2017-09-07 16:17:04 BST; 15s ago 97 | Main PID: 11695 (testprog) 98 | CGroup: /system.slice/testprog.service 99 | └─11695 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 100 | 101 | Sep 07 16:17:04 selinux-dev systemd[1]: Started SELinux Test Program. 102 | Sep 07 16:17:04 selinux-dev systemd[1]: Starting SELinux Test Program... 103 | Sep 07 16:17:04 selinux-dev testprog[11695]: Using configuration file: /etc/testprog.conf 104 | Sep 07 16:17:04 selinux-dev testprog[11695]: Wrote PID to /var/run/testprog.pid 105 | Sep 07 16:17:04 selinux-dev testprog[11695]: Writing output to: /var/testprog/testprg.txt 106 | Sep 07 16:17:04 selinux-dev testprog[11695]: Iteration count: -1 107 | ``` 108 | 109 | Once again it's working! However note this time: 110 | 111 | ``` 112 | [james@selinux-dev selinux-hands-on-labs]$ ps -fZp $(cat /var/run/testprog.pid) 113 | system_u:system_r:unconfined_service_t:s0 root 11695 1 0 16:17 ? 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 114 | ``` 115 | 116 | Notice the different context this time? We can still see that keyword in the type field of the context (more on that in a minute), `unconfined`, but the `user` and `role` have changed. At this stage we haven't defined any SELinux policy for this application, so why is it behaving differently? 117 | 118 | # More on contexts 119 | 120 | As we have already seen, SELinux contexts follow the `user:role:type:level` syntax. 121 | 122 | There is extensive reading available out there on this topic but in brief: 123 | 124 | ## user 125 | 126 | This is an identity known to the SELinux policy that is mapped to users on the system, and is authorised through policy for a specific set of roles. 127 | 128 | You can see the user mappings by installing and running the `semanage` tool: 129 | ``` 130 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install policycoreutils-python-utils 131 | ... 132 | 133 | ... 134 | [james@selinux-dev selinux-hands-on-labs]$ sudo semanage login -l 135 | 136 | Login Name SELinux User MLS/MCS Range Service 137 | 138 | __default__ unconfined_u s0-s0:c0.c1023 * 139 | root unconfined_u s0-s0:c0.c1023 * 140 | ``` 141 | 142 | Here we can see that the `root` user is mapped to the `unconfined_u` SELinux user, as is the `__default__` for all other users (so my `james` account will fall under this). 143 | 144 | ## role 145 | 146 | SELinux users are authorized for roles, and roles are authorized for domains (more on them later). As a result this gives SELinux Role-based Access Control (RBAC) and helps reduce the risk of privilege escalation attacks. For example normally getting to root on a compromised system is the Holy Grail - however you can run a process under a role that even `unconfined_r` doesn't have access to, and as a result SELinux will block access to the domains that role is authorized for. This is an unlikely example as the whole nature of the targeted policy is to give access to everything from the unconfined role, but it is technically possible. 147 | 148 | ## type 149 | 150 | Red Hat's own documentation says it best: "The type is an attribute of Type Enforcement. The type defines a domain for processes, and a type for files. SELinux policy rules define how types can access each other, whether it be a domain accessing a type, or a domain accessing another domain. Access is only allowed if a specific SELinux policy rule exists that allows it." 151 | 152 | ## level 153 | 154 | This is beyond the scope of this project at this stage and only relates to systems where MLS is implemented, though it may be added if there is demand for it. This field is only of value if the MLS policy is loaded and in operation - we do not need to worry about it under the targeted policy. MLS stands for Multi-Level Security and provides an additional fine-grained layer of system security to meet even higher security requirements than the targeted policy, such as compliance with EAL4+. 155 | 156 | # Inheritance 157 | 158 | Ok so given all this, how did we end up running testprog in the contexts we saw earlier? SELinux behaviour may be summed up simply here as: 159 | 160 | The type context of a process or file will always inherit the context of it's parent unless there is a policy to tell it otherwise. 161 | 162 | Let's take for example our testprog binary. We just copied it into place - no SELinux configuration was done. Yet it will have a context: 163 | 164 | ``` 165 | [james@selinux-dev selinux-hands-on-labs]$ ls -lZ /usr/bin/testprog 166 | -rwxr-xr-x. root root unconfined_u:object_r:bin_t:s0 /usr/bin/testprog 167 | ``` 168 | 169 | To see how it got that, let's have a look at it's parent item, in this case the `/usr/bin/` directory: 170 | 171 | ``` 172 | [james@selinux-dev selinux-hands-on-labs]$ ls -ldZ /usr/bin 173 | dr-xr-xr-x. root root system_u:object_r:bin_t:s0 /usr/bin 174 | ``` 175 | 176 | Notice that our binary inherited the `object_r` and `bin_t` type from it's parent directory. However the `user` field has stayed the same as the original file, inherited from the user that ran the command. 177 | 178 | Similar can be observed of our configuration file: 179 | 180 | ``` 181 | [james@selinux-dev selinux-hands-on-labs]$ ls -lZ /etc/testprog.conf 182 | -rw-r--r--. root root unconfined_u:object_r:etc_t:s0 /etc/testprog.conf 183 | [james@selinux-dev selinux-hands-on-labs]$ ls -ldZ /etc 184 | drwxr-xr-x. root root system_u:object_r:etc_t:s0 /etc 185 | ``` 186 | 187 | And even our data directory - note the inheritance across both the directory our `make install` command created, and also the data file created at runtime: 188 | 189 | ``` 190 | [james@selinux-dev selinux-hands-on-labs]$ ls -lZ /var/testprog/ 191 | -rw-r--r--. root root unconfined_u:object_r:var_t:s0 testprg.txt 192 | [james@selinux-dev selinux-hands-on-labs]$ ls -ldZ /var/testprog /var 193 | drwxr-xr-x. root root system_u:object_r:var_t:s0 /var 194 | drwxr-xr-x. root root unconfined_u:object_r:var_t:s0 /var/testprog 195 | ``` 196 | 197 | Processes operate in a similar way. When we ran `testprog` from the shell, we were actually running it from `bash` via `sudo`: 198 | 199 | ``` 200 | [james@selinux-dev selinux-hands-on-labs]$ sudo systemctl stop testprog 201 | [james@selinux-dev selinux-hands-on-labs]$ sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid & 202 | [1] 11818 203 | Using configuration file: /etc/testprog.conf 204 | Wrote PID to /var/run/testprog.pid 205 | Writing output to: /var/testprog/testprg.txt 206 | Iteration count: -1 207 | [james@selinux-dev selinux-hands-on-labs]$ ps -fZp $(cat /var/run/testprog.pid) 208 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 11819 11818 0 16:49 pts/0 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 209 | ... 210 | [james@selinux-dev selinux-hands-on-labs]$ ps -fZp 11818 211 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 11818 1221 0 16:49 pts/0 00:00:00 sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 212 | ... 213 | [james@selinux-dev selinux-hands-on-labs]$ ps -efZ | grep bash 214 | unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 james 1221 1220 0 14:59 pts/0 00:00:00 -bash 215 | ... 216 | ``` 217 | 218 | And through systemd: 219 | 220 | ``` 221 | [james@selinux-dev selinux-hands-on-labs]$ fg 222 | sudo /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 223 | ^C 224 | [james@selinux-dev selinux-hands-on-labs]$ sudo systemctl start testprog 225 | [james@selinux-dev selinux-hands-on-labs]$ ps -fZp $(cat /var/run/testprog.pid) 226 | system_u:system_r:unconfined_service_t:s0 root 11836 1 0 16:53 ? 00:00:00 /usr/bin/testprog /etc/testprog.conf /var/run/testprog.pid 227 | ... 228 | [james@selinux-dev selinux-hands-on-labs]$ ps -efZ | grep systemd 229 | system_u:system_r:init_t:s0 root 1 0 0 14:45 ? 00:00:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 21 230 | ... 231 | ``` 232 | 233 | Wait! We didn't see the `type` get inherited here. If you ever see that happen, suspect that an SELinux policy rule has made this happen. If you believe this is happening, the `sesearch` tool is your friend (it will have been installed alongside `seinfo` but just in case, the installation command is below): 234 | 235 | ``` 236 | [james@selinux-dev selinux-hands-on-labs]$ sudo dnf -y install setools-console 237 | ... 238 | [james@selinux-dev selinux-hands-on-labs]$ sesearch -s init_t -t unconfined_service_t -A -ds -dt 239 | ... 240 | allow init_t unconfined_service_t:process { siginh transition }; 241 | ... 242 | ``` 243 | 244 | The latter command searches the currently loaded SELinux policy for any rule allowing the source type `init_t` and the target type `unconfined_service_t`. We can see there is a rule specifically allowing process transition for anything started using the `init_t` type to the `unconfined_t` type, which confirms our original suspicion (there will be more output, but I've omitted this for clarity). 245 | 246 | # Recommended reading 247 | 248 | https://wiki.gentoo.org/wiki/SELinux/Tutorials/How_does_a_process_get_into_a_certain_context 249 | 250 | --------------------------------------------------------------------------------