SEPostgreSQL Draft

From PostgreSQL wiki
Jump to navigationJump to search

SE-PostgreSQL

Security Enhanced PostgreSQL (SE-PostgreSQL) is an option to apply additional access controls which collaborate with the operating system (SELinux).

This chapter provides a brief overview of SE-PostgreSQL, its features, steps for installation and references to the list of supported permissions.

The Fedora Project provides a comprehensive userguide for SELinux. We recommend that you read the documentation to understand the feature more fully. Fedora Security-Enhanced Linux User Guide

Brief overview

Characteristics

Conceptually, access control is deciding on a set of allowed (or denied) actions between a certain subject (such as database client) and a certain object (such as a table) based on previously defined rules. We can consider an access control feature something like a function which returns a bool value (allowed or denied) for the given subject, object and required actions. However, the way to make this access control decision depends on the type of security features and security model on which they stand.

The native database privilege mechanism checks a pair of the database role and allowed actions on a certain database object (called as ACL: Access Control List) when a user's query tries to access the database object. For example, we can grant SELECT permission to a certain user on a certain table. When the user tries to access the table, the native database privilege mechanism looks up a pair of a database role and allowed actions (SELECT in this case). It then allows the execution of the user's query if the ACL is matched.

The most significant characteristic of SE-PostgreSQL is to additionally enforce any database clients access control rules as managed by SELinux, which is an operating system feature. In other words, it shall correctly implement the access control model of SELinux on database objects, as if they were accesses to file system objects using system calls.

This characteristic leads to a few interesting features from SELinux. One is integration of the security policy as it ensures system-wide consistency in access controls. The other is mandatory access controls. It enables completeness of access controls without any exceptions.

See the following section for more details.

Security model

The way to make access control decisions depends on the type of security features and security model which it depends on.

When a user process tries to access a certain file using a system call, SELinux looks up a suitable entry from the security policy. The security policy is a (massive) set of access control rules. In SELinux, all access control rules are a combination of security context of subject and object, and actions to be allowed. The security context is an identifier of various kind of entities, such as files, sockets, processes, ipc objects and so on. Because the security policy of SELinux stores access control rules for different kinds of objects (e.g, some of them does not have any names, some of them are not persistent, ...), it is necessary to present identifiers to be used for access controls in a common format.

Database objects are not an exception. When SE-PostgreSQL asks SELinux whether the given accesses to a certain database objects should be allowed or not, it passes the security context of the database client and the database object, then it receives an access control decision from SELinux.

Security context

A security context is a colon separated short string in a common format independent from the type of object. The first field means SELinux user, the second is role, the third is type (also called a domain for processes), and the forth is level.

See the Fedora SELinux user guide : Chapter 3. SELinux contexts for more details.

$ ls -Z /etc/hosts
-rw-r--r--. root root system_u:object_r:etc_t:s0       /etc/hosts

This shows the security context of a certain file.

$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0

This shows the security context of the user shell process.

For example, when the above user tries to read /etc/hosts, SELinux looks up a set of allowed actions defined between unconfined_u:unconfined_r:unconfined_t:s0 and system_u:object_r:etc_t:s0. Then, SELinux prevents it if the matched rule does not contain file:{read} permission.

Note that the security context assigned to the process performs as if it were a set of privileges. [KaiGai -- is this correct ? GSW ]

SE-PostgreSQL obtains the security context of the peer process, and applies it as a security context of the database client. It also performs as if a set of privileges to the database objects. [KaiGai -- the last sentence was the same as the last one in the above paragraph; not sure what this means. Do you mean to clarify that SE-PostgreSQL will use the database client as the user in the access control rules, and treats the database object as the equivalent of a OS object / process ?]

The sepgsql_getcon() shows the security context of a database client. It is available on both of local and remote connections. However, it is necessary to set up Labeled IPsec feature to exchange the security context on remote connections. It goes beyond scope of the documentation. See the (TODO: put an external link to set up Labeled IPsec) for more details.

postgres=# SELECT sepgsql_getcon();
              sepgsql_getcon
-------------------------------------------
 unconfined_u:unconfined_r:unconfined_t:s0
(1 row)

On the other hand, SE-PostgreSQL will assign a certain security context on the database objects. We can see them using security_context system column as follows:

The security_context system column is not available in the initial version.

postgres=# SELECT security_context, relname, relkind
                  FROM pg_class WHERE oid = 'drink'::regclass;
           security_context           | relname | relkind
--------------------------------------+---------+---------
 system_u:object_r:sepgsql_table_t:s0 | drink   | r
(1 row)

The combination of all of them are checked in SE-PostgreSQL.

For example, when the above database client tries to update the drink table, SE-PostgreSQL asks SELinux whether the database client labeled as unconfined_u:unconfined_r:unconfined_t:s0 should be allowed to update the table labeled as system_u:object_r:sepgsql_table_t:s0, or not. SELinux also looks up a set of allowed actions defined between them, and replies to SE-PostgreSQL. Then SE-PostgreSQL prevents it, if the matched rule does not contain db_table:{update} permission.

Security policy

The security policy is a massive set of access control rules in SELinux, and a rule defines a set of allowed actions between a pair of security contexts (subject and object). There are three types of rules called TE (Type Enforcement), RBAC (Role Based Access Controls) and MLS (Multi Level Security). The security policy consists of these elements which perform different roles.

