sepgsql
mdx: format: md
sepgsql is a module that supports label-based mandatory access control (MAC) based on SELinux security policies.
| Warning: The current implementation has notable limitations and does not support mandatory access control for all actions. See Section C.35.7 for details. |
|---|
1. Overview
This module integrates with SELinux to provide an additional security checking layer on top of the security checks provided by Halo. From SELinux's perspective, this module allows Halo to act as a userspace object manager.
Every table or function access initiated by DML queries is checked against the system security policy. This checking is performed in addition to Halo's regular permission checks.
SELinux access control decisions are made using security labels, which are represented as strings such as system_u:object_r:sepgsql_table_t:s0. Each access control decision involves two labels: the label of the subject attempting to perform the action, and the label of the object on which the action is being performed. Since these labels can be applied to any kind of object, access control decisions for objects stored in the database (made through this module) follow the same general principles as for any other type of object (such as files). This design is intended to allow a central security policy to protect information assets regardless of how those assets are stored.
The SECURITY LABEL statement allows assigning security labels to database objects.
2. Installation
sepgsql can only be used on Linux 2.6.28 or later with SELinux enabled. This module cannot be used on any other platform. You will also need libselinux 2.1.10 or later and selinux-policy 3.9.13 or later (although some distributions may have backported the necessary rules to older policy versions).
You can check the status of SELinux using the sestatus command. A typical display is:
$ sestatus
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: enforcing
Mode from config file: enforcing
Policy version: 24
Policy from config file: targeted
If SELinux is not installed or enabled, you must install or enable it before installing this module.
3. Regression Tests
Due to the nature of SELinux, running regression tests for sepgsql requires some additional configuration steps, some of which must be performed by root. The regression tests cannot be run through the usual make check or make installcheck commands; you must set up the configuration and then invoke the test script manually. These tests must be run in the contrib/sepgsql directory of a configured Halo build tree. Although they require a build tree, the tests are designed to execute against an installed server, i.e., they are comparable to make installcheck (rather than make check).
First, set up sepgsql in a working database following the instructions in Section C.35.2. Note: The current operating system user must be able to connect to the database as a superuser without password authentication.
Second, compile and install the policy package for the regression tests. The sepgsql-regtest policy is a special policy package that provides a set of rules to be allowed during regression testing. It should be compiled from the policy source file sepgsql-regtest.te using make and a SELinux-provided Makefile. You will need to find the appropriate Makefile on your system; the paths shown below are just examples. (This Makefile is typically provided by the selinux-policy-devel or selinux-policy RPM package.) Once compiled, install the policy package using the semodule command, which loads the provided policy package into the kernel. If the package is correctly installed, semodule -l should list sepgsql-regtest as an available policy package:
$ cd .../contrib/sepgsql
$ make -f /usr/share/selinux/devel/Makefile
$ sudo semodule -u sepgsql-regtest.pp
$ sudo semodule -l | grep sepgsql
sepgsql-regtest 1.07
Third, enable sepgsql_regression_test_mode. For security reasons, the rules in sepgsql-regtest are not enabled by default. The sepgsql_regression_test_mode parameter enables the rules needed to run the regression tests. It can be enabled using the setsebool command:
$ sudo setsebool sepgsql_regression_test_mode on
$ getsebool sepgsql_regression_test_mode
sepgsql_regression_test_mode --> on
Fourth, verify that your shell operates in the unconfined_t domain:
$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
If necessary, refer to Section C.35.8 to adjust your working domain.
Finally, run the regression test script:
$ ./test_sepgsql
This script will attempt to verify that you have correctly completed all configuration steps, then it will run the regression tests for the sepgsql module.
After completing the tests, it is recommended that you disable the sepgsql_regression_test_mode parameter:
$ sudo setsebool sepgsql_regression_test_mode off
You may also want to remove the sepgsql-regtest policy entirely:
$ sudo semodule -r sepgsql-regtest
4. GUC Parameters
sepgsql.permissive (boolean)
Regardless of the system settings, this parameter makes sepgsql run in permissive mode. The default is off. This parameter can only be set in the postgresql.conf file or on the server command line.
When this parameter is on, sepgsql runs in permissive mode even if SELinux is running in enforcing mode. This parameter is primarily used for testing purposes.
sepgsql.debug_audit (boolean)
Regardless of the system policy settings, this parameter enables printing audit messages. The default is off, meaning messages are printed according to system settings.
SELinux's security policy also has rules that control whether specific accesses are logged. By default, denied accesses are logged, but allowed accesses are not.
This parameter forces all possible logging on regardless of the system policy.
5. Features
5.1. Controlled Object Classes
SELinux's security model describes all access control rules as relationships between a subject (typically a database client) and an object (such as a database object), each identified by a security label.
If an attempt is made to access an unlabeled object, the object is assumed to have been assigned the label unlabeled_t.
Currently, sepgsql allows security labels to be assigned to schemas, tables, columns, sequences, views, and functions. When using sepgsql, security labels are automatically assigned to supported database objects when they are created. These labels are called default security labels and are determined based on the system security policy, using the creator's label, the label assigned to the new object's parent, and the optional name of the constructed object.
A new database object basically inherits the security label of its parent object, but when the security policy has special type transition rules, a different label will be applied. For schemas, the parent object is the current database. For tables, sequences, views, and functions, the parent object is the containing schema. For columns, the parent object is the containing table.
5.2. DML Permissions
For tables, depending on the type of statement, all referenced target tables are checked for db_table:select, db_table:insert, db_table:update, or db_table:delete. In addition, db_table:select is checked for all tables whose columns are referenced in WHERE or RETURNING clauses, used as the data source for UPDATE (and other cases).
Column-level permissions are also checked for each referenced column. Not only is db_column:select checked when reading columns with SELECT, but also when columns are referenced in other DML statements. For columns modified by UPDATE or INSERT, db_column:update or db_column:insert is also checked.
For example, consider:
UPDATE t1 SET x = 2, y = func1(y) WHERE z = 100;
Here, db_column:update is checked for t1.x because it is being updated. db_column:{select update} is checked for t1.y because it is both updated and referenced. And db_column:select is checked for t1.z because it is only referenced. db_table:{select update} will also be checked at the table level.
For sequences, db_sequence:get_value is checked when a sequence object is referenced using SELECT. However, the permission to execute corresponding functions (such as lastval()) is currently not checked.
For views, db_view:expand is checked, and then the required permissions are checked individually for any objects expanded from the view.
For functions, db_procedure:{execute} is checked when a user attempts to execute a function in a query or using fast-path invocation. If the function is a trusted procedure, db_procedure: {entrypoint} permission is also checked to determine whether it can be executed as an entry point for a trusted program.
To access any schema object, db_schema:search permission is required on the schema containing it. When referencing an object without a schema qualifier, schemas on which the user does not have this permission will not be searched (as if the user does not have USAGE privilege on that schema). If an explicit schema qualifier is present, an error will occur when the user does not have the required permission on the mentioned schema.
The client must be allowed to access all referenced tables and columns, even if they are derived from view expansion. This ensures consistent access control rules regardless of how table contents are referenced.
The default database privilege system allows database superusers to modify system catalogs using DML commands and to reference or modify TOAST tables. When sepgsql is enabled, these operations are prohibited.
5.3. DDL Permissions
SELinux defines several permissions for each object type to control common operations such as creating, modifying, deleting, and relabeling security labels. Additionally, some object types have special permissions to control their specific operations, such as adding or removing name entries in a particular schema.
Creating a new database object requires create permission. SELinux grants or denies this permission based on the client's security label and proposes a security label for the new object. In some cases, additional privileges are required:
• CREATE DATABASE additionally requires getattr permission on the source database or template database.
• Creating a schema object additionally requires add_name permission on the parent schema.
• Creating a table additionally requires permission to create individual table columns, as if each table column were a separate top-level object.
• Creating a function marked as LEAKPROOF additionally requires install permission (this permission is also checked when setting LEAKPROOF on an existing function).
When executing a DROP command, drop is checked on the object to be removed. Permissions are also checked for objects that are indirectly deleted through CASCADE. Dropping objects contained within a specific schema (tables, views, sequences, and procedures) additionally requires remove_name on that schema.
When executing ALTER commands, setattr is checked on the object being modified for each object type. Dependent objects (such as indexes or triggers on a table) are exceptions; in such cases, permissions are checked on the parent object. In some cases, additional permissions are required:
• Moving an object to a new schema requires remove_name permission on the old schema and add_name permission on the new schema.
• Setting the LEAKPROOF attribute on a function requires install permission.
• Using SECURITY LABEL on an object additionally requires relabelfrom permission on the object with its old security label and relabelto permission with its new security label.
5.4. Trusted Procedures
Trusted procedures are similar to SECURITY DEFINER functions or setuid commands. SELinux provides a feature to allow trusted code to run with a security label different from the client's, typically to provide highly controlled access to sensitive data (for example, rows might be omitted or the precision of stored values might be reduced). Whether a function can serve as a trusted procedure is controlled by its security label and the operating system security policy. For example:
=## CREATE TABLE customer (
cid int primary key,
cname text,
credit text
);
CREATE TABLE
=## SECURITY LABEL ON COLUMN customer.credit
IS 'system_u:object_r:sepgsql_secret_table_t:s0';
SECURITY LABEL
=## CREATE FUNCTION show_credit(int) RETURNS text
AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
FROM customer WHERE cid = $1'
LANGUAGE sql;
CREATE FUNCTION
=## SECURITY LABEL ON FUNCTION show_credit(int)
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
SECURITY LABEL
The above operations should be performed by an administrative user.
=## SELECT * FROM customer;
ERROR: SELinux: security policy violation
=## SELECT cid, cname, show_credit(cid) FROM customer;
cid | cname | show_credit
-----+--------+---------------------
1 | taro | 1111-2222-3333-xxxx
2 | hanako | 5555-6666-7777-xxxx
(2 rows)
In this case, a regular user cannot directly reference customer.credit, but the trusted procedure show_credit allows the user to print the customer's credit card number with some digits masked out.
5.5. Dynamic Domain Transition
If the security policy allows it, SELinux's dynamic domain transition feature can be used to switch the security label of the client process (client domain) to a new context. The client domain needs setcurrent permission and dyntransition permission from the old domain to the new domain.
Dynamic domain transitions need to be carefully considered because, from the user's perspective, they allow users to switch their label and therefore their privileges, rather than being under mandatory system control (as is the case with trusted procedures). Therefore, dyntransition is only considered safe when used to switch to a domain with fewer privileges than the original domain. For example:
select sepgsql_getcon();
sepgsql_getcon
-------------------------------------------------------
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
(1 row)
SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-
s0:c1.c4');
sepgsql_setcon
----------------
t
(1 row)
regression=#SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-
s0:c1.c1023');
ERROR: SELinux: security policy violation
In the example above, we were allowed to switch from the larger range c1.c1023 to the smaller range c1.c4, but switching back was denied.
The combination of dynamic domain transitions and trusted procedures opens up an interesting use case that suits the processing lifecycle of typical connection pool software. Even if your connection pool software is not allowed to run most SQL commands, you can use the sepgsql_setcon() function from a trusted procedure to allow it to switch the client's security label. This procedure should require some evidence to authorize the request to change the client's label. After that, the session will have the target user's privileges instead of the connection pool's. The connection pool can later call sepgsql_setcon() again with a NULL argument to reverse the security label change, but this call must also be made from a trusted procedure with appropriate permission checks. The key point here is that only the trusted procedure actually has the permission to change the effective security label, and it does so only after receiving appropriate evidence. Of course, for secure operation, the evidence store (tables, procedure definitions, or whatever) must be protected from unauthorized access.
5.6. Miscellaneous
We unconditionally deny the LOAD command, since loading any module could easily bypass security policy enforcement.
6. Sepgsql Functions
Table C.29 shows the available functions.
Table C.29. Sepgsql Functions
| Function/Brief |
|---|
| sepgsql_getcon () → text Returns the client domain, i.e., the client's current security label |
| sepgsql_setcon ( text ) → boolean If the security policy allows, switches the current session's client domain to a new domain. It also accepts NULL input as a request to transition to the client's original domain |
| sepgsql_mcstrans_in ( text ) → text If the mcstrans daemon is running, converts the given qualified MLS/MCS range to raw format |
| sepgsql_mcstrans_out ( text ) → text If the mcstrans daemon is running, converts the given raw MLS/MCS range to qualified format |
| sepgsql_restorecon ( text ) → boolean Sets initial security labels for all objects in the current database. The parameter can be NULL or the name of a specification file to use as an alternative to the system default |
7. Limitations
Data Definition Language (DDL) Permissions
Due to implementation limitations, some DDL operations cannot check permissions.
Data Control Language (DCL) Permissions
Due to implementation limitations, DCL operations do not check permissions.
Row-Level Access Control
Halo supports row-level access, but sepgsql does not support row-level access.
Covert Channels
sepgsql does not attempt to hide the existence of a particular object, even if the user is not allowed to reference it. For example, even if we cannot obtain the contents of an invisible object, we can infer its existence through primary key conflicts, foreign key violations, and other results. The existence of a top-secret table cannot be hidden; we can only hope to hide its contents.