This is the second post in my “Informix and SElinux” series, In Informix and SElinux Part.1 I focus on showing how security is addressed by different existing technologies and how this technologies helps us to keep our systems hard to be cracked.
What’s SELinux and how it works
SELinux, is a project that was created in the year 2000 by the NSA (National Security Agency) with the intention of providing stricter security measures for access control and user permits, processes, files, devices, etc in Linux systems.
To understand how it works, think in each server file, directory, user, port, process or device as a RESOURCE. Each resource get a label (also named “context”) which defines its role type and will characterize the permissions and actions that are permitted. Each of the operating system resources including users and processes has some type of access control attribute. This resource type is also called a security context. A security context has three elements: user, role, and type identifiers. The name of the identifiers for each element are defined in the SELinux policy rules. But In a commonplace policy, 99% of the decisions are made based upon type only. SELinux allows or denies actions based upon rules saying “a process of context X can do so and so in relation with something with context Y”. In short: In SELinux, the context is everything. When the context applies to a process, the type is called “the domain”. There is no practical difference between a type and a domain because all three components are just names. The policy rules gives them significance. Example guess: When a resource like a file is going to be accessed by a program in a role, the kernel module is asked for permissions before certain operations are about to happen (‘hooks’).
Some role rules should allow this kind of action accessing this resource type. If not, permission is denied and the O.S. action cannot be performed. SELinux modifies many system commands by adding the -Z option to display the security contexts of objects and subjects. For example, ls -Z shows the security contexts of filesystem objects and ps -Z shows the security contexts of processes. The context is completely unrelated to classic UNIX user ID, group ID or whatever.
SELinux Policy
When enabling SELinux in your system, everything should work flawlessly as this secured system will include by default rules named policies to allow normal use of most Linux software. The heart of SELinux’ security engine is its policy. A policy is what the name implies: a set of rules that define the security and access rights for everything in the system. And when we say everything, we mean users, roles, processes, and files. The policy defines how each of these entities are related to one another. SELinux policy defines user access to roles, role access to domains, and domain access to types. First the user has to be authorized to enter a role, and then the role has to be authorized to access the domain. The domain in turn is restricted to access only certain types of files. There are two different policy types:
- Targeted policy – A policy based upon the paradigm, that only a few selected applications should be restricted by SELinux. All other activity relies on good old UNIX security
- Strict policy – A policy which attempts to control all activity with SELinux
As explained previously, SELinux policy is not something that replaces traditional DAC (user-group-others) security. If a DAC rule prohibits a user access to a file, SELinux policy rules won’t be evaluated because the first line of defense has already blocked access. SELinux security decisions come into play after DAC security has been evaluated. When an SELinux-enabled system starts, the policy is loaded into memory. SELinux policy comes in modular format, much like the kernel modules loaded at boot time. And just like the kernel modules, they can be dynamically added and removed from memory at run time. The policy store used by SELinux keeps track of the modules that have been loaded. The sestatus command shows the policy store name. The semodule -l command lists the SELinux policy modules currently loaded into memory.
To get a feeling for this, let’s run the semodule command:
semodule -l | less
How to prepare your system to work with SELinux?
Even you have SELinux activated in your system or not, most of utils used to administer and change SELinux configuration are installed in RHEL 8 by an “non-required” package named policycoreutils. So, first at all, ensure you have this package installed in your system before starting this guide by trying to install it again.
sudo dnf install policycoreutils policycoreutils-python
Now we should have a system that’s loaded with all the SELinux packages.
Do I have SELinux Active in my server?
sestatus is used to view the current status of the SELinux that is running on your system. This is what you get on RHEL 8 when you have SELinux enabled:
# sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown
status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 31
This shows you that SELinux is enabled and enforcing and we can continue with this tutorial. If you have SELinux disabled, sestatus execution will give you a different result:
sestatus
SELinux status: disabled
To activate SELinux kernel module you must edit /etc/sysconfig/selinux file and configure parameters in file like showed next:
SELINUX=enforcing
SELINUXTYPE=targeted
Then, reboot your system and you’ll get SELinux enabled.
Is Informix working properly in a SELinux enabled server?
In a targeted type SELinux configuration, all non explicitly confined into SELinux policy rules works without restrictions. So informix server will work properly. Depending of directory installation, binary types of informix will inherit diferent file types. If installed in informix home directory, /home/informix, files will get some types:
# ls -Z bin/oninit
unconfined_u:object_r:home_bin_t:s0 bin/oninit
# ls -Z lib/libastool.so
unconfined_u:object_r:user_home_t:s0 lib/libastool.so
If you install your informix server in other directory like /opt/informix file types will be different:
# ls -Z bin/oninit
unconfined_u:object_r:usr_t:s0 bin/oninit
# ls -Z lib/libastool.so
unconfined_u:object_r:usr_t:s0 lib/libastool.so
But they are still unconfined (not a explicit target are defined for it), so they will work without restrictions in both cases. Don’t misunderstand being unconfined (without a target policy) with having a unconfined_u user property. unconfined_u is just the “user” property of the SELinux file attributes. To see other users available in your system, you can execute:
# semanage user -l
Labeling MLS/ MLS/
SELinux User Prefix MCS Level MCS Range SELinux Roles
guest_u user s0 s0 guest_r
root user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r
staff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r unconfined_r
sysadm_u user s0 s0-s0:c0.c1023 sysadm_r
system_u user s0 s0-s0:c0.c1023 system_r unconfined_r
unconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_r
user_u user s0 s0 user_r
xguest_u user s0 s0 xguest_r
Why to integrate Informix in a targeted role?
Simple answer: Security When informix oninit program is launched, it does not run with the privileges of the user who launched it, but with that of root because the sticky bit present in executable permissions:
# ls -l $INFORMIXDIR/bin/oninit
-rwsr-sr--. 1 root informix 27466376 Jun 16 04:30 /home/informix/bin/oninit
So, if a hacker is able to break into your system and use some hacking techniques to inject improper code into the informix server program, it will be executed with root privileges and this is really bad. So, securing Informix into a SELinux target policy requires to create special SELinux types for informix files and executables and creating a policy rules definition module to activate SELinux security. To create this SELinux rule you need to be able to understand also the policy definition syntax.
Policy Rules
The “allow” rule
allow Source Target:Class Permission;
This means “grant Permission to a process of domain (type) Source on objects of type Target and class Class” Example:
allow unconfined_t mytype_t:file read ;
Which means “allow processes in domain (type) unconfined_t read permission on files of type mytype_t” Other allow-likes
- auditallow – Exactly like allow, but makes entries in the log (as in denials)
- dontaudit – This will not grant permission, but not log anything either
- neverallow – Not really a rule, but tells the rule compiler to exit with an error, if the specified permissions are granted by other rules. Used as an extra safeguard against bugs in the policy
Except for the opening keyword, the three above have the same syntax as allow. In case of contradiction between rules, the rule appearing later takes effect.
Type transitions for objects
Every created object has a default context For example, files and directories are created by default with their parent directory’s context It’s often desirable that the type of the new object will depend on who created it. “Who” means what domain (type) the process had. For example: If the X server creates a file in the /tmp directory, it should have type xdm_tmp_t, but if a “normal user” process does so, it should be user_tmp_t. The solution: Type transitions
type_transition Source Target:Class new_type;
This means “any object of class Class, which is created by a process in the domain (type) Source, and would by default get the type Target, will get the type new_type instead”. Example:
type_transition sshd_t tmp_t:file sshd_tmp_t;
Which means that if a process running in the sshd_t domain (most likely the ssh deamon) creates a plain regular file which should have gotten the tmp_t type (most likely because it’s in the /tmp directory), it should get the sshd_tmp_t instead. Note that this is not a permission rule. Rather, this tells SELinux itself to perform an action. The type transition for objects doesn’t require an additional permission rule. But several other actions need permission:
- Read-write access to the parent directory
- Creating a new file or directory with the new type
- To make things easier, a macro bundles the type transition statement with the permissions, file_type_auto_trans
Paraphrasing the last example, the following macro statement covers a variety of file types (plain files, directories, symlinks etc) and also handles the permissions. All in one: file_type_auto_trans(sshd_t, tmp_t, sshd_tmp_t);
Domain transitions for processes
type_transition Source Target:process new_type;
This means “when a process in the domain Source executes a file of type Target, change the process’ domain to new_type. Occurs when an application is executed – an exec() call. Note that it’s the same syntax as for objects, only the Class is held as process. Example: type_transition sshd_t shell_exec_t:process user_t;
Which means that if a process in the sshd_t domain runs an executable of type shell_exec_t (a shell, most likely) the process will continue in the user_t domain. For processes, the type_transition statement doesn’t include the permission. A lot of permissions need to be explicitly declared: The transition itself, reading and running the executable, and much more The domain_auto_trans macro includes the type transition statement and a lot of relevant permissions (such as allowing a pipe run between the two relevant domains) So instead of the previous example, we may want to go: domain_auto_trans(sshd_t, shell_exec_t, user_t);
In the absence of a matching transition rule, the executable will run without changing the domain. That requires the execute_no_trans permission
Type sets and class sets
A set can be put where a single type or class would normally appear, as long as it makes sense (to whom?) Curly brackets ‘{‘ and ‘}’ with space-delimited elements mean “for each element” The tilde character preceding an expression indicates the complement of the set The asterisk * represents all types or classes A minus sign preceding an element, within a curly brackets expressions reduces the element from the set Examples:
allow unconfined_t mytype_t:file { read getattr };
allow unconfined_t mytype_t:file * ;
role declaration
role ROLE types TYPE;
Meaning “it’s legal for a process context with role ROLE to be in the domain TYPE” Sets can be used for the type, but not for the role For a list of types currently known by the kernel: seinfo -r An attempt to enter a domain with an unauthorized role, will cause an “invalid context” error. Example:
role unconfined_r types mytype_t ;
Constraints
Every permission request must obey all constraints currently active in the kernel Shouldn’t be necessary in a policy module Since it isn’t so relevant, we’ll just take an example:
There’s mlsconstraint too, which constrains MLS-related permissions (this issue is barely documented)
constrain process transition ( u1 == u2 or t1 == privuser );
constrain process transition ( r1 == r2 or t1 == privrole );
constrain dir_file_class_set { create relabelto relabelfrom }
( u1 == u2 or t1 == privowner );
Declarations
Declaring types
type identifier attributelist ;
This declares the type with the name identifier The attributelist is optional. … and the name “attribute” is a misnomer. It’s more like a means for grouping types. Examples:
type mytype_t;
type crond_t, domain, privuser, privrole, privfd, privowner;
Given the type declaration above, if the attribute privuser is used where the syntax expected a type, this will include several types, including crond_t Same goes for domain, privrole, privfd and privowner
Declaring attributes and typeattribute
If you want your own attributes (in a module?) they need to be declared:
attribute myattributename;
Also, it’s possible to give a type an attribute in a separate statement:
typeattribute mytype_t theattribute;
Conclusion
This Post is going to finish and theory is also going to the end. In next Blog in series Informix and SElinux Part.3, we will start creating a SElinux module and defining rules to securize Informix executables.
Previous Post: Informix and SElinux Part 1
Vicente Salvador Cubedo