Informix and SELinux Part.3

This post shows a basic SELinux implementation for oninit. It’s working but not a complete-final implementation. There will be a next post in last quarter of 2022 with complete implementation rules.

Let`s get hands dirty in this post. We’re going to create an Informix SELinux policy and put Informix oninit under SELinux control.

You will need to write your own policy module files and maintain them over the lifecycle of your system. Maintaining SELinux policies isn’t that difficult, but it is not a fire-and-forget method: changes you make on the policy will be propagated through the updates. In some cases, you might need to update your policy to be compatible with upstream SELinux policy changes. When that occurs, it is important to keep track of the policy you made.

An SELinux security policy is a collection of SELinux rules. A policy is a core component of SELinux and is loaded into the kernel by SELinux user-space tools. The kernel enforces the use of an SELinux policy to evaluate access requests on the system. By default, SELinux denies all requests except for requests that correspond to the rules specified in the loaded policy.

Each SELinux policy rule describes an interaction between a process and a system resource:

ALLOW apache_process apache_log:FILE READ;

You can read this example rule as: The Apache process can read its logging file. In this rule, apache_process and apache_log are labels. An SELinux security policy assigns labels to processes and defines relations to system resources. This way, a policy maps operating-system entities to the SELinux layer.

Let’s first look at how a policy file looks like…

The structure of a SELinux policy module

When developing or updating SELinux policies, it is recommended to use the modular approach. In this situation, you can create your own policy module (say informix.pp and deal with it as if it was part of the global set of policies. Such a policy is written using a .te file (type enforcement) and an optional .fc file (file contexts) and .if file (interfaces).

Let’s create an example local policy (localpolicy.te) that contains an allow rule.

policy_module(localpolicy, 1.0)
 
gen_require(`
type user_t;
type var_log_t;
')
 
allow user_t var_log_t:dir { getattr search open read };