A comprehensive explanation of the security policy goes beyond the scope of this documentation. If you would like to understand the security policy in more detail, see the SELinux by example. In most cases, we can apply the default security policy provided by SELinux community as is, which contains various kind of previously defined types. See the The default security policy (This link kept external) for the list of previously defined types.

The TE rules constitute the major portion of the security policy, and it is the most significant element in the SELinux policy. Here, we introduce an examples to see how access control rules are described.

TE rules use the third field in the security context. It is called type or domain (for processes).

allow httpd_t sepgsql_ro_table_t : db_table {getattr select lock};

[KaiGai -- is the above missing a colon ? GSW]

This rule allows a few permissions between the httpd_t domain and the sepgsql_ro_table_t type. Note that this rule does not allow any writer operations such as update and insert. It prevents anything without explicit allowance, because SELinux uses white-list policy. [KaiGai -- this was "while" -- hope my guess is correct ... GSW]

The httpd_t domain is assigned on web server processes or web applications launched from a web server. The sepgsql_ro_table_t is one of the previously defined types for table objects. Here it means the web server process is available for read-only accesses on the tables labeled as sepgsql_ro_table_t using SQL queries, but not writing.

Please note that end users don't need to write their security policy in general. The default security policy provides various kind of previously defined rules, so users can assign them on the database objects.

Object class and permissions

Object class is a terminology which means the type of objects.

SELinux assigns a unique identifier for each object types, such as db_table for table objects, db_procedure for procedure objects and so on. Conventionally these are prefixed with db_* which means any objects with the object class are managed by database management system in the userspace, not the kernel.

Any permissions are associated with a certain object class, because it depends on the type of objects to determine what operations we can perform. For example, we could execute a procedure referencing a certain table, but not be able to execute queries on it directly. [KaiGai -- is this a correct change ? GSW]

All the object classes and permissions associated with SELinux are listed on the References. See the section for more details.

Mandatory access controls

The native database privilege mechanism is called discretionary access control (DAC), because resource owners can set up their access control rules at their discretion using GRANT and REVOKE. In addition, any database superuser can bypass its checks.

On the other hand, SE-PostgreSQL is an implementation of mandatory access controls (MAC) in PostgreSQL, and it performs orthogonally with respect to the native database privilege mechanism. MAC enforces its centralized security policy for all the users (which includes resource owners and database superusers), and does not allow any user to bypass any permission checks, so it is called mandatory.

Here is an example:

SELECT a, b FROM t WHERE c = 'aaa';

This query tries to refer to the table t and columns a and b, and to column c as a part of the WHERE clause.

First, the native database privilege mechanism checks whether the current database role has database superuser privilege, or not. If so, the rest of the standard permission checks are bypassed. Otherwise, it checks SELECT permission on the table t, then it also checks SELECT permission on all the referenced columns, if the SELECT is allowed on the table. In other word, it checks user's privileges on the table or all the referenced columns.

SE-PostgreSQL checks all the required permission individually. If the database client tries to perform as a database superuser, it checks db_database:{superuser} permission on the database. Then, it also checks db_table:{select} on the table t and db_column:{select} on the column a, b and c individually, independent of the access control decision on db_database:{superuser} and db_table:{select}.

This reflects a fundamental principle in SELinux. Its security model makes decisions on whether the user can execute a certain operation on a certain object, or not, based only on the relationship between the user and the object accessed. It is never overridden by any other permissions implicitly, such as db_database:{superuser}. This characteristic is called analyzability of the security policy.

Architecture

When SE-PostgreSQL makes its access control decision, it asks SELinux whether the required accesses should be allowed or not. Since SELinux is a feature in operating system, it causes a context switch which is a heavy operation in generally. However, the access control decisions replied from SELinux have been valid as far as the state of the security policy is unchanged. So, we can cache them to reduce the number of kernel invocations.

SE-PostgreSQL caches the access control decisions recently used. This mechanism is called userspace access vector cache (AVC). When SE-PostgreSQL needs to make its access control decision, it looks up a suitable entry for the required access on the AVC first, then it invokes system call to communicate with SELinux if not found. This design enables to minimize the performance loss due to the communication with external features.

SELinux has a few operational modes; enforcing, permissive and disabled. Enforcing mode checks its security policy and prevent accesses if violated. Permissive mode also checks its security policy, but it does not prevent anything. It enables to collect access denied logs to debug security policy. Disabled mode does not affect any code path, and performs as if here is no SELinux. SE-PostgreSQL also performs according to the SELinux's operational mode.

Features

Interfaces to security context

At the database privilege mechanism, we can use GRANT and REVOKE statement to set up various kind of access privileges on individual database objects. On the other hand, assignment of a certain security context on a database object is equivalent to setting up access privileges on the object in SE-PostgreSQL. The SELinux community provides its default security policy which contains various types of previously defined security context. See the The default security policy for more details.

The following example shows creation of a new table labeled as unconfined_u:object_r:sepgsql_ro_table_t:s0.

postgres=# CREATE TABLE t1 (
               a int
           ) SECURITY_CONTEXT = 'unconfined_u:object_r:sepgsql_ro_table_t:s0';
