Update sepgsql to add mandatory access control for TRUNCATE

Use SELinux "db_table: { truncate }" to check if permission is granted to
TRUNCATE. Update example SELinux policy to grant needed permission for
TRUNCATE. Add new regression test to demonstrate a positive and negative
cases. Test will only be run if the loaded SELinux policy has the
"db_table: { truncate }" permission. Makes use of recent commit which added
object TRUNCATE hook. Patch by Yuli Khodorkovskiy with minor
editorialization by me. Not back-patched because the object TRUNCATE hook
was not.

Author: Yuli Khodorkovskiy
Reviewed-by: Joe Conway
Discussion: https://postgr.es/m/CAFL5wJcomybj1Xdw7qWmPJRpGuFukKgNrDb6uVBaCMgYS9dkaA%40mail.gmail.com
This commit is contained in:
Joe Conway 2019-11-23 10:41:52 -05:00
parent f7a2002e82
commit 4f66c93f61
8 changed files with 152 additions and 1 deletions

View File

@ -0,0 +1,46 @@
--
-- Regression Test for TRUNCATE
--
--
-- Setup
--
CREATE TABLE julio_claudians (name text, birth_date date);
SECURITY LABEL ON TABLE julio_claudians IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0';
INSERT INTO julio_claudians VALUES ('Augustus', 'September 23, 63 BC'), ('Tiberius', 'November 16, 42 BC'), ('Caligula', 'August 31, 0012'), ('Claudius', 'August 1, 0010'), ('Nero', 'December 15, 0037');
CREATE TABLE flavians (name text, birth_date date);
SECURITY LABEL ON TABLE flavians IS 'system_u:object_r:sepgsql_table_t:s0';
INSERT INTO flavians VALUES ('Vespasian', 'November 17, 0009'), ('Titus', 'December 30, 0039'), ('Domitian', 'October 24, 0051');
SELECT * from julio_claudians;
name | birth_date
----------+---------------
Augustus | 09-23-0063 BC
Tiberius | 11-16-0042 BC
Caligula | 08-31-0012
Claudius | 08-01-0010
Nero | 12-15-0037
(5 rows)
SELECT * from flavians;
name | birth_date
-----------+------------
Vespasian | 11-17-0009
Titus | 12-30-0039
Domitian | 10-24-0051
(3 rows)
TRUNCATE TABLE julio_claudians; -- ok
TRUNCATE TABLE flavians; -- failed
ERROR: SELinux: security policy violation
SELECT * from julio_claudians;
name | birth_date
------+------------
(0 rows)
SELECT * from flavians;
name | birth_date
-----------+------------
Vespasian | 11-17-0009
Titus | 12-30-0039
Domitian | 10-24-0051
(3 rows)

View File

@ -188,6 +188,20 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
case OAT_TRUNCATE:
{
switch (classId)
{
case RelationRelationId:
sepgsql_relation_truncate(objectId);
break;
default:
/* Ignore unsupported object classes */
break;
}
}
break;
case OAT_POST_ALTER:
{
ObjectAccessPostAlter *pa_arg = arg;

View File

@ -516,6 +516,46 @@ sepgsql_relation_drop(Oid relOid)
}
}
/*
* sepgsql_relation_truncate
*
* Check privileges to TRUNCATE the supplied relation.
*/
void
sepgsql_relation_truncate(Oid relOid)
{
ObjectAddress object;
char *audit_name;
uint16_t tclass = 0;
char relkind = get_rel_relkind(relOid);
switch (relkind)
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
tclass = SEPG_CLASS_DB_TABLE;
break;
default:
/* ignore other relkinds */
return;
}
/*
* check db_table:{truncate} permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0;
audit_name = getObjectIdentity(&object);
sepgsql_avc_check_perms(&object,
tclass,
SEPG_DB_TABLE__TRUNCATE,
audit_name,
true);
pfree(audit_name);
}
/*
* sepgsql_relation_relabel
*

View File

@ -359,6 +359,9 @@ static struct
{
"lock", SEPG_DB_TABLE__LOCK
},
{
"truncate", SEPG_DB_TABLE__TRUNCATE
},
{
NULL, 0UL
},

View File

@ -150,6 +150,14 @@ allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_table { getattr selec
allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_column { getattr select update insert };
allow sepgsql_regtest_var_t sepgsql_regtest_var_table_t:db_tuple { select update insert delete };
optional_policy(`
gen_require(`
class db_table { truncate };
')
allow sepgsql_regtest_superuser_t sepgsql_regtest_foo_table_t:db_table { truncate };
')
optional_policy(`
gen_require(`
role unconfined_r;

View File

@ -145,6 +145,7 @@
#define SEPG_DB_TABLE__INSERT (1<<8)
#define SEPG_DB_TABLE__DELETE (1<<9)
#define SEPG_DB_TABLE__LOCK (1<<10)
#define SEPG_DB_TABLE__TRUNCATE (1<<11)
#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE)
#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP)
@ -312,6 +313,7 @@ extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
extern void sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum);
extern void sepgsql_relation_post_create(Oid relOid);
extern void sepgsql_relation_drop(Oid relOid);
extern void sepgsql_relation_truncate(Oid relOid);
extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
extern void sepgsql_relation_setattr(Oid relOid);

View File

@ -0,0 +1,24 @@
--
-- Regression Test for TRUNCATE
--
--
-- Setup
--
CREATE TABLE julio_claudians (name text, birth_date date);
SECURITY LABEL ON TABLE julio_claudians IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0';
INSERT INTO julio_claudians VALUES ('Augustus', 'September 23, 63 BC'), ('Tiberius', 'November 16, 42 BC'), ('Caligula', 'August 31, 0012'), ('Claudius', 'August 1, 0010'), ('Nero', 'December 15, 0037');
CREATE TABLE flavians (name text, birth_date date);
SECURITY LABEL ON TABLE flavians IS 'system_u:object_r:sepgsql_table_t:s0';
INSERT INTO flavians VALUES ('Vespasian', 'November 17, 0009'), ('Titus', 'December 30, 0039'), ('Domitian', 'October 24, 0051');
SELECT * from julio_claudians;
SELECT * from flavians;
TRUNCATE TABLE julio_claudians; -- ok
TRUNCATE TABLE flavians; -- failed
SELECT * from julio_claudians;
SELECT * from flavians;

View File

@ -287,6 +287,20 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
make REGRESS="label dml ddl alter misc" REGRESS_OPTS="--launcher ./launcher" installcheck
tests="label dml ddl alter misc"
# Check if the truncate permission exists in the loaded policy, and if so,
# run the truncate test
#
# Testing the TRUNCATE regression test can be done by manually adding
# the permission with CIL if necessary:
# sudo semodule -cE base
# sudo sed -i -E 's/(class db_table.*?) \)/\1 truncate\)/' base.cil
# sudo semodule -i base.cil
if [ -f /sys/fs/selinux/class/db_table/perms/truncate ]; then
tests+=" truncate"
fi
make REGRESS="$tests" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"