Note the use of the backtick (`) right after the gen_require call. This is important, as it denotes the start of the block (and the other regular tick character (‘) which denotes the end of the block).

Given this file, you can now easily create the SELinux policy module localpolicy.pp:

root # make -f /usr/share/selinux/devel/Makefile localpolicy.pp

Important to remember here is that the name of the file (localpolicy.te) and the name of the module inside the file (policy_module(localpolicy, 1.0)) and the name of the resulting binary file (localpolicy.pp) are related and need to use the same name (just a different suffix).

You can then load the resulting binary file:

root # semodule -i localpolicy.pp

Once loaded, it will remain loaded even after reboots. You could remove the localpolicy.te and other files if you want, but we strongly recommend that you keep the source files so that, if you need to make enhancements, you can just edit the files, rebuild the .pp file and load it again.

The reference policy framework

The mentioned gen_require block is part of the reference policy build framework, and will be translated during the make -f … command earlier on towards SELinux rules.

One of the principles within the reference policy framework is that you do not include types or attributes from other policies explicitly. Instead, you need to use the interfaces that are made available by the policy modules. In the above example, the role of the policy we had is to enhance the rights of the user_t domain. Hence, we can assume the user_t domain is indeed a good type to directly use. The other type (var_log_t) is an external one (it is a target type), so we need to use an interface (if it exists).

Using refpolicy interfaces

policy_module(localpolicy, 1.1)

gen_require(`
  type user_t;
')

# Allow user domain to list the contents of /var/log
logging_list_logs(user_t)

This makes the intention of the rule more clear. You can also add in comments in the file as you can see, making it easy to document your policy as you add to it more and more.

The intormix.te file

Let’s create a local policy that contains an allow rule.


dnf install selinux-policy-devel

mkdir $HOME/selinux
cd $HOME/selinux
ln -s /usr/share/selinux/devel/Makefile .

Analyzing Informix requirements for SE Linux permissions

A minimal module
Let’s start with informix.te as follows:

cat >> informix.te <<<EOF

module informix 0.0.1;

require {
role unconfined_r;
type unconfined_t;
class process transition;
}

type informix_t;
type informix_exec_t;
type informix_chunk_t;
type informix_tmp_t;

role unconfined_r types informix_t;

type_transition unconfined_t informix_exec_t : process informix_t;
EOF

It defines two new types, informix_t and informix_exec_t.
It also tells the SELinux core, that if a process in the unconfined_t domain runs an executable of whose type is informix_exec_t, the process should continue in the informix_t domain.
But nothing is allowed for these two types, so they are are both completely useless.

semodule -i informix.pp

setenforce 0
chcon -t informix_exec_t oninit
setenforce 1

Basic SELinux Informix Policy Source Code


module informix 0.1.0;

require {
role unconfined_r;
type unconfined_t;
type local_login_t;
type device_t;
type null_device_t;
type user_tty_device_t;
type default_t;
type init_t;
type ld_so_t;
type root_t;
type bin_t;
type etc_t;
type lib_t;
type usr_t;
type tmp_t;
type var_t;
type var_lib_t;
type var_run_t;
type init_var_run_t;
type initrc_t;
type user_home_dir_t;
type user_home_t;
type user_tmp_t;
type home_root_t;
type home_bin_t;
type ld_so_cache_t;
type passwd_file_t;
type locale_t;
type cpu_online_t;
type security_t;
type selinux_config_t;
type sssd_var_lib_t;
type sssd_public_t;
type sssd_t;
type sysfs_t;
type system_dbusd_t;
type system_dbusd_var_run_t;
type proc_t;

# TCP Socket
class tcp_socket { name_connect };
# Directory
class dir { manage_dir_perms };
# Normal file
class file { manage_file_perms mmap_exec_file_perms entrypoint execmod };
# Symbolic link
class lnk_file { read_lnk_file_perms };
# Character device file
class chr_file { rw_chr_file_perms };
# Special file for Unix domain socket
class sock_file { write };
# Permissions in process class are prepared to restrict operations between process
class process { sigkill signal signull sigchld sigstop execmem setsched transition noatsecure rlimitinh siginh fork };
# Permissions in filesystem class is used for access control to superblock
class filesystem { getattr };
# File Descriptor
class fd { use };
# DBus Access
class dbus { send_msg };
# UNIX Stream Socket
class unix_stream_socket { rw_socket_perms create connectto };
# Capability
class capability { net_admin sys_tty_config setuid setgid chown fowner sys_nice };
# Shared memory
class shm { create_shm_perms };
# Semaphores
class sem { create_sem_perms };
}

type informix_t;
type informix_exec_t; # Executable binary files

role unconfined_r types informix_t ;

# When you want a particular transition to be the default transition behavior for a domain

domain_auto_trans(unconfined_t, informix_exec_t, informix_t);

#============= informix_t ==============

allow informix_t informix_exec_t:file { getattr entrypoint execute map open read };

# Use the dynamic link/loader for automatic loading of shared libraries with legacy support
libs_legacy_use_ld_so(informix_t)

# Allow processes to inherit local login file descriptors
locallogin_use_fds(informix_t);
# Read and write to the null device (/dev/null) character device type
write_chr_files_pattern(informix_t,device_t,null_device_t);

allow informix_t self:dir { search };
allow informix_t self:file { open read };
allow informix_t self:lnk_file { read };
allow informix_t self:capability { net_admin sys_tty_config setuid setgid chown fowner sys_nice };
allow informix_t self:unix_stream_socket { create connect getopt setopt getattr read write };

# Allow signals
allow informix_t self:process { signal_perms execmem fork setsched };

# Allow semaphores and shared memory

# create_sem_perms – Defines permissions needed to create semaphores and read and write semaphores and their attributes

# create_shm_perms – Defines permissions needed to create shared memory segments and read and write shared memory segments and their attributes.

allow informix_t self:sem { create_sem_perms };
allow informix_t self:shm { create_shm_perms };

allow informix_t unconfined_t:fd use;

allow informix_t root_t:dir { getattr search };
allow informix_t user_tty_device_t:chr_file { append read write getattr ioctl };

allow informix_t user_home_t:dir { search getattr read write add_name remove_name };
allow informix_t user_home_t:file { getattr open map execute link create read write lock unlink };
allow informix_t user_home_t:lnk_file { read getattr };
allow informix_t user_home_dir_t:dir { getattr search };

allow informix_t user_tmp_t:dir { search getattr write add_name remove_name };
allow informix_t user_tmp_t:file { getattr open create read write setattr unlink append };

allow informix_t home_bin_t:dir { getattr search };
allow informix_t home_root_t:dir { getattr search };

allow informix_t var_t:dir { search_dir_perms };
allow informix_t var_run_t:dir { search_dir_perms };
allow informix_t var_run_t:lnk_file { read };
allow informix_t var_lib_t:dir { search_dir_perms };

# Send and receive messages from init over dbus
init_dbus_chat(informix_t)
# Allow search directory in the /run/systemd directory
init_search_pid_dirs(informix_t)

# Search the contents of /etc directories
files_search_etc(informix_t)
# Read generic files in /etc
files_read_etc_files(‘informix_t);
#allow informix_t etc_t:file { read_file_perms };
## Read the passwd passwords file (/etc/passwd)
## Allow to use sss nsswitch module for passwd and group.
## Allow to use systemd nsswitch module for passwd and group
## which is used for dynamic users.
auth_read_passwd(informix_t)

allow informix_t usr_t:dir { search_dir_perms };
allow informix_t usr_t:file { read_file_perms };

allow informix_t tmp_t:dir { search_dir_perms };

allow informix_t security_t:filesystem getattr;
allow informix_t selinux_config_t:dir search;

# Connect to sssd over a unix stream socket
sssd_search_lib(informix_t);

# Allow attempts to connect to session bus types with a unix stream socket
dbus_stream_connect_system_dbusd(informix_t);

# Send message to the system bus
dbus_send_system_bus(informix_t);

allow informix_t system_dbusd_var_run_t:dir search;
allow informix_t system_dbusd_var_run_t:sock_file write;

# Allow process to read localization information.
miscfiles_read_localization(informix_t);

# Allow process to read legacy time localization info
miscfiles_legacy_read_localization(informix_t);

# Read cpu online hardware state information
dev_read_cpu_online(informix_t);

allow informix_t default_t:dir { rw_dir_perms };
allow informix_t default_t:file { create open read write unlink getattr setattr };

###
## Search executables (bin_t)
#
corecmd_search_bin(informix_t);

# allow informix_t sysfs_t:dir { search open read getattr };
###
## devices
#
dev_search_sysfs(informix_t)
dev_read_sysfs(informix_t)

###
## Allow reading proc, system settings, and state
#
kernel_read_all_sysctls(informix_t)

# Get the attributes of files in /proc
kernel_getattr_proc_files(informix_t)
# Allow to (follow) symbolic links in /proc
kernel_read_proc_symlinks(informix_t)
# Allow to read files in /proc
allow informix_t proc_t:file { open read };

# Allow the informix process to write to the db files
type informix_dbfile_t;
allow informix_t informix_dbfile_t:file { manage_file_perms };
allow informix_t informix_dbfile_t:dir { manage_dir_perms };

# Type for temporary files
type informix_tmp_t;
files_tmp_file(informix_tmp_t);

#============= sssd_t ==============
allow sssd_t informix_t:dir search;
allow sssd_t informix_t:file { open read };

#============= system_dbusd_t ==============
allow system_dbusd_t informix_t:dir search;
allow system_dbusd_t informix_t:file { open read };

#============= init_t ==============
allow init_t informix_t:dir search;
allow init_t informix_t:file { open read getattr };
allow init_t informix_t:process { sigkill signal };

#============= initrc_t ==============
allow initrc_t informix_t:dir { getattr read search };
allow initrc_t informix_t:process { getpgid getsession signull };

#============ unconfined_t ==============
allow unconfined_t informix_t:dir getattr;
allow unconfined_t informix_t:shm { read write unix_read unix_write associate destroy getattr };
allow unconfined_t informix_t:process { noatsecure rlimitinh signull siginh transition };
allow unconfined_t informix_exec_t:file { execute getattr open read };

Pending work

Informix Installation dir should be tagged with specific labels: label for tmp_dir, label for etc dir and label for other dirs. Also chunk files should be tagged. Then, Policy file should be changed to honor this new tags. Currently, policy file is assuming Informix is installed in /home folder.

2022-07-18 Update: I’m working in another local policy file version. Using more and more SELinux macros and completing all pending tasks. Doing this in this post become not practical, so I’ll prepare another “last in the series” post. Hope this will be available in  couple of months.

Other posts in series

Informix and SElinux Part.1

Informix and SElinux Part.2

Informix and SElinux Part.4 (Available soon)

Vicente Salvador Cubedo

Published
Categorized as Blog

By Vicente Salvador

Board member since 2014, a user since 1989 and Informix fan. I'am software architect which allow me to combine technical and business skills.

2 comments

  1. Hi Vicente,
    I have some questions about your policy.
    Have you completed the pending work about this policy?
    I’m trying to work on it but it is very complex.
    IBM doesn’t provide documentation about it and I don’t know how to continue your policy.

    Thanks
    Davide

    1. I’m working on it. Current implementation works but it’s not secure enough.
      Hope I’ll have final version after summer.

      Feel free to ask any questions you can have or wait for final version. I know SELinux implementation is not easy. And SELinux for informix is really tough.
      Keep in contact.

Leave a comment

Your email address will not be published. Required fields are marked *