CREATE TABLE
postgres=# SELECT security_context, relname, relkind
                  FROM pg_class WHERE oid = 't1'::regclass;
              security_context               | relname | relkind
---------------------------------------------+---------+---------
 unconfined_u:object_r:sepgsql_ro_table_t:s0 | t1      | r
(1 row)

Focus on the third field of the security context, called as type. The sepgsql_ro_table_t means read-only tables for confined domains, such as web server processes. (See the Fedora SELinux user guide : Chapter.4 Targeted Policy for confined and unconfined domains.)

The SECURITY_CONTEXT is optional. When user tries to create a database object without any explicit security context, SE-PostgreSQL assigns a default security context provided by the security policy. Anywat, it assigns a certain security context on a managed database object.

The following example shows creation of a new table with a default security context: unconfined_u:object_r:sepgsql_table_t:s0 which allows anything expect for relabeling.

postgres=# CREATE TABLE t2 (
               a int
           );
CREATE TABLE
postgres=# SELECT security_context, relname, relkind
                  FROM pg_class WHERE oid = 't2'::regclass;
             security_context             | relname | relkind
------------------------------------------+---------+---------
 unconfined_u:object_r:sepgsql_table_t:s0 | t2      | r
(1 row)

The SECURITY_CONTEXT option is supported on various kind of CREATE and ALTER statement more than CREATE TABLE statement.

See the SQL command references for more details: CREATE DATABASE, ALTER DATABSE, CREATE TABLESPACE, ALTER TABLESPACE, CREATE SCHEMA, ALTER SCHEMA, CREATE TABLE, ALTER TABLE, CREATE FUNCTION, ALTER FUNCTION, CREATE SEQUENCE and ALTER SEQUENCE.

Using security_context system column

This is not supported in the initial version.

We can also set up security context of individual tuples using security_context system column which is writable and accepts inputs with TEXT type.

The following example shows a case when we insert a new tuple with an explicit security context.

postgres=# INSERT INTO drink (security_context, id, name, price)     \
               VALUES ('unconfined_u:object_r:sepgsql_table_t:s0:c0', 1, 'green tea', 150);
INSERT 16440 1
postgres=# SELECT security_context, * FROM drink;
              security_context               | id |   name    | price
---------------------------------------------+----+-----------+-------
 unconfined_u:object_r:sepgsql_table_t:s0:c0 |  1 | green tea |   150
(1 row)

The following example shows a case when we update tuples with user given security context. It can be dynamically computed as far as it has an appropriate format and user has enough privileges to change them.

postgres=# UPDATE drink SET security_context = 'unconfined_u:object_r:sepgsql_table_t:s0:c' || id;
UPDATE 1
postgres=# SELECT security_context, * FROM drink;
              security_context               | id |   name    | price
---------------------------------------------+----+-----------+-------
 unconfined_u:object_r:sepgsql_table_t:s0:c1 |  1 | green tea |   150
(1 row)

If the target list of SELECT INTO contains an attribute named as security_context, its contents used to provide an explicit security context for the newly inserted tuples.

postgres=# SELECT 'unconfined_u:object_r:sepgsql_ro_table_t:s0:c' || id AS security_context, * INTO t FROM drink;
SELECT
postgres=# SELECT security_context, * FROM t;
                security_context                | id |   name    | price
------------------------------------------------+----+-----------+-------
 unconfined_u:object_r:sepgsql_ro_table_t:s0:c1 |  1 | green tea |   100
 unconfined_u:object_r:sepgsql_ro_table_t:s0:c2 |  2 | coke      |   120
 unconfined_u:object_r:sepgsql_ro_table_t:s0:c3 |  3 | juice     |   130
 unconfined_u:object_r:sepgsql_ro_table_t:s0:c4 |  4 | cofee     |   180
(4 rows)

Access controls

The core PostgreSQL invokes SE-PostgreSQL routines using various kind of security hooks deployed on strategic points. The security hooks give SE-PostgreSQL necessary information to make its access control decision, then it returns a certain status which includes raising an error using ereport(). When SE-PostgreSQL is disabled, these security hooks do nothing or always return the status for success not to prevent anything in the regular code paths.

The way to return its status depends on object classes, permissions and the context on which security hooks are invoked.

See the following example:

SELECT name, price FROM drink;

This query refers the name and price columns within the drink table. In addition, it also resolves the public schema which contains the drink table.

In this case, SE-PostgreSQL raises an error, if the db_table:{select} is not allowed on the the drink table or the db_column:{select} is not allowed on the name or price columns.

On the other hand, if the db_schema:{search} is not allowed on the public schema, it suggests to skip the schema from the active schema search path, but does not raise an error.

In actually, most of permission checks raises an error, if required accesses are not allowed. Several exceptions are the db_database:{superuser}, db_schema:{search} and a few permissions corresponding to the row-level access controls.

If the db_database:{superuser} is not allowed, the database client performs as an unprivileged database role, even if the native database privilege mechanism allows him to perform as a database superuser.

Row-level access controls

This is not supported in the initial version.

The row-level access controls perform as if a filter which dropps any violated tuples on scanning the target tables.

See the following example:

postgres=# SELECT security_context, * FROM drink;
                security_context                | id | name  | price
------------------------------------------------+----+-------+-------
 system_u:object_r:sepgsql_table_t:Unclassified |  1 | water |   100
 system_u:object_r:sepgsql_table_t:Unclassified |  2 | coke  |   120
 system_u:object_r:sepgsql_table_t:Classified   |  3 | beer  |   240
 system_u:object_r:sepgsql_table_t:Classified   |  4 | wine  |   380
(4 rows)

If the database client can see the tuples labeled as Unclassified and Classified, it returns four tuples.

On the other hand, if the database client can see only Unclassified, any tuples labeled as Classified are filtered from the result set.

postgres=# SELECT security_context, * FROM drink;
                security_context                | id | name  | price
------------------------------------------------+----+-------+-------
 system_u:object_r:sepgsql_table_t:Unclassified |  1 | water |   100
 system_u:object_r:sepgsql_table_t:Unclassified |  2 | coke  |   120
(2 rows)

It is also applied on UPDATE and DELETE statement on scanning the target tables, not only SELECT statement. SE-PostgreSQL makes its access control decision on the fetched tuples prior to evaluation of user given conditions using WHERE clause, because user can give malicious functions which leaks its arguments to others. (Note that we assume index access methods are trusted, so here is no matter that row-level access controls are applied after the index accesses.)

The INSERT statement does not scan on the target tables, so it is not available to implement it as a filter. If user tries to insert a tuple with a certain security context, SE-PostgreSQL shall raise an error on violations.

When user tries to relabel security context using UPDATE statement, it shall also raise an error on violations to db_tuple:{relabelfrom relabelto} permissions. Because it is necessary to check the permissions after all the before-row-triggers, SE-PostgreSQL cannot make its decision on the scanning time.

The foreign-key constraints are implemented as trigger functions, and it launches the secondary queries to keep referencial integrity. In this case, it is undesirable to ignore invisible tuple, because it may refer the tuple which user tries to update. Thus, SE-PostgreSQL switches the behavior on row-level access controls under the foreign-key constraint checks. It raises an error on access violation in this case.

Trusted Procedure

The idea of trusted procedure enables to switch the security context of the database client temporary, as if the database privilege mechanism provides security definer functions.

The security definer functions switches an effective database role during execution of the function, and it affects access control decisions by the database privilege mechanism. In a similar manner, the trusted procedures switches security context of the database client during execution of the function. Please remind the security context of the database client is equivalent to a set of privileges for mandatory access controls.

At the default security policy, sepgsql_trusted_proc_exec_t is the only previously-defined type as a trusted procedure. It enables to provide a secure method to access invisible or read-only data.

For example, when the customer table is defined as follows, unprivileged users cannot see the contents of customer.credit because it is labeled as sepgsql_secret_table_t which means all the accesses are denied from confined domains.

CREATE TABLE customer (
    cid     integer primary key,
    cname   varchar(32),
    credit  varchar(32)
        SECURITY_CONTEXT = 'system_u:object_r:sepgsql_secret_table_t:s0'
);

Because the customer.credit stores credit card numbers of customers, we don't want to expose them web applications. But online shopping application needs to show a part of numbers users to confirm it. In this case, we can declare a trusted procedure for the purpose as follows:

CREATE OR REPLACE FUNCTION show_credit (integer) RETURNS text
LANGUAGE 'sql'
SECURITY_CONTEXT = 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0'
AS 'SELECT regexp_replace(credit, -[0-9]+, -xxxx, g) FROM customer WHERE cid = $1';

It performs as follows:

postgres=# SELECT cid, cname FROM customer;
 cid | cname
-----+-------
  10 | jack
  13 | adam
  14 | liza
(3 rows)

postgres=# SELECT cid, cname, credit FROM customer;
ERROR:  SELinux: security policy violation

Column level access control prevents confined database client to access customer.credit. But, the security context of the database client is temporary switched during execution of the show_credit(), and it returns the secret data partially hidden.

postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
 cid | cname |     show_credit
-----+-------+---------------------
  10 | jack  | 1111-xxxx-xxxx-xxxx
  13 | adam  | 5555-xxxx-xxxx-xxxx
  14 | liza  | 9876-xxxx-xxxx-xxxx
(3 rows)

Audit logs

When SE-PostgreSQL prevents user's query due to the access violation, it generates audit logs as follows:

LOG:  SELinux: denied { select } scontext=unconfined_u:unconfined_r:httpd_t:s0  \
                      tcontext=system_u:object_r:sepgsql_secret_table_t:s0      \
                      tclass=db_column name=customer.credit
STATEMENT:  select * from customer;

It records a fact that a database client with unconfined_u:unconfined_r:httpd_t:s0 tries to refer the customer.credit column labeled with system_u:object_r:sepgsql_secret_table_t:s0, but failed.

The security policy provides two kind of rules to control generation of audit logs. One is auditdeny rule to generate audit logs on access denied events. The other is auditallow rule to generate it on access allowed events. In the default, all the access denied events are recorded, and others are not recorded.

In addition, the default security policy also turned off access denied logs on db_tuple object class to suppress flood of audit logs, because a user's query can access large number of tuples at one time.

Restrictions

