My favorites | Sign in
Logo
                
Search
for
Updated Jun 23, 2008 by KaiGai.Kohei
Labels: Phase-Design
WhatIsSEPostgreSQL  

What is SE-PostgreSQL?

Security-Enhanced PostgreSQL (SE-PostgreSQL) is a security extension built in PostgreSQL. It works as a reference monitor within relational database management system, and provides fine-grained mandatory access control features collaborating with SELinux and its security policy.

These features enable to deploy a database management system into data flow control scheme, integrated with operating system. We call the most characteristic feature of SE-PostgreSQL as ''system-wide consistency in access controls''. Any other RDBMS cannot provide this feature in current.

Why we need SE-PostgreSQL

We can consider RDBMS, including PostgreSQL, is one of the methods to communicate between processes.

SELinux is an operating system feature to apply its security policy whenever we accesses any resource managed by operating system, like files, sockets, IPC objects and so on. Protection of leaking classified information is the most significant purpose of SELinux. However, it can be leaked via various kind of routes, when we have different security policy for several routes. As frequently said, the most vulnerable route is its security level of whole the system. Therefore, we have to apply a single unified security policy for various kind of resources, as SELinux doing.

It enables to protect leaking ''A Secret Information Asset'' managed by users with clearance into another one. But you have to pay attention there are several userspace object managers (like X-Window, RDBMS, etc...) which can be used as a method of inter-processes communication. The data-flows in the userspace are invisible from SELinux, because it works in the kernel.

It means they need to refer the security policy of SELinux to check whether the required access on userspace resources should be allowed, or not. One representative userspace object manager is relational database management systems which can manage massive amount of userspace resources.

SE-PostgreSQL can control accesses on its managing resources, based on the security policy of SELinux. It enables to apply a single unified policy onto database objects (like tables, columns, etc...) as SELinux doing on resources managed by operating system.

Information flow control unified with operating system

The above figure compares the original PostgreSQL with SE-PostgreSQL.

When a SystemHigh user holds classified information and he writes it into filesystem as a file, this file is labeled as Classified by SELinux. If a SystemLow user tries to access the information stored in this file, it is not succeeded because SELinux prevents users with lower clearance to access files with higher one.

However, when a SystemHigh user inserts his classified information into PostgreSQL as a database record, PostgreSQL does not handle it based on the security policy of SELinux, so this record does not have any available security context. Therefore, the SystemLow user can access this recored. It means breakage of data flow control scheme.

Meanwhile, the inserted record into SE-PostgreSQL is labeled as Classified, as SELinux doing on filesystem. SystemLow user cannot access this record, because the security policy of SELinux does not allow him to access and SE-PostgreSQL makes its decision according to the security policy.

In the result, same access control policy is applied for both passes, and there is no breakage of data flow control in SE-PostgreSQL.

mandatory access control

The original PostgreSQL has special privilleged users called as superuser. They can refere, modify and remove any database object without any limitation, as a traditional root can do anything on operating system.

SE-PostgreSQL provides mandatory access control (MAC) feature, and it is applied for any database client including privileged users.

When either MAC or database ACL configured with GRANT/REVOKE statement does not allow a client to access the reuired database object, it is not succeeded.

fine-grained access control

SE-PostgreSQL provides fine-grainer access control than the original PostgreSQL. It includes column- and tuple- level access control.

The table of drink contains six tuples. The four tuples have Unclassified label, and rest of them have Secret one. These attributes are stored in system column named as security_context. When SystemHigh user tries to dump this table with SELECT * FROM drink, the result set contains six tuples. However, SystemLow user get only four tuples, because all tuples labeled as Secret are filtered from the result set.

In column level access control, the current transaction is aborted when a user tries to access violated column. For example, consider the column salary of the following table is labeled as Secret.

+------------------------------+
| TABLE: person                |
+----+----------+-----+--------+
| id | name     | age | salary |
+----+----------+-----+--------+
| 10 | T.Yamada |  28 | $2500  |
| 12 | K.Suzuki |  31 | $3000  |
|  : |    :     |  :  |   :    |

When SystemLow user tries to execute "SELECT * FROM person;", it will be aborted because this query refers salary column without appropriate permission. It is necessary to drop violated columns like "SELECT id, name, age FROM person;".

Examples of SE-PostgreSQL

Row-level access controls

This section shows an example of row-level access controls.

Execute the following SQL operations at unconfined_t domain with SystemLow-SystemHigh, to set up a sample table and function.

CREATE TABLE genre (
    gid     integer primary key,
    dsc     varchar (32)
);
GRANT ALL ON genre TO PUBLIC;
 
CREATE TABLE drink (
    id      integer primary key,
    gid     integer references genre(gid),
    name    varchar(32),
    price   integer
);
GRANT ALL ON drink TO PUBLIC;
 
