Gentoo is one of the various distributions which supports SELinux as a Mandatory Access Control system to, amongst other things, mitigate the results of a succesfull exploit against software. So what about the recent PostgreSQL vulnerability?
When correctly configured, the PostgreSQL daemon will run in the
postgresql_t
domain. In SELinux-speak, a domain can be seen as a name
granted to a set of permissions (what is allowed) and assigned to one or
more processes. A process that "runs in domain postgresql_t" will be
governed by the policy rules (what is and isn't allowed) for that
domain.
The vulnerability we speak of is about creating new files or overwriting existing files, potentially corrupting the database itself (when the database files are overwritten). Creating new files is handled through the create privilege on files (and add_name on directories), writing into files is handled through the write privilege. Given certain circumstances, one could even write commands inside files that are executed by particular users on the system (btw, the link gives a great explanation on the vulnerability).
So let's look at what SELinux does and could have done.
In the current situation, as we explained, postgresql_t
is the only
domain we need to take into account (the PostgreSQL policy does not use
separate domains for the runtime processes). Let's look at what
directory labels it is allowed to write into:
$ sesearch -s postgresql_t -c dir -p add_name -SCATd
Found 11 semantic av rules:
allow postgresql_t postgresql_log_t : dir { add_name } ;
allow postgresql_t var_log_t : dir { add_name } ;
allow postgresql_t var_lock_t : dir { add_name } ;
allow postgresql_t tmp_t : dir { add_name } ;
allow postgresql_t postgresql_tmp_t : dir { add_name } ;
allow postgresql_t postgresql_var_run_t : dir { add_name } ;
allow postgresql_t postgresql_db_t : dir { add_name } ;
allow postgresql_t etc_t : dir { add_name } ;
allow postgresql_t tmpfs_t : dir { add_name } ;
allow postgresql_t var_lib_t : dir { add_name } ;
allow postgresql_t var_run_t : dir { add_name } ;
So the PostgreSQL service is allowed to create files inside directories labeled with one of the following labels:
postgresql_log_t
, used for PostgreSQL log files (/var/log/postgresql
)var_log_t
, used for the generic log files (/var/log
)var_lock_t
, used for lock files (/run/lock
or/var/lock
)tmp_t
, used for the temporary file directory (/tmp
or/var/tmp
)postgresql_tmp_t
, used for the PostgreSQL temporary files/directoriespostgresql_var_run_t
, used for the runtime information (like PID files) of PostgreSQL (/var/run/postgresql
)postgresql_db_t
, used for the PostgreSQL database files (/var/lib/postgresql
)etc_t
, used for the generic system configuration files (/etc/
)var_lib_t
, used for the/var/lib
datavar_run_t
, used for the/var/run
or/run
data
Next to this, depending on the label of the directory, the PostgreSQL service is allowed to write into files with the following label assigned (of importance to both creating new files as well as overwriting existing ones):
$ sesearch -s postgresql_t -c file -p write -SCATd
Found 11 semantic av rules:
allow postgresql_t postgresql_log_t : file { write } ;
allow postgresql_t postgresql_lock_t : file { write } ;
allow postgresql_t faillog_t : file { write } ;
allow postgresql_t lastlog_t : file { write } ;
allow postgresql_t postgresql_tmp_t : file { write } ;
allow postgresql_t hugetlbfs_t : file { write } ;
allow postgresql_t postgresql_var_run_t : file { write } ;
allow postgresql_t postgresql_db_t : file { write } ;
allow postgresql_t postgresql_t : file { write } ;
allow postgresql_t security_t : file { write } ;
allow postgresql_t etc_t : file { write } ;
Found 6 semantic te rules:
type_transition postgresql_t var_log_t : file postgresql_log_t;
type_transition postgresql_t var_lock_t : file postgresql_lock_t;
type_transition postgresql_t tmp_t : file postgresql_tmp_t;
type_transition postgresql_t tmpfs_t : file postgresql_tmp_t;
type_transition postgresql_t var_lib_t : file postgresql_db_t;
type_transition postgresql_t var_run_t : file postgresql_var_run_t;
If an exploit creates a new file, the add_name permission on the directory is needed. If otoh the exploit is overwriting existing files, I think the only permission needed here is the write on the files (also open but all the writes have open as well in the above case).
Now accessing and being able to write files into the database file
directory is expected - it is the functionality of the server, so unless
we could separate domains more, this is a "hit" we need to take. Sadly
though, this is also the label used for the PostgreSQL service account
home directory here (not sure if this is for all distributions), making
it more realistic that an attacker writes something in the home
directory .profile
file and hopes for the administrator to do
something like su postgres -.
Next, the etc_t
write privileges also worry me, not mainly because it
can write there, but also because I can hardly understand why -
PostgreSQL is supposed to run under its own, non-root user (luckily) so
unless there are etc_t
labeled directories owned by the PostgreSQL
service account (or world writeable - please no, kthx). And this isn't
an "inherited" permission from something - the policy currently has
files_manage_etc_files(postgresql_t)
set, and has been since 2005 or
earlier. I'm really wondering if this is still needed.
But I digress. Given that there are no PostgreSQL-owned directories nor
world-writeable ones in /etc
, let's look at a few other ones.
security_t
is used for the SELinux pseudo file system, and is used for the SEPostgreSQL support. From the looks of it, only the root Linux user has the rights to do really harmful things on this file system (and only if he too has write permissions onsecurity_t
), non-root should be limited to verifying if contexts exist or have particular rights. Still, I might investigate this further as I'm intrigued about many of the pseudo files in/sys/fs/selinux
that I'm not fully sure yet what they deal with.tmp_t
should not be a major concern. Most (if not all) daemons and services that use temporary files have file transitions to their own type so that access to these files, even if it would be allowed by regular permissions, is still prohibited by SELinuxlastlog_t
is also a weird one, again because it shouldn't be writeable for anyone else but root accounts; if succesfull, an attacker can overwrite the lastlog information which might be used by some as a means for debugging who was logged on when (part of forensics).
Given the information above, it is a bit sad to see that SELinux can't protect PostgreSQL users from this particular vulnerability - most of the "mitigation" (if any) is because the process runs as non-root to begin with (which is another hint at users not to think SELinux is sufficient to restrict the permissions of processes). But could it have been different?
In my opinion, yes, and I'll see if we can learn from it for the future.
First of all, we should do more policy code auditing. It might not be easy to remove policy rules generally, but we should at least try. I use a small script that enables auditing (SELinux auditing, so auditallow statements) for the entire domain, and then selectively disables auditing until I get no hits anymore. The remainder of auditallow statements warrant a closer look to see if they are still needed or not. I'll get onto that in the next few days.
Second, we might want to have service accounts use a different home directory, where they do have the necessary search privileges for, but no write privileges. Exploits that write stuff into a home directory (hoping for a su postgresql -) are then mitigated a bit.
Third, we might want to look into separating the domains according to
the architecture of the service. This requires intimate knowledge of the
ins and outs of PostgreSQL and might even require PostgreSQL patching,
so is not something light. But if no patching is needed (such as when
all process launches are done using known file executions) we could have
a separate domain for the master process, server processes and perhaps
even the various subfunction processes (like the WAL writer, BG writer,
etc.). The Postfix service has such a more diverse (but also complex)
policy. Such a subdomain structure in the policy might reduce the risk
if the vulnerable process (I think this is the master process) does not
need to write to database files (as this is handled by other processes),
so no postgresql_db_t
write privileges.
If others have ideas on how we can improve service security (for instance through SELinux policy development) or knows of other exploits related to this vulnerability that I didn't come across yet, please give a comment on it below.