Please be aware that a few restrictions when using SE-PostgreSQL.

It applies a few hardwired rules on accesses to system catalogs and toast relations. It prevents to modify system catalogs using general DML statements (INSERT, UPDATE and DELETE), so users need to use regular DDL statements instead. It also prevents to access toast relations using general DML statements (including SELECT), so only internal stuffs can access them.

It needs to disable several kind of optimization for the security reason. A SQL function declared with SQL language can be flatten and inlined by optimizer, but SE-PostgreSQL restrains this kind of optimization, if the function is labeled as trusted procedure.

(TODO: Put here any other cases need to disable optimizations)

Interactions between row-level access controls and unique or foreign key constrains may give a hint for existence of invisible tuples, although user cannot see the data itself. For example, when we tries to insert a new tuple with duplicated key value to an invisible tuple, it will raise an error, so we can know the existence of invisible tuples. Such kind of irregular information flows are called covert channels. It is also mentioned in the common criteria, but not required except for an extream environment in use. Note that SE-PostgreSQL does not care about elimination of covert channels.

Installation

This section introduces the steps to install SE-PostgreSQL.

System requirements

The following packages are necessary to install SE-PostgreSQL.

  • Linux kernel 2.6.24 or later, SELinux enabled
  • libselinux 2.0.80 or later
  • Reference policy x.y.z or later
    • (TODO: replace version number which includes all the new object classes and permissions.)

In addition, the following packages are also necessary, if you also installs development purpose policy.

  • policycoreutils 2.0.16 or later
  • checkpolicy 2.0.19 or later

Build and installation

You need to add --enable-selinux option at the configure script to build PostgreSQL binary with SE-PostgreSQL support.

$ ./configure --enable-selinux

Then, make it in normally. Here, we assume database cluster will be set up on the /opt/sepgsql.

$ make
$ su
# make install
# mkdir -p /opt/sepgsql
# chown sepgsql:users -R /opt/sepgsql

If you want to run testcases, it is also necessary to build a security policy module. The sepostgresql-devel.pp will be built.

$ make -C src/backend/security/sepgsql/policy
$ su
# semodule -i src/backend/security/sepgsql/policy/sepostgresql-devel.pp

SELinux requires any installed files to be labeled correctly. You need to assign appropriate security context on them.

# semanage fcontext -a -t usr_t '/usr/local/pgsql(/.*)?'
# semanage fcontext -a -t bin_t '/usr/local/pgsql/bin(/.*)?'
# semanage fcontext -a -t lib_t '/usr/local/pgsql/lib(/.*)?'
# semanage fcontext -a -t postgresql_exec_t '/usr/local/pgsql/bin/postgres'
# semanage fcontext -a -t postgresql_exec_t '/usr/local/pgsql/bin/initdb'
# semanage fcontext -a -t initrc_exec_t '/usr/local/pgsql/bin/pg_ctl'
# /sbin/restorecon -R /usr/local/pgsql
# semanage fcontext -a -t postgresql_db_t '/opt/sepgsql(/.*)?'
# /sbin/restorecon -R /opt/sepgsql

At the last, run initdb with --enable-selinux option.

$ /usr/local/pgsql/bin/initdb --enable-selinux -D /opt/sepgsql

Then, use the /usr/local/pgsql/bin/pg_ctl, because the default security policy switches the security context of the server process correctly.

$ /usr/local/pgsql/bin/pg_ctl -D /opt/sepgsql start

If you collect server logs, the log files need to be labeled as postgresql_log_t.

$ touch /opt/sepgsql/sepostgresql.log
$ chcon -t postgresql_log_t /opt/sepgsql/sepostgresql.log
$ pg_ctl -l /opt/sepgsql/sepostgresql.log -D /opt/sepgsql start

References

Object classes and permissions

This section provides a comprehensive reference to object classes and corresponding permissions.

Object class means a certain type of objects, and any permissions are characteristic for a certain object class. So, we describe a certain pair of an object class and permissions as follows:

<object class>:{<permission1> [<permission2> ...] }

Object classes

Now object classes are defined on the following database objects.

  • Databases
  • Tablespaces
  • Schamas (both of persistent and temporary)
  • Tables
  • Columns
  • Tuples (This is not supported in the initial version.)
  • Sequence
  • Procedure
  • Largeobejcts (This is not supported in the initial version.)

All the object classes has its characteristic name prefixed with db_* according to the SELinux's naming convention, such as db_table. It makes clear this type of objects are managed by userspace application, not a kernel object.

Common permissions

This six permissions are common for database objects. It reflects a fundamental principle in SELinux, how object should be handled on creation, deletion, alteration and relabeling.