INSERT INTO genre (gid, dsc)
    VALUES (5, 'soft drink'),
           (6, 'fizzwater'),
           (7, 'alcohol');
 
INSERT INTO drink (id, gid, name, price)
    VALUES (11, NULL, 'water', 100),
           (12,    6, 'coke',  120),
           (13,    5, 'milk',  150),
           (14,    5, 'juice', 130),
           (15,    6, 'cider', 140),
           (16, NULL, 'soup',  180),
           (17,    7, 'beer',  240),
           (18,    7, 'wine',  480);
 
UPDATE genre SET security_context = 'system_u:object_r:sepgsql_table_t:s0:c0' WHERE gid = 6;
UPDATE drink SET security_context = 'system_u:object_r:sepgsql_table_t:s0:c0' WHERE id IN (15,16,17);

...:unconfined_t:SystemLow-SystemHigh will get the following result, without any filtered tuple.

kaigai=# SELECT security_context, * FROM drink;
             security_context             | id | gid | name  | price
------------------------------------------+----+-----+-------+-------
 unconfined_u:object_r:sepgsql_table_t:s0 | 11 |     | water |   100
 unconfined_u:object_r:sepgsql_table_t:s0 | 12 |   6 | coke  |   120
 unconfined_u:object_r:sepgsql_table_t:s0 | 13 |   5 | milk  |   150
 unconfined_u:object_r:sepgsql_table_t:s0 | 14 |   5 | juice |   130
 unconfined_u:object_r:sepgsql_table_t:s0 | 18 |   7 | wine  |   480
 system_u:object_r:sepgsql_table_t:s0:c0  | 15 |   6 | cider |   140
 system_u:object_r:sepgsql_table_t:s0:c0  | 16 |     | soup  |   180
 system_u:object_r:sepgsql_table_t:s0:c0  | 17 |   7 | beer  |   240
(8 rows)
 
kaigai=#

If you have weaker permissions, any violated tuples are filtered from result set.

[kaigai@saba test]$ runcon -l s0 psql -q
kaigai=# SELECT sepgsql_getcon();
              sepgsql_getcon
-------------------------------------------
 unconfined_u:unconfined_r:unconfined_t:s0
(1 row)
 
kaigai=# SELECT security_context, * FROM drink;
             security_context             | id | gid | name  | price
------------------------------------------+----+-----+-------+-------
 unconfined_u:object_r:sepgsql_table_t:s0 | 11 |     | water |   100
 unconfined_u:object_r:sepgsql_table_t:s0 | 12 |   6 | coke  |   120
 unconfined_u:object_r:sepgsql_table_t:s0 | 13 |   5 | milk  |   150
 unconfined_u:object_r:sepgsql_table_t:s0 | 14 |   5 | juice |   130
 unconfined_u:object_r:sepgsql_table_t:s0 | 18 |   7 | wine  |   480
(5 rows)
 
kaigai=#

SE-PostgreSQL filters any violated tuple, even if a query contains multiple relations. It is done before joining relations, as if filtered tuples don't exist.

Pay attention, 'fizzwater' is filtered but 'coke' is remain in the result set.

[kaigai@saba test]$ runcon -l s0 psql -q
kaigai=# SELECT sepgsql_getcon();
              sepgsql_getcon
-------------------------------------------
 unconfined_u:unconfined_r:unconfined_t:s0
(1 row)
 
kaigai=# SELECT * FROM drink left join genre on drink.gid = genre.gid;
 id | gid | name  | price | gid |    dsc
----+-----+-------+-------+-----+------------
 11 |     | water |   100 |     |
 12 |   6 | coke  |   120 |     |
 13 |   5 | milk  |   150 |   5 | soft drink
 14 |   5 | juice |   130 |   5 | soft drink
 18 |   7 | wine  |   480 |   7 | alcohol
(5 rows)
 
kaigai=#

Column level access controls

This section shows an example of column-level access controls.

Execute the following SQL operations at unconfined_t domain, to set up a sample table and function.

CREATE TABLE customer (
    cid     integer primary key,
    cname   varchar(32),
    credit  varchar(32)  CONTEXT = 'system_u:object_r:sepgsql_secret_table_t:s0'
);
GRANT ALL ON customer TO PUBLIC;
 
INSERT INTO customer (cid, cname, credit)
    VALUES ( 10, 'jack', '1111-2222-3333-4444'),
           ( 13, 'adam', '5555-6666-7777-8888'),
           ( 14, 'liza', '9876-5432-1098-7654');
 
CREATE OR REPLACE FUNCTION show_credit (integer) RETURNS text
    LANGUAGE 'sql'
    CONTEXT = 'system_u:object_r:sepgsql_trusted_proc_t'
    AS 'SELECT substring(credit from ''^[0-9]+-'') || ''xxxx-xxxx-xxxx'' FROM customer WHERE CID = $1';

