mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
sepgsql, an SE-Linux integration for PostgreSQL
This is still pretty rough - among other things, the documentation needs work, and the messages need a visit from the style police - but this gets the basic framework in place. KaiGai Kohei
This commit is contained in:
parent
e5487f65fd
commit
968bc6fac9
122
configure
vendored
122
configure
vendored
@ -715,6 +715,7 @@ with_libxslt
|
|||||||
with_libxml
|
with_libxml
|
||||||
XML2_CONFIG
|
XML2_CONFIG
|
||||||
with_ossp_uuid
|
with_ossp_uuid
|
||||||
|
with_selinux
|
||||||
with_openssl
|
with_openssl
|
||||||
with_bonjour
|
with_bonjour
|
||||||
with_ldap
|
with_ldap
|
||||||
@ -837,6 +838,7 @@ with_pam
|
|||||||
with_ldap
|
with_ldap
|
||||||
with_bonjour
|
with_bonjour
|
||||||
with_openssl
|
with_openssl
|
||||||
|
with_selinux
|
||||||
with_readline
|
with_readline
|
||||||
with_libedit_preferred
|
with_libedit_preferred
|
||||||
with_ossp_uuid
|
with_ossp_uuid
|
||||||
@ -848,6 +850,7 @@ with_gnu_ld
|
|||||||
enable_largefile
|
enable_largefile
|
||||||
enable_float4_byval
|
enable_float4_byval
|
||||||
enable_float8_byval
|
enable_float8_byval
|
||||||
|
enable_float8_byval
|
||||||
'
|
'
|
||||||
ac_precious_vars='build_alias
|
ac_precious_vars='build_alias
|
||||||
host_alias
|
host_alias
|
||||||
@ -858,6 +861,7 @@ LDFLAGS
|
|||||||
LIBS
|
LIBS
|
||||||
CPPFLAGS
|
CPPFLAGS
|
||||||
CPP
|
CPP
|
||||||
|
CPPFLAGS
|
||||||
LDFLAGS_EX
|
LDFLAGS_EX
|
||||||
LDFLAGS_SL
|
LDFLAGS_SL
|
||||||
DOCBOOKSTYLE'
|
DOCBOOKSTYLE'
|
||||||
@ -1533,6 +1537,7 @@ Optional Packages:
|
|||||||
--with-ldap build with LDAP support
|
--with-ldap build with LDAP support
|
||||||
--with-bonjour build with Bonjour support
|
--with-bonjour build with Bonjour support
|
||||||
--with-openssl build with OpenSSL support
|
--with-openssl build with OpenSSL support
|
||||||
|
--with-selinux build with SELinux support
|
||||||
--without-readline do not use GNU Readline nor BSD Libedit for editing
|
--without-readline do not use GNU Readline nor BSD Libedit for editing
|
||||||
--with-libedit-preferred
|
--with-libedit-preferred
|
||||||
prefer BSD Libedit over GNU Readline
|
prefer BSD Libedit over GNU Readline
|
||||||
@ -5364,6 +5369,40 @@ fi
|
|||||||
$as_echo "$with_openssl" >&6; }
|
$as_echo "$with_openssl" >&6; }
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# SELinux
|
||||||
|
#
|
||||||
|
{ $as_echo "$as_me:$LINENO: checking whether to build with SELinux support" >&5
|
||||||
|
$as_echo_n "checking whether to build with SELinux support... " >&6; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Check whether --with-selinux was given.
|
||||||
|
if test "${with_selinux+set}" = set; then
|
||||||
|
withval=$with_selinux;
|
||||||
|
case $withval in
|
||||||
|
yes)
|
||||||
|
:
|
||||||
|
;;
|
||||||
|
no)
|
||||||
|
:
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
{ { $as_echo "$as_me:$LINENO: error: no argument expected for --with-selinux option" >&5
|
||||||
|
$as_echo "$as_me: error: no argument expected for --with-selinux option" >&2;}
|
||||||
|
{ (exit 1); exit 1; }; }
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
else
|
||||||
|
with_selinux=no
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ $as_echo "$as_me:$LINENO: result: $with_selinux" >&5
|
||||||
|
$as_echo "$with_selinux" >&6; }
|
||||||
|
|
||||||
#
|
#
|
||||||
# Readline
|
# Readline
|
||||||
@ -9291,6 +9330,89 @@ fi
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# for contrib/sepgsql
|
||||||
|
if test "$with_selinux" = yes; then
|
||||||
|
|
||||||
|
{ $as_echo "$as_me:$LINENO: checking for getpeercon_raw in -lselinux" >&5
|
||||||
|
$as_echo_n "checking for getpeercon_raw in -lselinux... " >&6; }
|
||||||
|
if test "${ac_cv_lib_selinux_getpeercon_raw+set}" = set; then
|
||||||
|
$as_echo_n "(cached) " >&6
|
||||||
|
else
|
||||||
|
ac_check_lib_save_LIBS=$LIBS
|
||||||
|
LIBS="-lselinux $LIBS"
|
||||||
|
cat >conftest.$ac_ext <<_ACEOF
|
||||||
|
/* confdefs.h. */
|
||||||
|
_ACEOF
|
||||||
|
cat confdefs.h >>conftest.$ac_ext
|
||||||
|
cat >>conftest.$ac_ext <<_ACEOF
|
||||||
|
/* end confdefs.h. */
|
||||||
|
|
||||||
|
/* Override any GCC internal prototype to avoid an error.
|
||||||
|
Use char because int might match the return type of a GCC
|
||||||
|
builtin and then its argument prototype would still apply. */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
char getpeercon_raw ();
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
return getpeercon_raw ();
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
rm -f conftest.$ac_objext conftest$ac_exeext
|
||||||
|
if { (ac_try="$ac_link"
|
||||||
|
case "(($ac_try" in
|
||||||
|
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||||
|
*) ac_try_echo=$ac_try;;
|
||||||
|
esac
|
||||||
|
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
|
||||||
|
$as_echo "$ac_try_echo") >&5
|
||||||
|
(eval "$ac_link") 2>conftest.er1
|
||||||
|
ac_status=$?
|
||||||
|
grep -v '^ *+' conftest.er1 >conftest.err
|
||||||
|
rm -f conftest.er1
|
||||||
|
cat conftest.err >&5
|
||||||
|
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||||
|
(exit $ac_status); } && {
|
||||||
|
test -z "$ac_c_werror_flag" ||
|
||||||
|
test ! -s conftest.err
|
||||||
|
} && test -s conftest$ac_exeext && {
|
||||||
|
test "$cross_compiling" = yes ||
|
||||||
|
$as_test_x conftest$ac_exeext
|
||||||
|
}; then
|
||||||
|
ac_cv_lib_selinux_getpeercon_raw=yes
|
||||||
|
else
|
||||||
|
$as_echo "$as_me: failed program was:" >&5
|
||||||
|
sed 's/^/| /' conftest.$ac_ext >&5
|
||||||
|
|
||||||
|
ac_cv_lib_selinux_getpeercon_raw=no
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf conftest.dSYM
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext
|
||||||
|
LIBS=$ac_check_lib_save_LIBS
|
||||||
|
fi
|
||||||
|
{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon_raw" >&5
|
||||||
|
$as_echo "$ac_cv_lib_selinux_getpeercon_raw" >&6; }
|
||||||
|
if test "x$ac_cv_lib_selinux_getpeercon_raw" = x""yes; then
|
||||||
|
cat >>confdefs.h <<_ACEOF
|
||||||
|
#define HAVE_LIBSELINUX 1
|
||||||
|
_ACEOF
|
||||||
|
|
||||||
|
LIBS="-lselinux $LIBS"
|
||||||
|
|
||||||
|
else
|
||||||
|
{ { $as_echo "$as_me:$LINENO: error: library 'libselinux' is required for SELinux support" >&5
|
||||||
|
$as_echo "$as_me: error: library 'libselinux' is required for SELinux support" >&2;}
|
||||||
|
{ (exit 1); exit 1; }; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
# for contrib/uuid-ossp
|
# for contrib/uuid-ossp
|
||||||
if test "$with_ossp_uuid" = yes ; then
|
if test "$with_ossp_uuid" = yes ; then
|
||||||
{ $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
|
{ $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
|
||||||
|
13
configure.in
13
configure.in
@ -676,6 +676,13 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
|
|||||||
AC_MSG_RESULT([$with_openssl])
|
AC_MSG_RESULT([$with_openssl])
|
||||||
AC_SUBST(with_openssl)
|
AC_SUBST(with_openssl)
|
||||||
|
|
||||||
|
#
|
||||||
|
# SELinux
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([whether to build with SELinux support])
|
||||||
|
PGAC_ARG_BOOL(with, selinux, no, [build with SELinux support])
|
||||||
|
AC_SUBST(with_selinux)
|
||||||
|
AC_MSG_RESULT([$with_selinux])
|
||||||
|
|
||||||
#
|
#
|
||||||
# Readline
|
# Readline
|
||||||
@ -948,6 +955,12 @@ if test "$with_libxslt" = yes ; then
|
|||||||
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
|
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# for contrib/sepgsql
|
||||||
|
if test "$with_selinux" = yes; then
|
||||||
|
AC_CHECK_LIB(selinux, getpeercon_raw, [],
|
||||||
|
[AC_MSG_ERROR([library 'libselinux' is required for SELinux support])])
|
||||||
|
fi
|
||||||
|
|
||||||
# for contrib/uuid-ossp
|
# for contrib/uuid-ossp
|
||||||
if test "$with_ossp_uuid" = yes ; then
|
if test "$with_ossp_uuid" = yes ; then
|
||||||
AC_CHECK_LIB(ossp-uuid, uuid_export,
|
AC_CHECK_LIB(ossp-uuid, uuid_export,
|
||||||
|
@ -61,6 +61,10 @@ ifeq ($(with_libxml),yes)
|
|||||||
SUBDIRS += xml2
|
SUBDIRS += xml2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(with_selinux),yes)
|
||||||
|
SUBDIRS += sepgsql
|
||||||
|
endif
|
||||||
|
|
||||||
# Missing:
|
# Missing:
|
||||||
# start-scripts \ (does not have a makefile)
|
# start-scripts \ (does not have a makefile)
|
||||||
|
|
||||||
|
@ -163,6 +163,10 @@ seg -
|
|||||||
Confidence-interval datatype (GiST indexing example)
|
Confidence-interval datatype (GiST indexing example)
|
||||||
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
|
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
|
||||||
|
|
||||||
|
sepgsql -
|
||||||
|
External security provider using SELinux
|
||||||
|
by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||||
|
|
||||||
spi -
|
spi -
|
||||||
Various trigger functions, examples for using SPI.
|
Various trigger functions, examples for using SPI.
|
||||||
|
|
||||||
|
1
contrib/sepgsql/.gitignore
vendored
Normal file
1
contrib/sepgsql/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/sepgsql.sql
|
25
contrib/sepgsql/Makefile
Normal file
25
contrib/sepgsql/Makefile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# contrib/sepgsql/Makefile
|
||||||
|
|
||||||
|
MODULE_big = sepgsql
|
||||||
|
OBJS = hooks.o selinux.o label.o dml.o \
|
||||||
|
schema.o relation.o proc.o
|
||||||
|
DATA_built = sepgsql.sql sepgsql-regtest.pp
|
||||||
|
REGRESS = label dml misc
|
||||||
|
EXTRA_CLEAN = -r tmp *.pp sepgsql-regtest.if sepgsql-regtest.fc
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/sepgsql
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
SHLIB_LINK += $(filter -lselinux, $(LIBS))
|
||||||
|
REGRESS_OPTS += --launcher $(top_builddir)/contrib/sepgsql/launcher
|
||||||
|
|
||||||
|
sepgsql-regtest.pp: sepgsql-regtest.te
|
||||||
|
$(MAKE) -f $(DESTDIR)/usr/share/selinux/devel/Makefile $@
|
353
contrib/sepgsql/dml.c
Normal file
353
contrib/sepgsql/dml.c
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/dml.c
|
||||||
|
*
|
||||||
|
* Routines to handle DML permission checks
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/sysattr.h"
|
||||||
|
#include "access/tupdesc.h"
|
||||||
|
#include "catalog/catalog.h"
|
||||||
|
#include "catalog/heap.h"
|
||||||
|
#include "catalog/pg_attribute.h"
|
||||||
|
#include "catalog/pg_class.h"
|
||||||
|
#include "catalog/pg_inherits_fn.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "commands/tablecmds.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
#include "nodes/bitmapset.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fixup_whole_row_references
|
||||||
|
*
|
||||||
|
* When user reference a whole of row, it is equivalent to reference to
|
||||||
|
* all the user columns (not system columns). So, we need to fix up the
|
||||||
|
* given bitmapset, if it contains a whole of the row reference.
|
||||||
|
*/
|
||||||
|
static Bitmapset *
|
||||||
|
fixup_whole_row_references(Oid relOid, Bitmapset *columns)
|
||||||
|
{
|
||||||
|
Bitmapset *result;
|
||||||
|
HeapTuple tuple;
|
||||||
|
AttrNumber natts;
|
||||||
|
AttrNumber attno;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/* if no whole of row references, do not anything */
|
||||||
|
index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
|
||||||
|
if (!bms_is_member(index, columns))
|
||||||
|
return columns;
|
||||||
|
|
||||||
|
/* obtain number of attributes */
|
||||||
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "cache lookup failed for relation %u", relOid);
|
||||||
|
natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
|
||||||
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
|
/* fix up the given columns */
|
||||||
|
result = bms_copy(columns);
|
||||||
|
result = bms_del_member(result, index);
|
||||||
|
|
||||||
|
for (attno=1; attno <= natts; attno++)
|
||||||
|
{
|
||||||
|
tuple = SearchSysCache2(ATTNUM,
|
||||||
|
ObjectIdGetDatum(relOid),
|
||||||
|
Int16GetDatum(attno));
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
index = attno - FirstLowInvalidHeapAttributeNumber;
|
||||||
|
|
||||||
|
result = bms_add_member(result, index);
|
||||||
|
|
||||||
|
ReleaseSysCache(tuple);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fixup_inherited_columns
|
||||||
|
*
|
||||||
|
* When user is querying on a table with children, it implicitly accesses
|
||||||
|
* child tables also. So, we also need to check security label of child
|
||||||
|
* tables and columns, but here is no guarantee attribute numbers are
|
||||||
|
* same between the parent ans children.
|
||||||
|
* It returns a bitmapset which contains attribute number of the child
|
||||||
|
* table based on the given bitmapset of the parent.
|
||||||
|
*/
|
||||||
|
static Bitmapset *
|
||||||
|
fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
|
||||||
|
{
|
||||||
|
AttrNumber attno;
|
||||||
|
Bitmapset *tmpset;
|
||||||
|
Bitmapset *result = NULL;
|
||||||
|
char *attname;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* obviously, no need to do anything here
|
||||||
|
*/
|
||||||
|
if (parentId == childId)
|
||||||
|
return columns;
|
||||||
|
|
||||||
|
tmpset = bms_copy(columns);
|
||||||
|
while ((index = bms_first_member(tmpset)) > 0)
|
||||||
|
{
|
||||||
|
attno = index + FirstLowInvalidHeapAttributeNumber;
|
||||||
|
/*
|
||||||
|
* whole-row-reference shall be fixed-up later
|
||||||
|
*/
|
||||||
|
if (attno == InvalidAttrNumber)
|
||||||
|
{
|
||||||
|
result = bms_add_member(result, index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
attname = get_attname(parentId, attno);
|
||||||
|
if (!attname)
|
||||||
|
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
||||||
|
attno, parentId);
|
||||||
|
attno = get_attnum(childId, attname);
|
||||||
|
if (attno == InvalidAttrNumber)
|
||||||
|
elog(ERROR, "cache lookup failed for attribute %s of relation %u",
|
||||||
|
attname, childId);
|
||||||
|
|
||||||
|
index = attno - FirstLowInvalidHeapAttributeNumber;
|
||||||
|
result = bms_add_member(result, index);
|
||||||
|
|
||||||
|
pfree(attname);
|
||||||
|
}
|
||||||
|
bms_free(tmpset);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check_relation_privileges
|
||||||
|
*
|
||||||
|
* It actually checks required permissions on a certain relation
|
||||||
|
* and its columns.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
check_relation_privileges(Oid relOid,
|
||||||
|
Bitmapset *selected,
|
||||||
|
Bitmapset *modified,
|
||||||
|
uint32 required,
|
||||||
|
bool abort)
|
||||||
|
{
|
||||||
|
char relkind = get_rel_relkind(relOid);
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
Bitmapset *columns;
|
||||||
|
int index;
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hardwired Policies:
|
||||||
|
* SE-PostgreSQL enforces
|
||||||
|
* - clients cannot modify system catalogs using DMLs
|
||||||
|
* - clients cannot reference/modify toast relations using DMLs
|
||||||
|
*/
|
||||||
|
if (sepgsql_getenforce() > 0)
|
||||||
|
{
|
||||||
|
Oid relnamespace = get_rel_namespace(relOid);
|
||||||
|
|
||||||
|
if (IsSystemNamespace(relnamespace) &&
|
||||||
|
(required & (SEPG_DB_TABLE__UPDATE |
|
||||||
|
SEPG_DB_TABLE__INSERT |
|
||||||
|
SEPG_DB_TABLE__DELETE)) != 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("selinux: hardwired security policy violation")));
|
||||||
|
|
||||||
|
if (relkind == RELKIND_TOASTVALUE)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("selinux: hardwired security policy violation")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check permissions on the relation
|
||||||
|
*/
|
||||||
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
||||||
|
switch (relkind)
|
||||||
|
{
|
||||||
|
case RELKIND_RELATION:
|
||||||
|
result = sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_TABLE,
|
||||||
|
required,
|
||||||
|
get_rel_name(relOid),
|
||||||
|
abort);
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RELKIND_SEQUENCE:
|
||||||
|
Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
|
||||||
|
|
||||||
|
if (required & SEPG_DB_TABLE__SELECT)
|
||||||
|
result = sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_SEQUENCE,
|
||||||
|
SEPG_DB_SEQUENCE__GET_VALUE,
|
||||||
|
get_rel_name(relOid),
|
||||||
|
abort);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
case RELKIND_VIEW:
|
||||||
|
result = sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_VIEW,
|
||||||
|
SEPG_DB_VIEW__EXPAND,
|
||||||
|
get_rel_name(relOid),
|
||||||
|
abort);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* nothing to be checked */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check permissions on the columns
|
||||||
|
*/
|
||||||
|
selected = fixup_whole_row_references(relOid, selected);
|
||||||
|
modified = fixup_whole_row_references(relOid, modified);
|
||||||
|
columns = bms_union(selected, modified);
|
||||||
|
|
||||||
|
while ((index = bms_first_member(columns)) >= 0)
|
||||||
|
{
|
||||||
|
AttrNumber attnum;
|
||||||
|
uint32 column_perms = 0;
|
||||||
|
char audit_name[NAMEDATALEN * 2 + 10];
|
||||||
|
|
||||||
|
if (bms_is_member(index, selected))
|
||||||
|
column_perms |= SEPG_DB_COLUMN__SELECT;
|
||||||
|
if (bms_is_member(index, modified))
|
||||||
|
{
|
||||||
|
if (required & SEPG_DB_TABLE__UPDATE)
|
||||||
|
column_perms |= SEPG_DB_COLUMN__UPDATE;
|
||||||
|
if (required & SEPG_DB_TABLE__INSERT)
|
||||||
|
column_perms |= SEPG_DB_COLUMN__INSERT;
|
||||||
|
}
|
||||||
|
if (column_perms == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* obtain column's permission */
|
||||||
|
attnum = index + FirstLowInvalidHeapAttributeNumber;
|
||||||
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
|
||||||
|
snprintf(audit_name, sizeof(audit_name), "%s.%s",
|
||||||
|
get_rel_name(relOid), get_attname(relOid, attnum));
|
||||||
|
|
||||||
|
result = sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN,
|
||||||
|
column_perms,
|
||||||
|
audit_name,
|
||||||
|
abort);
|
||||||
|
if (!result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_dml_privileges
|
||||||
|
*
|
||||||
|
* Entrypoint of the DML permission checks
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sepgsql_dml_privileges(List *rangeTabls, bool abort)
|
||||||
|
{
|
||||||
|
ListCell *lr;
|
||||||
|
|
||||||
|
foreach (lr, rangeTabls)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = lfirst(lr);
|
||||||
|
uint32 required = 0;
|
||||||
|
List *tableIds;
|
||||||
|
ListCell *li;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only regular relations shall be checked
|
||||||
|
*/
|
||||||
|
if (rte->rtekind != RTE_RELATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find out required permissions
|
||||||
|
*/
|
||||||
|
if (rte->requiredPerms & ACL_SELECT)
|
||||||
|
required |= SEPG_DB_TABLE__SELECT;
|
||||||
|
if (rte->requiredPerms & ACL_INSERT)
|
||||||
|
required |= SEPG_DB_TABLE__INSERT;
|
||||||
|
if (rte->requiredPerms & ACL_UPDATE)
|
||||||
|
{
|
||||||
|
if (!bms_is_empty(rte->modifiedCols))
|
||||||
|
required |= SEPG_DB_TABLE__UPDATE;
|
||||||
|
else
|
||||||
|
required |= SEPG_DB_TABLE__LOCK;
|
||||||
|
}
|
||||||
|
if (rte->requiredPerms & ACL_DELETE)
|
||||||
|
required |= SEPG_DB_TABLE__DELETE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip, if nothing to be checked
|
||||||
|
*/
|
||||||
|
if (required == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this RangeTblEntry is also supposed to reference inherited
|
||||||
|
* tables, we need to check security label of the child tables.
|
||||||
|
* So, we expand rte->relid into list of OIDs of inheritance
|
||||||
|
* hierarchy, then checker routine will be invoked for each
|
||||||
|
* relations.
|
||||||
|
*/
|
||||||
|
if (!rte->inh)
|
||||||
|
tableIds = list_make1_oid(rte->relid);
|
||||||
|
else
|
||||||
|
tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
|
||||||
|
|
||||||
|
foreach (li, tableIds)
|
||||||
|
{
|
||||||
|
Oid tableOid = lfirst_oid(li);
|
||||||
|
Bitmapset *selectedCols;
|
||||||
|
Bitmapset *modifiedCols;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* child table has different attribute numbers, so we need
|
||||||
|
* to fix up them.
|
||||||
|
*/
|
||||||
|
selectedCols = fixup_inherited_columns(rte->relid, tableOid,
|
||||||
|
rte->selectedCols);
|
||||||
|
modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
|
||||||
|
rte->modifiedCols);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check permissions on individual tables
|
||||||
|
*/
|
||||||
|
if (!check_relation_privileges(tableOid,
|
||||||
|
selectedCols,
|
||||||
|
modifiedCols,
|
||||||
|
required, abort))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
list_free(tableIds);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
182
contrib/sepgsql/expected/dml.out
Normal file
182
contrib/sepgsql/expected/dml.out
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for DML Permissions
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Setup
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (a int, b text);
|
||||||
|
SECURITY LABEL ON TABLE t1 IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||||
|
CREATE TABLE t2 (x int, y text);
|
||||||
|
SECURITY LABEL ON TABLE t2 IS 'system_u:object_r:sepgsql_ro_table_t:s0';
|
||||||
|
INSERT INTO t2 VALUES (1, 'xxx'), (2, 'yyy'), (3, 'zzz');
|
||||||
|
CREATE TABLE t3 (s int, t text);
|
||||||
|
SECURITY LABEL ON TABLE t3 IS 'system_u:object_r:sepgsql_fixed_table_t:s0';
|
||||||
|
INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
|
||||||
|
CREATE TABLE t4 (m int, n text);
|
||||||
|
SECURITY LABEL ON TABLE t4 IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
INSERT INTO t4 VALUES (1, 'mmm'), (2, 'nnn'), (3, 'ooo');
|
||||||
|
CREATE TABLE t5 (e text, f text, g text);
|
||||||
|
SECURITY LABEL ON TABLE t5 IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
CREATE TABLE customer (cid int primary key, cname text, ccredit text);
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "customer_pkey" for table "customer"
|
||||||
|
SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
INSERT INTO customer VALUES (1, 'Taro', '1111-2222-3333-4444'),
|
||||||
|
(2, 'Hanako', '5555-6666-7777-8888');
|
||||||
|
CREATE FUNCTION customer_credit(int) RETURNS text
|
||||||
|
AS 'SELECT regexp_replace(ccredit, ''-[0-9]+$'', ''-????'') FROM customer WHERE cid = $1'
|
||||||
|
LANGUAGE sql;
|
||||||
|
SECURITY LABEL ON FUNCTION customer_credit(int)
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
SELECT objtype, objname, label FROM pg_seclabels
|
||||||
|
WHERE provider = 'selinux'
|
||||||
|
AND objtype in ('table', 'column')
|
||||||
|
AND objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g');
|
||||||
|
objtype | objname | label
|
||||||
|
---------+---------+---------------------------------------------
|
||||||
|
table | t1 | system_u:object_r:sepgsql_table_t:s0
|
||||||
|
table | t2 | system_u:object_r:sepgsql_ro_table_t:s0
|
||||||
|
table | t3 | system_u:object_r:sepgsql_fixed_table_t:s0
|
||||||
|
table | t4 | system_u:object_r:sepgsql_secret_table_t:s0
|
||||||
|
table | t5 | system_u:object_r:sepgsql_table_t:s0
|
||||||
|
column | t5.g | system_u:object_r:sepgsql_secret_table_t:s0
|
||||||
|
column | t5.f | system_u:object_r:sepgsql_ro_table_t:s0
|
||||||
|
column | t5.e | system_u:object_r:sepgsql_table_t:s0
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
-- Hardwired Rules
|
||||||
|
UPDATE pg_attribute SET attisdropped = true
|
||||||
|
WHERE attrelid = 't5'::regclass AND attname = 'f'; -- failed
|
||||||
|
ERROR: selinux: hardwired security policy violation
|
||||||
|
--
|
||||||
|
-- Simple DML statements
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM t1; -- ok
|
||||||
|
a | b
|
||||||
|
---+-----
|
||||||
|
1 | aaa
|
||||||
|
2 | bbb
|
||||||
|
3 | ccc
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM t2; -- ok
|
||||||
|
x | y
|
||||||
|
---+-----
|
||||||
|
1 | xxx
|
||||||
|
2 | yyy
|
||||||
|
3 | zzz
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM t3; -- ok
|
||||||
|
s | t
|
||||||
|
---+-----
|
||||||
|
1 | sss
|
||||||
|
2 | ttt
|
||||||
|
3 | uuu
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM t4; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
SELECT * FROM t5; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
SELECT e,f FROM t5; -- ok
|
||||||
|
e | f
|
||||||
|
---+---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM customer; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
SELECT cid, cname, customer_credit(cid) FROM customer; -- ok
|
||||||
|
cid | cname | customer_credit
|
||||||
|
-----+--------+---------------------
|
||||||
|
1 | Taro | 1111-2222-3333-????
|
||||||
|
2 | Hanako | 5555-6666-7777-????
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM t5; -- ok
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM t5 WHERE g IS NULL; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
INSERT INTO t1 VALUES (4, 'abc'); -- ok
|
||||||
|
INSERT INTO t2 VALUES (4, 'xyz'); -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
INSERT INTO t3 VALUES (4, 'stu'); -- ok
|
||||||
|
INSERT INTO t4 VALUES (4, 'mno'); -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
INSERT INTO t5 VALUES (1,2,3); -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
INSERT INTO t5 (e,f) VALUES ('abc', 'def'); -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
INSERT INTO t5 (e) VALUES ('abc'); -- ok
|
||||||
|
UPDATE t1 SET b = b || '_upd'; -- ok
|
||||||
|
UPDATE t2 SET y = y || '_upd'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
UPDATE t3 SET t = t || '_upd'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
UPDATE t4 SET n = n || '_upd'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
UPDATE t5 SET e = 'xyz'; -- ok
|
||||||
|
UPDATE t5 SET e = f || '_upd'; -- ok
|
||||||
|
UPDATE t5 SET e = g || '_upd'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
DELETE FROM t1; -- ok
|
||||||
|
DELETE FROM t2; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
DELETE FROM t3; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
DELETE FROM t4; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
DELETE FROM t5; -- ok
|
||||||
|
DELETE FROM t5 WHERE f IS NULL; -- ok
|
||||||
|
DELETE FROM t5 WHERE g IS NULL; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
--
|
||||||
|
-- COPY TO/FROM statements
|
||||||
|
--
|
||||||
|
COPY t1 TO '/dev/null'; -- ok
|
||||||
|
COPY t2 TO '/dev/null'; -- ok
|
||||||
|
COPY t3 TO '/dev/null'; -- ok
|
||||||
|
COPY t4 TO '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t5 TO '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t5(e,f) TO '/dev/null'; -- ok
|
||||||
|
COPY t1 FROM '/dev/null'; -- ok
|
||||||
|
COPY t2 FROM '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t3 FROM '/dev/null'; -- ok
|
||||||
|
COPY t4 FROM '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t5 FROM '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t5 (e,f) FROM '/dev/null'; -- failed
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
COPY t5 (e) FROM '/dev/null'; -- ok
|
||||||
|
--
|
||||||
|
-- Clean up
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
------------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS t1 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t2 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t3 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t4 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t5 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS customer CASCADE;
|
109
contrib/sepgsql/expected/label.out
Normal file
109
contrib/sepgsql/expected/label.out
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
--
|
||||||
|
-- Regression Tests for Label Management
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- Setup
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (a int, b text);
|
||||||
|
INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||||
|
SELECT * INTO t2 FROM t1 WHERE a % 2 = 0;
|
||||||
|
CREATE FUNCTION f1 () RETURNS text
|
||||||
|
AS 'SELECT sepgsql_getcon()'
|
||||||
|
LANGUAGE sql;
|
||||||
|
CREATE FUNCTION f2 () RETURNS text
|
||||||
|
AS 'SELECT sepgsql_getcon()'
|
||||||
|
LANGUAGE sql;
|
||||||
|
SECURITY LABEL ON FUNCTION f2()
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
CREATE FUNCTION f3 () RETURNS text
|
||||||
|
AS 'BEGIN
|
||||||
|
RAISE EXCEPTION ''an exception from f3()'';
|
||||||
|
RETURN NULL;
|
||||||
|
END;' LANGUAGE plpgsql;
|
||||||
|
SECURITY LABEL ON FUNCTION f3()
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
--
|
||||||
|
-- Tests for default labeling behavior
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
CREATE TABLE t3 (s int, t text);
|
||||||
|
INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
|
||||||
|
SELECT objtype, objname, label FROM pg_seclabels
|
||||||
|
WHERE provider = 'selinux'
|
||||||
|
AND objtype in ('table', 'column')
|
||||||
|
AND objname in ('t1', 't2', 't3');
|
||||||
|
objtype | objname | label
|
||||||
|
---------+---------+-----------------------------------------------
|
||||||
|
table | t1 | unconfined_u:object_r:sepgsql_table_t:s0
|
||||||
|
table | t2 | unconfined_u:object_r:sepgsql_table_t:s0
|
||||||
|
table | t3 | unconfined_u:object_r:user_sepgsql_table_t:s0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Tests for SECURITY LABEL
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SECURITY LABEL ON TABLE t1
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok
|
||||||
|
SECURITY LABEL ON TABLE t2
|
||||||
|
IS 'invalid seuciryt context'; -- be failed
|
||||||
|
ERROR: invalid security label: "invalid seuciryt context"
|
||||||
|
SECURITY LABEL ON COLUMN t2
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- be failed
|
||||||
|
ERROR: improper relation name (too many dotted names):
|
||||||
|
SECURITY LABEL ON COLUMN t2.b
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok
|
||||||
|
--
|
||||||
|
-- Tests for Trusted Procedures
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT f1(); -- normal procedure
|
||||||
|
f1
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT f2(); -- trusted procedure
|
||||||
|
f2
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT f3(); -- trusted procedure that raises an error
|
||||||
|
ERROR: an exception from f3()
|
||||||
|
SELECT sepgsql_getcon(); -- client's label must be restored
|
||||||
|
sepgsql_getcon
|
||||||
|
-----------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Clean up
|
||||||
|
--
|
||||||
|
SELECT sepgsql_getcon(); -- confirm client privilege
|
||||||
|
sepgsql_getcon
|
||||||
|
------------------------------------------------------
|
||||||
|
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS t1 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t2 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t3 CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f1() CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f2() CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f3() CASCADE;
|
5
contrib/sepgsql/expected/misc.out
Normal file
5
contrib/sepgsql/expected/misc.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for Misc Permission Checks
|
||||||
|
--
|
||||||
|
LOAD '$libdir/sepgsql'; -- failed
|
||||||
|
ERROR: SELinux: LOAD is not allowed anyway.
|
446
contrib/sepgsql/hooks.c
Normal file
446
contrib/sepgsql/hooks.c
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/hooks.c
|
||||||
|
*
|
||||||
|
* Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/objectaccess.h"
|
||||||
|
#include "catalog/pg_class.h"
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "libpq/auth.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "tcop/utility.h"
|
||||||
|
#include "utils/guc.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Declarations
|
||||||
|
*/
|
||||||
|
void _PG_init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Saved hook entries (if stacked)
|
||||||
|
*/
|
||||||
|
static object_access_hook_type next_object_access_hook = NULL;
|
||||||
|
static ClientAuthentication_hook_type next_client_auth_hook = NULL;
|
||||||
|
static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
|
||||||
|
static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
|
||||||
|
static fmgr_hook_type next_fmgr_hook = NULL;
|
||||||
|
static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GUC: sepgsql.permissive = (on|off)
|
||||||
|
*/
|
||||||
|
static bool sepgsql_permissive;
|
||||||
|
|
||||||
|
bool
|
||||||
|
sepgsql_get_permissive(void)
|
||||||
|
{
|
||||||
|
return sepgsql_permissive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GUC: sepgsql.debug_audit = (on|off)
|
||||||
|
*/
|
||||||
|
static bool sepgsql_debug_audit;
|
||||||
|
|
||||||
|
bool
|
||||||
|
sepgsql_get_debug_audit(void)
|
||||||
|
{
|
||||||
|
return sepgsql_debug_audit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_client_auth
|
||||||
|
*
|
||||||
|
* Entrypoint of the client authentication hook.
|
||||||
|
* It switches the client label according to getpeercon(), and the current
|
||||||
|
* performing mode according to the GUC setting.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sepgsql_client_auth(Port *port, int status)
|
||||||
|
{
|
||||||
|
char *context;
|
||||||
|
|
||||||
|
if (next_client_auth_hook)
|
||||||
|
(*next_client_auth_hook)(port, status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the case when authentication failed, the supplied socket
|
||||||
|
* shall be closed soon, so we don't need to do anything here.
|
||||||
|
*/
|
||||||
|
if (status != STATUS_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Getting security label of the peer process using API of libselinux.
|
||||||
|
*/
|
||||||
|
if (getpeercon_raw(port->sock, &context) < 0)
|
||||||
|
ereport(FATAL,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("selinux: failed to get the peer label")));
|
||||||
|
|
||||||
|
sepgsql_set_client_label(context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch the current performing mode from INTERNAL to either
|
||||||
|
* DEFAULT or PERMISSIVE.
|
||||||
|
*/
|
||||||
|
if (sepgsql_permissive)
|
||||||
|
sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
|
||||||
|
else
|
||||||
|
sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_object_access
|
||||||
|
*
|
||||||
|
* Entrypoint of the object_access_hook. This routine performs as
|
||||||
|
* a dispatcher of invocation based on access type and object classes.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sepgsql_object_access(ObjectAccessType access,
|
||||||
|
Oid classId,
|
||||||
|
Oid objectId,
|
||||||
|
int subId)
|
||||||
|
{
|
||||||
|
if (next_object_access_hook)
|
||||||
|
(*next_object_access_hook)(access, classId, objectId, subId);
|
||||||
|
|
||||||
|
switch (access)
|
||||||
|
{
|
||||||
|
case OAT_POST_CREATE:
|
||||||
|
switch (classId)
|
||||||
|
{
|
||||||
|
case NamespaceRelationId:
|
||||||
|
sepgsql_schema_post_create(objectId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RelationRelationId:
|
||||||
|
if (subId == 0)
|
||||||
|
sepgsql_relation_post_create(objectId);
|
||||||
|
else
|
||||||
|
sepgsql_attribute_post_create(objectId, subId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ProcedureRelationId:
|
||||||
|
sepgsql_proc_post_create(objectId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Ignore unsupported object classes */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unexpected object access type: %d", (int)access);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_exec_check_perms
|
||||||
|
*
|
||||||
|
* Entrypoint of DML permissions
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
sepgsql_exec_check_perms(List *rangeTabls, bool abort)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If security provider is stacking and one of them replied 'false'
|
||||||
|
* at least, we don't need to check any more.
|
||||||
|
*/
|
||||||
|
if (next_exec_check_perms_hook &&
|
||||||
|
!(*next_exec_check_perms_hook)(rangeTabls, abort))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!sepgsql_dml_privileges(rangeTabls, abort))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_needs_fmgr_hook
|
||||||
|
*
|
||||||
|
* It informs the core whether the supplied function is trusted procedure,
|
||||||
|
* or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
|
||||||
|
* abort time of function invocation.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
sepgsql_needs_fmgr_hook(Oid functionId)
|
||||||
|
{
|
||||||
|
char *old_label;
|
||||||
|
char *new_label;
|
||||||
|
char *function_label;
|
||||||
|
|
||||||
|
if (next_needs_fmgr_hook &&
|
||||||
|
(*next_needs_fmgr_hook)(functionId))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SELinux needs the function to be called via security_definer
|
||||||
|
* wrapper, if this invocation will take a domain-transition.
|
||||||
|
* We call these functions as trusted-procedure, if the security
|
||||||
|
* policy has a rule that switches security label of the client
|
||||||
|
* on execution.
|
||||||
|
*/
|
||||||
|
old_label = sepgsql_get_client_label();
|
||||||
|
new_label = sepgsql_proc_get_domtrans(functionId);
|
||||||
|
if (strcmp(old_label, new_label) != 0)
|
||||||
|
{
|
||||||
|
pfree(new_label);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pfree(new_label);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Even if not a trusted-procedure, this function should not be inlined
|
||||||
|
* unless the client has db_procedure:{execute} permission.
|
||||||
|
* Please note that it shall be actually failed later because of same
|
||||||
|
* reason with ACL_EXECUTE.
|
||||||
|
*/
|
||||||
|
function_label = sepgsql_get_label(ProcedureRelationId, functionId, 0);
|
||||||
|
if (sepgsql_check_perms(sepgsql_get_client_label(),
|
||||||
|
function_label,
|
||||||
|
SEPG_CLASS_DB_PROCEDURE,
|
||||||
|
SEPG_DB_PROCEDURE__EXECUTE,
|
||||||
|
NULL, false) != true)
|
||||||
|
{
|
||||||
|
pfree(function_label);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pfree(function_label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_fmgr_hook
|
||||||
|
*
|
||||||
|
* It switches security label of the client on execution of trusted
|
||||||
|
* procedures.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sepgsql_fmgr_hook(FmgrHookEventType event,
|
||||||
|
FmgrInfo *flinfo, Datum *private)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
char *old_label;
|
||||||
|
char *new_label;
|
||||||
|
Datum next_private;
|
||||||
|
} *stack;
|
||||||
|
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case FHET_START:
|
||||||
|
stack = (void *)DatumGetPointer(*private);
|
||||||
|
if (!stack)
|
||||||
|
{
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
|
||||||
|
stack = palloc(sizeof(*stack));
|
||||||
|
stack->old_label = NULL;
|
||||||
|
stack->new_label = sepgsql_proc_get_domtrans(flinfo->fn_oid);
|
||||||
|
stack->next_private = 0;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
*private = PointerGetDatum(stack);
|
||||||
|
}
|
||||||
|
Assert(!stack->old_label);
|
||||||
|
stack->old_label = sepgsql_set_client_label(stack->new_label);
|
||||||
|
|
||||||
|
if (next_fmgr_hook)
|
||||||
|
(*next_fmgr_hook)(event, flinfo, &stack->next_private);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FHET_END:
|
||||||
|
case FHET_ABORT:
|
||||||
|
stack = (void *)DatumGetPointer(*private);
|
||||||
|
|
||||||
|
if (next_fmgr_hook)
|
||||||
|
(*next_fmgr_hook)(event, flinfo, &stack->next_private);
|
||||||
|
|
||||||
|
sepgsql_set_client_label(stack->old_label);
|
||||||
|
stack->old_label = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unexpected event type: %d", (int)event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_utility_command
|
||||||
|
*
|
||||||
|
* It tries to rough-grained control on utility commands; some of them can
|
||||||
|
* break whole of the things if nefarious user would use.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sepgsql_utility_command(Node *parsetree,
|
||||||
|
const char *queryString,
|
||||||
|
ParamListInfo params,
|
||||||
|
bool isTopLevel,
|
||||||
|
DestReceiver *dest,
|
||||||
|
char *completionTag)
|
||||||
|
{
|
||||||
|
if (next_ProcessUtility_hook)
|
||||||
|
(*next_ProcessUtility_hook)(parsetree, queryString, params,
|
||||||
|
isTopLevel, dest, completionTag);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check command tag to avoid nefarious operations
|
||||||
|
*/
|
||||||
|
switch (nodeTag(parsetree))
|
||||||
|
{
|
||||||
|
case T_LoadStmt:
|
||||||
|
/*
|
||||||
|
* We reject LOAD command across the board on enforcing mode,
|
||||||
|
* because a binary module can arbitrarily override hooks.
|
||||||
|
*/
|
||||||
|
if (sepgsql_getenforce())
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("SELinux: LOAD is not allowed anyway.")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* Right now we don't check any other utility commands,
|
||||||
|
* because it needs more detailed information to make
|
||||||
|
* access control decision here, but we don't want to
|
||||||
|
* have two parse and analyze routines individually.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Original implementation
|
||||||
|
*/
|
||||||
|
standard_ProcessUtility(parsetree, queryString, params,
|
||||||
|
isTopLevel, dest, completionTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module load/unload callback
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PG_init(void)
|
||||||
|
{
|
||||||
|
char *context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We allow to load the SE-PostgreSQL module on single-user-mode or
|
||||||
|
* shared_preload_libraries settings only.
|
||||||
|
*/
|
||||||
|
if (IsUnderPostmaster)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("Not allowed to load SE-PostgreSQL now")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check availability of SELinux on the platform.
|
||||||
|
* If disabled, we cannot activate any SE-PostgreSQL features,
|
||||||
|
* and we have to skip rest of initialization.
|
||||||
|
*/
|
||||||
|
if (is_selinux_enabled() < 1)
|
||||||
|
{
|
||||||
|
sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql.permissive = (on|off)
|
||||||
|
*
|
||||||
|
* This variable controls performing mode of SE-PostgreSQL
|
||||||
|
* on user's session.
|
||||||
|
*/
|
||||||
|
DefineCustomBoolVariable("sepgsql.permissive",
|
||||||
|
"Turn on/off permissive mode in SE-PostgreSQL",
|
||||||
|
NULL,
|
||||||
|
&sepgsql_permissive,
|
||||||
|
false,
|
||||||
|
PGC_SIGHUP,
|
||||||
|
GUC_NOT_IN_SAMPLE,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql.debug_audit = (on|off)
|
||||||
|
*
|
||||||
|
* This variable allows users to turn on/off audit logs on access
|
||||||
|
* control decisions, independent from auditallow/auditdeny setting
|
||||||
|
* in the security policy.
|
||||||
|
* We intend to use this option for debugging purpose.
|
||||||
|
*/
|
||||||
|
DefineCustomBoolVariable("sepgsql.debug_audit",
|
||||||
|
"Turn on/off debug audit messages",
|
||||||
|
NULL,
|
||||||
|
&sepgsql_debug_audit,
|
||||||
|
false,
|
||||||
|
PGC_USERSET,
|
||||||
|
GUC_NOT_IN_SAMPLE,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up dummy client label.
|
||||||
|
*
|
||||||
|
* XXX - note that PostgreSQL launches background worker process
|
||||||
|
* like autovacuum without authentication steps. So, we initialize
|
||||||
|
* sepgsql_mode with SEPGSQL_MODE_INTERNAL, and client_label with
|
||||||
|
* the security context of server process.
|
||||||
|
* Later, it also launches background of user session. In this case,
|
||||||
|
* the process is always hooked on post-authentication, and we can
|
||||||
|
* initialize the sepgsql_mode and client_label correctly.
|
||||||
|
*/
|
||||||
|
if (getcon_raw(&context) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("selinux: unable to get security label of server")));
|
||||||
|
sepgsql_set_client_label(context);
|
||||||
|
|
||||||
|
/* Security label provider hook */
|
||||||
|
register_label_provider(SEPGSQL_LABEL_TAG,
|
||||||
|
sepgsql_object_relabel);
|
||||||
|
|
||||||
|
/* Client authentication hook */
|
||||||
|
next_client_auth_hook = ClientAuthentication_hook;
|
||||||
|
ClientAuthentication_hook = sepgsql_client_auth;
|
||||||
|
|
||||||
|
/* Object access hook */
|
||||||
|
next_object_access_hook = object_access_hook;
|
||||||
|
object_access_hook = sepgsql_object_access;
|
||||||
|
|
||||||
|
/* DML permission check */
|
||||||
|
next_exec_check_perms_hook = ExecutorCheckPerms_hook;
|
||||||
|
ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
|
||||||
|
|
||||||
|
/* Trusted procedure hooks */
|
||||||
|
next_needs_fmgr_hook = needs_fmgr_hook;
|
||||||
|
needs_fmgr_hook = sepgsql_needs_fmgr_hook;
|
||||||
|
|
||||||
|
next_fmgr_hook = fmgr_hook;
|
||||||
|
fmgr_hook = sepgsql_fmgr_hook;
|
||||||
|
|
||||||
|
/* ProcessUtility hook */
|
||||||
|
next_ProcessUtility_hook = ProcessUtility_hook;
|
||||||
|
ProcessUtility_hook = sepgsql_utility_command;
|
||||||
|
}
|
477
contrib/sepgsql/label.c
Normal file
477
contrib/sepgsql/label.c
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/label.c
|
||||||
|
*
|
||||||
|
* Routines to support SELinux labels (security context)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "catalog/catalog.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_attribute.h"
|
||||||
|
#include "catalog/pg_class.h"
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "commands/dbcommands.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "libpq/libpq-be.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/rel.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
#include <selinux/label.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* client_label
|
||||||
|
*
|
||||||
|
* security label of the client process
|
||||||
|
*/
|
||||||
|
static char *client_label = NULL;
|
||||||
|
|
||||||
|
char *
|
||||||
|
sepgsql_get_client_label(void)
|
||||||
|
{
|
||||||
|
return client_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
sepgsql_set_client_label(char *new_label)
|
||||||
|
{
|
||||||
|
char *old_label = client_label;
|
||||||
|
|
||||||
|
client_label = new_label;
|
||||||
|
|
||||||
|
return old_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_get_label
|
||||||
|
*
|
||||||
|
* It returns a security context of the specified database object.
|
||||||
|
* If unlabeled or incorrectly labeled, the system "unlabeled" label
|
||||||
|
* shall be returned.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
|
||||||
|
{
|
||||||
|
ObjectAddress object;
|
||||||
|
char *label;
|
||||||
|
|
||||||
|
object.classId = classId;
|
||||||
|
object.objectId = objectId;
|
||||||
|
object.objectSubId = subId;
|
||||||
|
|
||||||
|
label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
|
||||||
|
if (!label || security_check_context_raw((security_context_t)label))
|
||||||
|
{
|
||||||
|
security_context_t unlabeled;
|
||||||
|
|
||||||
|
if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("selinux: unable to get initial security label")));
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
label = pstrdup(unlabeled);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
freecon(unlabeled);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
|
freecon(unlabeled);
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_object_relabel
|
||||||
|
*
|
||||||
|
* An entrypoint of SECURITY LABEL statement
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* validate format of the supplied security label,
|
||||||
|
* if it is security context of selinux.
|
||||||
|
*/
|
||||||
|
if (seclabel &&
|
||||||
|
security_check_context_raw((security_context_t) seclabel) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_NAME),
|
||||||
|
errmsg("invalid security label: \"%s\"", seclabel)));
|
||||||
|
/*
|
||||||
|
* Do actual permission checks for each object classes
|
||||||
|
*/
|
||||||
|
switch (object->classId)
|
||||||
|
{
|
||||||
|
case NamespaceRelationId:
|
||||||
|
sepgsql_schema_relabel(object->objectId, seclabel);
|
||||||
|
break;
|
||||||
|
case RelationRelationId:
|
||||||
|
if (object->objectSubId == 0)
|
||||||
|
sepgsql_relation_relabel(object->objectId,
|
||||||
|
seclabel);
|
||||||
|
else
|
||||||
|
sepgsql_attribute_relabel(object->objectId,
|
||||||
|
object->objectSubId,
|
||||||
|
seclabel);
|
||||||
|
break;
|
||||||
|
case ProcedureRelationId:
|
||||||
|
sepgsql_proc_relabel(object->objectId, seclabel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unsupported object type: %u", object->classId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEXT sepgsql_getcon(VOID)
|
||||||
|
*
|
||||||
|
* It returns the security label of the client.
|
||||||
|
*/
|
||||||
|
PG_FUNCTION_INFO_V1(sepgsql_getcon);
|
||||||
|
Datum
|
||||||
|
sepgsql_getcon(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
char *client_label;
|
||||||
|
|
||||||
|
if (!sepgsql_is_enabled())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELinux: now disabled")));
|
||||||
|
|
||||||
|
client_label = sepgsql_get_client_label();
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(cstring_to_text(client_label));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEXT sepgsql_mcstrans_in(TEXT)
|
||||||
|
*
|
||||||
|
* It translate the given qualified MLS/MCS range into raw format
|
||||||
|
* when mcstrans daemon is working.
|
||||||
|
*/
|
||||||
|
PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
|
||||||
|
Datum
|
||||||
|
sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *label = PG_GETARG_TEXT_P(0);
|
||||||
|
char *raw_label;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if (!sepgsql_is_enabled())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELinux: now disabled")));
|
||||||
|
|
||||||
|
if (selinux_trans_to_raw_context(text_to_cstring(label),
|
||||||
|
&raw_label) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("SELinux: internal error on mcstrans")));
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
result = pstrdup(raw_label);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
freecon(raw_label);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
freecon(raw_label);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(cstring_to_text(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEXT sepgsql_mcstrans_out(TEXT)
|
||||||
|
*
|
||||||
|
* It translate the given raw MLS/MCS range into qualified format
|
||||||
|
* when mcstrans daemon is working.
|
||||||
|
*/
|
||||||
|
PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
|
||||||
|
Datum
|
||||||
|
sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *label = PG_GETARG_TEXT_P(0);
|
||||||
|
char *qual_label;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if (!sepgsql_is_enabled())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELinux: now disabled")));
|
||||||
|
|
||||||
|
if (selinux_raw_to_trans_context(text_to_cstring(label),
|
||||||
|
&qual_label) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("SELinux: internal error on mcstrans")));
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
result = pstrdup(qual_label);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
freecon(qual_label);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
freecon(qual_label);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(cstring_to_text(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* exec_object_restorecon
|
||||||
|
*
|
||||||
|
* This routine is a helper called by sepgsql_restorecon; it set up
|
||||||
|
* initial security labels of database objects within the supplied
|
||||||
|
* catalog OID.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
|
||||||
|
{
|
||||||
|
Relation rel;
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
char *database_name = get_database_name(MyDatabaseId);
|
||||||
|
char *namespace_name;
|
||||||
|
Oid namespace_id;
|
||||||
|
char *relation_name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the target catalog. We don't want to allow writable
|
||||||
|
* accesses by other session during initial labeling.
|
||||||
|
*/
|
||||||
|
rel = heap_open(catalogId, AccessShareLock);
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, InvalidOid, false,
|
||||||
|
SnapshotNow, 0, NULL);
|
||||||
|
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
|
||||||
|
{
|
||||||
|
Form_pg_namespace nspForm;
|
||||||
|
Form_pg_class relForm;
|
||||||
|
Form_pg_attribute attForm;
|
||||||
|
Form_pg_proc proForm;
|
||||||
|
char objname[NAMEDATALEN * 4 + 10];
|
||||||
|
int objtype = 1234;
|
||||||
|
ObjectAddress object;
|
||||||
|
security_context_t context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The way to determine object name depends on object classes.
|
||||||
|
* So, any branches set up `objtype', `objname' and `object' here.
|
||||||
|
*/
|
||||||
|
switch (catalogId)
|
||||||
|
{
|
||||||
|
case NamespaceRelationId:
|
||||||
|
nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
objtype = SELABEL_DB_SCHEMA;
|
||||||
|
snprintf(objname, sizeof(objname), "%s.%s",
|
||||||
|
database_name, NameStr(nspForm->nspname));
|
||||||
|
|
||||||
|
object.classId = NamespaceRelationId;
|
||||||
|
object.objectId = HeapTupleGetOid(tuple);
|
||||||
|
object.objectSubId = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RelationRelationId:
|
||||||
|
relForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
if (relForm->relkind == RELKIND_RELATION)
|
||||||
|
objtype = SELABEL_DB_TABLE;
|
||||||
|
else if (relForm->relkind == RELKIND_SEQUENCE)
|
||||||
|
objtype = SELABEL_DB_SEQUENCE;
|
||||||
|
else if (relForm->relkind == RELKIND_VIEW)
|
||||||
|
objtype = SELABEL_DB_VIEW;
|
||||||
|
else
|
||||||
|
continue; /* no need to assign security label */
|
||||||
|
|
||||||
|
namespace_name = get_namespace_name(relForm->relnamespace);
|
||||||
|
snprintf(objname, sizeof(objname), "%s.%s.%s",
|
||||||
|
database_name, namespace_name,
|
||||||
|
NameStr(relForm->relname));
|
||||||
|
pfree(namespace_name);
|
||||||
|
|
||||||
|
object.classId = RelationRelationId;
|
||||||
|
object.objectId = HeapTupleGetOid(tuple);
|
||||||
|
object.objectSubId = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttributeRelationId:
|
||||||
|
attForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
|
||||||
|
continue; /* no need to assign security label */
|
||||||
|
|
||||||
|
objtype = SELABEL_DB_COLUMN;
|
||||||
|
|
||||||
|
namespace_id = get_rel_namespace(attForm->attrelid);
|
||||||
|
namespace_name = get_namespace_name(namespace_id);
|
||||||
|
relation_name = get_rel_name(attForm->attrelid);
|
||||||
|
snprintf(objname, sizeof(objname), "%s.%s.%s.%s",
|
||||||
|
database_name, namespace_name,
|
||||||
|
relation_name, NameStr(attForm->attname));
|
||||||
|
pfree(relation_name);
|
||||||
|
pfree(namespace_name);
|
||||||
|
|
||||||
|
object.classId = RelationRelationId;
|
||||||
|
object.objectId = attForm->attrelid;
|
||||||
|
object.objectSubId = attForm->attnum;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ProcedureRelationId:
|
||||||
|
proForm = (Form_pg_proc) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
objtype = SELABEL_DB_PROCEDURE;
|
||||||
|
|
||||||
|
namespace_name = get_namespace_name(proForm->pronamespace);
|
||||||
|
snprintf(objname, sizeof(objname), "%s.%s.%s",
|
||||||
|
database_name, namespace_name,
|
||||||
|
NameStr(proForm->proname));
|
||||||
|
pfree(namespace_name);
|
||||||
|
|
||||||
|
object.classId = ProcedureRelationId;
|
||||||
|
object.objectId = HeapTupleGetOid(tuple);
|
||||||
|
object.objectSubId = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
elog(ERROR, "Bug? %u is not supported to set initial labels",
|
||||||
|
catalogId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
|
||||||
|
{
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check SELinux permission to relabel the fetched object,
|
||||||
|
* then do the actual relabeling.
|
||||||
|
*/
|
||||||
|
sepgsql_object_relabel(&object, context);
|
||||||
|
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
freecon(context);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
freecon(context);
|
||||||
|
}
|
||||||
|
else if (errno == ENOENT)
|
||||||
|
ereport(WARNING,
|
||||||
|
(errmsg("no valid initial label on %s (type=%d), skipped",
|
||||||
|
objname, objtype)));
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("libselinux: internal error")));
|
||||||
|
}
|
||||||
|
systable_endscan(sscan);
|
||||||
|
|
||||||
|
heap_close(rel, NoLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BOOL sepgsql_restorecon(TEXT specfile)
|
||||||
|
*
|
||||||
|
* This function tries to assign initial security labels on all the object
|
||||||
|
* within the current database, according to the system setting.
|
||||||
|
* It is typically invoked by sepgsql-install script just after initdb, to
|
||||||
|
* assign initial security labels.
|
||||||
|
*
|
||||||
|
* If @specfile is not NULL, it uses explicitly specified specfile, instead
|
||||||
|
* of the system default.
|
||||||
|
*/
|
||||||
|
PG_FUNCTION_INFO_V1(sepgsql_restorecon);
|
||||||
|
Datum
|
||||||
|
sepgsql_restorecon(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
struct selabel_handle *sehnd;
|
||||||
|
struct selinux_opt seopts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SELinux has to be enabled on the running platform.
|
||||||
|
*/
|
||||||
|
if (!sepgsql_is_enabled())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELinux: now disabled")));
|
||||||
|
/*
|
||||||
|
* Check DAC permission. Only superuser can set up initial
|
||||||
|
* security labels, like root-user in filesystems
|
||||||
|
*/
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("must be superuser to restore initial contexts")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open selabel_lookup(3) stuff. It provides a set of mapping
|
||||||
|
* between an initial security label and object class/name due
|
||||||
|
* to the system setting.
|
||||||
|
*/
|
||||||
|
if (PG_ARGISNULL(0))
|
||||||
|
{
|
||||||
|
seopts.type = SELABEL_OPT_UNUSED;
|
||||||
|
seopts.value = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
seopts.type = SELABEL_OPT_PATH;
|
||||||
|
seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
|
||||||
|
}
|
||||||
|
sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
|
||||||
|
if (!sehnd)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("SELinux internal error")));
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Right now, we have no support labeling on the shared
|
||||||
|
* database objects, such as database, role, or tablespace.
|
||||||
|
*/
|
||||||
|
exec_object_restorecon(sehnd, NamespaceRelationId);
|
||||||
|
exec_object_restorecon(sehnd, RelationRelationId);
|
||||||
|
exec_object_restorecon(sehnd, AttributeRelationId);
|
||||||
|
exec_object_restorecon(sehnd, ProcedureRelationId);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
selabel_close(sehnd);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
|
selabel_close(sehnd);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(true);
|
||||||
|
}
|
52
contrib/sepgsql/launcher
Normal file
52
contrib/sepgsql/launcher
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# A wrapper script to launch psql command in regression test
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "usage: `basename $0` <command> [options...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUNCON=`which runcon`
|
||||||
|
if [ ! -e "$RUNCON" ]; then
|
||||||
|
echo "runcon command is not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read SQL from stdin
|
||||||
|
#
|
||||||
|
TEMP=`mktemp`
|
||||||
|
CONTEXT=""
|
||||||
|
|
||||||
|
while IFS='\\n' read LINE
|
||||||
|
do
|
||||||
|
if echo "$LINE" | grep -q "^-- @SECURITY-CONTEXT="; then
|
||||||
|
if [ -s "$TEMP" ]; then
|
||||||
|
if [ -n "$CONTEXT" ]; then
|
||||||
|
"$RUNCON" "$CONTEXT" $* < "$TEMP"
|
||||||
|
else
|
||||||
|
$* < $TEMP
|
||||||
|
fi
|
||||||
|
truncate -s0 $TEMP
|
||||||
|
fi
|
||||||
|
CONTEXT=`echo "$LINE" | sed 's/^-- @SECURITY-CONTEXT=//g'`
|
||||||
|
LINE="SELECT sepgsql_getcon(); -- confirm client privilege"
|
||||||
|
fi
|
||||||
|
echo "$LINE" >> $TEMP
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -s "$TEMP" ]; then
|
||||||
|
if [ -n "$CONTEXT" ]; then
|
||||||
|
"$RUNCON" "$CONTEXT" $* < "$TEMP"
|
||||||
|
else
|
||||||
|
$* < $TEMP
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# cleanup temp file
|
||||||
|
rm -f $TEMP
|
158
contrib/sepgsql/proc.c
Normal file
158
contrib/sepgsql/proc.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/proc.c
|
||||||
|
*
|
||||||
|
* Routines corresponding to procedure objects
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "access/sysattr.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_proc_post_create
|
||||||
|
*
|
||||||
|
* This routine assigns a default security label on a newly defined
|
||||||
|
* procedure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_proc_post_create(Oid functionId)
|
||||||
|
{
|
||||||
|
Relation rel;
|
||||||
|
ScanKeyData skey;
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
Oid namespaceId;
|
||||||
|
ObjectAddress object;
|
||||||
|
char *scontext;
|
||||||
|
char *tcontext;
|
||||||
|
char *ncontext;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch namespace of the new procedure. Because pg_proc entry is not
|
||||||
|
* visible right now, we need to scan the catalog using SnapshotSelf.
|
||||||
|
*/
|
||||||
|
rel = heap_open(ProcedureRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&skey,
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(functionId));
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
|
||||||
|
SnapshotSelf, 1, &skey);
|
||||||
|
|
||||||
|
tuple = systable_getnext(sscan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "catalog lookup failed for proc %u", functionId);
|
||||||
|
|
||||||
|
namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
|
||||||
|
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute a default security label when we create a new procedure
|
||||||
|
* object under the specified namespace.
|
||||||
|
*/
|
||||||
|
scontext = sepgsql_get_client_label();
|
||||||
|
tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
|
||||||
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||||
|
SEPG_CLASS_DB_PROCEDURE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign the default security label on a new procedure
|
||||||
|
*/
|
||||||
|
object.classId = ProcedureRelationId;
|
||||||
|
object.objectId = functionId;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
||||||
|
|
||||||
|
pfree(tcontext);
|
||||||
|
pfree(ncontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_proc_relabel
|
||||||
|
*
|
||||||
|
* It checks privileges to relabel the supplied function
|
||||||
|
* by the `seclabel'.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_proc_relabel(Oid functionId, const char *seclabel)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *audit_name;
|
||||||
|
|
||||||
|
audit_name = get_func_name(functionId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_procedure:{setattr relabelfrom} permission
|
||||||
|
*/
|
||||||
|
tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_PROCEDURE,
|
||||||
|
SEPG_DB_PROCEDURE__SETATTR |
|
||||||
|
SEPG_DB_PROCEDURE__RELABELFROM,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
pfree(tcontext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_procedure:{relabelto} permission
|
||||||
|
*/
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
seclabel,
|
||||||
|
SEPG_CLASS_DB_PROCEDURE,
|
||||||
|
SEPG_DB_PROCEDURE__RELABELTO,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
pfree(audit_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_proc_get_domtrans
|
||||||
|
*
|
||||||
|
* It computes security label of the client that shall be applied when
|
||||||
|
* the current client invokes the supplied function.
|
||||||
|
* This computed label is either same or different from the current one.
|
||||||
|
* If security policy informed the function is a trusted-procedure,
|
||||||
|
* we need to switch security label of the client during execution of
|
||||||
|
* the function.
|
||||||
|
*
|
||||||
|
* Also note that the translated label shall be allocated using palloc().
|
||||||
|
* So, need to switch memory context, if you want to hold the string in
|
||||||
|
* someone except for CurrentMemoryContext.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
sepgsql_proc_get_domtrans(Oid functionId)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *ncontext;
|
||||||
|
|
||||||
|
tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
|
||||||
|
|
||||||
|
ncontext = sepgsql_compute_create(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_PROCESS);
|
||||||
|
pfree(tcontext);
|
||||||
|
|
||||||
|
return ncontext;
|
||||||
|
}
|
267
contrib/sepgsql/relation.c
Normal file
267
contrib/sepgsql/relation.c
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/label.c
|
||||||
|
*
|
||||||
|
* Routines corresponding to relation/attribute objects
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "access/sysattr.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_attribute.h"
|
||||||
|
#include "catalog/pg_class.h"
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_attribute_post_create
|
||||||
|
*
|
||||||
|
* This routine assigns a default security label on a newly defined
|
||||||
|
* column, using ALTER TABLE ... ADD COLUMN.
|
||||||
|
* Note that this routine is not invoked in the case of CREATE TABLE,
|
||||||
|
* although it also defines columns in addition to table.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *ncontext;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only attributes within regular relation have individual
|
||||||
|
* security labels.
|
||||||
|
*/
|
||||||
|
if (get_rel_relkind(relOid) != RELKIND_RELATION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute a default security label when we create a new procedure
|
||||||
|
* object under the specified namespace.
|
||||||
|
*/
|
||||||
|
scontext = sepgsql_get_client_label();
|
||||||
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
||||||
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN);
|
||||||
|
/*
|
||||||
|
* Assign the default security label on a new procedure
|
||||||
|
*/
|
||||||
|
object.classId = RelationRelationId;
|
||||||
|
object.objectId = relOid;
|
||||||
|
object.objectSubId = attnum;
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
||||||
|
|
||||||
|
pfree(tcontext);
|
||||||
|
pfree(ncontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_attribute_relabel
|
||||||
|
*
|
||||||
|
* It checks privileges to relabel the supplied column
|
||||||
|
* by the `seclabel'.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
|
||||||
|
const char *seclabel)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char audit_name[NAMEDATALEN * 2 + 10];
|
||||||
|
|
||||||
|
if (get_rel_relkind(relOid) != RELKIND_RELATION)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("cannot set security label on non-regular columns")));
|
||||||
|
|
||||||
|
snprintf(audit_name, sizeof(audit_name), "%s.%s",
|
||||||
|
get_rel_name(relOid), get_attname(relOid, attnum));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_column:{setattr relabelfrom} permission
|
||||||
|
*/
|
||||||
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN,
|
||||||
|
SEPG_DB_COLUMN__SETATTR |
|
||||||
|
SEPG_DB_COLUMN__RELABELFROM,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
pfree(tcontext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_column:{relabelto} permission
|
||||||
|
*/
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
seclabel,
|
||||||
|
SEPG_CLASS_DB_COLUMN,
|
||||||
|
SEPG_DB_PROCEDURE__RELABELTO,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_relation_post_create
|
||||||
|
*
|
||||||
|
* The post creation hook of relation/attribute
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_relation_post_create(Oid relOid)
|
||||||
|
{
|
||||||
|
Relation rel;
|
||||||
|
ScanKeyData skey;
|
||||||
|
SysScanDesc sscan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
Form_pg_class classForm;
|
||||||
|
ObjectAddress object;
|
||||||
|
uint16 tclass;
|
||||||
|
char *scontext; /* subject */
|
||||||
|
char *tcontext; /* schema */
|
||||||
|
char *rcontext; /* relation */
|
||||||
|
char *ccontext; /* column */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch catalog record of the new relation. Because pg_class entry is
|
||||||
|
* not visible right now, we need to scan the catalog using SnapshotSelf.
|
||||||
|
*/
|
||||||
|
rel = heap_open(RelationRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&skey,
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(relOid));
|
||||||
|
|
||||||
|
sscan = systable_beginscan(rel, ClassOidIndexId, true,
|
||||||
|
SnapshotSelf, 1, &skey);
|
||||||
|
|
||||||
|
tuple = systable_getnext(sscan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "catalog lookup failed for relation %u", relOid);
|
||||||
|
|
||||||
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
if (classForm->relkind == RELKIND_RELATION)
|
||||||
|
tclass = SEPG_CLASS_DB_TABLE;
|
||||||
|
else if (classForm->relkind == RELKIND_SEQUENCE)
|
||||||
|
tclass = SEPG_CLASS_DB_SEQUENCE;
|
||||||
|
else if (classForm->relkind == RELKIND_VIEW)
|
||||||
|
tclass = SEPG_CLASS_DB_VIEW;
|
||||||
|
else
|
||||||
|
goto out; /* No need to assign individual labels */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute a default security label when we create a new relation
|
||||||
|
* object under the specified namespace.
|
||||||
|
*/
|
||||||
|
scontext = sepgsql_get_client_label();
|
||||||
|
tcontext = sepgsql_get_label(NamespaceRelationId,
|
||||||
|
classForm->relnamespace, 0);
|
||||||
|
rcontext = sepgsql_compute_create(scontext, tcontext, tclass);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign the default security label on the new relation
|
||||||
|
*/
|
||||||
|
object.classId = RelationRelationId;
|
||||||
|
object.objectId = relOid;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We also assigns a default security label on columns of the new
|
||||||
|
* regular tables.
|
||||||
|
*/
|
||||||
|
if (classForm->relkind == RELKIND_RELATION)
|
||||||
|
{
|
||||||
|
AttrNumber index;
|
||||||
|
|
||||||
|
ccontext = sepgsql_compute_create(scontext, rcontext,
|
||||||
|
SEPG_CLASS_DB_COLUMN);
|
||||||
|
for (index = FirstLowInvalidHeapAttributeNumber + 1;
|
||||||
|
index <= classForm->relnatts;
|
||||||
|
index++)
|
||||||
|
{
|
||||||
|
if (index == InvalidAttrNumber)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (index == ObjectIdAttributeNumber && !classForm->relhasoids)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
object.classId = RelationRelationId;
|
||||||
|
object.objectId = relOid;
|
||||||
|
object.objectSubId = index;
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
|
||||||
|
}
|
||||||
|
pfree(ccontext);
|
||||||
|
}
|
||||||
|
pfree(rcontext);
|
||||||
|
out:
|
||||||
|
systable_endscan(sscan);
|
||||||
|
heap_close(rel, AccessShareLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_relation_relabel
|
||||||
|
*
|
||||||
|
* It checks privileges to relabel the supplied relation by the `seclabel'.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_relation_relabel(Oid relOid, const char *seclabel)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *audit_name;
|
||||||
|
char relkind;
|
||||||
|
uint16_t tclass = 0;
|
||||||
|
|
||||||
|
relkind = get_rel_relkind(relOid);
|
||||||
|
if (relkind == RELKIND_RELATION)
|
||||||
|
tclass = SEPG_CLASS_DB_TABLE;
|
||||||
|
else if (relkind == RELKIND_SEQUENCE)
|
||||||
|
tclass = SEPG_CLASS_DB_SEQUENCE;
|
||||||
|
else if (relkind == RELKIND_VIEW)
|
||||||
|
tclass = SEPG_CLASS_DB_VIEW;
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("cannot set security labels on relations except "
|
||||||
|
"for tables, sequences or views")));
|
||||||
|
|
||||||
|
audit_name = get_rel_name(relOid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_xxx:{setattr relabelfrom} permission
|
||||||
|
*/
|
||||||
|
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
||||||
|
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
tclass,
|
||||||
|
SEPG_DB_TABLE__SETATTR |
|
||||||
|
SEPG_DB_TABLE__RELABELFROM,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
pfree(tcontext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_xxx:{relabelto} permission
|
||||||
|
*/
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
seclabel,
|
||||||
|
tclass,
|
||||||
|
SEPG_DB_TABLE__RELABELTO,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
}
|
98
contrib/sepgsql/schema.c
Normal file
98
contrib/sepgsql/schema.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/schema.c
|
||||||
|
*
|
||||||
|
* Routines corresponding to schema objects
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "commands/seclabel.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_schema_post_create
|
||||||
|
*
|
||||||
|
* This routine assigns a default security label on a newly defined
|
||||||
|
* schema.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_schema_post_create(Oid namespaceId)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *ncontext;
|
||||||
|
ObjectAddress object;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: Right now, we assume pg_database object has a fixed
|
||||||
|
* security label, because pg_seclabel does not support to store
|
||||||
|
* label of shared database objects.
|
||||||
|
*/
|
||||||
|
tcontext = "system_u:object_r:sepgsql_db_t:s0";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute a default security label when we create a new schema
|
||||||
|
* object under the working database.
|
||||||
|
*/
|
||||||
|
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||||
|
SEPG_CLASS_DB_SCHEMA);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assign the default security label on a new procedure
|
||||||
|
*/
|
||||||
|
object.classId = NamespaceRelationId;
|
||||||
|
object.objectId = namespaceId;
|
||||||
|
object.objectSubId = 0;
|
||||||
|
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
|
||||||
|
|
||||||
|
pfree(ncontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_schema_relabel
|
||||||
|
*
|
||||||
|
* It checks privileges to relabel the supplied schema
|
||||||
|
* by the `seclabel'.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
|
||||||
|
{
|
||||||
|
char *scontext = sepgsql_get_client_label();
|
||||||
|
char *tcontext;
|
||||||
|
char *audit_name;
|
||||||
|
|
||||||
|
audit_name = get_namespace_name(namespaceId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_schema:{setattr relabelfrom} permission
|
||||||
|
*/
|
||||||
|
tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
|
||||||
|
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
tcontext,
|
||||||
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
|
SEPG_DB_SCHEMA__SETATTR |
|
||||||
|
SEPG_DB_SCHEMA__RELABELFROM,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check db_schema:{relabelto} permission
|
||||||
|
*/
|
||||||
|
sepgsql_check_perms(scontext,
|
||||||
|
seclabel,
|
||||||
|
SEPG_CLASS_DB_SCHEMA,
|
||||||
|
SEPG_DB_SCHEMA__RELABELTO,
|
||||||
|
audit_name,
|
||||||
|
true);
|
||||||
|
|
||||||
|
pfree(tcontext);
|
||||||
|
pfree(audit_name);
|
||||||
|
}
|
631
contrib/sepgsql/selinux.c
Normal file
631
contrib/sepgsql/selinux.c
Normal file
@ -0,0 +1,631 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/selinux.c
|
||||||
|
*
|
||||||
|
* Interactions between userspace and selinux in kernelspace,
|
||||||
|
* using libselinux api.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
|
|
||||||
|
#include "sepgsql.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* selinux_catalog
|
||||||
|
*
|
||||||
|
* This mapping table enables to translate the name of object classes and
|
||||||
|
* access vectors to/from their own codes.
|
||||||
|
* When we ask SELinux whether the required privileges are allowed or not,
|
||||||
|
* we use security_compute_av(3). It needs us to represent object classes
|
||||||
|
* and access vectors using 'external' codes defined in the security policy.
|
||||||
|
* It is determinded in the runtime, not build time. So, it needs an internal
|
||||||
|
* service to translate object class/access vectors which we want to check
|
||||||
|
* into the code which kernel want to be given.
|
||||||
|
*/
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
const char *class_name;
|
||||||
|
uint16 class_code;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
const char *av_name;
|
||||||
|
uint32 av_code;
|
||||||
|
} av[32];
|
||||||
|
} selinux_catalog[] = {
|
||||||
|
{
|
||||||
|
"process", SEPG_CLASS_PROCESS,
|
||||||
|
{
|
||||||
|
{ "transition", SEPG_PROCESS__TRANSITION },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file", SEPG_CLASS_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_FILE__READ },
|
||||||
|
{ "write", SEPG_FILE__WRITE },
|
||||||
|
{ "create", SEPG_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_FILE__RENAME },
|
||||||
|
{ "append", SEPG_FILE__APPEND },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dir", SEPG_CLASS_DIR,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_DIR__READ },
|
||||||
|
{ "write", SEPG_DIR__WRITE },
|
||||||
|
{ "create", SEPG_DIR__CREATE },
|
||||||
|
{ "getattr", SEPG_DIR__GETATTR },
|
||||||
|
{ "unlink", SEPG_DIR__UNLINK },
|
||||||
|
{ "rename", SEPG_DIR__RENAME },
|
||||||
|
{ "search", SEPG_DIR__SEARCH },
|
||||||
|
{ "add_name", SEPG_DIR__ADD_NAME },
|
||||||
|
{ "remove_name", SEPG_DIR__REMOVE_NAME },
|
||||||
|
{ "rmdir", SEPG_DIR__RMDIR },
|
||||||
|
{ "reparent", SEPG_DIR__REPARENT },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lnk_file", SEPG_CLASS_LNK_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_LNK_FILE__READ },
|
||||||
|
{ "write", SEPG_LNK_FILE__WRITE },
|
||||||
|
{ "create", SEPG_LNK_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_LNK_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_LNK_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_LNK_FILE__RENAME },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chr_file", SEPG_CLASS_CHR_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_CHR_FILE__READ },
|
||||||
|
{ "write", SEPG_CHR_FILE__WRITE },
|
||||||
|
{ "create", SEPG_CHR_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_CHR_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_CHR_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_CHR_FILE__RENAME },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blk_file", SEPG_CLASS_BLK_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_BLK_FILE__READ },
|
||||||
|
{ "write", SEPG_BLK_FILE__WRITE },
|
||||||
|
{ "create", SEPG_BLK_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_BLK_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_BLK_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_BLK_FILE__RENAME },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sock_file", SEPG_CLASS_SOCK_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_SOCK_FILE__READ },
|
||||||
|
{ "write", SEPG_SOCK_FILE__WRITE },
|
||||||
|
{ "create", SEPG_SOCK_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_SOCK_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_SOCK_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_SOCK_FILE__RENAME },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fifo_file", SEPG_CLASS_FIFO_FILE,
|
||||||
|
{
|
||||||
|
{ "read", SEPG_FIFO_FILE__READ },
|
||||||
|
{ "write", SEPG_FIFO_FILE__WRITE },
|
||||||
|
{ "create", SEPG_FIFO_FILE__CREATE },
|
||||||
|
{ "getattr", SEPG_FIFO_FILE__GETATTR },
|
||||||
|
{ "unlink", SEPG_FIFO_FILE__UNLINK },
|
||||||
|
{ "rename", SEPG_FIFO_FILE__RENAME },
|
||||||
|
{ NULL, 0UL }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_database", SEPG_CLASS_DB_DATABASE,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_DATABASE__CREATE },
|
||||||
|
{ "drop", SEPG_DB_DATABASE__DROP },
|
||||||
|
{ "getattr", SEPG_DB_DATABASE__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_DATABASE__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_DATABASE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_DATABASE__RELABELTO },
|
||||||
|
{ "access", SEPG_DB_DATABASE__ACCESS },
|
||||||
|
{ "load_module", SEPG_DB_DATABASE__LOAD_MODULE },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_schema", SEPG_CLASS_DB_SCHEMA,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_SCHEMA__CREATE },
|
||||||
|
{ "drop", SEPG_DB_SCHEMA__DROP },
|
||||||
|
{ "getattr", SEPG_DB_SCHEMA__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_SCHEMA__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_SCHEMA__RELABELTO },
|
||||||
|
{ "search", SEPG_DB_SCHEMA__SEARCH },
|
||||||
|
{ "add_name", SEPG_DB_SCHEMA__ADD_NAME },
|
||||||
|
{ "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_table", SEPG_CLASS_DB_TABLE,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_TABLE__CREATE },
|
||||||
|
{ "drop", SEPG_DB_TABLE__DROP },
|
||||||
|
{ "getattr", SEPG_DB_TABLE__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_TABLE__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_TABLE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_TABLE__RELABELTO },
|
||||||
|
{ "select", SEPG_DB_TABLE__SELECT },
|
||||||
|
{ "update", SEPG_DB_TABLE__UPDATE },
|
||||||
|
{ "insert", SEPG_DB_TABLE__INSERT },
|
||||||
|
{ "delete", SEPG_DB_TABLE__DELETE },
|
||||||
|
{ "lock", SEPG_DB_TABLE__LOCK },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_sequence", SEPG_CLASS_DB_SEQUENCE,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_SEQUENCE__CREATE },
|
||||||
|
{ "drop", SEPG_DB_SEQUENCE__DROP },
|
||||||
|
{ "getattr", SEPG_DB_SEQUENCE__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_SEQUENCE__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_SEQUENCE__RELABELTO },
|
||||||
|
{ "get_value", SEPG_DB_SEQUENCE__GET_VALUE },
|
||||||
|
{ "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE },
|
||||||
|
{ "set_value", SEPG_DB_SEQUENCE__SET_VALUE },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_procedure", SEPG_CLASS_DB_PROCEDURE,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_PROCEDURE__CREATE },
|
||||||
|
{ "drop", SEPG_DB_PROCEDURE__DROP },
|
||||||
|
{ "getattr", SEPG_DB_PROCEDURE__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_PROCEDURE__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_PROCEDURE__RELABELTO },
|
||||||
|
{ "execute", SEPG_DB_PROCEDURE__EXECUTE },
|
||||||
|
{ "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT },
|
||||||
|
{ "install", SEPG_DB_PROCEDURE__INSTALL },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_column", SEPG_CLASS_DB_COLUMN,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_COLUMN__CREATE },
|
||||||
|
{ "drop", SEPG_DB_COLUMN__DROP },
|
||||||
|
{ "getattr", SEPG_DB_COLUMN__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_COLUMN__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_COLUMN__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_COLUMN__RELABELTO },
|
||||||
|
{ "select", SEPG_DB_COLUMN__SELECT },
|
||||||
|
{ "update", SEPG_DB_COLUMN__UPDATE },
|
||||||
|
{ "insert", SEPG_DB_COLUMN__INSERT },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_tuple", SEPG_CLASS_DB_TUPLE,
|
||||||
|
{
|
||||||
|
{ "relabelfrom", SEPG_DB_TUPLE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_TUPLE__RELABELTO },
|
||||||
|
{ "select", SEPG_DB_TUPLE__SELECT },
|
||||||
|
{ "update", SEPG_DB_TUPLE__UPDATE },
|
||||||
|
{ "insert", SEPG_DB_TUPLE__INSERT },
|
||||||
|
{ "delete", SEPG_DB_TUPLE__DELETE },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_blob", SEPG_CLASS_DB_BLOB,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_BLOB__CREATE },
|
||||||
|
{ "drop", SEPG_DB_BLOB__DROP },
|
||||||
|
{ "getattr", SEPG_DB_BLOB__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_BLOB__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_BLOB__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_BLOB__RELABELTO },
|
||||||
|
{ "read", SEPG_DB_BLOB__READ },
|
||||||
|
{ "write", SEPG_DB_BLOB__WRITE },
|
||||||
|
{ "import", SEPG_DB_BLOB__IMPORT },
|
||||||
|
{ "export", SEPG_DB_BLOB__EXPORT },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_language", SEPG_CLASS_DB_LANGUAGE,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_LANGUAGE__CREATE },
|
||||||
|
{ "drop", SEPG_DB_LANGUAGE__DROP },
|
||||||
|
{ "getattr", SEPG_DB_LANGUAGE__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_LANGUAGE__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_LANGUAGE__RELABELTO },
|
||||||
|
{ "implement", SEPG_DB_LANGUAGE__IMPLEMENT },
|
||||||
|
{ "execute", SEPG_DB_LANGUAGE__EXECUTE },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"db_view", SEPG_CLASS_DB_VIEW,
|
||||||
|
{
|
||||||
|
{ "create", SEPG_DB_VIEW__CREATE },
|
||||||
|
{ "drop", SEPG_DB_VIEW__DROP },
|
||||||
|
{ "getattr", SEPG_DB_VIEW__GETATTR },
|
||||||
|
{ "setattr", SEPG_DB_VIEW__SETATTR },
|
||||||
|
{ "relabelfrom", SEPG_DB_VIEW__RELABELFROM },
|
||||||
|
{ "relabelto", SEPG_DB_VIEW__RELABELTO },
|
||||||
|
{ "expand", SEPG_DB_VIEW__EXPAND },
|
||||||
|
{ NULL, 0UL },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_mode
|
||||||
|
*
|
||||||
|
* SEPGSQL_MODE_DISABLED: Disabled on runtime
|
||||||
|
* SEPGSQL_MODE_DEFAULT: Same as system settings
|
||||||
|
* SEPGSQL_MODE_PERMISSIVE: Always permissive mode
|
||||||
|
* SEPGSQL_MODE_INTERNAL: Same as permissive, except for no audit logs
|
||||||
|
*/
|
||||||
|
static int sepgsql_mode = SEPGSQL_MODE_INTERNAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_is_enabled
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sepgsql_is_enabled(void)
|
||||||
|
{
|
||||||
|
return (sepgsql_mode != SEPGSQL_MODE_DISABLED ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_get_mode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sepgsql_get_mode(void)
|
||||||
|
{
|
||||||
|
return sepgsql_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_set_mode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sepgsql_set_mode(int new_mode)
|
||||||
|
{
|
||||||
|
int old_mode = sepgsql_mode;
|
||||||
|
|
||||||
|
sepgsql_mode = new_mode;
|
||||||
|
|
||||||
|
return old_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_getenforce
|
||||||
|
*
|
||||||
|
* It returns whether the current working mode tries to enforce access
|
||||||
|
* control decision, or not. It shall be enforced when sepgsql_mode is
|
||||||
|
* SEPGSQL_MODE_DEFAULT and system is running in enforcing mode.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sepgsql_getenforce(void)
|
||||||
|
{
|
||||||
|
if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
|
||||||
|
security_getenforce() > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_audit_log
|
||||||
|
*
|
||||||
|
* It generates a security audit record. In the default, it writes out
|
||||||
|
* audit records into standard PG's logfile. It also allows to set up
|
||||||
|
* external audit log receiver, such as auditd in Linux, using the
|
||||||
|
* sepgsql_audit_hook.
|
||||||
|
*
|
||||||
|
* SELinux can control what should be audited and should not using
|
||||||
|
* "auditdeny" and "auditallow" rules in the security policy. In the
|
||||||
|
* default, all the access violations are audited, and all the access
|
||||||
|
* allowed are not audited. But we can set up the security policy, so
|
||||||
|
* we can have exceptions. So, it is necessary to follow the suggestion
|
||||||
|
* come from the security policy. (av_decision.auditallow and auditdeny)
|
||||||
|
*
|
||||||
|
* Security audit is an important feature, because it enables us to check
|
||||||
|
* what was happen if we have a security incident. In fact, ISO/IEC15408
|
||||||
|
* defines several security functionalities for audit features.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_audit_log(bool denied,
|
||||||
|
const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
uint32 audited,
|
||||||
|
const char *audit_name)
|
||||||
|
{
|
||||||
|
StringInfoData buf;
|
||||||
|
const char *class_name;
|
||||||
|
const char *av_name;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* lookup name of the object class */
|
||||||
|
Assert(tclass < SEPG_CLASS_MAX);
|
||||||
|
class_name = selinux_catalog[tclass].class_name;
|
||||||
|
|
||||||
|
/* lookup name of the permissions */
|
||||||
|
initStringInfo(&buf);
|
||||||
|
appendStringInfo(&buf, "%s {",
|
||||||
|
(denied ? "denied" : "allowed"));
|
||||||
|
for (i=0; selinux_catalog[tclass].av[i].av_name; i++)
|
||||||
|
{
|
||||||
|
if (audited & (1UL << i))
|
||||||
|
{
|
||||||
|
av_name = selinux_catalog[tclass].av[i].av_name;
|
||||||
|
appendStringInfo(&buf, " %s", av_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendStringInfo(&buf, " }");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call external audit module, if loaded
|
||||||
|
*/
|
||||||
|
appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
|
||||||
|
scontext, tcontext, class_name);
|
||||||
|
if (audit_name)
|
||||||
|
appendStringInfo(&buf, " name=%s", audit_name);
|
||||||
|
|
||||||
|
ereport(LOG, (errmsg("SELinux: %s", buf.data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_compute_avd
|
||||||
|
*
|
||||||
|
* It actually asks SELinux what permissions are allowed on a pair of
|
||||||
|
* the security contexts and object class. It also returns what permissions
|
||||||
|
* should be audited on access violation or allowed.
|
||||||
|
* In most cases, subject's security context (scontext) is a client, and
|
||||||
|
* target security context (tcontext) is a database object.
|
||||||
|
*
|
||||||
|
* The access control decision shall be set on the given av_decision.
|
||||||
|
* The av_decision.allowed has a bitmask of SEPG_<class>__<perms>
|
||||||
|
* to suggest a set of allowed actions in this object class.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sepgsql_compute_avd(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
struct av_decision *avd)
|
||||||
|
{
|
||||||
|
const char *tclass_name;
|
||||||
|
security_class_t tclass_ex;
|
||||||
|
struct av_decision avd_ex;
|
||||||
|
int i, deny_unknown = security_deny_unknown();
|
||||||
|
|
||||||
|
/* Get external code of the object class*/
|
||||||
|
Assert(tclass < SEPG_CLASS_MAX);
|
||||||
|
Assert(tclass == selinux_catalog[tclass].class_code);
|
||||||
|
|
||||||
|
tclass_name = selinux_catalog[tclass].class_name;
|
||||||
|
tclass_ex = string_to_security_class(tclass_name);
|
||||||
|
|
||||||
|
if (tclass_ex == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the current security policy does not support permissions
|
||||||
|
* corresponding to database objects, we fill up them with dummy
|
||||||
|
* data.
|
||||||
|
* If security_deny_unknown() returns positive value, undefined
|
||||||
|
* permissions should be denied. Otherwise, allowed
|
||||||
|
*/
|
||||||
|
avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
|
||||||
|
avd->auditallow = 0U;
|
||||||
|
avd->auditdeny = ~0U;
|
||||||
|
avd->flags = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ask SELinux what is allowed set of permissions on a pair of the
|
||||||
|
* security contexts and the given object class.
|
||||||
|
*/
|
||||||
|
if (security_compute_av_flags_raw((security_context_t)scontext,
|
||||||
|
(security_context_t)tcontext,
|
||||||
|
tclass_ex, 0, &avd_ex) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("SELinux could not compute av_decision: "
|
||||||
|
"scontext=%s tcontext=%s tclass=%s",
|
||||||
|
scontext, tcontext, tclass_name)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SELinux returns its access control decision as a set of permissions
|
||||||
|
* represented in external code which depends on run-time environment.
|
||||||
|
* So, we need to translate it to the internal representation before
|
||||||
|
* returning results for the caller.
|
||||||
|
*/
|
||||||
|
memset(avd, 0, sizeof(struct av_decision));
|
||||||
|
|
||||||
|
for (i=0; selinux_catalog[tclass].av[i].av_name; i++)
|
||||||
|
{
|
||||||
|
access_vector_t av_code_ex;
|
||||||
|
const char *av_name = selinux_catalog[tclass].av[i].av_name;
|
||||||
|
uint32 av_code = selinux_catalog[tclass].av[i].av_code;
|
||||||
|
|
||||||
|
av_code_ex = string_to_av_perm(tclass_ex, av_name);
|
||||||
|
if (av_code_ex == 0)
|
||||||
|
{
|
||||||
|
/* fill up undefined permissions */
|
||||||
|
if (!deny_unknown)
|
||||||
|
avd->allowed |= av_code;
|
||||||
|
avd->auditdeny |= av_code;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avd_ex.allowed & av_code_ex)
|
||||||
|
avd->allowed |= av_code;
|
||||||
|
if (avd_ex.auditallow & av_code_ex)
|
||||||
|
avd->auditallow |= av_code;
|
||||||
|
if (avd_ex.auditdeny & av_code_ex)
|
||||||
|
avd->auditdeny |= av_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_compute_create
|
||||||
|
*
|
||||||
|
* It returns a default security context to be assigned on a new database
|
||||||
|
* object. SELinux compute it based on a combination of client, upper object
|
||||||
|
* which owns the new object and object class.
|
||||||
|
*
|
||||||
|
* For example, when a client (staff_u:staff_r:staff_t:s0) tries to create
|
||||||
|
* a new table within a schema (system_u:object_r:sepgsql_schema_t:s0),
|
||||||
|
* SELinux looks-up its security policy. If it has a special rule on the
|
||||||
|
* combination of these security contexts and object class (db_table),
|
||||||
|
* it returns the security context suggested by the special rule.
|
||||||
|
* Otherwise, it returns the security context of schema, as is.
|
||||||
|
*
|
||||||
|
* We expect the caller already applies sanity/validation checks on the
|
||||||
|
* given security context.
|
||||||
|
*
|
||||||
|
* scontext: security context of the subject (mostly, peer process).
|
||||||
|
* tcontext: security context of the the upper database object.
|
||||||
|
* tclass: class code (SEPG_CLASS_*) of the new object in creation
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
sepgsql_compute_create(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass)
|
||||||
|
{
|
||||||
|
security_context_t ncontext;
|
||||||
|
security_class_t tclass_ex;
|
||||||
|
const char *tclass_name;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
/* Get external code of the object class*/
|
||||||
|
Assert(tclass < SEPG_CLASS_MAX);
|
||||||
|
|
||||||
|
tclass_name = selinux_catalog[tclass].class_name;
|
||||||
|
tclass_ex = string_to_security_class(tclass_name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ask SELinux what is the default context for the given object class
|
||||||
|
* on a pair of security contexts
|
||||||
|
*/
|
||||||
|
if (security_compute_create_raw((security_context_t)scontext,
|
||||||
|
(security_context_t)tcontext,
|
||||||
|
tclass_ex, &ncontext) < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("SELinux could not compute a new context: "
|
||||||
|
"scontext=%s tcontext=%s tclass=%s",
|
||||||
|
scontext, tcontext, tclass_name)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libselinux returns malloc()'ed string, so we need to copy it
|
||||||
|
* on the palloc()'ed region.
|
||||||
|
*/
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
result = pstrdup(ncontext);
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
freecon(ncontext);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
freecon(ncontext);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sepgsql_check_perms
|
||||||
|
*
|
||||||
|
* It makes access control decision without userspace caching mechanism.
|
||||||
|
* If SELinux denied the required accesses on the pair of security labels,
|
||||||
|
* it raises an error or returns false.
|
||||||
|
*
|
||||||
|
* scontext: security label of the subject (mostly, peer process)
|
||||||
|
* tcontext: security label of the object being referenced
|
||||||
|
* tclass: class code (SEPG_CLASS_*) of the object being referenced
|
||||||
|
* required: a mask of required permissions (SEPG_<class>__<perm>)
|
||||||
|
* audit_name: a human readable object name for audit logs, or NULL.
|
||||||
|
* abort: true, if caller wants to raise an error on access violation
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sepgsql_check_perms(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
uint32 required,
|
||||||
|
const char *audit_name,
|
||||||
|
bool abort)
|
||||||
|
{
|
||||||
|
struct av_decision avd;
|
||||||
|
uint32 denied;
|
||||||
|
uint32 audited;
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
|
||||||
|
|
||||||
|
denied = required & ~avd.allowed;
|
||||||
|
|
||||||
|
if (sepgsql_get_debug_audit())
|
||||||
|
audited = (denied ? denied : required);
|
||||||
|
else
|
||||||
|
audited = (denied ? (denied & avd.auditdeny)
|
||||||
|
: (required & avd.auditallow));
|
||||||
|
|
||||||
|
if (denied &&
|
||||||
|
sepgsql_getenforce() > 0 &&
|
||||||
|
(avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0)
|
||||||
|
result = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It records a security audit for the request, if needed.
|
||||||
|
* But, when SE-PgSQL performs 'internal' mode, it needs to keep silent.
|
||||||
|
*/
|
||||||
|
if (audited && sepgsql_mode != SEPGSQL_MODE_INTERNAL)
|
||||||
|
{
|
||||||
|
sepgsql_audit_log(denied,
|
||||||
|
scontext,
|
||||||
|
tcontext,
|
||||||
|
tclass,
|
||||||
|
audited,
|
||||||
|
audit_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result && abort)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("SELinux: security policy violation")));
|
||||||
|
return result;
|
||||||
|
}
|
59
contrib/sepgsql/sepgsql-regtest.te
Normal file
59
contrib/sepgsql/sepgsql-regtest.te
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
policy_module(sepgsql-regtest, 1.01)
|
||||||
|
|
||||||
|
## <desc>
|
||||||
|
## <p>
|
||||||
|
## Allow to launch regression test of SE-PostgreSQL
|
||||||
|
## Don't switch to TRUE in normal cases
|
||||||
|
## </p>
|
||||||
|
## </desc>
|
||||||
|
gen_tunable(sepgsql_regression_test_mode, false)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test domains for database administrators
|
||||||
|
#
|
||||||
|
role sepgsql_regtest_dba_r;
|
||||||
|
userdom_base_user_template(sepgsql_regtest_dba)
|
||||||
|
userdom_manage_home_role(sepgsql_regtest_dba_r, sepgsql_regtest_dba_t)
|
||||||
|
userdom_write_user_tmp_sockets(sepgsql_regtest_user_t)
|
||||||
|
optional_policy(`
|
||||||
|
postgresql_admin(sepgsql_regtest_dba_t, sepgsql_regtest_dba_r)
|
||||||
|
postgresql_stream_connect(sepgsql_regtest_dba_t)
|
||||||
|
')
|
||||||
|
optional_policy(`
|
||||||
|
unconfined_stream_connect(sepgsql_regtest_dba_t)
|
||||||
|
unconfined_rw_pipes(sepgsql_regtest_dba_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
#
|
||||||
|
# Dummy domain for unpriv users
|
||||||
|
#
|
||||||
|
role sepgsql_regtest_user_r;
|
||||||
|
userdom_base_user_template(sepgsql_regtest_user)
|
||||||
|
userdom_manage_home_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t)
|
||||||
|
userdom_write_user_tmp_sockets(sepgsql_regtest_user_t)
|
||||||
|
optional_policy(`
|
||||||
|
postgresql_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t)
|
||||||
|
postgresql_stream_connect(sepgsql_regtest_user_t)
|
||||||
|
')
|
||||||
|
optional_policy(`
|
||||||
|
unconfined_stream_connect(sepgsql_regtest_user_t)
|
||||||
|
unconfined_rw_pipes(sepgsql_regtest_user_t)
|
||||||
|
')
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rules to launch psql in the dummy domains
|
||||||
|
#
|
||||||
|
optional_policy(`
|
||||||
|
gen_require(`
|
||||||
|
role unconfined_r;
|
||||||
|
type unconfined_t;
|
||||||
|
type sepgsql_trusted_proc_t;
|
||||||
|
')
|
||||||
|
tunable_policy(`sepgsql_regression_test_mode',`
|
||||||
|
allow unconfined_t sepgsql_regtest_dba_t : process { transition };
|
||||||
|
allow unconfined_t sepgsql_regtest_user_t : process { transition };
|
||||||
|
')
|
||||||
|
role unconfined_r types sepgsql_regtest_dba_t;
|
||||||
|
role unconfined_r types sepgsql_regtest_user_t;
|
||||||
|
role unconfined_r types sepgsql_trusted_proc_t;
|
||||||
|
')
|
288
contrib/sepgsql/sepgsql.h
Normal file
288
contrib/sepgsql/sepgsql.h
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* contrib/sepgsql/sepgsql.h
|
||||||
|
*
|
||||||
|
* Definitions corresponding to SE-PostgreSQL
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef SEPGSQL_H
|
||||||
|
#define SEPGSQL_H
|
||||||
|
|
||||||
|
#include "catalog/objectaddress.h"
|
||||||
|
#include <selinux/selinux.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SE-PostgreSQL Label Tag
|
||||||
|
*/
|
||||||
|
#define SEPGSQL_LABEL_TAG "selinux"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SE-PostgreSQL performing mode
|
||||||
|
*/
|
||||||
|
#define SEPGSQL_MODE_DEFAULT 1
|
||||||
|
#define SEPGSQL_MODE_PERMISSIVE 2
|
||||||
|
#define SEPGSQL_MODE_INTERNAL 3
|
||||||
|
#define SEPGSQL_MODE_DISABLED 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internally used code of object classes
|
||||||
|
*/
|
||||||
|
#define SEPG_CLASS_PROCESS 0
|
||||||
|
#define SEPG_CLASS_FILE 1
|
||||||
|
#define SEPG_CLASS_DIR 2
|
||||||
|
#define SEPG_CLASS_LNK_FILE 3
|
||||||
|
#define SEPG_CLASS_CHR_FILE 4
|
||||||
|
#define SEPG_CLASS_BLK_FILE 5
|
||||||
|
#define SEPG_CLASS_SOCK_FILE 6
|
||||||
|
#define SEPG_CLASS_FIFO_FILE 7
|
||||||
|
#define SEPG_CLASS_DB_DATABASE 8
|
||||||
|
#define SEPG_CLASS_DB_SCHEMA 9
|
||||||
|
#define SEPG_CLASS_DB_TABLE 10
|
||||||
|
#define SEPG_CLASS_DB_SEQUENCE 11
|
||||||
|
#define SEPG_CLASS_DB_PROCEDURE 12
|
||||||
|
#define SEPG_CLASS_DB_COLUMN 13
|
||||||
|
#define SEPG_CLASS_DB_TUPLE 14
|
||||||
|
#define SEPG_CLASS_DB_BLOB 15
|
||||||
|
#define SEPG_CLASS_DB_LANGUAGE 16
|
||||||
|
#define SEPG_CLASS_DB_VIEW 17
|
||||||
|
#define SEPG_CLASS_MAX 18
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internally used code of access vectors
|
||||||
|
*/
|
||||||
|
#define SEPG_PROCESS__TRANSITION (1<<0)
|
||||||
|
|
||||||
|
#define SEPG_FILE__READ (1<<0)
|
||||||
|
#define SEPG_FILE__WRITE (1<<1)
|
||||||
|
#define SEPG_FILE__CREATE (1<<2)
|
||||||
|
#define SEPG_FILE__GETATTR (1<<3)
|
||||||
|
#define SEPG_FILE__UNLINK (1<<4)
|
||||||
|
#define SEPG_FILE__RENAME (1<<5)
|
||||||
|
#define SEPG_FILE__APPEND (1<<6)
|
||||||
|
|
||||||
|
#define SEPG_DIR__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_DIR__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_DIR__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_DIR__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_DIR__RENAME (SEPG_FILE__RENAME)
|
||||||
|
#define SEPG_DIR__SEARCH (1<<6)
|
||||||
|
#define SEPG_DIR__ADD_NAME (1<<7)
|
||||||
|
#define SEPG_DIR__REMOVE_NAME (1<<8)
|
||||||
|
#define SEPG_DIR__RMDIR (1<<9)
|
||||||
|
#define SEPG_DIR__REPARENT (1<<10)
|
||||||
|
|
||||||
|
#define SEPG_LNK_FILE__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_LNK_FILE__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_LNK_FILE__RENAME (SEPG_FILE__RENAME)
|
||||||
|
|
||||||
|
#define SEPG_CHR_FILE__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_CHR_FILE__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_CHR_FILE__RENAME (SEPG_FILE__RENAME)
|
||||||
|
|
||||||
|
#define SEPG_BLK_FILE__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_BLK_FILE__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_BLK_FILE__RENAME (SEPG_FILE__RENAME)
|
||||||
|
|
||||||
|
#define SEPG_SOCK_FILE__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_SOCK_FILE__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_SOCK_FILE__RENAME (SEPG_FILE__RENAME)
|
||||||
|
|
||||||
|
#define SEPG_FIFO_FILE__READ (SEPG_FILE__READ)
|
||||||
|
#define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE)
|
||||||
|
#define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE)
|
||||||
|
#define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR)
|
||||||
|
#define SEPG_FIFO_FILE__UNLINK (SEPG_FILE__UNLINK)
|
||||||
|
#define SEPG_FIFO_FILE__RENAME (SEPG_FILE__RENAME)
|
||||||
|
|
||||||
|
#define SEPG_DB_DATABASE__CREATE (1<<0)
|
||||||
|
#define SEPG_DB_DATABASE__DROP (1<<1)
|
||||||
|
#define SEPG_DB_DATABASE__GETATTR (1<<2)
|
||||||
|
#define SEPG_DB_DATABASE__SETATTR (1<<3)
|
||||||
|
#define SEPG_DB_DATABASE__RELABELFROM (1<<4)
|
||||||
|
#define SEPG_DB_DATABASE__RELABELTO (1<<5)
|
||||||
|
#define SEPG_DB_DATABASE__ACCESS (1<<6)
|
||||||
|
#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7)
|
||||||
|
|
||||||
|
#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_SCHEMA__SEARCH (1<<6)
|
||||||
|
#define SEPG_DB_SCHEMA__ADD_NAME (1<<7)
|
||||||
|
#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8)
|
||||||
|
|
||||||
|
#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_TABLE__SELECT (1<<6)
|
||||||
|
#define SEPG_DB_TABLE__UPDATE (1<<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__INDEXON (1<<11)
|
||||||
|
|
||||||
|
#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6)
|
||||||
|
#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7)
|
||||||
|
#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8)
|
||||||
|
|
||||||
|
#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_PROCEDURE__EXECUTE (1<<6)
|
||||||
|
#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7)
|
||||||
|
#define SEPG_DB_PROCEDURE__INSTALL (1<<8)
|
||||||
|
|
||||||
|
#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_COLUMN__SELECT (1<<6)
|
||||||
|
#define SEPG_DB_COLUMN__UPDATE (1<<7)
|
||||||
|
#define SEPG_DB_COLUMN__INSERT (1<<8)
|
||||||
|
|
||||||
|
#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP)
|
||||||
|
|
||||||
|
#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_BLOB__READ (1<<6)
|
||||||
|
#define SEPG_DB_BLOB__WRITE (1<<7)
|
||||||
|
#define SEPG_DB_BLOB__IMPORT (1<<8)
|
||||||
|
#define SEPG_DB_BLOB__EXPORT (1<<9)
|
||||||
|
|
||||||
|
#define SEPG_DB_LANGUAGE__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_LANGUAGE__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_LANGUAGE__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_LANGUAGE__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_LANGUAGE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_LANGUAGE__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_LANGUAGE__IMPLEMENT (1<<6)
|
||||||
|
#define SEPG_DB_LANGUAGE__EXECUTE (1<<7)
|
||||||
|
|
||||||
|
#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE)
|
||||||
|
#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP)
|
||||||
|
#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR)
|
||||||
|
#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR)
|
||||||
|
#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM)
|
||||||
|
#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO)
|
||||||
|
#define SEPG_DB_VIEW__EXPAND (1<<6)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hooks.c
|
||||||
|
*/
|
||||||
|
extern bool sepgsql_get_permissive(void);
|
||||||
|
extern bool sepgsql_get_debug_audit(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* selinux.c
|
||||||
|
*/
|
||||||
|
extern bool sepgsql_is_enabled(void);
|
||||||
|
extern int sepgsql_get_mode(void);
|
||||||
|
extern int sepgsql_set_mode(int new_mode);
|
||||||
|
extern bool sepgsql_getenforce(void);
|
||||||
|
|
||||||
|
extern void sepgsql_audit_log(bool denied,
|
||||||
|
const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
uint32 audited,
|
||||||
|
const char *audit_name);
|
||||||
|
|
||||||
|
extern void sepgsql_compute_avd(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
struct av_decision *avd);
|
||||||
|
|
||||||
|
extern char *sepgsql_compute_create(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass);
|
||||||
|
|
||||||
|
extern bool sepgsql_check_perms(const char *scontext,
|
||||||
|
const char *tcontext,
|
||||||
|
uint16 tclass,
|
||||||
|
uint32 required,
|
||||||
|
const char *audit_name,
|
||||||
|
bool abort);
|
||||||
|
/*
|
||||||
|
* label.c
|
||||||
|
*/
|
||||||
|
extern char *sepgsql_get_client_label(void);
|
||||||
|
extern char *sepgsql_set_client_label(char *new_label);
|
||||||
|
extern char *sepgsql_get_label(Oid relOid, Oid objOid, int32 subId);
|
||||||
|
|
||||||
|
extern void sepgsql_object_relabel(const ObjectAddress *object,
|
||||||
|
const char *seclabel);
|
||||||
|
|
||||||
|
extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dml.c
|
||||||
|
*/
|
||||||
|
extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* schema.c
|
||||||
|
*/
|
||||||
|
extern void sepgsql_schema_post_create(Oid namespaceId);
|
||||||
|
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* relation.c
|
||||||
|
*/
|
||||||
|
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
|
||||||
|
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
|
||||||
|
const char *seclabel);
|
||||||
|
extern void sepgsql_relation_post_create(Oid relOid);
|
||||||
|
extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* proc.c
|
||||||
|
*/
|
||||||
|
extern void sepgsql_proc_post_create(Oid functionId);
|
||||||
|
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
|
||||||
|
extern char *sepgsql_proc_get_domtrans(Oid functionId);
|
||||||
|
|
||||||
|
#endif /* SEPGSQL_H */
|
36
contrib/sepgsql/sepgsql.sql.in
Normal file
36
contrib/sepgsql/sepgsql.sql.in
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
--
|
||||||
|
-- contrib/sepgsql/sepgsql.sql
|
||||||
|
--
|
||||||
|
-- [Step to install]
|
||||||
|
--
|
||||||
|
-- 1. Run initdb
|
||||||
|
-- to set up a new database cluster.
|
||||||
|
--
|
||||||
|
-- 2. Edit $PGDATA/postgresql.conf
|
||||||
|
-- to add 'MODULE_PATHNAME' to shared_preload_libraries.
|
||||||
|
--
|
||||||
|
-- Example)
|
||||||
|
-- shared_preload_libraries = 'MODULE_PATHNAME'
|
||||||
|
--
|
||||||
|
-- 3. Run this script for each databases
|
||||||
|
-- This script installs corresponding functions, and assigns initial
|
||||||
|
-- security labels on target database objects.
|
||||||
|
-- It can be run both single-user mode and multi-user mode, according
|
||||||
|
-- to your preference.
|
||||||
|
--
|
||||||
|
-- Example)
|
||||||
|
-- $ for DBNAME in template0 template1 postgres; \
|
||||||
|
-- do \
|
||||||
|
-- postgres --single -F -c exit_on_error=true -D $PGDATA $DBNAME \
|
||||||
|
-- < /path/to/script/sepgsql.sql > /dev/null \
|
||||||
|
-- done
|
||||||
|
--
|
||||||
|
-- 4. Start postmaster,
|
||||||
|
-- if you initialized the database in single-user mode.
|
||||||
|
--
|
||||||
|
LOAD 'MODULE_PATHNAME';
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_getcon() RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_getcon' LANGUAGE C;
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_out(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_out' LANGUAGE C STRICT;
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_restorecon(text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_restorecon' LANGUAGE C;
|
||||||
|
SELECT sepgsql_restorecon(NULL);
|
118
contrib/sepgsql/sql/dml.sql
Normal file
118
contrib/sepgsql/sql/dml.sql
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for DML Permissions
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Setup
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (a int, b text);
|
||||||
|
SECURITY LABEL ON TABLE t1 IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||||
|
|
||||||
|
CREATE TABLE t2 (x int, y text);
|
||||||
|
SECURITY LABEL ON TABLE t2 IS 'system_u:object_r:sepgsql_ro_table_t:s0';
|
||||||
|
INSERT INTO t2 VALUES (1, 'xxx'), (2, 'yyy'), (3, 'zzz');
|
||||||
|
|
||||||
|
CREATE TABLE t3 (s int, t text);
|
||||||
|
SECURITY LABEL ON TABLE t3 IS 'system_u:object_r:sepgsql_fixed_table_t:s0';
|
||||||
|
INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
|
||||||
|
|
||||||
|
CREATE TABLE t4 (m int, n text);
|
||||||
|
SECURITY LABEL ON TABLE t4 IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
INSERT INTO t4 VALUES (1, 'mmm'), (2, 'nnn'), (3, 'ooo');
|
||||||
|
|
||||||
|
CREATE TABLE t5 (e text, f text, g text);
|
||||||
|
SECURITY LABEL ON TABLE t5 IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0';
|
||||||
|
SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
|
||||||
|
CREATE TABLE customer (cid int primary key, cname text, ccredit text);
|
||||||
|
SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
INSERT INTO customer VALUES (1, 'Taro', '1111-2222-3333-4444'),
|
||||||
|
(2, 'Hanako', '5555-6666-7777-8888');
|
||||||
|
CREATE FUNCTION customer_credit(int) RETURNS text
|
||||||
|
AS 'SELECT regexp_replace(ccredit, ''-[0-9]+$'', ''-????'') FROM customer WHERE cid = $1'
|
||||||
|
LANGUAGE sql;
|
||||||
|
SECURITY LABEL ON FUNCTION customer_credit(int)
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
|
||||||
|
SELECT objtype, objname, label FROM pg_seclabels
|
||||||
|
WHERE provider = 'selinux'
|
||||||
|
AND objtype in ('table', 'column')
|
||||||
|
AND objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g');
|
||||||
|
|
||||||
|
-- Hardwired Rules
|
||||||
|
UPDATE pg_attribute SET attisdropped = true
|
||||||
|
WHERE attrelid = 't5'::regclass AND attname = 'f'; -- failed
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Simple DML statements
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
|
||||||
|
SELECT * FROM t1; -- ok
|
||||||
|
SELECT * FROM t2; -- ok
|
||||||
|
SELECT * FROM t3; -- ok
|
||||||
|
SELECT * FROM t4; -- failed
|
||||||
|
SELECT * FROM t5; -- failed
|
||||||
|
SELECT e,f FROM t5; -- ok
|
||||||
|
|
||||||
|
SELECT * FROM customer; -- failed
|
||||||
|
SELECT cid, cname, customer_credit(cid) FROM customer; -- ok
|
||||||
|
|
||||||
|
SELECT count(*) FROM t5; -- ok
|
||||||
|
SELECT count(*) FROM t5 WHERE g IS NULL; -- failed
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES (4, 'abc'); -- ok
|
||||||
|
INSERT INTO t2 VALUES (4, 'xyz'); -- failed
|
||||||
|
INSERT INTO t3 VALUES (4, 'stu'); -- ok
|
||||||
|
INSERT INTO t4 VALUES (4, 'mno'); -- failed
|
||||||
|
INSERT INTO t5 VALUES (1,2,3); -- failed
|
||||||
|
INSERT INTO t5 (e,f) VALUES ('abc', 'def'); -- failed
|
||||||
|
INSERT INTO t5 (e) VALUES ('abc'); -- ok
|
||||||
|
|
||||||
|
UPDATE t1 SET b = b || '_upd'; -- ok
|
||||||
|
UPDATE t2 SET y = y || '_upd'; -- failed
|
||||||
|
UPDATE t3 SET t = t || '_upd'; -- failed
|
||||||
|
UPDATE t4 SET n = n || '_upd'; -- failed
|
||||||
|
UPDATE t5 SET e = 'xyz'; -- ok
|
||||||
|
UPDATE t5 SET e = f || '_upd'; -- ok
|
||||||
|
UPDATE t5 SET e = g || '_upd'; -- failed
|
||||||
|
|
||||||
|
DELETE FROM t1; -- ok
|
||||||
|
DELETE FROM t2; -- failed
|
||||||
|
DELETE FROM t3; -- failed
|
||||||
|
DELETE FROM t4; -- failed
|
||||||
|
DELETE FROM t5; -- ok
|
||||||
|
DELETE FROM t5 WHERE f IS NULL; -- ok
|
||||||
|
DELETE FROM t5 WHERE g IS NULL; -- failed
|
||||||
|
|
||||||
|
--
|
||||||
|
-- COPY TO/FROM statements
|
||||||
|
--
|
||||||
|
COPY t1 TO '/dev/null'; -- ok
|
||||||
|
COPY t2 TO '/dev/null'; -- ok
|
||||||
|
COPY t3 TO '/dev/null'; -- ok
|
||||||
|
COPY t4 TO '/dev/null'; -- failed
|
||||||
|
COPY t5 TO '/dev/null'; -- failed
|
||||||
|
COPY t5(e,f) TO '/dev/null'; -- ok
|
||||||
|
|
||||||
|
COPY t1 FROM '/dev/null'; -- ok
|
||||||
|
COPY t2 FROM '/dev/null'; -- failed
|
||||||
|
COPY t3 FROM '/dev/null'; -- ok
|
||||||
|
COPY t4 FROM '/dev/null'; -- failed
|
||||||
|
COPY t5 FROM '/dev/null'; -- failed
|
||||||
|
COPY t5 (e,f) FROM '/dev/null'; -- failed
|
||||||
|
COPY t5 (e) FROM '/dev/null'; -- ok
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Clean up
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
|
||||||
|
DROP TABLE IF EXISTS t1 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t2 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t3 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t4 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t5 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS customer CASCADE;
|
73
contrib/sepgsql/sql/label.sql
Normal file
73
contrib/sepgsql/sql/label.sql
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
--
|
||||||
|
-- Regression Tests for Label Management
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Setup
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (a int, b text);
|
||||||
|
INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||||
|
SELECT * INTO t2 FROM t1 WHERE a % 2 = 0;
|
||||||
|
|
||||||
|
CREATE FUNCTION f1 () RETURNS text
|
||||||
|
AS 'SELECT sepgsql_getcon()'
|
||||||
|
LANGUAGE sql;
|
||||||
|
|
||||||
|
CREATE FUNCTION f2 () RETURNS text
|
||||||
|
AS 'SELECT sepgsql_getcon()'
|
||||||
|
LANGUAGE sql;
|
||||||
|
SECURITY LABEL ON FUNCTION f2()
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
|
||||||
|
CREATE FUNCTION f3 () RETURNS text
|
||||||
|
AS 'BEGIN
|
||||||
|
RAISE EXCEPTION ''an exception from f3()'';
|
||||||
|
RETURN NULL;
|
||||||
|
END;' LANGUAGE plpgsql;
|
||||||
|
SECURITY LABEL ON FUNCTION f3()
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Tests for default labeling behavior
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
CREATE TABLE t3 (s int, t text);
|
||||||
|
INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
|
||||||
|
|
||||||
|
SELECT objtype, objname, label FROM pg_seclabels
|
||||||
|
WHERE provider = 'selinux'
|
||||||
|
AND objtype in ('table', 'column')
|
||||||
|
AND objname in ('t1', 't2', 't3');
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Tests for SECURITY LABEL
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0
|
||||||
|
SECURITY LABEL ON TABLE t1
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok
|
||||||
|
SECURITY LABEL ON TABLE t2
|
||||||
|
IS 'invalid seuciryt context'; -- be failed
|
||||||
|
SECURITY LABEL ON COLUMN t2
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- be failed
|
||||||
|
SECURITY LABEL ON COLUMN t2.b
|
||||||
|
IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Tests for Trusted Procedures
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
|
||||||
|
SELECT f1(); -- normal procedure
|
||||||
|
SELECT f2(); -- trusted procedure
|
||||||
|
SELECT f3(); -- trusted procedure that raises an error
|
||||||
|
SELECT sepgsql_getcon(); -- client's label must be restored
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Clean up
|
||||||
|
--
|
||||||
|
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
|
||||||
|
DROP TABLE IF EXISTS t1 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t2 CASCADE;
|
||||||
|
DROP TABLE IF EXISTS t3 CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f1() CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f2() CASCADE;
|
||||||
|
DROP FUNCTION IF EXISTS f3() CASCADE;
|
5
contrib/sepgsql/sql/misc.sql
Normal file
5
contrib/sepgsql/sql/misc.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
--
|
||||||
|
-- Regression Test for Misc Permission Checks
|
||||||
|
--
|
||||||
|
|
||||||
|
LOAD '$libdir/sepgsql'; -- failed
|
@ -116,6 +116,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
|
|||||||
&pgtrgm;
|
&pgtrgm;
|
||||||
&pgupgrade;
|
&pgupgrade;
|
||||||
&seg;
|
&seg;
|
||||||
|
&sepgsql;
|
||||||
&contrib-spi;
|
&contrib-spi;
|
||||||
&sslinfo;
|
&sslinfo;
|
||||||
&tablefunc;
|
&tablefunc;
|
||||||
|
@ -129,6 +129,7 @@
|
|||||||
<!entity pgupgrade SYSTEM "pgupgrade.sgml">
|
<!entity pgupgrade SYSTEM "pgupgrade.sgml">
|
||||||
<!entity seg SYSTEM "seg.sgml">
|
<!entity seg SYSTEM "seg.sgml">
|
||||||
<!entity contrib-spi SYSTEM "contrib-spi.sgml">
|
<!entity contrib-spi SYSTEM "contrib-spi.sgml">
|
||||||
|
<!entity sepgsql SYSTEM "sepgsql.sgml">
|
||||||
<!entity sslinfo SYSTEM "sslinfo.sgml">
|
<!entity sslinfo SYSTEM "sslinfo.sgml">
|
||||||
<!entity tablefunc SYSTEM "tablefunc.sgml">
|
<!entity tablefunc SYSTEM "tablefunc.sgml">
|
||||||
<!entity test-parser SYSTEM "test-parser.sgml">
|
<!entity test-parser SYSTEM "test-parser.sgml">
|
||||||
|
704
doc/src/sgml/sepgsql.sgml
Normal file
704
doc/src/sgml/sepgsql.sgml
Normal file
@ -0,0 +1,704 @@
|
|||||||
|
<!-- doc/src/sgml/sepgsql.sgml -->
|
||||||
|
|
||||||
|
<sect1 id="sepgsql">
|
||||||
|
<title>sepgsql</title>
|
||||||
|
|
||||||
|
<indexterm zone="sepgsql">
|
||||||
|
<primary>sepgsql</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <filename>sepgsql</> is a module which performs as an external
|
||||||
|
security provider; to support label based mandatory access control
|
||||||
|
(MAC) base on <productname>SELinux</> policy.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This extension won't build at all unless the installation was configured
|
||||||
|
with <literal>--with-selinux</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="sepgsql-overview">
|
||||||
|
<title>Overview</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<productname>PostgreSQL</> provides various kind of hooks. Some of these
|
||||||
|
hooks can be utilized to make access control decision on the supplied
|
||||||
|
users' accesses on database objects.
|
||||||
|
We call plug-in modules making access control decision based on its own
|
||||||
|
security model as an external security provider.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This module acquires control on these strategic points, then it asks
|
||||||
|
<productname>SELinux</> to check whether the supplied access shall be
|
||||||
|
allowed, or not. Then, it returns its access control decision.
|
||||||
|
If violated, this module prevents this access with rising an error for
|
||||||
|
example.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A series of making decision is done independently from the default
|
||||||
|
database privilege mechanism. Users must be allowed with both of access
|
||||||
|
control models, whenever they try to access something.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
We can see <productname>SELinux</> as a function which takes two arguments
|
||||||
|
then returns a bool value; allowed or denied. The first argument in this
|
||||||
|
analogy is label of subject which tries to reference a certain obejct.
|
||||||
|
The other one is label of the object being referenced in this operation.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Label is a formatted string,
|
||||||
|
like <literal>system_u:object_r:sepgsql_table_t:s0</>.
|
||||||
|
It is not a property depending on characteristics of a certain kind of
|
||||||
|
object, so we can apply common credentials on either database objects
|
||||||
|
or others.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
<productname>PostgreSQL</> 9.1 or later supports
|
||||||
|
<xref linkend="sql-security-label"> statement that allows to assign
|
||||||
|
a security label on specified database objects, if user wants to change
|
||||||
|
label from the creation default.
|
||||||
|
Also <productname>SELinux</> provides an interface to obtain security
|
||||||
|
label of the peer process that connected to.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
These facilities enable to integrate <productname>SELinux</> model within
|
||||||
|
access controls to database objects. Because it makes access control
|
||||||
|
decision according to a common centralized security policy (a set of rules),
|
||||||
|
its decision will be always consistent independent from the way to store
|
||||||
|
information assets.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="sepgsql-installation">
|
||||||
|
<title>Installation</title>
|
||||||
|
<para>
|
||||||
|
The <filename>sepgsql</> module requires the following packages to install.
|
||||||
|
Please check it at first.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><productname>Linux kernel</productname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
v2.6.28 or later with built with SELinux enabled
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><productname>libselinux</productname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
v2.0.80 or later
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This library provides a set of APIs to communicate with
|
||||||
|
<productname>SELinux</> in kernel.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><productname>selinux-policy</productname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
v3.9.13 or later
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The default security policy provides a set of access control rules.
|
||||||
|
Some of distribution may backports necessary rules, even if base
|
||||||
|
policy was older than above version.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
<para>
|
||||||
|
<productname>SE-PostgreSQL</> needs <productname>SELinux</> being
|
||||||
|
available on the platform. You can check the current setting using
|
||||||
|
<command>sestatus</>.
|
||||||
|
<screen>
|
||||||
|
$ sestatus
|
||||||
|
SELinux status: enabled
|
||||||
|
SELinuxfs mount: /selinux
|
||||||
|
Current mode: enforcing
|
||||||
|
Mode from config file: enforcing
|
||||||
|
Policy version: 24
|
||||||
|
Policy from config file: targeted
|
||||||
|
</screen>
|
||||||
|
If disabled or not-installed, you need to set up <productname>SELinux</>
|
||||||
|
prior to all the installation step of <productname>SE-PostgreSQL</>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
On the compile time, add <literal>--with-selinux</> option to
|
||||||
|
the <command>configure</> script to check existence of
|
||||||
|
the <productname>libselinux</>, and to set a flag whether
|
||||||
|
we build this contrib module, or not.
|
||||||
|
<screen>
|
||||||
|
$ ./configure --enable-debug --enable-cassert --with-selinux
|
||||||
|
$ make
|
||||||
|
$ make install
|
||||||
|
</screen>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Next to the <command>initdb</>, add <literal>'$libdir/sepgsql'</>
|
||||||
|
to <xref linkend="guc-shared-preload-libraries"> in
|
||||||
|
the <filename>postgresql.conf</>.
|
||||||
|
|
||||||
|
It enables to load <filename>sepgsql</> on the starting up of
|
||||||
|
postmaster process.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Then, load the <filename>sepgsql.sql</filename> script for each databases.
|
||||||
|
It installs functions corresponding to security label management, and
|
||||||
|
tries to assign initial labels on the target objects.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The following instruction assumes your installation is under the
|
||||||
|
<filename>/usr/local/pgsql</> directory, and the database cluster is in
|
||||||
|
<filename>/usr/local/pgsql/data</>. Substitute your paths appropriately.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ initdb -D $PGDATA
|
||||||
|
$ vi $PGDATA/postgresql.conf
|
||||||
|
$ for DBNAME in template0 template1 postgres; do
|
||||||
|
postgres --single -F -O -c exit_on_error=true -D $PGDATA $DBNAME \
|
||||||
|
< /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null
|
||||||
|
done
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
If all the installation process was done with no errors, start postmaster
|
||||||
|
process. <productname>SE-PostgreSQL</> shall prevent violated accesses
|
||||||
|
according to the security policy of <productname>SELinux</>.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="sepgsql-regression">
|
||||||
|
<title>Regression Tests</title>
|
||||||
|
<para>
|
||||||
|
The regression test of this module requires a few more configurations
|
||||||
|
on the platform system, in addition to the above installation process.
|
||||||
|
See the following steps.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
First, install the policy package for regression test.
|
||||||
|
The <filename>sepgsql-regtest.pp</> is a special purpose policy package
|
||||||
|
that provides a set of rules to be allowed during the regression test
|
||||||
|
cases. It shall be installed at <filename>/usr/local/pgsql/share/contrib</>
|
||||||
|
directory in the default setup.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
You need to install this policy package using <command>semodule</>
|
||||||
|
command which enables to link supplied policy packages and load them
|
||||||
|
into the kernel space. If you could install the pakage correctly,
|
||||||
|
<literal><command>semodule</> -l</> prints sepgsql-regtest as a part
|
||||||
|
of policy packages currently available.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ su
|
||||||
|
# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
|
||||||
|
# semodule -l
|
||||||
|
:
|
||||||
|
sepgsql-regtest 1.03
|
||||||
|
:
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
Second, turn on the <literal>sepgsql_regression_test_mode</>.
|
||||||
|
We don't enable all the rules in the <filename>sepgsql-regtest.pp</>
|
||||||
|
in the default, for your system's safety.
|
||||||
|
The <literal>sepgsql_regression_test_mode</literal> parameter is associated
|
||||||
|
with rules to launch regression test.
|
||||||
|
It can be turned on using <command>setsebool</> command.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ su
|
||||||
|
# setsebool sepgsql_regression_test_mode on
|
||||||
|
# getsebool sepgsql_regression_test_mode
|
||||||
|
sepgsql_regression_test_mode --> on
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
Last, kick the regression test from the <literal>unconfined_t</> domain.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This test policy is designed to kick each test cases from the
|
||||||
|
<literal>unconfined_t</> domain that is a default choice in most of
|
||||||
|
the known <literal>SELinux</> installation base.
|
||||||
|
So, you don't need to set up anything special, as long as you didn't
|
||||||
|
change default configuration of SELinux before.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The <command>id</> command tells us the current working domain.
|
||||||
|
Confirm your shell is now performing with <literal>unconfined_t</>
|
||||||
|
domain as follows.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ id -Z
|
||||||
|
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
If not an expected one, you should revert this configuration.
|
||||||
|
The <xref linkend="sepgsql-resources"> section will give you
|
||||||
|
some useful hints.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Then, you will see the all-green result of regression test,
|
||||||
|
if we have no problem here.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ make -C contrib/sepgsql/ installcheck
|
||||||
|
:
|
||||||
|
../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin \
|
||||||
|
--dbname=contrib_regression --launcher ../../contrib/sepgsql/launcher \
|
||||||
|
label dml
|
||||||
|
(using postmaster on Unix socket, default port)
|
||||||
|
============== dropping database "contrib_regression" ==============
|
||||||
|
DROP DATABASE
|
||||||
|
============== creating database "contrib_regression" ==============
|
||||||
|
CREATE DATABASE
|
||||||
|
ALTER DATABASE
|
||||||
|
============== running regression test queries ==============
|
||||||
|
test label ... ok
|
||||||
|
test dml ... ok
|
||||||
|
test misc ... ok
|
||||||
|
|
||||||
|
=====================
|
||||||
|
All 3 tests passed.
|
||||||
|
=====================
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
If <command>pg_regress</> failed to launch <command>psql</> command,
|
||||||
|
here is a hint to fix up the matter.
|
||||||
|
|
||||||
|
When we try to launch <command>psql</> command with restrictive
|
||||||
|
privileges, the <command>psql</> must eb labeled as <literal>bin_t</>.
|
||||||
|
If not, try to run <command>restorecon</> to fix up security label of
|
||||||
|
the commands as expected.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
$ restorecon -R /usr/local/pgsql/
|
||||||
|
</screen>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="sepgsql-parameters">
|
||||||
|
<title>GUC Parameters</title>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry id="guc-sepgsql-permissive" xreflabel="sepgsql.permissive">
|
||||||
|
<term><varname>sepgsql.permissive</> (<type>boolean</type>)</term>
|
||||||
|
<indexterm>
|
||||||
|
<primary><varname>sepgsql.permissive</> configuration parameter</primary>
|
||||||
|
</indexterm>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This parameter enables to perform <productname>SE-PostgreSQL</>
|
||||||
|
in permissive mode independent from the system setting.
|
||||||
|
The default is off (according to the system setting).
|
||||||
|
This parameter can only be set in the <filename>postgresql.conf</>
|
||||||
|
file or on the server command line.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
We have two performing mode except for disabled; The one is enforcing
|
||||||
|
mode that checks the security policy on references and actually prevents
|
||||||
|
violated accesses. The other is permissive mode that only checks
|
||||||
|
the security policy, but does not prevents anything except for log
|
||||||
|
generation.
|
||||||
|
This log shall be utilized for debugging of the security policy itself.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
When this parameter is on, <productname>SE-PostgreSQL</> performs
|
||||||
|
in permissive mode, even if the platform system is working on enforcing
|
||||||
|
mode.
|
||||||
|
We recommend users to keep the default setting, except for the case
|
||||||
|
when we develop security policy by ourself.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry id="guc-sepgsql-debug-audit" xreflabel="sepgsql.debug_audit">
|
||||||
|
<term><varname>sepgsql.debug_audit</> (<type>boolean</>)</>
|
||||||
|
<indexterm>
|
||||||
|
<primary><varname>sepgsql.debug_audit</> configuration parameter</>
|
||||||
|
</indexterm>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This parameter enables to print audit messages independent from
|
||||||
|
the policy setting.
|
||||||
|
The default is off (according to the security policy setting).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The security policy of <productname>SELinux</> also has rules to
|
||||||
|
control what accesses shall be logged, or not.
|
||||||
|
In the default, any access violations are logged, but any allowed
|
||||||
|
accesses are not logged.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
When this parameter is on, all the possible logs shall be printed
|
||||||
|
independently from the policy settings.
|
||||||
|
We recommend to keep the variable turned off in normal cases to
|
||||||
|
avoid noisy messages.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="sepgsql-features">
|
||||||
|
<title>Features</title>
|
||||||
|
<sect3>
|
||||||
|
<title>controlled object classes</title>
|
||||||
|
<para>
|
||||||
|
The security model of <productname>SELinux</> describes all the access
|
||||||
|
control rules as a relationship between a subject entity (typically,
|
||||||
|
it is a client of database) and an object entity.
|
||||||
|
And, these entities are identified by a security label.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
We call a set of these rules as security policy.
|
||||||
|
All the access control decision shall be made according to the security
|
||||||
|
policy, when we ask SELinux whether the required action shall be allowed
|
||||||
|
or not.
|
||||||
|
Thus, we have no way to control accesses on any sort of objects without
|
||||||
|
security labels.
|
||||||
|
(<productname>SELinux</> assumes <literal>unlabeled_t</> is assigned,
|
||||||
|
if no valid security label is assigned on the target object.)
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This version of <productname>SE-PostgreSQL</> supports to assign
|
||||||
|
a security label on these database object classes: schema, table, column,
|
||||||
|
sequence, view and procedure.
|
||||||
|
Other database object classes are not supported to assign security label
|
||||||
|
on, right now.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A security label shall be automatically assigned to the supported
|
||||||
|
database objects on their creation time.
|
||||||
|
This label is called as a default security label; being decided according
|
||||||
|
to the security policy, or a pair of security label of the client and
|
||||||
|
upper object for more correctly.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A new database object basically inherits security label of the upper
|
||||||
|
object. A new column inherits security label of its parent table for
|
||||||
|
instance.
|
||||||
|
If and when the security policy has special rules called as
|
||||||
|
type-transition on a pair of the client and upper object, we can assign
|
||||||
|
an individual label as a default. The upper object depends on sort of
|
||||||
|
object classes as follows.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>schema</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the current database.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>table</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the schema object which owns the new table.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>column</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the table object which owns the new column.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>sequence</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the schema object which owns the new sequence.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>view</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the schema object which owns the new view.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>procedure</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Its upper object is the schema object which owns the new procedure.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</sect3>
|
||||||
|
<sect3>
|
||||||
|
<title>DML Permissions</title>
|
||||||
|
<para>
|
||||||
|
This section introduces what permissions shall be checked on DML;
|
||||||
|
<literal>SELECT</>, <literal>INSERT</>, <literal>UPDATE</> and
|
||||||
|
<literal>DELETE</>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
DML statements are used to reference or modify contents within
|
||||||
|
the specified database objects; such as tables or columns.
|
||||||
|
We basically checks access rights of the client on all the appeared
|
||||||
|
objects in the given statement, and kind of privileges depend on
|
||||||
|
class of object and sort of accesses.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For tables, <literal>db_table:select</>, <literal>db_table:insert</>,
|
||||||
|
<literal>db_table:update</> or <literal>db_table:delete</> shall be
|
||||||
|
checked for all the appeared target tables depending on the sort of
|
||||||
|
statement;
|
||||||
|
In addition, <literal>db_table:select</> shall be also checked for
|
||||||
|
all the tables that containin the columns to be referenced in
|
||||||
|
<literal>WHERE</> or <literal>RETURNING</> clause, as a data source
|
||||||
|
of <literal>UPDATE</>, and so on.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
<synopsis>
|
||||||
|
UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
|
||||||
|
</synopsis>
|
||||||
|
In this case, we must have <literal>db_table:select</>, not only
|
||||||
|
<literal>db_table:update</>, because <literal>t1.a</> is referenced
|
||||||
|
within <literal>WHERE</> clause.
|
||||||
|
Also note that column-level permission shall be checked individually.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The client must be allowed to reference all the appeared tables and
|
||||||
|
columns, even if they are originated from views then expanded, unlike
|
||||||
|
the default database privileges, because we intend to apply consistent
|
||||||
|
access control rules independent from the route to reference contents
|
||||||
|
of the tables.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For columns, <literal>db_column:select</> shall be also checked on
|
||||||
|
not only the columns being read using <literal>SELECT</>, but being
|
||||||
|
referenced in other DML statement.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Of course, it also checks <literal>db_column:update</> or
|
||||||
|
<literal>db_column:insert</> on the column being modified by
|
||||||
|
<literal>UPDATE</> or <literal>INSERT</>.
|
||||||
|
Note that we have no definition of column-level delete permission,
|
||||||
|
like as the default database privilege doing.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
<synopsis>
|
||||||
|
UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
|
||||||
|
</synopsis>
|
||||||
|
In this case, it checks <literal>db_column:update</> on
|
||||||
|
the <literal>t1.x</> being updated, <literal>db_column:{select update}</>
|
||||||
|
on the <literal>t1.y</> being updated and referenced,
|
||||||
|
and <literal>db_column:select</> on the <literal>t1.z</> being only
|
||||||
|
referenced in the <literal>WHERE</> clause.
|
||||||
|
Also note that <literal>db_table:{select update}</> shall be checked
|
||||||
|
in the table-level granularity.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For sequences, <literal>db_sequence:get_value</> when we reference
|
||||||
|
a sequence object using <literal>SELECT</>, however, note that we
|
||||||
|
cannot check permissions on execution of corresponding functions
|
||||||
|
such as <literal>lastval()</> right now, although they performs same
|
||||||
|
job, because here is no object access hook to acquire controls.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For views, <literal>db_view:expand</> shall be checked, then any other
|
||||||
|
corresponding permissions shall be also checked on the objects being
|
||||||
|
expanded from the view, individually.
|
||||||
|
Note that both of permissions have to be allowed.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For procedures, <literal>db_procedure:{execute}</> is defined, but not
|
||||||
|
checked in this version.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Here is a few more corner cases.
|
||||||
|
The default database privilege system allows database superusers to
|
||||||
|
modify system catalogs using DML commands, and reference or modify
|
||||||
|
toast tables, however, both of the cases shall be denied when
|
||||||
|
<productname>SE-PostgreSQL</> is enabled.
|
||||||
|
</para>
|
||||||
|
</sect3>
|
||||||
|
<sect3>
|
||||||
|
<title>DDL Permissions</title>
|
||||||
|
<para>
|
||||||
|
On <xref linkend="sql-security-label"> command, <literal>setattr</> and
|
||||||
|
<literal>relabelfrom</> shall be checked on the object being relabeled
|
||||||
|
with an old security label, then <literal>relabelto</> on the supplied
|
||||||
|
new security label.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In a case when multiple label providers are installed and user tries
|
||||||
|
to set a security label, but is not managed by <productname>SELinux</>,
|
||||||
|
only <literal>setattr</> should be checked here.
|
||||||
|
However, it is not unavailable because of limitation of the hook.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
As we will describe in <xref linkend="sepgsql-limitations"> section,
|
||||||
|
<productname>SE-PostgreSQL</> does not control any other DDL operations.
|
||||||
|
</para>
|
||||||
|
</sect3>
|
||||||
|
<sect3>
|
||||||
|
<title>Trusted Procedure</title>
|
||||||
|
<para>
|
||||||
|
It is a similar idea to security definer functions or set-uid commands
|
||||||
|
on operating systems. <productname>SELinux</> provides a feature to
|
||||||
|
switch privilege of the client (that is a security label of the client
|
||||||
|
for more correctness) during execution of certain functions; being
|
||||||
|
called as trusted procedures.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A trusted function is a function with a special security label being
|
||||||
|
set up as a trusted procedure.
|
||||||
|
So, we need to assign the special security label on the function that
|
||||||
|
we hope it to perform as a trusted procedure, by administrative users.
|
||||||
|
The default security policy also provides this special security label.
|
||||||
|
See the following example.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
postgres=# CREATE TABLE customer (
|
||||||
|
cid int primary key,
|
||||||
|
cname text,
|
||||||
|
credit text
|
||||||
|
);
|
||||||
|
CREATE TABLE
|
||||||
|
postgres=# SECURITY LABEL ON COLUMN customer.credit
|
||||||
|
IS 'system_u:object_r:sepgsql_secret_table_t:s0';
|
||||||
|
SECURITY LABEL
|
||||||
|
postgres=# CREATE FUNCTION show_credit(int) RETURNS text
|
||||||
|
AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
|
||||||
|
FROM customer WHERE cid = $1'
|
||||||
|
LANGUAGE sql;
|
||||||
|
CREATE FUNCTION
|
||||||
|
postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
|
||||||
|
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
|
||||||
|
SECURITY LABEL
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
Above operations shall be done by administrative users.
|
||||||
|
</para>
|
||||||
|
<screen>
|
||||||
|
postgres=# SELECT * FROM customer;
|
||||||
|
ERROR: SELinux: security policy violation
|
||||||
|
postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
|
||||||
|
cid | cname | show_credit
|
||||||
|
-----+--------+---------------------
|
||||||
|
1 | taro | 1111-2222-3333-xxxx
|
||||||
|
2 | hanako | 5555-6666-7777-xxxx
|
||||||
|
(2 rows)
|
||||||
|
</screen>
|
||||||
|
<para>
|
||||||
|
In this case, a regular user cannot reference <literal>customer.credit</>
|
||||||
|
directly, but a trusted procedure <literal>show_credit</> enables us
|
||||||
|
to print credit number of customers with a bit modification.
|
||||||
|
</para>
|
||||||
|
</sect3>
|
||||||
|
<sect3>
|
||||||
|
<title>Miscellaneous</title>
|
||||||
|
<para>
|
||||||
|
In this version, we reject <xref linkend="sql-load"> command across
|
||||||
|
the board, because the binary module can override security hooks to
|
||||||
|
make access control decision. It means a risk to invalidate all the
|
||||||
|
control by security providers.
|
||||||
|
</para>
|
||||||
|
</sect3>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="sepgsql-limitations">
|
||||||
|
<title>Limitations</title>
|
||||||
|
<para>
|
||||||
|
This section introduces limitations of <productname>SE-PostgreSQL</>
|
||||||
|
in this version.
|
||||||
|
</para>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Userspace access vector cache</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<productname>SE-PostgreSQL</> tells <productname>SELinux</> its access
|
||||||
|
control decision. It takes system call invocation being heavy, however,
|
||||||
|
we can reduce number of the invocations using caching mechanism; called
|
||||||
|
as access vector cache in <productname>SELinux</>.
|
||||||
|
Because of code size, <productname>SE-PostgreSQL</> does not support
|
||||||
|
this mechanism yet.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>DDL Permissions</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Now <productname>PostgreSQL</> does not provide a set of hooks on
|
||||||
|
the DDL routines.
|
||||||
|
It means plugin modules cannot acquire control here,
|
||||||
|
so <productname>SE-PostgreSQL</> does not check DDL Permissions
|
||||||
|
right now.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Row-level access control</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Now <productname>SE-PostgreSQL</> does not support row-level access
|
||||||
|
control, because a few needed facilities are not supported yet.
|
||||||
|
The one is security labels on users' tables. The other is behavior of
|
||||||
|
optimizer. Also see <xref linkend="rules-privileges"> for more details.
|
||||||
|
We know similar issue on VIEW.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term>Covert channels</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<productname>SE-PostgreSQL</> never tries to hide existence of
|
||||||
|
a certain object, even if user is not allowed to reference.
|
||||||
|
For example, we can infer an existence of invisible object using
|
||||||
|
primary-key confliction, foreign-key violation, and so on, even if
|
||||||
|
we cannot reference contents of these objects.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="sepgsql-resources">
|
||||||
|
<title>External Resources</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><ulink url="http://wiki.postgresql.org/wiki/SEPostgreSQL">SE-PostgreSQL Introduction</ulink></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This wikipage provides a brief-overview, security design, architecture,
|
||||||
|
administration and upcoming feature for more details.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><ulink url="http://docs.fedoraproject.org/selinux-user-guide/">Fedora SELinux User Guide</ulink></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This document provides wide spectrum of knowledge to administrate
|
||||||
|
SELinux on your systems.
|
||||||
|
It primary focuses on Fedora, but not limited to Fedora.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><ulink url="http://docs.fedoraproject.org/selinux-faq">Fedora SELinux FAQ</ulink></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This document provides FAQs about SELinux.
|
||||||
|
It primary focuses on Fedora, but not limited to Fedora.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="sepgsql-author">
|
||||||
|
<title>Author</title>
|
||||||
|
<para>
|
||||||
|
KaiGai Kohei (<email>kaigai@ak.jp.nec.com</email>)
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
@ -158,6 +158,7 @@ with_python = @with_python@
|
|||||||
with_tcl = @with_tcl@
|
with_tcl = @with_tcl@
|
||||||
with_openssl = @with_openssl@
|
with_openssl = @with_openssl@
|
||||||
with_ossp_uuid = @with_ossp_uuid@
|
with_ossp_uuid = @with_ossp_uuid@
|
||||||
|
with_selinux = @with_selinux@
|
||||||
with_libxml = @with_libxml@
|
with_libxml = @with_libxml@
|
||||||
with_libxslt = @with_libxslt@
|
with_libxslt = @with_libxslt@
|
||||||
with_system_tzdata = @with_system_tzdata@
|
with_system_tzdata = @with_system_tzdata@
|
||||||
|
@ -84,6 +84,7 @@ bool debug = false;
|
|||||||
char *inputdir = ".";
|
char *inputdir = ".";
|
||||||
char *outputdir = ".";
|
char *outputdir = ".";
|
||||||
char *psqldir = PGBINDIR;
|
char *psqldir = PGBINDIR;
|
||||||
|
char *launcher = NULL;
|
||||||
static _stringlist *loadlanguage = NULL;
|
static _stringlist *loadlanguage = NULL;
|
||||||
static int max_connections = 0;
|
static int max_connections = 0;
|
||||||
static char *encoding = NULL;
|
static char *encoding = NULL;
|
||||||
@ -1871,6 +1872,7 @@ help(void)
|
|||||||
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
|
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
|
||||||
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
|
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
|
||||||
printf(_(" --use-existing use an existing installation\n"));
|
printf(_(" --use-existing use an existing installation\n"));
|
||||||
|
printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
|
||||||
printf(_("\n"));
|
printf(_("\n"));
|
||||||
printf(_("Options for \"temp-install\" mode:\n"));
|
printf(_("Options for \"temp-install\" mode:\n"));
|
||||||
printf(_(" --no-locale use C locale\n"));
|
printf(_(" --no-locale use C locale\n"));
|
||||||
@ -1922,6 +1924,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
|
|||||||
{"create-role", required_argument, NULL, 18},
|
{"create-role", required_argument, NULL, 18},
|
||||||
{"temp-config", required_argument, NULL, 19},
|
{"temp-config", required_argument, NULL, 19},
|
||||||
{"use-existing", no_argument, NULL, 20},
|
{"use-existing", no_argument, NULL, 20},
|
||||||
|
{"launcher", required_argument, NULL, 21},
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2015,6 +2018,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
|
|||||||
case 20:
|
case 20:
|
||||||
use_existing = true;
|
use_existing = true;
|
||||||
break;
|
break;
|
||||||
|
case 21:
|
||||||
|
launcher = strdup(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* getopt_long already emitted a complaint */
|
/* getopt_long already emitted a complaint */
|
||||||
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
|
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
|
||||||
|
@ -41,6 +41,7 @@ extern _stringlist *dblist;
|
|||||||
extern bool debug;
|
extern bool debug;
|
||||||
extern char *inputdir;
|
extern char *inputdir;
|
||||||
extern char *outputdir;
|
extern char *outputdir;
|
||||||
|
extern char *launcher;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This should not be global but every module should be able to read command
|
* This should not be global but every module should be able to read command
|
||||||
|
@ -33,6 +33,7 @@ psql_start_test(const char *testname,
|
|||||||
char outfile[MAXPGPATH];
|
char outfile[MAXPGPATH];
|
||||||
char expectfile[MAXPGPATH];
|
char expectfile[MAXPGPATH];
|
||||||
char psql_cmd[MAXPGPATH * 3];
|
char psql_cmd[MAXPGPATH * 3];
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look for files in the output dir first, consistent with a vpath search.
|
* Look for files in the output dir first, consistent with a vpath search.
|
||||||
@ -58,7 +59,11 @@ psql_start_test(const char *testname,
|
|||||||
add_stringlist_item(resultfiles, outfile);
|
add_stringlist_item(resultfiles, outfile);
|
||||||
add_stringlist_item(expectfiles, expectfile);
|
add_stringlist_item(expectfiles, expectfile);
|
||||||
|
|
||||||
snprintf(psql_cmd, sizeof(psql_cmd),
|
if (launcher)
|
||||||
|
offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
|
||||||
|
"%s ", launcher);
|
||||||
|
|
||||||
|
snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
|
||||||
SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
|
SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
|
||||||
psqldir ? psqldir : "",
|
psqldir ? psqldir : "",
|
||||||
psqldir ? "/" : "",
|
psqldir ? "/" : "",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user