create
The db_xxx:{create} permission shall be checked on a new database object labeled when user's query tries to create a new database object.
SELinux requires any new objects to be labeled on their creation time. If user provides an explicit security context (using SECURITY_CONTEXT option with CREATE statement for example), it shall be assigned on the new object. Otherwise, a default security context shall be assigned on the new object. The default security context is provided by the security policy, and it is computed based on the security context of database client, the security context of upper object and its object class.
Note that the db_xxx:{create} permission shall be checked on the new object itself with its security context, independent from the way to give it on the new object.
In the database privilege mechanism, a privilege to be checked on the creation of database objects depends on object type. It checks CREATEDB privilege on the database role for a new database, it checks superuser privilege for a new tablespace, and it checks CREATE privilege on the schema object if a new object belongs to a certain schema (such as tables, procedures, ...).
drop
The db_xxx:{drop} permission shall be checked on the database object when user's query tries to drop certain database objects. It is also checked for all the candidates to be dropped, even if it is a case of cascaded drop.
In the database privilege mechanism, it checks ownership of the database objects on its deletion.
getattr
This is not supported in the initial version.
The db_xxx:{getattr} permission shall be checked on the individual database object when user's query refers system catalogs which represents a certain database obejct.
This check is implemented as row-level access controls, so database objects in violation shall be filtered out from the result set of user's SELECT.
setattr
The db_xxx:{setattr} permission shall be checked on the database obejct when user's query tries to update its attributes. It is done ALTER statement typically, but not all. For example, CREATE OR REPLACE FUNCTION statement enables to replace definition of the existing function. In this case, it also should be checked rather than db_xxx:{create}, because the query creates no new object.
In the database privilege mechanism, it checks ownership of the database object on its alteration.
relabelfrom
The db_xxx:{relabelfrom} permission shall be checked on the database object with older security context, when user tries to change the security context of a certain database object. Note that db_xxx:{setattr} is also checked in this case.
relabelto
The db_xxx:{relabelto} permission shall be checked on the database object with newer security context, when user tries to change the security context of a certain database object.

Database permissions

The db_database object class is assigned on database objects. It inherits six common permissions, and defines three characteristic permissions.

access
The db_database:{access} permission shall be checked on the database, when the client open connection to the database.
In the database privilege mechanism, it is equivalent to the CONNECT on databases, except for it bypasses checks for database superusers.
load_module
This is not supported in the initial version.
The db_database:{load_module} permission shall be checked when user's query makes the backend to load a certain external dynamic link library, between the current database and the library file itself. It means this permission checks the capability of the database to load a certain binary module, not the client's privilege.
Please note that it does not control shared_preload_libraries and local_preload_libraries.
superuser
The db_database:{superuser} permission shall be checked on the database, when the client performs as database superuser.
Violations of the permission does not raise an error. In this case, the client performs as an unprivileged database role.
In the database privilege mechanism, it is equivalent to the database superuser privilege needless to say.

Tablespace permissions

The db_tablespace object class assigned on tablespace objects. It inherits six common permissions, and defines one characteristic permission.

createon
The db_tablespace:{createon} permission shall be checked on the target tablespace, when user's query tries to create a new database object on the tablespace except for database's default one.
In the database privilege mechanism, it is equivalent to CREATE on tablespaces.

Schema permissions

Either db_schema or db_schema_temp object classes are assigned on schema objects. Both of them inherit six common permissions, and defines three characteristic and identical permissions.

The reason why different object class is assigned on temporary schema is to provide a characteristic default security context for temporary database objects, different from persistent ones.

search
The db_schema:{search} permission shall be checked on the target schema, when user's query tries to search database objects within a certain schema object.
Violation of the permission does not raise an error. In this case, the backend drops the violated schema from the schema search path.
In the database privilege mechanism, it is equivalent to USAGE on namespaces.
add_name
This is not supported in the initial version.
The db_schema:{add_name} permission shall be checked on the target schema, when user's query tries to add a database object to a certain schema, tries to rename a database object within a certain schema, or tries to change the schema of database object. At the last case, it is checked on the newer schema.
remove_name
This is not supported in the initial version.
The db_schema:{remove_name} permission shall be checked on the target schema, when user's query tries to remove a database object from a certain schema, tries to rename a database object within a certain schema, or tries to change the schema of database object. At the last case, it is checked on the older schema.

Table permissions

The db_table object class is assigned on table objects, which means any tuples within pg_class system catalog and its relkind equals to RELKIND_RELATION. It inherits six common permissions, and defines five characteristic permissions.

select
The db_table:{select} permission shall be checked on the tables which user's query tries to refer using SELECT, COPY TO and any other kind of references such as WHERE or RETURNING clause with UPDATE statement.
The target tables to be checked are equivalent to the ones picked up by the database privilege mechanism. Please note that SELinux also needs to check db_column:{select} permission on all the columns to be refered, not only tables.
update
The db_table:{update} permission shall be checked on the tables which user's query tries to update using UPDATE.
Please note that SELinux also needs to check db_column:{update} permission on all the columns to be updated, not only tables.
insert
The db_table:{insert} permission shall be checked on the tables which user's query tries to insert using INSERT, SELECT INTO or COPY FROM.
Please note that SELinux also needs to check db_column:{delete} permission on all the columns to be inserted, not only tables.
delete
The db_table:{delete} permission shall be checked on the tables which user's query tries to delete using DELETE or TRUNCATE.
lock
The db_table:{lock} permission shall be checked on the tables which user's query tries to acquire explicit table locks using LOCK or SELECT ... FOR SHARE/UPDATE. Please note that it does not checked on the implicit table locks.

Column permissions

The db_column object class is assigned on column objects, which means any tuples within pg_attribute system catalog and its attrelid points to a certain table. It inherits six common permissions, and defines three characteristic permissions.