Pay attention customer.credit is declared as sepgsql_secret_table_t.

It means non-administrative domain canno read this column, as follows:

[tak@saba ~]$ id -Z
user_u:user_r:user_t:s0
 
[tak@saba ~]$ psql postgres
Welcome to psql 8.3.0, the PostgreSQL interactive terminal.
 
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit
 
postgres=# SELECT sepgsql_getcon();
     sepgsql_getcon
-------------------------
 user_u:user_r:user_t:s0
(1 row)
 
postgres=# SELECT * FROM customer;
ERROR:  SELinux: denied { select }                            \
        scontext=user_u:user_r:user_t:s0                      \
        tcontext=system_u:object_r:sepgsql_secret_table_t:s0  \
        tclass=db_column name=customer.credit

The following example shows clearly that refering customer.credit prevents to execute SQL.

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

The idea of ''trusted-procedure'' enables to provide safe methods to access prevented database objects.

Pay attention show_credit(integer) is declared as sepgsql_trusted_proc_t. Invokation of sepgsql_trusted_proc_t makes domain transition to sepgsql_trusted_domain_t. The default security policy allows this domain to access widespread database objects. show_credit(integer) returns head of 4 chars in credit card number and rest of turned letter. It can refer sepgsql_secret_table_t column, but it does not leak the contains.

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)
 
postgres=#

How to build/install SE-PostgreSQL

To apply RPM package is a recommended way to install/set up SE-PostgreSQL. We now provide sepostgresql RPM package, and you can obtain it from:

http://download.fedora.redhat.com/pub/fedora/linux/development/

However, you can build it by yourself.

ENVIRONMENT

Please confirm your system environment.

BUILD

$ tar jxvf postgresql-snapshot.tar.bz2
$ cd postgresql-snapshot
$ patch -p1 < sepostgresql-sepgsql-8.4devel-3-r914.patch
$ patch -p1 < sepostgresql-pg_dump-8.4devel-3-r914.patch
$ patch -p1 < sepostgresql-policy-8.4devel-3-r914.patch
$ patch -p1 < sepostgresql-docs-8.4devel-3-r914.patch
$ ./configure --enable-selinux
$ make
$ make -C ./contrib/sepgsql_policy

$ su
# /usr/sbin/semodule -i ./contrib/sepgsql_policy/sepostgresql.pp
  (NOTE: apply sepostgresql-devel.pp for selinux-policy-3.4.2, or later.)
# make install
# /sbin/restorecon -R /usr/local/pgsql

SETUP

$ mkdir -p $PGDATA
$ chcon -t postgresql_db_t -R $PGDATA
$ initdb
$ pg_ctl start

SUMMARYS FOR EVERY PATCHES

[1/4] - sepostgresql-sepgsql-8.4devel-3-r914.patch

This patch provides core facilities of PGACE/SE-PostgreSQL.

PGACE (PostgreSQL Access Control Extension) framework has a similar concept of LSM (Linux Security Module). It can provide a guest module several hooks at strategic points. The guest module can make its decision whether required actions should be allowed, or not. In addition, PGACE also provides falicilites to manage security attribute of database objects. A security attribute is associated with any database object, and PGACE enables to translate it between internal identifier and text representation.

Security-Enhanced PostgreSQL (SE-PostgreSQL) is a security extension built in PostgreSQL, to provide system-wide consistency in access controls. It enables to apply a single unigied security policy of SELinux for both operating system and database management system. In addition, it also provides fine-grained mandatory access which includes column-/row- level non-bypassable access control even if privileged database users.

[2/4] - sepostgresql-pg_dump-8.4devel-3-r914.patch

This patch gives us a feature to dump database with security attribute. It is turned on with '--enable-selinux' option at pg_dump/pg_dumpall, when the server works as SE- version. No need to say, users need to have enough capabilities to dump whole of database. It it same when they tries to restore the database.

[3/4] - sepostgresql-policy-8.4devel-3-r914.patch

This patch gives us the default (development) security policy of SE-PostgreSQL. You can build it as a security poicy module, and link it with the existing distributor's policy.

selinux-policy-3.4.2 or later contains most of the policy. If it is instaleld on your system, use sepostgresql-devel.pp instead. It provides several booleans useful for development.

  • sepgsql_enable_auditallow
  • It enables to print/write access allowed logs (default: off).
  • sepgsql_enable_auditdeny
  • It enables to print/write access denied logs (default: on).
  • sepgsql_regression_test_mode
  • It gives us additional permissions to run regression test (default: off).

[4/4] - sepostgresql-docs-8.4devel-3-r914.patch

This patch gives us documentation updates which include a step by step guide of build & installation, a feature description of SE-PostgreSQL, administration hints and design of the PGACE security framework.


Sign in to add a comment
Hosted by Google Code