SEPostgreSQL
From PostgreSQL Wiki
Please note this article is now just a draft state.
This article provides comprehensive documentation for Security-Enhanced PostgreSQL (SE-PostgreSQL). SE-PostgreSQL is a built-in enhancement of PostgreSQL, providing additional access controls based on Security-Enhanced Linux (SELinux) security policy. The following chapters show the background, overview, design, and references (object classes, permissions, etc.) for SE-PostgreSQL.
Contents
|
Introduction
This chapter shows the overview and target of SE-PostgreSQL.
What is SE-PostgreSQL
Databases are significant facilities for managing information assets. Databases enable information to be searched for, retrieved, and stored in more elegant ways than with file systems.
Most existing RDBMSs apply their own access controls, for example, GRANT and REVOKE, without collaborating with operating system. This can result in inconsistent access controls compared to ones on filesystem and so on.
Some modern operating systems have enhanced access control features, such as SELinux. The design of most of these features is based upon the reference monitor model (which came from 1980's researches), that allows all system access to be managed by a centralized security policy. The reference monitor model assumes that object managers (for example, operating systems) can capture all system access, and then make decisions about whether that access is allowed or denied. Note: an operating systems is not the object manager in all cases. A Relational Database Management System (RDBMS) is an object manager for database objects, similar to operating systems being object managers for file system objects. Previously, RDBMSs made access control decisions independently from a centralized security policy, and as such, meticulous care to keep its consistency between OS and RDBMS.
SE-PostgreSQL is a built-in enhancement of PostgreSQL, providing fine-grained mandatory access control (MAC) for database objects. SE-PostgreSQL makes access control decisions based on SELinux security policy, the same way user access to file system objects is managed by the operating system. It provides the following significant features:
- Mandatory access controls
PostgreSQL uses the concept of a database superuser that can bypass all access controls of native PostgreSQL. On the contrary, SE-PostgreSQL enforces its access control on any client, without exception, even if the client is a database superuser. Clients can only access database objects when access is allowed by both native PostgreSQL and SE-PostgreSQL, similar to the relationship between Discretionary Access Control (DAC) and Mandatory Access Control (MAC) on certain operating systems.
- Fine-grained access controls
Granularity of access control depends on the RDBMS. SE-PostgreSQL allows access control to be configured at the column and row levels (only a few commercial RDBMSs support column and row level access-control options).
Columns and rows are the smallest unit of database objects. Access control on these objects allows database administrators to apply flexible access control on them.
- Consistency in access controls
SELinux requires all processes and objects to have a security context that represents its privileges and attributes. SE-PostgreSQL assigns a security context to each database tuple (rows stored in tables), and makes access control decisions based on the privileges and attributes as if SELinux applies them in the kernel.
libselinux provides an interesting API named getpeercon(), that enables you to get the security context of a peer process connecting to a server process. SE-PostgreSQL uses security contexts to make decisions. This is a significant feature for consistency in access controls, in that privileges for clients and the attributes for objects are represented in the same form.
Why we need consistency in access controls
Consider RDBMSs (including PostgreSQL) as an inter-process communication channel, the same as file systems, networks, IPC, and so on.
The most significant purpose for security mechanisms is to protect classified information being leaked. On subsystems with individual access controls -- not a centralized one -- an attacker can choose the weakest inter-process communication channel.
SELinux is an operating system feature that applies a single, unified security policy over the whole operating system. System calls are intercepted and inspected. This approach allows policy to be enforced with minimal costs; however, keep in mind that several user-space object managers, such as the X Window System and RDBMSs, can be used as inter-process communication channels. Resources managed in user-space are invisible to the kernel, leaving two options for security policy: the traditional all-or-nothing policy, that does not allow user-space objects to be shared (this is unrealistic), or the enhancement of user-space object managers, making their access control decisions based on the single, unified security policy of SELinux.
The above figure demonstrates consistent data-flow controls. It requires that classified information (Higher Sensitivity Level Domain) does not leak to unclassified domains (Lower Sensitivity Level Domain) via inter-process communication channels, whether those channels are networks, RDBMSs, and so on.
In this example, when a user with clearance logs in, their user process is labeled with SystemHigh, allowing them access to secret information labeled with Secret. In contrast, when a user without clearance logs in, their user process is labeled with SystemLow, preventing them access to secret information labeled with Secret. In addition, SystemHigh processes are prohibited from sending messages to SystemLow processes. These restrictions are independent from the type of inter-process communication channel being used (restrictions apply to the whole operating system).
In accordance to security policy, SELinux prevents user processes without clearance from reading secret information stored in objects managed by the Linux kernel, as well as preventing user processes with clearance from writing secret information to unclassified objects.
Architecture
This chapter introduces the SE-PostgreSQL architecture.
Interactions between subsystems
SE-PostgreSQL is a built-in enhancement of PostgreSQL, providing features based on the reference monitor model. When an action (such as a client query) is invoked in SE-PostgreSQL, it asks SELinux whether the action should be allowed or denied. SE-PostgreSQL then applies the decision made by SELinux, meaning that, SELinux indirectly controls access in user-space.
For example, when a given query requires to update column A and B of table T, SE-PostgreSQL asks SELinux whether the client is able to update the table table, columns and tuples, or not.
At first, the client sends SQL queries written in text format. It invokes all the following query processing chains. The given queries are delivered to the SQL parser, analyzer and rewriter. The parser splits up the given queries in text format to tree structured tokens. Then, the tokens are delivered into the analyzer to resolve identifier of object names and to confirm its accessibility. The analyzer reorganizes them to the common data format called as Query tree. If the given queries use views, the query rewriter expand them into raw expression described by SELECT statement typically.
SE-PostgreSQL checks the Query tree here. It walks on the given Query trees to pick up all appeared tables, columns and procedures. It makes a list of them to check client's privileges later. We call it as Query Proxy phase. Please note that all views are already expanded at this moment. SE-PostgreSQL does not care about any views used in queries because it means how database objects are accessed, not what database objects. SE-PostgreSQL always checks client's privileges on the expanded relations.
At the next, the Query trees are delivered to the optimizer to make execution plan trees. The executor accesses to databases and returns a result set to the client.
The later hooks of SE-PostgreSQL are here. It is invoked at the head of the executor to check access privileges to database objects listed at the prior phase. It makes access control decision communicating with the in-kernel SELinux, if necessary. (In the most cases, the access control decisions are cached in userspace to minimize the times of kernel invocation.) It the given queries contains violated accesses to tables, columns and procedures, SE-PostgreSQL rases an error to abort query execution. However, privileges on tuples are not checked here, because we cannot know what tuples are accessed until the executor works in actual.
SE-PostgreSQL is also invoked to check whether the fetched tuples are violated, or not, during the executor running. The feached violated tules are filtered from the result set, but it does not raise an error different from tables, columns and so on. We call it as Tuple Filter. It achieves row-level access controls which enables to show clients as if violated tuples does not exist here.
PGACE security framework
The core PostgreSQL invokes SE-PostgreSQL subsystem via series of wrapper functions called as PGACE (PostgreSQL Access Control Extention) security framework. It provides a few dozen of hooks deployed on strategic points of PostgreSQL. These hooks are declared as static inline functions, and they contain codes to invoke SE-PostgreSQL which is activated only when we give --enable-selinux option in configure scripts.
The purpose of this architecture is minimization of impact to add a new security facility. SE-PostgreSQL is one of the options of enhanced security mechanisms, however, we can consider similar facilities based on different security design and philosophy.
The PGACE security framework is designed references from LSM (Linux Security Module). It enables to hide complexity of mixed enhanced security features to core implementation. We call implementations of enhanced security feature as the guest. The gust can put codes to invoke itself on PGACE hooks, enclosed by #if ... #endif block.
Here is an example of PGACE hooks, which is invoked just before a new tuple is inserted into relation. SE-PostgreSQL need to be invoked to assign proper security context and to check the privileges of the client, so we put the following code on the pgaceHeapTupleInsert() hook.
+---- ExecInsert() ----------------------------+
| : |
+---|--O if (!pgaceHeapTupleInsert(....)) |
| | return; /* skip insertion */ |
| | : |
| +----------------------------------------------+
|
| at src/include/security/pgace.h
| +---- pgaceHeapTupleInsert() -----------------------------------+
+--->| #if defined(HAVE_SELINUX) |
| if (selinux_enabled) { |
| - attach default security attribute |
| - check whether the client can insert the tuple, or not. |
| } |
| #endif |
| return true; /* default behavir of this hook */ |
+---------------------------------------------------------------+
This hook requires the guest returns true or false to decide whether the given tuple should be inserted, or not. When the SE-PostgreSQL feature is enables on build-time and run-time, this hook invokes SE-PostgreSQL subsystem to attach proper security attribute and to check client's privileges. If the security policy does not allow to insert, SE-PostgreSQL returns false to prevent the action.
The default behavior does not change existing bahavior of core PostgreSQL implementation. If a new guest does not want to use all the hooks, it can remain these hooks unchanged. It remains existing behavior.
Security context management
This sections introduces how SE-PostgreSQL manages security attribute of database objects which called as security context in SELinux. It is an important feature to keep correctness of access controls.
What is security context ?
SELinux makes its access control decision orthogonally from exising security facilities. For example, traditional filesystem permission system makes its decision based on the user/group identifier of process and permission bits of files. We can consider them as a relationship between an attribute of resources from more abstracted viewpoint. SELinux assigns any resources its own attribute called as security context. All access control rules are described in relationship between the security context of subject (like a process) and object (like a file and so on).
The security context is represented as a formatted text which has four fields separated by : character.
Example of security context.
system_u:system_r:postgresql_t:s0 for PostgreSQL server process system_u:object_r:shadow_t:s0 for /etc/shadow file unconfined_u:object_r:user_home_t:s0 for user home directory
The first field means SELinux user, the second one is a role, the third one is a type which is specially called as a domain when it is assigned to processes and the last one is level and categories called as a range.
You can confirm the security context of a process by id -Z or ps -Z command, ls -Z command for files and netstat -Z command for network sockets.
SELinux has a few access controls models, but they does not use anything except for the security context to make its decision. See the SELinux overview for the detail of access control models in SELinux. The essential is SELinux does not provide any access control decisions without security context.
security_context system column
SE-PostgreSQL assigns a security context for each tuples, because these are not visible objects from the kernel, so the userspace object manager (like RDBMS) has to manage proper security context to query SELinux whether given accesses are violated, or not.
PostgreSQL has an idea of system column. It enables to expose users metadata of every tuples, like a tableoid. These system columns are implicitly generated, but not expanded by SELECT * FROM ... statement, so they work as if hidden columns.
SE-PostgreSQL added a new system column named as security_context to expose the security context of individual tuples.
postgres=# SELECT security_context, * FROM drink ORDER BY id;
security_context | id | name | price
-------------------------------------------------+----+-------+-------
system_u:object_r:sepgsql_table_t | 1 | water | 100
system_u:object_r:sepgsql_ro_table_t:Classified | 2 | coke | 120
system_u:object_r:sepgsql_table_t:Classified | 3 | juice | 130
system_u:object_r:sepgsql_ro_table_t | 4 | cofee | 180
(4 rows)
The new system column has a characteristic which is writable, but traditional system columns are read-only. This feature enables to change the security context via UPDATE statement, if client has enough privileges.
postgres=# UPDATE drink SET security_context =
'system_u:object_r:sepgsql_secret_table_t' WHERE id in (1,4);
UPDATE 2
postgres=# SELECT security_context, * FROM drink ORDER BY id;
security_context | id | name | price
-------------------------------------------------+----+-------+-------
system_u:object_r:sepgsql_secret_table_t | 1 | water | 100
system_u:object_r:sepgsql_ro_table_t:Classified | 2 | coke | 120
system_u:object_r:sepgsql_table_t:Classified | 3 | juice | 130
system_u:object_r:sepgsql_secret_table_t | 4 | cofee | 180
(4 rows)
When we insert a new tuple without explicit security context, SE-PostgreSQL assigns it a default security context, but we can specify it explicitly using the security_context system column.
postgres=# INSERT INTO drink (security_context, id, name, price)
VALUES ('system_u:object_r:sepgsql_table_t:Secret', 5, 'beer', 280);
INSERT 16493 1
postgres=# SELECT security_context, * FROM drink ORDER BY id;
security_context | id | name | price
-------------------------------------------------+----+-------+-------
system_u:object_r:sepgsql_secret_table_t | 1 | water | 100
system_u:object_r:sepgsql_ro_table_t:Classified | 2 | coke | 120
system_u:object_r:sepgsql_table_t:Classified | 3 | juice | 130
system_u:object_r:sepgsql_secret_table_t | 4 | cofee | 180
system_u:object_r:sepgsql_table_t:Secret | 5 | beer | 280
(5 rows)
The COPY statement, SELECT INTO and CREATE TABLE AS statement also support the writable system column facility. The details are noted later.
The security_context system column is declared as TEXT type, so we can apply extensive pre-defined text functions to modify it. Some of new functions are also added to modify security context. See the SELinux overview for more details.
interaction with pg_security system catalog
Most of security context has a few dozen bytes length in text representation, and they have a characteristic that massive objects tend to share small number of security context. It enables to store the seuciry context of individual tuples so effectively.
Every tuples does not have its security context in text representation. Alternatively, it has a security identifier declared as oid within padding area of HeapTupleHeaderData structure as existing oid attribute doing. The security identifier indicates a tuple within pg_security system catalog, and pg_security holds text representations of security context.
SE-PostgreSQL is implemented on the PGACE security framework. It provides a facility to translate security context between an external text representation and an internal security identifier.
When a client requires to input a security context, the pgaceTranslateSecurityLabelIn() looks up pg_security system catalog and assigns a matched security identifier to HeapTupleHeader. If no matched entry is on pg_security, it inserts a new text representation into pg_security and applies the object identifier of newly inserted entry.
When a client reuires to output a security context, the pgaceTranslateSecurityLabelOut() looks up pg_security system catalog and fetchs a text representation to be returned. If no matched entry is on pg_security, SE-PostgreSQL applies an alternative security context called as an initial security context. It is a fallback when we start up SE-PostgreSQL with database cluster files constructed by vanilla PostgreSQL.
Specifications
This chapter introduces the details of access controls in SE-PostgreSQL.
What permissions to be required
As the Interactions between subsystems notes, SE-PostgreSQL walks on the given Query trees to pick up appered tables, columns and procedures at the Query Proxy. It decides what permission to be required during the phase.
- Example of simple query
SELECT name, price * 1.2 FROM drink WHERE id < 20 ORDER BY size;
There are four columns within drink table in this query. However, the role of them are individually different. The name and the price appear in the target list of SELECT statement directly or as a part of calculating formula. The id appears as a part of conditional clause and the size appears as a key to sort.
All of them are indeed refered, however, we can consider they should be categorized into two different cases. The one is a reference to column with exposing its content, and the other is a reference to column without exposing its content.
The name and price can expose its content to clients in this case, but the query executor consumes the contents of id and size, so they are not exposed to clients. SE-PostgreSQL distinguishes the two references. The former is represented as db_column:{select} permission, and db_column:{use} permission for others.
The drink table is accessed in the SELECT statement, so db_table:{select} permission is required in minimum. In addition, the table is refered via the four columns which requires {select} and {use} permission. Thus, we have to be allowed db_table:{use select} permission which is logical sum of column's permissions.
essentials:
- It requires {select} permission for references with exposing its content.
- It requires {use} permission for references without exposing its content.
Please note that the {use} permission should be ensured the contents of its target are consumed within server process, without returning to clients. If a malicious function is defined, it enables to write out somewhere, so arguments of user defined functions should be handled carefully. SE-PostgreSQL applies {select} permission on arguments of user defined function, even if it appers within WHERE clause.
- Example of subquery
SELECT name, price FROM drink WHERE id in (SELECT ordered FROM bill WHERE bid = 1234);
This query contains a subquery on WHERE clause. SE-PostgreSQL considers the example is equivalent to a case when we get a result set of the subquery then gives them as a comparison list. Thus, it requires us db_table:{use select} permission for bill, db_column:{select} for ordered and db_column:{use} for bid, not only {use} permission.
A similar case is functions implemented with SQL language. SE-PostgreSQL always handles them as if they are top-level queries, independent from places of invocation.
essentials:
- SE-PostgreSQL handles subqueries as if they are top-level queries.
- Example of functions
SELECT name, age(birthday) FROM person WHERE id = 16;
This query contains two function invocations explicitly and implicitly. The above two examples don't mention about functions, but the Query Proxy also picks up appeared functions.
The age() function explicitly appeared on the query. It has an argument and the birthday column is given, so it requires us db_procedure:{execute} and db_column:{select} permissions. The int4eq() function implicitly appeared on the query. It is the implementation of compare operator between integer values, and contained as the part of WHERE clause.
In the result, the following permissions are required.
- db_table:{use select} for person table
- db_column:{select} for name and columns
- db_column:{use} for id column
- db_procedure:{execute} for age() and int4eq() functions
essentials:
- SE-PostgreSQL also checks implicit invocation of functions, not only explicit one. It considers operators or type casting as a function invocation.
- Example of UPDATE
UPDATE drink SET name = 'coke', price = 1.2 * price WHERE id = 51;
This query updates the name and price column of the drink table. However, please note that the query refers the drink table in the moment. The price column is also used as a part of calculating formula to compute new value, not only the target of update. We can consider it as exposing the content of the column, so it requires us db_column:{select} permission. The id column appears at the WHERE clause, so it also requires us db_column:{use} permission.
The drink table is accessed in UPDATE statement as the target, so db_table:{update} permission is required in minimum. In addition, the table is also refered via price and id columns, so we have to be allowed db_table:{use select update} permission which is a logical sum of column's permissions.
In the case of INSERT statement,
essentials:
- We have to be allowed proper permission for the target table of INSERT, UPDATE and DELETE statement as a minimum requirement.
- We have to be allowed proper permission for the target columns of INSERT and UPDATE statement as a minimum requirement.
- It considers a calculating formula on the righthand of target columns in UPDATE as exposing its content, so db_column:{select} is additionally required, if it contains references to columns.
- It considers WHERE clause at UPDATE and DELETE statement as references without exposing, so db_column:{use} is additionally required.
- Example of RETURNING clause
DELETE FROM drink RETURNING id, name, price;
This query tries to delete any tuple within drink table, and returns the deleted tuples. The drink table is accessed in the DELETE statement as a target, so db_table:{delete} permission is required in minimum. In addition, the table is also refered via id, name and price columns as a part of RETURNING clause, so db_column:{select} has to be allowed on these columns because it is a reference with exposing. Then, db_table:{select delete} has to be allowed on the drink table as a logical sum of column's permissions.
essentials:
- {select} permission is additionally required for RETURNING clause.
How the access control works
SE-PostgreSQL is invoked to check the above required permissions at the head of executor. If it can found one or more access violations, it raises an error with audit messages as follows:
postgres=# select cid, cname, credit from customer;
ERROR: SELinux: denied { select } scontext=staff_u:staff_r:staff_t \
tcontext=system_u:object_r:sepgsql_secret_table_t tclass=db_column name=customer.credit
This messages shows the client (staff_u:staff_t:staff_t) does not have db_column:{select} permission on the customer.credit column labeled as system_u:object_r:sepgsql_secret_table_t, and the current transaction aborted.
The privileges of clients
The security policy of SELinux checks whether the required operatins are explicitly allowed, or not. As we mentioned, an element of security policy is constructed three keys. The one is the kind of action, the others are security context of client and database object.
The security context of database obejcts are obvious, these are represented by security_context system column of tuples each tuples. However, the security context of client is decided by another way.
SELinux provides an interesting API named as getpeercon(). It enables server processes to obtain the security context of client process. For example, it returns "staff_u:staff_r:staff_t" when a psql frontend process running with "staff_u:staff_r:staff_t". In other cases, it can return "system_u:system_r:httpd_t" when a PHP script invoked from httpd server.
SELinux makes decison for accesses on filesystem objects based on the security context of processes and the security context of files, so we can also consider the security context of processes as privileges of users. In a similar way, SE-PostgreSQL makes decision for accesses on database objects based on the security context of clients and the security context of them.
In other word, the getpeercon() delivers the privileges on operating system, and SE-PostgreSQL applies the privileges on its access controls orthogonally from database authentication.
It does not need any additional configuratin, when we connect to the SE-PostgreSQL via UNIX domain socket. However, it requires a bit more configurations of labeled networking, when we connect via TCP/IP stream socket. See Labeled networking for more details.
Tuple-level access controls
The tuple-level access control has different characteristics compared to others.
SE-PostgreSQL checks privileges of client during the scanning target relations, not the head of executor, because we cannot expect what tuples are refered until the executor actually works. SE-PostgreSQL simply filters any violated tuples from the result set, not raises an error, so the client can see the relation which does not contain any violated tuples.
We consider the following table as an example. It contains eight tuples. Two of them are labeled as Secret, other two of them labeled as Classified, and the rest of them are labeled as Unclassidied.
| security_context | id | name | price |
|---|---|---|---|
| system_u:object_r:sepgsql_table_t:Unclassidied | 1 | water | 100 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 2 | coke | 120 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 3 | juice | 130 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 4 | cofee | 180 |
| system_u:object_r:sepgsql_table_t:Classified | 5 | beer | 240 |
| system_u:object_r:sepgsql_table_t:Classified | 6 | sake | 320 |
| system_u:object_r:sepgsql_table_t:Secret | 7 | wine | 380 |
| system_u:object_r:sepgsql_table_t:Secret | 8 | tea | 140 |
When a client which can refer only Unclassified information, it can get the following result set with unconditional SELECT statement.
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:Unclassified | 3 | juice | 130
system_u:object_r:sepgsql_table_t:Unclassified | 4 | cofee | 180
(4 rows)
Meanwhile, a client which can refer Unclassified and Classified, it can get the following result set in same queries.
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:Unclassified | 3 | juice | 130
system_u:object_r:sepgsql_table_t:Unclassified | 4 | cofee | 180
system_u:object_r:sepgsql_table_t:Classified | 5 | beer | 240
system_u:object_r:sepgsql_table_t:Classified | 6 | sake | 320
(6 rows)
Please note that SE-PostgreSQL applies the filter prior to joining two relations, not the result of join.
For example, the following table stores campaign informatin related to the above drink table, but some of drinks has no campaign.
| security_context | id | info |
|---|---|---|
| system_u:object_r:sepgsql_table_t:Classified | 1 | '20% discount' |
| system_u:object_r:sepgsql_table_t:Unclassified | 2 | '1 more bottle per 5 ones' |
| system_u:object_r:sepgsql_table_t:Secret | 4 | 'sold out today' |
| system_u:object_r:sepgsql_table_t:Classified | 5 | 'tax free' |
When a client which can refer only Unclassified information, it can get the following result set with unconditional SELECT statement.
postgres=# SELECT d.*, info FROM drink d LEFT OUTER JOIN campaign c ON d.id = c.id; id | name | price | info ----+-------+-------+-------------------------- 1 | water | 100 | 2 | coke | 120 | 1 more bottle per 5 ones 3 | juice | 130 | 4 | cofee | 180 | (4 rows)
Meanwhile, a client which can refer Unclassified and Classified, it can get the following result set in same queries.
postgres=# SELECT d.*, info FROM drink d LEFT OUTER JOIN campaign c ON d.id = c.id; id | name | price | info ----+-------+-------+-------------------------- 1 | water | 100 | 20% discount 2 | coke | 120 | 1 more bottle per 5 ones 3 | juice | 130 | 4 | cofee | 180 | 5 | beer | 240 | tax free 6 | sake | 320 | (6 rows)
If it filters violated tuples after table join, the 'water' tuple is joined at first then filtered out because its campaign info is labeled as 'Classified', but the result inconsistent. The tuple-level access controls in SE-PostgreSQL works as if violated tuples do not exist on the relation in normal channels.
Updates of security_context
The security_context system column has a special characteristic which allows users to specify its new value in spite of being system column. We have two cases of them. The one is insertion of a new tuple with a explicitly specified security context, the other is updates of existing tuples with a new security context.
When the security_context is listed on the targets of INSERT statement, SE-PostgreSQL tries to apply the specified security context, not a default one. Because the column is declared as TEXT automatically, it can be specified as a text representation. No need to say, the specified security context has to keep correctness in its format. In the normal cases, SE-PostgreSQL assigns a newly inserted tuple a default security context come from the security policy, but the explicitly specified security context has higher priority than the default one, so a newly inserted tuple is labeled as the explicitly specified, if exist. Then, SE-PostgreSQL checks privileges of client to insert a tuple (db_tuple:{insert}) labeled as the security context. The COPY FROM statement with security_context column has same behavior.
When the security_context is listed on the target of UPDATE statement, SE-PostgreSQL tries to change the security context of matched tuples, elsewhere, the older one is preserved. No need to say, the specified security context has to keep corectness in its format. When it has a differences between older and newer security contexts, SE-PostgreSQL checks privileges of client to relabel the security context of tuple, so clients must have db_tuple:{relabelfrom} permission for the older version of tuple and db_tuple:{relabelto} permission for the newer one.
SELECT INTO statement
The SELECT INTO statement is a powerful tool to declare a new table and to insert tuples at the same moment. When a source result set contains security_context column, it is considered as an explicitly specified one.
For example, we can generate several text representations of security context as follows:
postgres=# SELECT 'system_u:object_r:sepgsql_table_t:s0:c' || id, id, name, price FROM drink;
?column? | id | name | price
-----------------------------------------+----+-------+-------
system_u:object_r:sepgsql_table_t:s0:c1 | 1 | water | 100
system_u:object_r:sepgsql_table_t:s0:c2 | 2 | coke | 120
system_u:object_r:sepgsql_table_t:s0:c3 | 3 | juice | 130
system_u:object_r:sepgsql_table_t:s0:c4 | 4 | cofee | 180
system_u:object_r:sepgsql_table_t:s0:c5 | 5 | beer | 240
system_u:object_r:sepgsql_table_t:s0:c6 | 6 | sake | 320
(6 rows)
It can be used as explicitly specified security context, if it has security_context as its name.
postgres=# SELECT 'system_u:object_r:sepgsql_table_t:s0:c' || id AS security_context,
id, name, price INTO t1 FROM drink;
SELECT
postgres=# SELECT security_context, * FROM t1;
security_context | id | name | price
-----------------------------------------+----+-------+-------
system_u:object_r:sepgsql_table_t:s0:c1 | 1 | water | 100
system_u:object_r:sepgsql_table_t:s0:c2 | 2 | coke | 120
system_u:object_r:sepgsql_table_t:s0:c3 | 3 | juice | 130
system_u:object_r:sepgsql_table_t:s0:c4 | 4 | cofee | 180
system_u:object_r:sepgsql_table_t:s0:c5 | 5 | beer | 240
system_u:object_r:sepgsql_table_t:s0:c6 | 6 | sake | 320
(6 rows)
CREATE/ALTER with SECURITY_CONTECT
Several CREATE and ALTER statements have SECURITY_CONTEXT option to specify the security context of targeted object. See the New SQL statements for more details.
SE-PostgreSQL considers CREATE something with SECURITY_CONTEXT option is equivalent to inserting a new tuple into proper system catalog with explicit security context. In a similar way, it considers ALTER something with SECURITY_CONTEXT option is equivalent to changes of tuple's security context within proper system catalog.
See the Access controls on system catalogs for more details to operate system catalogs.
Access controls on system catalogs
In most cases, SE-PostgreSQL applies db_tuple object class to check access privileges to tuples. However, we need special cares for the operations on system catalogs, because they have special meanings.
The following example updates the pg_attribute system catalog.
postgres=# SELECT * FROM t; a | b ---+----- 1 | aaa 2 | bbb 3 | (3 rows) postgres=# SELECT attname, attnotnull FROM pg_attribute WHERE attrelid = 16553 and attnum > 0; attname | attnotnull ---------+------------ a | t b | f (2 rows) postgres=# INSERT INTO t VALUES (4, 'ddd'), (5, null); INSERT 0 2
The t table contains two columns, the one has NOT NULL constraint, and the other does not have. At the next, we update pg_attribute system catalog directly.
postgres=# UPDATE pg_attribute SET attnotnull = true WHERE attrelid = 16553 and attname = 'b'; UPDATE 1 postgres=# INSERT INTO t VALUES (6, 'fff'), (7, null); ERROR: null value in column "b" violates not-null constraint
The NOT NULL constraint is added using UPDATE statement, without ALTER TABLE. It shows us we should consider these operations are equivalent. Thus, SE-PostgreSQL checks db_column:{setattr} permission when we tries to update the pg_attribute system catalog directly, not db_tuple:{update}. There are no differences from when we modify the constraint via ALTER TABLE statement.
The security policy of SELinux defines several database related object classes which reflect to system catalogs, and SE-PostgreSQL applies special cares for them.
- pg_database system catalog
SE-PostgreSQL applies db_database object class for operations on tuples within pg_database system catalog. It requires db_database:{getattr} for SELECT, db_database:{create} for INSERT, db_database:{setattr} for UPDATE and db_database:{drop} for DELETE. Its default security context is decided by the one of client itself.
- pg_class system catalog
SE-PostgreSQL applies db_table object class for operations on tuples within pg_class system catalog and its relkind field has RELKIND_RELATION. It requires db_table:{getattr} for SELECT, db_table:{create} for INSERT, db_table:{setattr} for UPDATE and db_table:{drop} for DELETE.Its default security context is inherited from the current database.
- pg_attribute system catalog
SE-PostgreSQL applies db_column object class for operations on tuples within pg_attribute system catalog and relkind field of its parent relation has RELKIND_RELATION. It requires db_column:{getattr} for SELECT, db_column:{create} for INSERT, db_column:{setattr} for UPDATE and db_column:{drop} for DELETE. When the attisdropped is turned off, SE-PostgreSQL considers this attribute is dropped, so it also requires db_column:{drop} permission. Its default security context is inherited from its parent relation.
- pg_proc system catalog
SE-PostgreSQL applies db_procedure object class for operations on tuples within pg_proc system catalog. It requires db_procedure:{getattr} for SELECT, db_procedure:{create} for INSERT, db_procedure:{setattr} for UPDATE and db_procedure:{drop} for DELETE. When the given procedure is implemented with C language and stored within external DDL file, it also requires db_database:{install_module} permission. Its default security context is inherited from the current database.
- pg_largeobject system catalog
SE-PostgreSQL applies db_blob object class for operations on tuples within pg_largeobject system catalog. It has a characteristic that a large object is constructed several tuples. In a narrow sense, it means create a new large obejct to insert a tuple which has unique loid, and drop a large object to delete the last tuple which has a specific loid. However, it is a bit hard to trap these events on operations via generic DDL statements. (It's not impossible, to scan pg_largeobject with SnapshotSelf enables to trap it, but too heavy.) Thus, SE-PostgreSQL assumes INSERT into pg_largeobject has a possibility to create a new large object, so it always requires db_blob:{create write} permission, and DELETE from pg_largeobject has a possibility to drop large objects, so it always requires db_blob:{drop write} permission. It requires db_blob:{getattr} for SELECT and db_blob:{read} if the SELECT contains data. Its default security context is inherited from the current database for a newly created largeobject, or inherits the security context of prior pages.
- pg_security system catalog
SE-PostgreSQL always disallow users to insert, update and delete tuples within the system catalog directly, because it has to keep the relationship between security identifier and text representation in proper state, so unexpectable updates gives fatal damages to the access control foundation. It allows to refer the system catalog as a generic table.
More defails
Trusted procedure
SELinux has an idea of domain transition. It gives processes a chance to change its working domain (security context) on execution of a new program. For example, a init script (initrc_t) forks a new process and executes /usr/bin/postgres (postgresql_exec_t), then the process get postgresql_t domain to work as a postgresql server process.
In normal cases, the security context of process is unchanged around program execution. But the security policy can contain special rules called as TYPE_TRANSITION.
TYPE_TRANSITION initrc_t postgresql_exec_t : file postgresql_t;
The above policy means the security context of a process working in initrc_t domain has to be changed when it execute a file labeled as postgresql_exec_t.
The trusted procedure is a similar idea. It gives a chance to change the security context of the client during function invocation. If a TYPE_TRANSITION rule is put on the db_procedure object class between the security context of client and the function, SE-PostgreSQL changes the security context of client temporarily.
We can use the feature to restrict the way to access classified information.
In the default security policy, it declares sepgsql_trusted_proc_exec_t type. When a non-administrative user invokes functions labeled as the type, this function works as if it has administrative privileges.
For example, generic users cannot refer credit column.
postgres=# SELECT sepgsql_getcon();
sepgsql_getcon
-------------------------
staff_u:staff_r:staff_t
(1 row)
postgres=# SELECT cid, cname, credit FROM customer;
ERROR: SELinux: denied { select } scontext=staff_u:staff_r:staff_t
tcontext=system_u:object_r:sepgsql_secret_table_t
tclass=db_column name=customer.credit
postgres=# SELECT cid, cname FROM customer;
cid | cname
-----+-------
10 | jack
13 | adam
14 | liza
(3 rows)
However, a trusted procedure is declared to refer the credit column in a secure way.
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 substring(credit from ''^[0-9]+-'') || ''xxxx-xxxx-xxxx'' FROM customer WHERE CID = $1';
It enables generic users to refer the credit column, but a part of them are 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)
Trigger functions
SE-PostgreSQL checks db_column:{select} or db_column:{use} permission for columns which appear in the argument list of functions. However, when a function is invoked as a trigger, it need a special care.
Most of before/after-row triggers deliver whole of the affected tuple to the invoked functions, we can consider it is equivalent to invocation of a function with all the columns within the table triggered. Thus, SE-PostgreSQL checks db_column:{select} permission for all the column. The before-row-insert trigger is an exception, because its given NEW tuple comes from the client, or is already allowed to SELECT.
The reason why it has to check permissions for all the columns is we cannot expect what columns are refered within the user defined trigger function. However, there are small number of exceptions. The foreign key constraint is implemented as built-in trigger functions in PostgreSQL, so we can ensure the columns to be refered preliminarily.
The following two table declarations are an example.
CREATE TABLE pk_tbl (
pid int primary key,
name text
);
CREATE TABLE fk_tbl (
fid int references pk_tbl(pid),
memo text
);
When we update a tuple within fk_tbl, a trigger function is invoked to confirm the existance of newer fid value in pk_tbl, and SE-PostgreSQL checks db_column:{select update} permission on fid column, but db_column:{select} is not necessary for memo column, because it is not used for the integrity consistency checks.
Unique constraint
The unique constraint which includes primary keys does not allow two or more tuples within a same table to hold same key values. SE-PostgreSQL has per-tuple granularity in its access controls and filters out any violated tuples from the result set, so it seems to clients as if the violated tuples does not exist.
The unique constraint works on the most fundamental level which means any tuples must have individually unique key values at the phase prior to all the filtering mechanism. For example, the primary key of the following table is id column, and it stores six tuples, but two of them are invisible from Unclassified client.
| security_context | id | name | price |
|---|---|---|---|
| system_u:object_r:sepgsql_table_t:Unclassidied | 1 | water | 100 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 2 | coke | 120 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 3 | juice | 130 |
| system_u:object_r:sepgsql_table_t:Unclassidied | 4 | cofee | 180 |
| system_u:object_r:sepgsql_table_t:Classified | 5 | beer | 240 |
| system_u:object_r:sepgsql_table_t:Classified | 6 | sake | 320 |
In this case, SE-PostgreSQL returns four tuples for unconditional SELECT come from Unclassified client due to access violations on the rest of two. However, it also prevents the client to insert or update the id column to invisible value, because it violates unique constraint at the most fundamental level.
Please note that the characteristics enable unprivileged clients to infer existance of invisible key values. See Covert channels for more details.
Foreign Key constraint
See the Trigger functions section for related topics.
SE-PostgreSQL filters out any violated tuples from result set on its tuple-level access controls, and it gives clients a view as if there are no violated tuples. However, we need a special care to handle foreign key constraint and tuple-level access controls properly.
When we update or delete tuples with a primary key refered by one or more foreign keys, the constraint checks whether there are any tuple refering the primary key, or not. If one or more tuples with foreign key refers the modified primary key, the query can be aborted or pre-defined actions are kicked, like SET NULL, CASCADE and so on.
However, tuple-level access controls can prevent the checks and actions to work correctly. For example, when a foreign key has SET NULL actions on deletion of primary keys refered, deletion of a primary key invokes the action, but the tuple-level access controls prevent to set NULL on violated tuples, so it can break referential integrities.
SE-PostgreSQL switches its internal state of tuple-level access controls while foreign key constraint works. When a client modifies a tuple with primary keys which are refered by one or more invisible foreign keys, the tuple-level access control mechanism raises an error to abort the current transaction.
Please consider the following the campaign table refers the drink table at the above section, for example.
| security_context | id | info |
|---|---|---|
| system_u:object_r:sepgsql_table_t:Unclassified | 1 | '20% discount' |
| system_u:object_r:sepgsql_table_t:Classified | 2 | '1 more bottle per 5 ones' |
| system_u:object_r:sepgsql_table_t:Unclassified | 4 | 'sold out today' |
| system_u:object_r:sepgsql_table_t:Classified | 5 | 'tax free' |
If an Unclassified client tries to delete a tuple of 'coke' within drink, it seems to him being possible because no tuples within campaign refers it, but SE-PostgreSQL prevents it and raises an error due to an invisible tuple within campaign refering it. In this case, the tuple-level access controls don't works as filtering mechanism. If it simply filtered violated tuples like a generic cases, the referential integrity from the viewpoint of Classified would be breken.
Please note that the characteristics enable unprivileged clients to infer existance of invisible key values. See Covert channels for more details.
Loading shared library module
PostgreSQL allows to load shared library module to implement user defined functions. However, we have to pay mention for risks that they can bypass and break whole the security mechanism, because it works within same memory address space, so we have no way to trap their accesses on database objects and security mechanism itself.
When a shared library module is installed as an implementation of user defined functions, SE-PostgreSQL checks db_database:{install_module} privileges of client to the database and the shared library module. This check is applied at once on declaration of a function.
When a shared library module is loaded to the process memory space, SE-PostgreSQL checks db_database:{load_module} permission between the database and the shared library module. This check does not evaluate the privileges of client. Its purpose is to check the capability of the database.
Import/Export a large object
The lo_import() and lo_export() allows clients to read/write server side filesystem object with privileges of SE-PostgreSQL server process.
However, it can be a tunnel to leak or manupulate violated filesystem objects, so SE-PostgreSQL also checks privileges of client to read or write the filesystem object, not only privileges to read or write and create a large object.
TRUNCATE statement
The TRUNCATE statement deletes all the tuples within specified tables. We can consider it is equivalent to unconditional DELETE from the viewpoint of access controls. However, it cannot control privileges to delete individual tuples, so SE-PostgreSQL requires client to have privileges to delete all the tuples within specified tables preliminary. If not, it raises an error and the current transaction will be aborted.
Covert channels
Interactions between tuple-level access controls and unique/foreign-key constrains have characteristics to be kept in mind.
When a user tries to insert a tuple with a primary key value which is already on the target table, but invisible from the user due to the tuple-level access controls. SE-PostgreSQL prevents them to insert tuples with duplicate primary key or pairs of values which restricted by unique constraint, and it raises an error to abort the query. But it enables users to infer existance of invisible tuples.
Foreign-key constraint has a similar behavior. When there is a tuple with primary key refered by one or more tuples which have foreign key constraint, but they are invisible from the users, SE-PostgreSQL prevent him to update or delete the tuple, and it raises an error likewise. But it also enables users to infer existance of invisible tuples.
These information flows are called as "covert channels" or "illicit channels", which means information flows go though irregular or unexpected path. Some of evaluation criterias mentioned about covert channels. For example, the Common Criteria requires to care at FDP_IFF.3, FDP_IFF.4 and FDP_IFF.5. However, FDP_IFF.3 and 4 does not require to eliminate all the information flows via covert channels, and FTP_IFF.5 is an extream requirement for generic enterprise class systems. So, SE-PostgreSQL does not care about them.
There is a practical action to be recommended. Consider to use non-natural keys to avoid actual information assets to be infered. In this case, a malicious one can infer the primary/foreign key valus, but they are junks.
SELinux overview
This chapter introduces the overview of SELinux, which includes its security design and basic facilities to administrate it.
SELinux and reference monitor
The shortest answer to the question of "what is the SELinux?" is that it is an implementation of reference monitor within the Linux kernel. The reference monitor is a small software module to control all the accesses to data objects and devices based on its security policy, which have to be 1) tamperproof, 2) always-invoked and 3) small enough to be verifiable.
At first, please consider the reference monitor model on the most abstraced design layer. In our information systems, all the data objects are managed by something like operating systems, so we have to send a request to access the objects and the object manager works as an alternatives of us. We can consider three entities here. The one is resources to be accessed, the others are a method to access and subject of the access.
In the case of operating systems, we have to invoke a proper system call, like read(2), to access target resources. It also means all we have to do to trap all the accesses to filesystem objects is to monitor the invocations of system calls. Linux has a feature to help implement in-kernel reference monitor called as LSM (Linux Security Module). It provides some dozen of hooks at the strategic points in the kernel, and SELinux is implemented as the guest of the LSM security framework. Thus, when we invoke a system call, its code path go through on the security hooks and invokes SELinux subsystem. SELinux makes its decision based on its security policy and orthogonally from any existing mechanism like filesystem permissions, then it returns an error if it is violated.
We can apply this model to accesses on database objects easily. In this case, the resources to be accessed are database objects, and we have to use SQL interface to access them. When we send SQL queries, SE-PostgreSQL traps them to check the privileges of clients, and makes its decision on the accesses based on the security policy of SELinux, orthogonally from any existing mechanism like database acl.
XACE/SELinux is another example to utilize in-kernel reference monitor from userspace applications. In this case, resources are X-window objects, and X-protcol is a method to access.
See the The privileges of clients. It says SE-PostgreSQL applies the security context of peer process as privileges of client, independently from the database authentication. A same security policy always returns same result for the same combination between two security contexts, so it also means we can appliy consistent rules in access controls. In addition, all decisions in access controls are centralized manageable due to the single unified security policy, and non-bypassable even if clients are database super users due to its characteristics of always-invoked.
The following a few sections describes the details of rules to make a decision.
Security context
This subsection introduces the details of security context again.
See the Security context management to understand implementation in SE-PostgreSQL.
A security context is an attribute represented in text format, and contains all the information to make a decision in access controls by SELinux. It has four fields separated by colon character, these are "SELinux user", "role", "type" and "range".
SELinux makes its access control decision based on a few different security models, and they adopt individually different field to make its decision. It need all the security models to allow the required accesses to get a positive result.
The first field is "SELinux user" which corresponds to user identifier of the operating system. user_u is a substitute when a proper SELinux user does not defined in the security policy. The second field is "Role" which is used in RBAC. This field is available at only processes, so object_r is uniformly set to any resources. The third field is "Type" or "Domain". We call is as "Domain" when the security context is assigned to process. The type or domain is used as an identifier of type enforcement which is most significant security model in the SELinux. The fourth field is "Range" which is used in MLS (Multi Level Security) or MCS (Multi Catagory Security) mechanism. This field can be translated into human readable format by mcstrans service. For example, it enables to display s0:c0.c1023 as SystemHigh.
We introduce these security models below.
Type Enforcement
TE (Type Enforcement) is the most significant security model in SELinux. It adopts the third field of security context as its identifier, and we describe the relationship between two types. We can call a type assigned to processes as a domain, but here is no fundamental difference other than its name.
In the following example, the security policy allows a process labeled as postgresql_t to read or write files labeled as postgresql_db_t, and to bind a socket a specific port number labeled as postgresql_port_t. SELinux enforces all the entities which appear on access controls to have a type. The type is an abstraction of its position in access controls, so same result will be returned for the same combination of a domain, a type and actions. In the default security policy, the postgresql server process has postgresql_t domain, the database files stored in /var/lib/pgsql/data/* have postgresql_db_t type, the web contents files stored in /var/www/html/* have httpd_sys_content_t type, and the well-known port number of PostgreSQL has postgresql_port_t type.
This security policy defines two relations. The one is between postgresql_t and postgresql_db_t, and the other is between postgresql_t and postgresql_port_t. The former rule allows postgresql_t domain which contains PostgreSQL server process to read and write files labeled as postgresql_db_t which contains database files. The other rule allows postgresql_t domain to bind a tcp port number labeled as postgresql_port_t which contains the well-known PostgreSQL port (5432). However, there is no explicit rules between postgresql_t domain and httpd_sys_content_t type, so SELinux prevents all accesses between them due to its white list rule.
The actual type enforcement rules are a large set of these rules. However, the recent upstreamed security policy is well moduled, and rich pre-defined macros and templates will help to understand type enforcement rules.
Domain transition
It is important to understand how to assign a security context of processes, not executable program files. A process inherits the domain of its parent process in the normal cases. However, the /sbin/init is the common ancestor of all the processes. If SELinux had no feature to switch domain of processes, all processes would work same domain. But it is incorrect.
SELinux has a mechanism to switch domain of processes, called as "domain transition".
It replaces the domain of process on execve() system call which kicks a new executable program file. For example, the following policy enables to replace the domain of process labeled as initrc_t into postgresql_t when it executes a program file labeled as postgresql_exec_t.
TYPE_TRANSITION initrc_t postgresql_exec_t : process postgresql_t;
In the default security policy, the initrc_t is a domain for init scripts, and the postgresql_exec_t is a type for /usr/bin/postgres executable file. If there was no TYPE_TRANSITION, PostgreSQL server processes kicked by init script would inherit initrc_t, however, the TYPE_TRANSITION rule enables to change the domain of the process, then it works within postgresql_t domain as we expected.
The /sbin/init works on the init_t domain, so rest of child processes are assigned their proper domain via chains of domain transition.
We can apply this scheme for resources except for processes. The following example uses file as the object class, it means the security context of newly created file under the directory labeled as var_log_t is postgresql_log_t
TYPE_TRANSITION postgresql_t var_log_t : file postgresql_log_t;
This idea is also applied in SE-PostgreSQL, to specify the security context of newly created database objects.
Role Based Access Control
SELinux implements its RBAC (Role Based Access Control) feature as a boundary of domain transitions. The role is a set of domains, and it is identified by the second field of security context. Please note that it is not a set of types. This idea is applied to processes only.
When a process tries to switch its working domain, both of older and newer domain have to be contained the current role. Please note that domain transition replaces the third field in security context of process, but it does not affect the role because it is a different concept independently configured.
When a user assigned a role of foo_r which dominates dom_A_t, dom_B_t and dom_C_t, and his initial domain was dom_A_t, the RBAC policy allows his process to switch its domain to dom_B_t and dom_C_t, but it disallows to switch to dom_X_t even if type enforcement has a rule to translate to the domain.
If a role of baz_r dominates dom_X_t in addition to them, and he is assigned the baz_r role at the login time, the above domain transition will be allowed.
We can use this feature to actualize limited privileged users. For example, we will be able to see a root user with a role of database administration can manage database configuration, start/stop sever process and so on, but he does not touch configurations related to mail server, web server and so on.
However, we have to wait for more improvement in the default security policy to see the vision. So, we don't use the RBAC feature aggressively in the default policy of SE-PostgreSQL,
MLS and MCS
The MLS (Multi Level Security) is a design of traditional mandatory access controls based on the Bell-La-Padulla model. It uses the fourth field in the security context called as range. The MCS (Multi Categories Security) is a simplified design of MLS model.
The range field of processes have two parts separated by a hyphen character (-), but rest of resources have a unique range field. The left-hand of the part is called as a lower-range, and the right-hand of the part is called as a higher-range. In addition, individual range has two elements separated by colon character. The left-hand is a sensitivity level like s0, and the right-hand is categories like c0.c15. SELinux applies the sensitivity on access controls based on the hierarchical relationship, and applies the categories on access controls based on the nclusion relationship.
Any range field have a sensitivity level and zero or more categories. We have a few notation rules for categories. It can be omissible when no categories are associated. It allows to enumerate several categories separated by a comma character, and it especially allows to describe a consecutive categorites separated by a period character.
The following example shows a security context of a process, and its range field is s0-s0:c0,c3.c7,c9. It has two parts because the owner of the security context is process. The left-hand is s0, so there is no categories here, and the right-hand is s0:c0,c3.c7,c9, so it has c0, c3, c4, c5, c6, c7 and c9.
[kaigai@saba ~]$ id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0,c3.c7,c9
MLS rules
The access control rules in MLS are described as a relationship between lower- or higher-range of process and range of resources. Which range is checked depends on the domain of the process working on. Some of special domains are configured to apply higher-range in checks of MLS rules, but most of domains apply lower-range there.
When a process with s2:c1.c2-s3:c0.c3 range working on non-special domain tries to read files, SELinux prevents it to read a file labeled as s3:c0:c1 because s3 is higher than s2, and a file labeled as s1:c2.c3 because c3 is not dominated by c1.c2. However, SELinux does not prevent to read a file labeled as s2:c1.c2 and s1:c1.c2 because these are equal or dominated by the lower-range of the process.
When the process tries to write files, SELinux does not allow it except for a file labeled as s2:c1.c2 which is same as lower-range of process.
In summary, the following rules are requirements by MLS policy
- When a process tries to read a resource, its sensitivity has to be equal or lower than the sensitivity of the process.
- When a process tries to read a resource, its categories have to be equal or dominated by the categories of the process.
- When a process tries to write a resource, its range is has to be equal to the lower-range of the process.
MCS rules
The MCS model is a simplified implementation of MLS. It makes all the processes and resources to share a unique sensitivity level, so all sensitivities have equal-relationship, thus, it decides accessibility based on inclusion relationship only.
The lower- and higher-range get different meanings compared to MLS ruls. In MCS rules, the lower-range means the default range when it creates a new resource, and the higher-range means the privileges of the process.
When a process with s0-s0:c1.c2 tries to read or write files, SELinux prevents the action for a file labeled as s0:c0.c1 because c0 is not dominated by c1.c2, but SELinux allows it for a file labeled as s0:c2 and uncategorized.
In the default security policy, MCS rules are activated, and MLS is an option.
Customizing the SELinux
Enforcing and Permissive mode
SELinux has two working modes: enforcing and permissive mode. In the both working modes, SELinux checks its security policy to decide whether the given action should be allowed, or not. However, SELinux does not prevent anything in the permissive mode, but it also generates audit log records to report violated accesses. It can be used to list up what permissions are required by the running applications.
We can put the system default behavior on /etc/selinux/config, and the setenforce command enables us to switch the working mode dynamically.
[kaigai@saba ~]$ cat /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=enforcing
SE-PostgreSQL follows the working mode of in-kernel SELinux in the default. But, we can set up its own working mode by the guc variable of sepostgresql.
Booleans
The boolean feature allows several part of the security policy to be conditional. The boolean is a series of variables managed by SELinux, which has a status either on or off. One or more blocks of rules depend on the boolean, and it allows administrators to set a new value, so it is possible to customize the security policy setting without modification of security policy.
For example, SELinux does not allow Samba file server to access user's home directories in the default. However, conditional policies to allow them depend on the boolean of samba_enable_home_dirs which has false in the default, as follows:
if (samba_enable_home_dirs)
{
allow smbd_t user_home_t : file { create read write append unlink .... };
allow ambd_t user_home_t : dir { create search add_name remove_name unlink ... }; :
:
}
So, the Samba file server need to turn on the boolean to expose the user's home directories.
The security policy of SE-PostgreSQL also provides a few booleans to allow end-users to customize its security policy. See the Pre-defined object types and booleans for more details.
security policy modules
The security policy to be loaded to the kernel consists of a base policy module and a few dozen of security policy modules. We can install, upgrade or uninstall security policy modules necessary or unnecessary.
The /usr/sbin/semodule is an interface to manage security policy modules. The -l option enables to display the list of installed security policy modules, the -i option enables to install a new security policy module, and so on.
[root@saba ~]# /usr/sbin/semodule -l amavis 1.7.0 amtu 1.1.0 apcupsd 1.3.0 : sepostgresql-devel 3.14 : xguest 1.0.0 zabbix 1.1.0
SE-PostgreSQL provides an additional security policy modules named as sepostgresql-devel.pp. It enables developers to control audit messages and to run the regression test correctly.
system-config-selinux
The system-config-selinux is a fine GUI utility to customize SELinux. It enables to set up the working mode of SELinux, the default security context of filesystem objects and network resources, the mapping between users and range on login time, human readable forms of ranges and policy modules.
Administration
Installation and setup
This section introduces two ways to install and setup SE-PostgreSQL on your system. The one is rpm installation, the other is build from source tarball.
SE-PostgreSQL requires the following packages for its setup. Please confirm your environment and their versions.
- Linux kernel with SELinux (2.6.24 or later recommended)
- selinux-policy 3.4.2 or later
- policycoreutils 2.0.16 or later
- libselinux 2.0.43 or later
- checkpolicy for installation from source tarball
- libselinux-devel 2.0.43 or later for installation from source tarball
installation from rpm package
The SE-PostgreSQL package is now distributed via Fedora project. The sepostgresql package is available on Fedora 8 or later releases.
If you already set up a recent Fedora release on your system, the installation of SE-PostgreSQL is very simple, as follows:
# <b>yum install sepostgresql</b> Loaded plugins: refresh-packagekit Setting up Install Process Parsing package install arguments Resolving Dependencies --> Running transaction check ---> Package sepostgresql.i386 0:8.3.4-2.1077.fc10 set to be updated --> Processing Dependency: postgresql-server = 8.3.4 for package: sepostgresql --> Running transaction check ---> Package postgresql-server.i386 0:8.3.4-1.fc10 set to be updated --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: sepostgresql i386 8.3.4-2.1077.fc10 rawhide 2.0 M Installing for dependencies: postgresql-server i386 8.3.4-1.fc10 rawhide 4.6 M Transaction Summary ================================================================================ Install 2 Package(s) Update 0 Package(s) Remove 0 Package(s) Total download size: 6.6 M Is this ok [y/N]: y Downloading Packages: (1/2): sepostgresql-8.3.4-2.1077.fc10.i386.rpm | 2.0 MB 00:00 (2/2): postgresql-server-8.3.4-1.fc10.i386.rpm | 4.6 MB 00:01 ------------------------------------------------------------------------------------------ Total 1.3 MB/s | 6.6 MB 00:04 Running rpm_check_debug Running Transaction Test Finished Transaction Test Transaction Test Succeeded Running Transaction Installing : postgresql-server [1/2] Installing : sepostgresql [2/2] Installed: sepostgresql.i386 0:8.3.4-2.1077.fc10 Dependency Installed: postgresql-server.i386 0:8.3.4-1.fc10 Complete!
If you don't use yum, you can get the package from Fedora mirrors:
The rpm installation script labels proper security context installed files and links its security policy module, so we don't need to do anything more.
At the next, you have to initialize the database cluster. In the default, SE-PostgreSQL deploys its database cluster at /var/lib/sepgsql, but it can be replaced by the configuration at /etc/sysconfig/sepostgresql.
The /etc/init.d/sepostgresql script with initdb initializes the database cluster, so it has to be completed before starting up server processes.
# env LANG=C /etc/init.d/sepostgresql initdb Initializing database: [ OK ]
Then, it will get ready to start up server processes.
# env LANG=C /etc/init.d/sepostgresql start Starting sepostgresql service: [ OK ]
The initial databases are set up by sepgsql database role, add some more roles if necessary.
# su - sepgsql
-bash-3.2$ createuser foo
Shall the new role be a superuser? (y/n)
:
installation from source tarball
Please note this section assumes PostgreSQL v8.4 with SE-PostgreSQL feature
When we build and install SE-PostgreSQL from the source tarball, it requires us a few more steps to work it correctly with SELinux. However, most of them are common with the vanilla PostgreSQL on SELinux platform.
At first, download the source tarball of PostgreSQL from http://www.postgresql.org/ftp/source/ , and extract it.
$ wget ftp://ftp.postgresql.org/pub/source/v8.4.0/postgresql-8.4.0.tar.bz2 $ tar jxvf postgresql-8.4.0.tar.bz2 $ cd postgresql-8.4.0 $ ./configure --enable-selinux
We have to add the configure option of --enable-selinux to activate SE-PostgreSQL feature.
$ make $ make -C src/backend/security/sepgsql/policy
The sources of security policy module for SE-PostgreSQL are stored within src/backend/security/sepgsql/policy, and it need to invoke make independently. It generates sepostgresql.pp and sepostgresql-devel.pp. The former sepostgresql.pp module is necessary, if you intend to install SE-PostgreSQL on the system with selinux-policy-3.4.2 or prior. The other sepostgresql-devel.pp provides a few additional rules needed for developers. It enables to turn on/off audit messages, and a boolean to run the regression test correctly.
We can install these security policy modules using the semodule command.
$ su # /usr/sbin/semodule -i src/backend/security/sepgsql/policy/sepostgresql-devel.pp
Then, install the built files.
# make install
Make a directory for a newly created database cluster, and change ownership of the directory.
# mkdir -p /opt/sepgsql # chown sepgsql:sepgsql /opt/sepgsql
Confirm the security context of the installed files and the database cluster. In the most cases, incorrect security context is assigned for the installation from source tarball, so it makes troubles.
# ls -Z /usr/local/pgsql/bin/postgres -rwxr-xr-x root root unconfined_u:object_r:bin_t:s0 /usr/local/pgsql/bin/postgres # ls -Zd /opt/sepgsql drwxr-xr-x sepgsql sepgsql unconfined_u:object_r:usr_t:s0 /opt/sepgsql
For example, a newly created files at /usr/local/pgsql/bin is labeled as bin_t type, however, The /usr/local/pgsql/bin/postgres has to be labeled as postgresql_exec_t type. A newly created directories at /opt is labeled as usr_t type, however, database cluster has to be labeled as postgresql_db_t type.
RPM installation resolves such kind of labeling issues, becayse its post-install script assigns proper security context for the installed files. When we install PostgreSQL from the source tarball by hand, we have to manage it by hand. If you have installed the sepostgresql-devel.pp policy module, we can assign proper security context because it contains proper relationship between filenames and its expected security context. The /sbin/restorecon is a command to set specified files their default security context defined in the security policy, like:
# /sbin/restorecon -R /usr/local/pgsql
It enables to restore proper security contexts, even if you specify your own prefix on the configure script, because it automatically generates proper pathes when make -C src/backend/security/sepgsql/policy. However, please note that you have to assign postgresql_db_t by hand, if the detabase clusted is deployed except for /var/lib/sepgsql.
If you don't install neither sepostgresql.pp nor sepostgresql-devel.pp, confirm the security context of the following files which assumes /usr/local/pgsql as its prefix.
- /usr/local/pgsql/bin/postgres should be labeled as postgresql_exec_t type
- /usr/local/pgsql/bin/initdb should be labeled as postgresql_exec_t type
- /usr/local/pgsql/bin/pg_ctl should be labeled as initrc_exec_t type
- rest of /usr/local/pgsql/bin/* should be labeled as bin_t type
- files stored within /usr/local/pgsql/lib should be labeled as lib_t type
- rest of /ust/local/pgsql/* should be labeled as usr_t type
- any files and directories of database cluster should be labeled as postgresql_db_t type
And, you can assign proper security context by hand, as follows:
# chcon -t usr_t -R /usr/local/pgsql # chcon -t bin_t -R /usr/local/pgsql/bin # chcon -t lib_t -R /usr/local/pgsql/lib # chcon -t postgresql_exec_t /usr/local/pgsql/bin/postgres # chcon -t postgresql_exec_t /usr/local/pgsql/bin/initdb # chcon -t initrc_exec_t /usr/local/pgsql/bin/pg_ctl # chcon -t postgresql_db_t -R /opt/sepgsql
At the last, invoke initdb to initialize the database cluster.
$ export PGDATA=/opt/sepgsql
$ /usr/local/pgsql/bin/initdb
The files belonging to this database system will be owned by user "sepgsql".
This user must also own the server process.
The database cluster will be initialized with locale en_US.UTF-8.
The default database encoding has accordingly been set to UTF8.
The default text search configuration will be set to "english".
fixing permissions on existing directory /opt/sepgsql ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 32MB
creating configuration files ... ok
creating template1 database in /opt/sepgsql/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok
WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.
Success. You can now start the database server using:
/usr/local/pgsql/bin/postgres -D /opt/sepgsql
or
/usr/local/pgsql/bin/pg_ctl -D /opt/sepgsql -l logfile start
Then, invoke the /usr/local/pgsql/bin/pg_ctl, not a /usr/local/pgsql/bin/postgres directly, because the default security policy does not allow user domain to translate to the server domain.
$ touch /opt/sepgsql/sepostgresql.log $ chcon -t postgresql_log_t /opt/sepgsql/sepostgresql.log $ pg_ctl -l /opt/sepgsql/sepostgresql.log start
If you collect server logs, the log files has to be labeled as postgresql_log_t.
setup options
Now SE-PostgreSQL has an option to activate/deactivate its functionality.
- sepostgresql = [default | enforcing | permissive | disabled]
This guc option can only be set when the server process starts up. It does not allowed to change it on runtime.
- default
- It is applied when we omit the guc option. It means SE-PostgreSQL works according to the mode of SELinux.
- enforcing
- It means SE-PostgreSQL works in enforcing mode which checks security policy, applies mandatory access controls and generates audit records. When we specify this mode, SELinux must not be disabled.
- permissive
- It means SE-PostgreSQL works in permissive mode which checks security policy and generates audit records, but does not apply mandatory access controls. When we specify this mode, SELinux must not be disabled.
- disabled
- It means SE-PostgreSQL is disabled.
- Please note that any tuples inserted or updated during SE-PostgreSQL is disabled are not labeled properly. We have to label them proper security context after it is enabled again.
Labeled networks
As we mentioned before, SE-PostgreSQL applies the security context of the peer process as privileges of clients when it makes decisions on access controls. The libselinux provides getpeercon() API which enables to obtain the security context of the peer process for the given socket file descriptor. We don't need special configurations for the connection come from UNIX domain socket (local connection), however, an additional configurations called as "Labeled networks" are necessary for the connection come from TCP/IP socket (remove connection).
The labeled networks is an enhancement of IPsec. When we open a connection on IPsec'ed communication channel, an enhanced key exchange daemon (racoon) send an encryption key and the security context of the process to the peer side. The kernel working on the peer side stores the information, and returens it applications via getpeercon().
This section introduces the way to set up the labeled networks between two hosts, which is the simplest case.
- Requirements
The labeled networks requires the following packages.
- Linux kernel, labeled networks enabled
- ipsec-tools-0.6.5-6, or later
A kernel config option of CONFIG_SECURITY_NETWORK has to be enabled. This option is enabled on the recent kernel packages at Red Hat Enterprise Linux 5, Fedora 7 or later and so on.
Red Hat provides a well document, see the following document for more information.
- Assumption
At the following example, SE-PostgreSQL server process works on 192.168.11.6, and the database client process works on 192.168.11.8. They can communicate each other without any proxies, routers and so on.
- Add entries to SPD (Security Policy Database)
At first, we have to inform communications between 192.168.11.6 and 192.168.11.8 should be transported via IPsec tunnels. The following example requires outbound packets to 192.168.11.8 from 192.168.11.6 and its reverse should be IPsec'ed with ESP mode, and both of SPD entries has system_u:object_r:ipsec_spd_t:s0 as its security context.
Make a file to describe the following configuration at the server and client side. Please note the enumelated IP addresses are reversed at the client side.
Server side (192.168.11.6):
spdadd 192.168.11.6 192.168.11.8 any -ctx 1 1 "system_u:object_r:ipsec_spd_t:s0" -P out ipsec esp/transport//require; spdadd 192.168.11.8 192.168.11.6 any -ctx 1 1 "system_u:object_r:ipsec_spd_t:s0" -P in ipsec esp/transport//require;
The setkey command enables to load the configuration as follows:
# setkey -f <SPD configuration file>
- Edit the /etc/racoon/racoon.conf
At the next, edit the /etc/racoon/racoon.conf to add the configuration block to communicate between 192.168.11.6 and 192.168.11.6. In this example, we use pre-shared-key method to authenticate the remote host for simplification of explanation. Please note the enumelated IP addresses are reversed at the client side.
Server side (192.168.11.6):
# Racoon IKE daemon configuration file.
# See 'man racoon.conf' for a description of the format and entries.
path include "/etc/racoon";
path pre_shared_key "/etc/racoon/psk.txt";
path certificate "/etc/racoon/certs";
path script "/etc/racoon/scripts";
sainfo anonymous
{
#pfs_group 2;
lifetime time 1 hour ;
encryption_algorithm 3des, blowfish 448, rijndael ;
authentication_algorithm hmac_sha1, hmac_md5 ;
compression_algorithm deflate ;
}
remote 192.168.11.8
{
exchange_mode aggressive, main;
my_identifier address;
proposal {
encryption_algorithm 3des;
hash_algorithm sha1;
authentication_method pre_shared_key;
dh_group 2 ;
}
}
- Edit the /etc/racoon/psk.txt
At the next, we add an entry of pre-shared-key. The key-phrase has to be common between the server and client side.
Server side (192.168.11.6):
# file for pre-shared keys used for IKE authentication # format is: 'identifier' 'key' # For example: # # 10.1.1.1 flibbertigibbet # www.example.com 12345 # foo@www.example.com micropachycephalosaurus 192.168.11.8 somethingsecrettext
- Start racoon
Start the racoon daemon at the both of sides.
# service racoon start Starting racoon: [ OK ]
If your configuration is correct, the security context of the peer process is delivered to the server process, and SE-PostgreSQL applies it as the privileges of client. We can confirm it using sepgsql_getcon() function.
static fallback labels
Labeled networks enables to deliver the security context of peer processes, we recommend to apply the feature as far as possible. However, it also has a restriction that SELinux has to be enabled on the client side, so it makes impossible client processes to work on any other operating system. They don't assign processes a security attribute fundamentally.
SELinux provides an alternative way to assign a security context of client, called as static fallback labels. It associates a connection an alternative security context based on the source IP address and inbounding network device, when the labeled networks is unavailable.
It requires the following packages:
- Linux kernel v2.6.25 or later
- netlabel_tools v0.18 or later
In this example, we intend to assign an alternative security context staff_u:staff_r:staff_t:s0:c1 for connections come from 192.168.12.0/24 via eth0, staff_u:staff_r:staff_t:s0:c2 for connections come from 192.168.20.0/24 via eth1, and user_u:user_r:user_t:s0 for elsewhere.
The netlabelctl with unlbl subcommand enables to set up the configuration we intend. We can give an alternative security context for a network address with netmask and a network interface. If it does not care inbounding network devide, default should be placed instead.
# netlabelctl unlbl add interface:eth0 address:192.168.12.0/24 ¥
label:staff_u:staff_r:staff_t:s0:c1
# netlabelctl unlbl add interface:eth1 address:192.168.20.0/24 ¥
label:staff_u:staff_r:staff_t:s0:c2
# netlabelctl unlbl add default address:0.0.0.0/0 ¥
label:user_u:user_r:user_t:s0
Backup and restore
PostgreSQL provides official backup utilities, pg_dump and pg_dumpall. The latest version of them has --security-context option to backup databases with security context. We can restore them via pg_restore command without any specific option.
No need to say, the backup and restore process is not an exception for access controls. The database administrator should have enough privileges to refer and restore them. If not so, the backup is aborted or violated tuples are filtered.
An example output:
<b>$ pg_dump --security-context postgres</b>
:
:
--
-- Name: customer; Type: TABLE; Schema: public; Owner: kaigai; Tablespace:
--
CREATE TABLE customer (
cid integer NOT NULL,
cname character varying(32),
credit character varying(32) SECURITY_CONTEXT = 'system_u:object_r:sepgsql_secret_table_t:s0'
) SECURITY_CONTEXT = 'unconfined_u:object_r:sepgsql_table_t:s0';
ALTER TABLE public.customer OWNER TO kaigai;
:
:
--
-- Data for Name: customer; Type: TABLE DATA; Schema: public; Owner: kaigai
--
COPY customer (security_context, cid, cname, credit) FROM stdin;
unconfined_u:object_r:sepgsql_table_t:s0 10 jack 1111-2222-3333-4444
unconfined_u:object_r:sepgsql_table_t:s0 13 adam 5555-6666-7777-8888
unconfined_u:object_r:sepgsql_table_t:s0 14 liza 9876-5432-1098-7654
\.
:
:
References
Object classes and permission
We can consider the security policy of SELinux as a massive set of allowed actions between two entities. The entities are identified by security context, and the action is a combination of a object class and permissions. The default security policy of SELinux currently defines the following object classes and permissions. This section provides a comprehensive explanation for them.
| db_database | db_table | db_procedure | db_column | db_blob | db_tuple |
|---|---|---|---|---|---|
| create | create | create | create | create | relabelfrom |
| drop | drop | drop | drop | drop | relabelto |
| getattr | getattr | getattr | getattr | getattr | use |
| setattr | setattr | setattr | setattr | setattr | select |
| relabelfrom | relabelfrom | relabelfrom | relabelfrom | relabelfrom | update |
| relabelto | relabelto | relabelto | relabelto | relabelto | insert |
| access | use | execute | use | read | delete |
| install_module | select | entrypoint | select | write | |
| load_module | update | update | import | ||
| get_param | insert | insert | export | ||
| set_param | delete | ||||
| lock |
- db_database object class
This object class is applied when a client tries to access on the database itself or tuples within pg_database system catalogs.
- default security context
- A newly created database object inherits the security context of the client process, if no type transitions. Type transition can be described between the security of the client process and itself in this class. In the default security policy, all new databases are labeled as sepgsql_db_t by the pre-defined type transition rule.
- create
- This permission is required on CREATE DATABASE statement, or insertion of new tuples into pg_database system catalog. SE-PostgreSQL assigns a newly created database a default security context or explicitly specified security context, so the security policy has to allow a client db_database:{create} permission on the new database.
- drop
- This permission is required on DROP DATABASE statement, or deletion of tuples tuples within pg_database system catalog.
- getattr
- This permission is required on exposing contents of pg_database system catalog via SELECT statement and so on.
- setattr
- This permission is required on ALTER DATABASE statement, or updates of tuples within pg_database system catalog.
- relabelfrom
- This permission is required on ALTER DATABASE with SECURITY_CONTEXT option, or updating security_context of tuples within pg_database system catalog. On relabels from old_context to new_context, the security policy has to allow a client db_database:{setattr relabelfrom} permission on the old_context.
- relabelto
- This permission is required on ALTER DATABASE with SECURITY_CONTEXT option, or updating security_context of tuples within pg_database system catalog. On relabels from old_context to new_context, the security policy has to allow a client db_database:{relabelto} permission on the old_context.
- access
- This permission is required when a client connects to SE-PostgreSQL with a specific database. PostgreSQL requires us to choose a database at login, so the security policy has to allow a client db_database:{access} permission on the target database.
- Please note that the current PostgreSQL does not allow to change database within the duration of the connection, so this permission is checked at once at the begining.
- install_module
- This permission is required when we define or alter SQL functions implemented within an external dynamic link library (DLL). The security policy has to allow a client db_database:{install_module} permission on both of the current database and the DLL file.
- For example, when a client (staff_t domain) defines a function within a DLL file labeled as shlib_t on the database labeled as sepgsql_db_t, the following policies are required.
- allow staff_t sepgsql_db_t : db_database { install_module };
- allow staff_t shlib_t : db_database { install_module };
- load_module
- This permission is required when a dynamic link library (DLL) is loaded to the process address space of SE-PostgreSQL. The security policy has to allow the database (not a client) db_database:{load_module} on the DDL file. Please note that thie permission can be checked whenever DLL files are loaded, different from db_database:{install_module} permission.
- get_param
- This permission is required when we refer GUC parameters via SHOW statement. The security policy has to allow a client db_database:{get_param} permission on the current database. Please note that any GUC parameter does not have its security context, and this permission is evaluated on the database.
- set_param
- This permission is required when we update GUC parameters via SET statement. The security policy has to allow a client db_database:{set_param} permission on the current database. Please note that any GUC parameter does not have its security context, and this permission is evaluated on the database.
- db_table object class
This object class is applied when a client tries to access on tables or tuples within pg_class system catalogs which has RELKIND_RELATION as its relkind.
- default security context
- A newly created table object inherits the security context of the database, if no type transitions. Type transition can be described between the security of the client process and the one of the database in this class. In the default security policy, all new tables are labeled as sepgsql_table_t or USER_sepgsql_table_t by the pre-defined type transition rule.
- create
- This permission is required on CREATE TABLE statement, or insertion of new tuples into pg_class system catalog. SE-PostgreSQL assigns a newly created table a default security context or explicitly specified security context, so the security policy has to allow a client db_table:{create} permission on the new table.
- drop
- This permission is required on DROP TABLE statement, or deletion of tuples within pg_class system catalog.
- getattr
- This permission is required on exposing contents of pg_class system catalog via SELECT statement and so on.
- setattr
- This permission is required on ALTER TABLE statement, or updates of tuples within pg_class system catalog.
- relabelfrom
- This permission is required on ALTER TABLE with SECURITY_CONTEXT option, or updating security_context of tuples within pg_class system catalog. On relabels from old_context to new_context, the security policy has to allow a client db_table:{setattr relabelfrom} permission on the old_context.
- relabelto
- This permission is required on ALTER TABLE with SECURITY_CONTEXT option, or updating security_context of tuples within pg_class system catalog. On relabels from old_context to new_context, the security policy has to allow a client db_table:{relabelto} permission on the new_context.
- use
- This permission is required when a given query refers a table which contains columns listed on the conditional clause or others without exposing its content. It means a client can refer the table, but cannot expose its contents.
- For example, the following UPDATE statement requires db_table:{use} permission not only db_table:{update}, because it refers drink table via id column, but its content is not exposed to the client.
- UPDATE drink SET price = 120 WHERE id = 4;
- select
- This permission is required on the following cases:
- A table is refered by SELECT or COPY TO statement.
- A given query has RETURNING clause.
- A table is refered by calculating formulas.
- For example, the following DELETE statement requires db_table:{select} permission not only db_table:{delete}, because it also exposes the contents of table via RETURNING clause.
- DELETE FROM drink RETURNING *;
- The following UPDATE statement also requires db_table:{select} permission not only db_table:{update}, because it uses price column within drink table as a source of new price value, then the information can be exposed via updated value.
- UPDATE drink SET price = price * 2;
- The differences between db_table:{use} and db_table:{select} is whether the contents of table is exposed directly, or not. Please note that a user can theoretically infer the contents of table with only db_table:{use} permission by repeating nonsense queries so many times.
- update
- This permission is required on the table to be updated via UPDATE statement.
- insert
- This permission is required on the table to be inserted via INSERT or COPY FROM statement.
- delete
- This permission is required on the table to be deleted via DELETE or TRUNCATE statement.
- lock
- This permission is required on explicit table locks by LOCK statement. Please note that it is not required on implicit table locks.
- db_procedure object class
- This object class is applied when a client tries to invoke functions or accesses tuples within pg_proc system catalogs.
- default security context
- A newly created function object inherits the security context of the database, if no type transitions. Type transition can be described between the security of the client process and the one of the database in this class. In the default security policy, all new functions are labeled as sepgsql_proc_t or USER_sepgsql_proc_exec_t by the pre-defined type transition rule.
- create
- This permission is required on CREATE FUNCTION statement, or insertion of new tuples into pg_proc system catalog. SE-PostgreSQL assigns a newly created table a default security context or explicitly specified security context, so the security policy has t