select
The db_column:{select} permission shall be checked on the columns which user's query tries to refer using SELECT, COPY TO and any other kind of references such as WHERE or RETURNING clause with UPDATE statement.
The target columns to be checked are equivalent to the ones picked up by the database privilege mechanism. Please note that SELinux also needs to check db_table:{select} on the tables, not only columns.
update
The db_column:{update} permission shall be checked on the columns which user's query tries to update using UPDATE.
Please note that SELinux also needs to check db_table:{update} on the tables, not only columns.
insert
The db_column:{insert} permission shall be checked on the columns which user's query tries to insert using INSERT, SELECT INTO or COPY FROM.
At the case when the client is not allowed to insert an explicit value on a certain column, a default value (or NULL if not configured) shall be applied on the column.
Please note that SELinux also needs to check db_table:{insert} on the tables, not only columns.

Tuple permissions

This is not supported in the initial version.

The db_tuple object class is assigned on tuple objects, which means any tuples within regular tables (including system catalogs) and are not associated with a certain other object class. It has six permissions equivalent to the common permissions, except for its names.

select
The db_tuple:{select} permission shall be checked on the tuples which user's query tries to refer using SELECT, COPY TO and any other kind of references such as WHERE or RETURNING clause with UPDATE statement.
It is an equivalent permission to db_xxx:{getattr} in other object class.
update
The db_tuple:{update} permission shall be checked on the tuples which user's query tries to update using UPDATE.
It is an equivalent permission to db_xxx:{setattr} in other object class.
insert
The db_tuple:{insert} permission shall be checked on the tuples which user's query tries to insert using INSERT, SELECT INTO or COPY FROM.
It is an equivalent permission to db_xxx:{create} in other object class. A default security context shall be assigned on the new tupls, if no explicit security context given. Then, it is checked on the tuples labeled.
delete
The db_tuple:{delete} permission shall be checked on the tuples which user's query tries to delete using DELETE or TRUNCATE.
It is an equivalent permission to db_xxx:{drop} in other object class.
relabelfrom
It is an equivalent permission to db_xxx:{relabelfrom} in other object class.
relabelto
It is an equivalent permission to db_xxx:{relabelto} in other object class.

Sequence permissions

The db_sequence object class is assigned on table objects, which means any tuples within pg_class system catalog and its relkind equals to RELKIND_SEQUENCE. It inherits six common permissions, and defines three characteristic permissions.

get_value
The db_sequence:{get_value} shall be checked on the sequence object, when user's query tries to refer it without any changes.
In the database privilege mechanism, it is equivalent to SELECT or USAGE on sequences.
next_value
The db_sequence:{next_value} shall be checked on the sequence object, when user's query tries to refer it with an increment.
In the database privilege mechanism, it is equivalent to UPDATE or USAGE on sequences.
set_value
The db_sequence:{set_value} shall be checked on the sequence object, when user's query tries to set a discretionary value.
In the database privilege mechanism, it is equivalent to UPDATE or USAGE on sequences.

Procedure permissions

The db_procedure object class is assigned on procedure objects. It inherits six common permissions, and defines four characteristic permissions.

execute
The db_procedure:{execute} shall be checked on the procedure object, when user's query tries to execute a procedure and an operator implemented with the procedure.
In the database privilege mechanism, it is equivalent to EXECUTE on procedures.
entrypoint
The db_procedure:{entrypoint} shall be checked on the procedure object, when user's query tries to invoke trusted procedure, in addition to db_procedure:{execute}.
install
This is not supported in the initial version.
The db_procedure:{install} shall be checked on the procedure object, when user's query tries to define a database object which can internally invoke user defined functions without db_procedure:{execute} checks.
In particular, type input/output handlers, cast functions, conversion functions, foreign data wrapper validator, trigger functions and tsearch parser/template functions.
untrusted
This is not supported in the initial version.
The db_procedure:{untrusted} shall be checked on the procedure object, when user's query tries to create a new one or replace its definition with untrusted language, in addition to db_procedure:{create} or db_procedure:{setattr}.
(TODO: Language objects should be also controlled using SELinux later.)
In the database privilege mechanism, it is equivalent to superuser privilege.

Largeobject permissions

This is not supported in the initial version.

The db_blob object class is assigned on largeobjects. It inherits six common permissions, and defines four characteristic permissions. In the database privilege mechanism, it does not provide access controls on largeobjects now.

read
The db_blob:{read} permission shall be checked on the largeobejct, when user's query tries to read contents of the largeobject using loread() or lo_export().
write
The db_blob:{write} permission shall be checked on the largeobject, when user's query tries to write contents of the largeobject using lowrite(), lo_truncate() or lo_import().
import
The db_blob:{import} permission shall be checked on the largeobject, when user's query tries to import a certain filesystem object to the largeobject newly created.
Please note that db_blob:{write} and file:{read} on the source file shall be also checked.
export
The db_blob:{export} permission shall be checked on the largeobject, when user's query tries to export the largeobject to a certain filesystem object.
Please note that db_blob:{read} and file:{write} on the destination file shall be also checked.

Filesystem interactions

This is not supported in the initial version.

PostgreSQL allows users to access server side filesystem using a few interfaces.

read
It is checked when user's query tries to read a certain file on the server side filesystem.
Note that SELinux checks file:{read} permission to the pair of PostgreSQL server process and the target file, not database client.
In particular, this permission is checked on COPY FROM filename statement, lo_import() function and pg_read_file() function.
write
It is checked when user's query tries to write a certain file on the server side filesystem.
Note that SELinux checks file:{write} permission to the pair of PostgreSQL server process and the target file, not database client.
In particular, this permission is checked on COPY TO filename statement and lo_export() function.

SQL command reference

This chapter is a draft to be added on the SQL command reference.

CREATE DATABASE

CREATE DATABASE name
    [ [ WITH ] [ OWNER [=] dbowner ]
         [ TEMPLATE [=] template ]
         [ ENCODING [=] encoding ]
         [ LC_COLLATE [=] lc_collate ]
         [ LC_CTYPE [=] lc_ctype ]
         [ TABLESPACE [=] tablespace ]
         [ CONNECTION LIMIT [=] connlimit ] ]
         [ SECURITY_CONTEXT [=] security_context ]
security_context
The security context to be assigned on the newly created database.
It is only available when SE-PostgreSQL is enabled.

ALTER DATABASE

ALTER DATABASE name SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the database.
It is only available when SE-PostgreSQL is enabled.

CREATE TABLESPACE

CREATE TABLESPACE tablespacename [ OWNER username ]
[ SECURITY_CONTEXT security_context ] LOCATION 'directory'
security_context
The security context to be assigned on the newly created tablespace.
It is only available when SE-PostgreSQL is enabled.

ALTER TABLESPACE

ALTER TABLESPACE name SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the tablespace.
It is only available when SE-PostgreSQL is enabled.

CREATE SCHEMA

CREATE SCHEMA schemaname [ AUTHORIZATION username ]
    [ SECURITY_CONTEXT security_context ] [ schema_element [ ... ] ]
CREATE SCHEMA AUTHORIZATION username
    [ SECURITY_CONTEXT security_context ] [ schema_element [ ... ] ]
security_context
The security context to be assigned on the newly created schema.
It is only available when SE-PostgreSQL is enabled.

ALTER SCHEMA

ALTER SCHEMA name SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the schema.
It is only available when SE-PostgreSQL is enabled.

CREATE TABLE

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( [
  { column_name data_type [ DEFAULT default_expr ] [ column_constraint [ ... ] ] [ SECURITY_CONTEXT column_context ]
    | table_constraint
    | LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }
    [, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace ]
[ SECURITY_CONTEXT table_context ]
column_context
The security context to be assigned on the newly created column.
It is only available when SE-PostgreSQL is enabled.
table_context
The security context to be assigned on the newly created table.
It is only available when SE-PostgreSQL is enabled.

ALTER TABLE

ALTER TABLE name SECURITY_CONTEXT TO security_context
ALTER TABLE name ALTER [ COLUMN ] column SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the table or column.
It is only available when SE-PostgreSQL is enabled.

CREATE FUNCTION

CREATE [ OR REPLACE ] FUNCTION
    name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } defexpr ] [, ...] ] )
    [ RETURNS rettype
      | RETURNS TABLE ( colname coltype [, ...] ) ]
  { LANGUAGE langname
    | WINDOW
    | IMMUTABLE | STABLE | VOLATILE
    | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
    | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    | COST execution_cost
    | ROWS result_rows
    | SET configuration_parameter { TO value | = value | FROM CURRENT }
    | AS 'definition'
    | AS 'obj_file', 'link_symbol'
    | SECURITY_CONTEXT security_context
  } ...
    [ WITH ( attribute [, ...] ) ]
security_context
The security context to be assigned on the newly created function.
It is only available when SE-PostgreSQL is enabled.

ALTER FUNCTION

ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] )
    SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the function.
It is only available when SE-PostgreSQL is enabled.

CREATE SEQUENCE

CREATE [ TEMPORARY | TEMP ] SEQUENCE name [ INCREMENT [ BY ] increment ]
    [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
    [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { table.column | NONE } ]
    [ SECURITY_CONTEXT security_context ]
security_context
The security context to be assigned on the newly created sequence.
It is only available when SE-PostgreSQL is enabled.

ALTER SEQUENCE

ALTER SEQUENCE name SECURITY_CONTEXT TO security_context
security_context
The security context to be relabeled on the sequence.
It is only available when SE-PostgreSQL is enabled.

PostgreSQL client application

This chapter is a draft to be added on the PostgreSQL client applications.

pg_dump

description

When SE-PostgreSQL is available, it is necessary to invoke pg_dump with enough privileges set to access all the database objects to be dumped. The default security policy provides a previously-defined security context which is available to dump whole of the database, called as unconfined domain. Please confirm whether your shell runs with unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023, or not, in this case. (It may be shown as unconfined_u:unconfined_r:unconfined_t:SystemLow-SystemHigh when mcstransd daemon running.)

You can confirm the security context of shell process as follows:

$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

options

--security-context
It enables to dump security context of database objects as a part of data definitions and data itself. Use this option, if SE-PostgreSQL is available.

pg_dumpall

--security-context
It enables to dump security context of database objects as a part of data definitions and data itself. Use this option, if SE-PostgreSQL is available.