mirror of
https://github.com/postgres/postgres.git
synced 2025-06-03 01:21:48 +03:00
Userspace access vector cache for contrib/sepgsql.
KaiGai Kohei
This commit is contained in:
parent
3d14bd2563
commit
4232c4b406
24
configure
vendored
24
configure
vendored
@ -9481,9 +9481,9 @@ fi
|
||||
# for contrib/sepgsql
|
||||
if test "$with_selinux" = yes; then
|
||||
|
||||
{ $as_echo "$as_me:$LINENO: checking for selinux_sepgsql_context_path in -lselinux" >&5
|
||||
$as_echo_n "checking for selinux_sepgsql_context_path in -lselinux... " >&6; }
|
||||
if test "${ac_cv_lib_selinux_selinux_sepgsql_context_path+set}" = set; then
|
||||
{ $as_echo "$as_me:$LINENO: checking for selinux_status_open in -lselinux" >&5
|
||||
$as_echo_n "checking for selinux_status_open in -lselinux... " >&6; }
|
||||
if test "${ac_cv_lib_selinux_selinux_status_open+set}" = set; then
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
@ -9501,11 +9501,11 @@ cat >>conftest.$ac_ext <<_ACEOF
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char selinux_sepgsql_context_path ();
|
||||
char selinux_status_open ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return selinux_sepgsql_context_path ();
|
||||
return selinux_status_open ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
@ -9531,12 +9531,12 @@ $as_echo "$ac_try_echo") >&5
|
||||
test "$cross_compiling" = yes ||
|
||||
$as_test_x conftest$ac_exeext
|
||||
}; then
|
||||
ac_cv_lib_selinux_selinux_sepgsql_context_path=yes
|
||||
ac_cv_lib_selinux_selinux_status_open=yes
|
||||
else
|
||||
$as_echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_cv_lib_selinux_selinux_sepgsql_context_path=no
|
||||
ac_cv_lib_selinux_selinux_status_open=no
|
||||
fi
|
||||
|
||||
rm -rf conftest.dSYM
|
||||
@ -9544,9 +9544,9 @@ 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_selinux_sepgsql_context_path" >&5
|
||||
$as_echo "$ac_cv_lib_selinux_selinux_sepgsql_context_path" >&6; }
|
||||
if test "x$ac_cv_lib_selinux_selinux_sepgsql_context_path" = x""yes; then
|
||||
{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_selinux_status_open" >&5
|
||||
$as_echo "$ac_cv_lib_selinux_selinux_status_open" >&6; }
|
||||
if test "x$ac_cv_lib_selinux_selinux_status_open" = x""yes; then
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBSELINUX 1
|
||||
_ACEOF
|
||||
@ -9554,8 +9554,8 @@ _ACEOF
|
||||
LIBS="-lselinux $LIBS"
|
||||
|
||||
else
|
||||
{ { $as_echo "$as_me:$LINENO: error: library 'libselinux', version 2.0.93 or newer, is required for SELinux support" >&5
|
||||
$as_echo "$as_me: error: library 'libselinux', version 2.0.93 or newer, is required for SELinux support" >&2;}
|
||||
{ { $as_echo "$as_me:$LINENO: error: library 'libselinux', version 2.0.99 or newer, is required for SELinux support" >&5
|
||||
$as_echo "$as_me: error: library 'libselinux', version 2.0.99 or newer, is required for SELinux support" >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
|
||||
|
@ -964,8 +964,8 @@ fi
|
||||
|
||||
# for contrib/sepgsql
|
||||
if test "$with_selinux" = yes; then
|
||||
AC_CHECK_LIB(selinux, selinux_sepgsql_context_path, [],
|
||||
[AC_MSG_ERROR([library 'libselinux', version 2.0.93 or newer, is required for SELinux support])])
|
||||
AC_CHECK_LIB(selinux, selinux_status_open, [],
|
||||
[AC_MSG_ERROR([library 'libselinux', version 2.0.99 or newer, is required for SELinux support])])
|
||||
fi
|
||||
|
||||
# for contrib/uuid-ossp
|
||||
|
@ -1,7 +1,7 @@
|
||||
# contrib/sepgsql/Makefile
|
||||
|
||||
MODULE_big = sepgsql
|
||||
OBJS = hooks.o selinux.o label.o dml.o \
|
||||
OBJS = hooks.o selinux.o uavc.o label.o dml.o \
|
||||
schema.o relation.o proc.o
|
||||
DATA_built = sepgsql.sql
|
||||
|
||||
|
@ -150,12 +150,11 @@ check_relation_privileges(Oid relOid,
|
||||
uint32 required,
|
||||
bool abort)
|
||||
{
|
||||
char relkind = get_rel_relkind(relOid);
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
ObjectAddress object;
|
||||
char *audit_name;
|
||||
Bitmapset *columns;
|
||||
int index;
|
||||
char relkind = get_rel_relkind(relOid);
|
||||
bool result = true;
|
||||
|
||||
/*
|
||||
@ -184,45 +183,43 @@ check_relation_privileges(Oid relOid,
|
||||
/*
|
||||
* Check permissions on the relation
|
||||
*/
|
||||
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
|
||||
audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
|
||||
object.classId = RelationRelationId;
|
||||
object.objectId = relOid;
|
||||
object.objectSubId = 0;
|
||||
audit_name = getObjectDescription(&object);
|
||||
switch (relkind)
|
||||
{
|
||||
case RELKIND_RELATION:
|
||||
result = sepgsql_check_perms(scontext,
|
||||
tcontext,
|
||||
SEPG_CLASS_DB_TABLE,
|
||||
required,
|
||||
audit_name,
|
||||
abort);
|
||||
result = sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_TABLE,
|
||||
required,
|
||||
audit_name,
|
||||
abort);
|
||||
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,
|
||||
audit_name,
|
||||
abort);
|
||||
result = sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_SEQUENCE,
|
||||
SEPG_DB_SEQUENCE__GET_VALUE,
|
||||
audit_name,
|
||||
abort);
|
||||
break;
|
||||
|
||||
case RELKIND_VIEW:
|
||||
result = sepgsql_check_perms(scontext,
|
||||
tcontext,
|
||||
SEPG_CLASS_DB_VIEW,
|
||||
SEPG_DB_VIEW__EXPAND,
|
||||
audit_name,
|
||||
abort);
|
||||
result = sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_VIEW,
|
||||
SEPG_DB_VIEW__EXPAND,
|
||||
audit_name,
|
||||
abort);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* nothing to be checked */
|
||||
break;
|
||||
}
|
||||
pfree(tcontext);
|
||||
pfree(audit_name);
|
||||
|
||||
/*
|
||||
@ -242,7 +239,6 @@ check_relation_privileges(Oid relOid,
|
||||
{
|
||||
AttrNumber attnum;
|
||||
uint32 column_perms = 0;
|
||||
ObjectAddress object;
|
||||
|
||||
if (bms_is_member(index, selected))
|
||||
column_perms |= SEPG_DB_COLUMN__SELECT;
|
||||
@ -258,20 +254,17 @@ check_relation_privileges(Oid relOid,
|
||||
|
||||
/* obtain column's permission */
|
||||
attnum = index + FirstLowInvalidHeapAttributeNumber;
|
||||
tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
|
||||
|
||||
object.classId = RelationRelationId;
|
||||
object.objectId = relOid;
|
||||
object.objectSubId = attnum;
|
||||
audit_name = getObjectDescription(&object);
|
||||
|
||||
result = sepgsql_check_perms(scontext,
|
||||
tcontext,
|
||||
SEPG_CLASS_DB_COLUMN,
|
||||
column_perms,
|
||||
audit_name,
|
||||
abort);
|
||||
pfree(tcontext);
|
||||
result = sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_COLUMN,
|
||||
column_perms,
|
||||
audit_name,
|
||||
abort);
|
||||
pfree(audit_name);
|
||||
|
||||
if (!result)
|
||||
|
@ -184,9 +184,7 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
|
||||
static bool
|
||||
sepgsql_needs_fmgr_hook(Oid functionId)
|
||||
{
|
||||
char *old_label;
|
||||
char *new_label;
|
||||
char *function_label;
|
||||
ObjectAddress object;
|
||||
|
||||
if (next_needs_fmgr_hook &&
|
||||
(*next_needs_fmgr_hook) (functionId))
|
||||
@ -198,14 +196,8 @@ sepgsql_needs_fmgr_hook(Oid functionId)
|
||||
* 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);
|
||||
if (sepgsql_avc_trusted_proc(functionId) != NULL)
|
||||
return true;
|
||||
}
|
||||
pfree(new_label);
|
||||
|
||||
/*
|
||||
* Even if not a trusted-procedure, this function should not be inlined
|
||||
@ -213,17 +205,15 @@ sepgsql_needs_fmgr_hook(Oid functionId)
|
||||
* 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);
|
||||
object.classId = ProcedureRelationId;
|
||||
object.objectId = functionId;
|
||||
object.objectSubId = 0;
|
||||
if (!sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_PROCEDURE,
|
||||
SEPG_DB_PROCEDURE__EXECUTE,
|
||||
SEPGSQL_AVC_NOAUDIT, false))
|
||||
return true;
|
||||
}
|
||||
pfree(function_label);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -251,33 +241,31 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
|
||||
if (!stack)
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
const char *cur_label = sepgsql_get_client_label();
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
|
||||
stack = palloc(sizeof(*stack));
|
||||
stack->old_label = NULL;
|
||||
stack->new_label = sepgsql_proc_get_domtrans(flinfo->fn_oid);
|
||||
stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
|
||||
stack->next_private = 0;
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
if (strcmp(cur_label, stack->new_label) != 0)
|
||||
{
|
||||
/*
|
||||
* process:transition permission between old and new
|
||||
* label, when user tries to switch security label of the
|
||||
* client on execution of trusted procedure.
|
||||
*/
|
||||
sepgsql_check_perms(cur_label, stack->new_label,
|
||||
SEPG_CLASS_PROCESS,
|
||||
SEPG_PROCESS__TRANSITION,
|
||||
NULL, true);
|
||||
}
|
||||
/*
|
||||
* process:transition permission between old and new label,
|
||||
* when user tries to switch security label of the client
|
||||
* on execution of trusted procedure.
|
||||
*/
|
||||
if (stack->new_label)
|
||||
sepgsql_avc_check_perms_label(stack->new_label,
|
||||
SEPG_CLASS_PROCESS,
|
||||
SEPG_PROCESS__TRANSITION,
|
||||
NULL, true);
|
||||
|
||||
*private = PointerGetDatum(stack);
|
||||
}
|
||||
Assert(!stack->old_label);
|
||||
stack->old_label = sepgsql_set_client_label(stack->new_label);
|
||||
if (stack->new_label)
|
||||
stack->old_label = sepgsql_set_client_label(stack->new_label);
|
||||
|
||||
if (next_fmgr_hook)
|
||||
(*next_fmgr_hook) (event, flinfo, &stack->next_private);
|
||||
@ -290,7 +278,8 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
|
||||
if (next_fmgr_hook)
|
||||
(*next_fmgr_hook) (event, flinfo, &stack->next_private);
|
||||
|
||||
sepgsql_set_client_label(stack->old_label);
|
||||
if (stack->old_label)
|
||||
sepgsql_set_client_label(stack->old_label);
|
||||
stack->old_label = NULL;
|
||||
break;
|
||||
|
||||
@ -433,6 +422,9 @@ _PG_init(void)
|
||||
errmsg("SELinux: failed to get server security label: %m")));
|
||||
sepgsql_set_client_label(context);
|
||||
|
||||
/* Initialize userspace access vector cache */
|
||||
sepgsql_avc_init();
|
||||
|
||||
/* Security label provider hook */
|
||||
register_label_provider(SEPGSQL_LABEL_TAG,
|
||||
sepgsql_object_relabel);
|
||||
|
@ -96,64 +96,30 @@ sepgsql_proc_post_create(Oid functionId)
|
||||
void
|
||||
sepgsql_proc_relabel(Oid functionId, const char *seclabel)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
char *audit_name;
|
||||
ObjectAddress object;
|
||||
char *audit_name;
|
||||
|
||||
audit_name = getObjectDescriptionOids(ProcedureRelationId, functionId);
|
||||
object.classId = ProcedureRelationId;
|
||||
object.objectId = functionId;
|
||||
object.objectSubId = 0;
|
||||
audit_name = getObjectDescription(&object);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_PROCEDURE,
|
||||
SEPG_DB_PROCEDURE__SETATTR |
|
||||
SEPG_DB_PROCEDURE__RELABELFROM,
|
||||
audit_name,
|
||||
true);
|
||||
/*
|
||||
* check db_procedure:{relabelto} permission
|
||||
*/
|
||||
sepgsql_check_perms(scontext,
|
||||
seclabel,
|
||||
SEPG_CLASS_DB_PROCEDURE,
|
||||
SEPG_DB_PROCEDURE__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
sepgsql_avc_check_perms_label(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;
|
||||
}
|
||||
|
@ -79,10 +79,8 @@ void
|
||||
sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
|
||||
const char *seclabel)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
char *audit_name;
|
||||
ObjectAddress object;
|
||||
char *audit_name;
|
||||
|
||||
if (get_rel_relkind(relOid) != RELKIND_RELATION)
|
||||
ereport(ERROR,
|
||||
@ -97,26 +95,20 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber 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);
|
||||
|
||||
sepgsql_avc_check_perms(&object,
|
||||
SEPG_CLASS_DB_COLUMN,
|
||||
SEPG_DB_COLUMN__SETATTR |
|
||||
SEPG_DB_COLUMN__RELABELFROM,
|
||||
audit_name,
|
||||
true);
|
||||
/*
|
||||
* check db_column:{relabelto} permission
|
||||
*/
|
||||
sepgsql_check_perms(scontext,
|
||||
seclabel,
|
||||
SEPG_CLASS_DB_COLUMN,
|
||||
SEPG_DB_PROCEDURE__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
|
||||
pfree(tcontext);
|
||||
sepgsql_avc_check_perms_label(seclabel,
|
||||
SEPG_CLASS_DB_COLUMN,
|
||||
SEPG_DB_PROCEDURE__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
pfree(audit_name);
|
||||
}
|
||||
|
||||
@ -227,8 +219,7 @@ out:
|
||||
void
|
||||
sepgsql_relation_relabel(Oid relOid, const char *seclabel)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
ObjectAddress object;
|
||||
char *audit_name;
|
||||
char relkind;
|
||||
uint16_t tclass = 0;
|
||||
@ -246,31 +237,27 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
|
||||
errmsg("cannot set security labels on relations except "
|
||||
"for tables, sequences or views")));
|
||||
|
||||
audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
|
||||
object.classId = RelationRelationId;
|
||||
object.objectId = relOid;
|
||||
object.objectSubId = 0;
|
||||
audit_name = getObjectDescription(&object);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
sepgsql_avc_check_perms(&object,
|
||||
tclass,
|
||||
SEPG_DB_TABLE__SETATTR |
|
||||
SEPG_DB_TABLE__RELABELFROM,
|
||||
audit_name,
|
||||
true);
|
||||
/*
|
||||
* check db_xxx:{relabelto} permission
|
||||
*/
|
||||
sepgsql_check_perms(scontext,
|
||||
seclabel,
|
||||
tclass,
|
||||
SEPG_DB_TABLE__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
|
||||
pfree(tcontext);
|
||||
sepgsql_avc_check_perms_label(seclabel,
|
||||
tclass,
|
||||
SEPG_DB_TABLE__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
pfree(audit_name);
|
||||
}
|
||||
|
@ -65,35 +65,30 @@ sepgsql_schema_post_create(Oid namespaceId)
|
||||
void
|
||||
sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
char *audit_name;
|
||||
ObjectAddress object;
|
||||
char *audit_name;
|
||||
|
||||
audit_name = getObjectDescriptionOids(NamespaceRelationId, namespaceId);
|
||||
object.classId = NamespaceRelationId;
|
||||
object.objectId = namespaceId;
|
||||
object.objectSubId = 0;
|
||||
audit_name = getObjectDescription(&object);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
sepgsql_avc_check_perms(&object,
|
||||
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);
|
||||
sepgsql_avc_check_perms_label(seclabel,
|
||||
SEPG_CLASS_DB_SCHEMA,
|
||||
SEPG_DB_SCHEMA__RELABELTO,
|
||||
audit_name,
|
||||
true);
|
||||
pfree(audit_name);
|
||||
}
|
||||
|
@ -642,7 +642,7 @@ bool
|
||||
sepgsql_getenforce(void)
|
||||
{
|
||||
if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
|
||||
security_getenforce() > 0)
|
||||
selinux_status_getenforce() > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "fmgr.h"
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
#include <selinux/avc.h>
|
||||
|
||||
/*
|
||||
* SE-PostgreSQL Label Tag
|
||||
@ -245,6 +246,22 @@ extern bool sepgsql_check_perms(const char *scontext,
|
||||
uint32 required,
|
||||
const char *audit_name,
|
||||
bool abort);
|
||||
/*
|
||||
* uavc.c
|
||||
*/
|
||||
#define SEPGSQL_AVC_NOAUDIT ((void *)(-1))
|
||||
extern bool sepgsql_avc_check_perms_label(const char *tcontext,
|
||||
uint16 tclass,
|
||||
uint32 required,
|
||||
const char *audit_name,
|
||||
bool abort);
|
||||
extern bool sepgsql_avc_check_perms(const ObjectAddress *tobject,
|
||||
uint16 tclass,
|
||||
uint32 required,
|
||||
const char *audit_name,
|
||||
bool abort);
|
||||
extern char *sepgsql_avc_trusted_proc(Oid functionId);
|
||||
extern void sepgsql_avc_init(void);
|
||||
|
||||
/*
|
||||
* label.c
|
||||
@ -286,6 +303,5 @@ extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
|
||||
*/
|
||||
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 */
|
||||
|
511
contrib/sepgsql/uavc.c
Normal file
511
contrib/sepgsql/uavc.c
Normal file
@ -0,0 +1,511 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* contrib/sepgsql/uavc.c
|
||||
*
|
||||
* Implementation of userspace access vector cache; that enables to cache
|
||||
* access control decisions recently used, and reduce number of kernel
|
||||
* invocations to avoid unnecessary performance hit.
|
||||
*
|
||||
* Copyright (c) 2011, PostgreSQL Global Development Group
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/hash.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/seclabel.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
#include "sepgsql.h"
|
||||
|
||||
/*
|
||||
* avc_cache
|
||||
*
|
||||
* It enables to cache access control decision (and behavior on execution of
|
||||
* trusted procedure, db_procedure class only) for a particular pair of
|
||||
* security labels and object class in userspace.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32 hash; /* hash value of this cache entry */
|
||||
char *scontext; /* security context of the subject */
|
||||
char *tcontext; /* security context of the target */
|
||||
uint16 tclass; /* object class of the target */
|
||||
|
||||
uint32 allowed; /* permissions to be allowed */
|
||||
uint32 auditallow; /* permissions to be audited on allowed */
|
||||
uint32 auditdeny; /* permissions to be audited on denied */
|
||||
|
||||
bool permissive; /* true, if permissive rule */
|
||||
bool hot_cache; /* true, if recently referenced */
|
||||
bool tcontext_is_valid;
|
||||
/* true, if tcontext is valid */
|
||||
char *ncontext; /* temporary scontext on execution of trusted
|
||||
* procedure, or NULL elsewhere */
|
||||
} avc_cache;
|
||||
|
||||
/*
|
||||
* Declaration of static variables
|
||||
*/
|
||||
#define AVC_NUM_SLOTS 512
|
||||
#define AVC_NUM_RECLAIM 16
|
||||
#define AVC_DEF_THRESHOLD 384
|
||||
|
||||
static MemoryContext avc_mem_cxt;
|
||||
static List *avc_slots[AVC_NUM_SLOTS]; /* avc's hash buckets */
|
||||
static int avc_num_caches; /* number of caches currently used */
|
||||
static int avc_lru_hint; /* index of the buckets to be reclaimed next */
|
||||
static int avc_threshold; /* threshold to launch cache-reclaiming */
|
||||
static char *avc_unlabeled; /* system 'unlabeled' label */
|
||||
|
||||
/*
|
||||
* Hash function
|
||||
*/
|
||||
static uint32
|
||||
sepgsql_avc_hash(const char *scontext, const char *tcontext, uint16 tclass)
|
||||
{
|
||||
return hash_any((const unsigned char *)scontext, strlen(scontext))
|
||||
^ hash_any((const unsigned char *)tcontext, strlen(tcontext))
|
||||
^ tclass;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset all the avc caches
|
||||
*/
|
||||
static void
|
||||
sepgsql_avc_reset(void)
|
||||
{
|
||||
MemoryContextReset(avc_mem_cxt);
|
||||
|
||||
memset(avc_slots, 0, sizeof(List *) * AVC_NUM_SLOTS);
|
||||
avc_num_caches = 0;
|
||||
avc_lru_hint = 0;
|
||||
avc_unlabeled = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reclaim caches recently unreferenced
|
||||
*/
|
||||
static void
|
||||
sepgsql_avc_reclaim(void)
|
||||
{
|
||||
ListCell *cell;
|
||||
ListCell *next;
|
||||
ListCell *prev;
|
||||
int index;
|
||||
|
||||
while (avc_num_caches >= avc_threshold - AVC_NUM_RECLAIM)
|
||||
{
|
||||
index = avc_lru_hint;
|
||||
|
||||
prev = NULL;
|
||||
for (cell = list_head(avc_slots[index]); cell; cell = next)
|
||||
{
|
||||
avc_cache *cache = lfirst(cell);
|
||||
|
||||
next = lnext(cell);
|
||||
if (!cache->hot_cache)
|
||||
{
|
||||
avc_slots[index]
|
||||
= list_delete_cell(avc_slots[index], cell, prev);
|
||||
|
||||
pfree(cache->scontext);
|
||||
pfree(cache->tcontext);
|
||||
if (cache->ncontext)
|
||||
pfree(cache->ncontext);
|
||||
pfree(cache);
|
||||
|
||||
avc_num_caches--;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache->hot_cache = false;
|
||||
prev = cell;
|
||||
}
|
||||
}
|
||||
avc_lru_hint = (avc_lru_hint + 1) % AVC_NUM_SLOTS;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_check_valid
|
||||
*
|
||||
* It checks whether the cached entries are still valid, or not.
|
||||
* If security policy has been reloaded since last reference of access
|
||||
* vector cache, we have to release all the entries, because they are
|
||||
* not valid yet.
|
||||
*/
|
||||
static bool
|
||||
sepgsql_avc_check_valid(void)
|
||||
{
|
||||
if (selinux_status_updated() > 0)
|
||||
{
|
||||
sepgsql_avc_reset();
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_unlabeled
|
||||
*
|
||||
* It returns an alternative label to be applied when no label or invalid
|
||||
* label would be assigned on objects.
|
||||
*/
|
||||
static char *
|
||||
sepgsql_avc_unlabeled(void)
|
||||
{
|
||||
if (!avc_unlabeled)
|
||||
{
|
||||
security_context_t unlabeled;
|
||||
|
||||
if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("SELinux: failed to get initial security label: %m")));
|
||||
PG_TRY();
|
||||
{
|
||||
avc_unlabeled = MemoryContextStrdup(avc_mem_cxt, unlabeled);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
freecon(unlabeled);
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
freecon(unlabeled);
|
||||
}
|
||||
return avc_unlabeled;
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_compute
|
||||
*
|
||||
* A fallback path, when cache mishit. It asks SELinux its access control
|
||||
* decision for the supplied pair of security context and object class.
|
||||
*/
|
||||
static avc_cache *
|
||||
sepgsql_avc_compute(const char *scontext, const char *tcontext, uint16 tclass)
|
||||
{
|
||||
char *ucontext = NULL;
|
||||
char *ncontext = NULL;
|
||||
MemoryContext oldctx;
|
||||
avc_cache *cache;
|
||||
uint32 hash;
|
||||
int index;
|
||||
struct av_decision avd;
|
||||
|
||||
hash = sepgsql_avc_hash(scontext, tcontext, tclass);
|
||||
index = hash % AVC_NUM_SLOTS;
|
||||
|
||||
/*
|
||||
* Validation check of the supplied security context.
|
||||
* Because it always invoke system-call, frequent check should be avoided.
|
||||
* Unless security policy is reloaded, validation status shall be kept, so
|
||||
* we also cache whether the supplied security context was valid, or not.
|
||||
*/
|
||||
if (security_check_context_raw((security_context_t)tcontext) != 0)
|
||||
ucontext = sepgsql_avc_unlabeled();
|
||||
|
||||
/*
|
||||
* Ask SELinux its access control decision
|
||||
*/
|
||||
if (!ucontext)
|
||||
sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
|
||||
else
|
||||
sepgsql_compute_avd(scontext, ucontext, tclass, &avd);
|
||||
|
||||
/*
|
||||
* To boost up trusted procedure checks on db_procedure object
|
||||
* class, we also confirm the decision when user calls a procedure
|
||||
* labeled as 'tcontext'.
|
||||
*/
|
||||
if (tclass == SEPG_CLASS_DB_PROCEDURE)
|
||||
{
|
||||
if (!ucontext)
|
||||
ncontext = sepgsql_compute_create(scontext, tcontext,
|
||||
SEPG_CLASS_PROCESS);
|
||||
else
|
||||
ncontext = sepgsql_compute_create(scontext, ucontext,
|
||||
SEPG_CLASS_PROCESS);
|
||||
if (strcmp(scontext, ncontext) == 0)
|
||||
{
|
||||
pfree(ncontext);
|
||||
ncontext = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up an avc_cache object
|
||||
*/
|
||||
oldctx = MemoryContextSwitchTo(avc_mem_cxt);
|
||||
|
||||
cache = palloc0(sizeof(avc_cache));
|
||||
|
||||
cache->hash = hash;
|
||||
cache->scontext = pstrdup(scontext);
|
||||
cache->tcontext = pstrdup(tcontext);
|
||||
cache->tclass = tclass;
|
||||
|
||||
cache->allowed = avd.allowed;
|
||||
cache->auditallow = avd.auditallow;
|
||||
cache->auditdeny = avd.auditdeny;
|
||||
cache->hot_cache = true;
|
||||
if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
|
||||
cache->permissive = true;
|
||||
if (!ucontext)
|
||||
cache->tcontext_is_valid = true;
|
||||
if (ncontext)
|
||||
cache->ncontext = pstrdup(ncontext);
|
||||
|
||||
avc_num_caches++;
|
||||
|
||||
if (avc_num_caches > avc_threshold)
|
||||
sepgsql_avc_reclaim();
|
||||
|
||||
avc_slots[index] = lcons(cache, avc_slots[index]);
|
||||
|
||||
MemoryContextSwitchTo(oldctx);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_lookup
|
||||
*
|
||||
* It lookups a cache entry that matches with the supplied object
|
||||
* identifiers and object class. If not found, it tries to create
|
||||
* a new cache entry.
|
||||
*/
|
||||
static avc_cache *
|
||||
sepgsql_avc_lookup(const char *scontext, const char *tcontext, uint16 tclass)
|
||||
{
|
||||
avc_cache *cache;
|
||||
ListCell *cell;
|
||||
uint32 hash;
|
||||
int index;
|
||||
|
||||
hash = sepgsql_avc_hash(scontext, tcontext, tclass);
|
||||
index = hash % AVC_NUM_SLOTS;
|
||||
|
||||
foreach (cell, avc_slots[index])
|
||||
{
|
||||
cache = lfirst(cell);
|
||||
|
||||
if (cache->hash == hash &&
|
||||
cache->tclass == tclass &&
|
||||
strcmp(cache->tcontext, tcontext) == 0 &&
|
||||
strcmp(cache->scontext, scontext) == 0)
|
||||
{
|
||||
cache->hot_cache = true;
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
/* not found, so insert a new cache */
|
||||
return sepgsql_avc_compute(scontext, tcontext, tclass);
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_check_perms(_label)
|
||||
*
|
||||
* It returns 'true', if the security policy suggested to allow the required
|
||||
* permissions. Otherwise, it returns 'false' or raises an error according
|
||||
* to the 'abort' argument.
|
||||
* The 'tobject' and 'tclass' identify the target object being referenced,
|
||||
* and 'required' is a bitmask of permissions (SEPG_*__*) defined for each
|
||||
* object classes.
|
||||
* The 'audit_name' is the object name (optional). If SEPGSQL_AVC_NOAUDIT
|
||||
* was supplied, it means to skip all the audit messages.
|
||||
*/
|
||||
bool
|
||||
sepgsql_avc_check_perms_label(const char *tcontext,
|
||||
uint16 tclass, uint32 required,
|
||||
const char *audit_name, bool abort)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
avc_cache *cache;
|
||||
uint32 denied;
|
||||
uint32 audited;
|
||||
bool result;
|
||||
|
||||
sepgsql_avc_check_valid();
|
||||
do {
|
||||
result = true;
|
||||
|
||||
/*
|
||||
* If target object is unlabeled, we assume it has
|
||||
* system 'unlabeled' security context instead.
|
||||
*/
|
||||
if (tcontext)
|
||||
cache = sepgsql_avc_lookup(scontext, tcontext, tclass);
|
||||
else
|
||||
cache = sepgsql_avc_lookup(scontext,
|
||||
sepgsql_avc_unlabeled(), tclass);
|
||||
|
||||
denied = required & ~cache->allowed;
|
||||
|
||||
/*
|
||||
* Compute permissions to be audited
|
||||
*/
|
||||
if (sepgsql_get_debug_audit())
|
||||
audited = (denied ? (denied & ~0) : (required & ~0));
|
||||
else
|
||||
audited = denied ? (denied & cache->auditdeny)
|
||||
: (required & cache->auditallow);
|
||||
|
||||
if (denied)
|
||||
{
|
||||
/*
|
||||
* In permissive mode or permissive domain, violated permissions
|
||||
* shall be audited on the log files at once, and implicitly
|
||||
* allowed them to avoid flood of access denied logs, because
|
||||
* the purpose of permissive mode/domain is to collect violation
|
||||
* log to fix up security policy itself.
|
||||
*/
|
||||
if (!sepgsql_getenforce() || cache->permissive)
|
||||
cache->allowed |= required;
|
||||
else
|
||||
result = false;
|
||||
}
|
||||
} while (!sepgsql_avc_check_valid());
|
||||
|
||||
/*
|
||||
* In the case when we have something auditable actions here,
|
||||
* sepgsql_audit_log shall be called with text representation of
|
||||
* security labels for both of subject and object.
|
||||
* It records this access violation, so DBA will be able to find
|
||||
* out unexpected security problems later.
|
||||
*/
|
||||
if (audited != 0 &&
|
||||
audit_name != SEPGSQL_AVC_NOAUDIT &&
|
||||
sepgsql_get_mode() != SEPGSQL_MODE_INTERNAL)
|
||||
{
|
||||
sepgsql_audit_log(!!denied,
|
||||
cache->scontext,
|
||||
cache->tcontext_is_valid ?
|
||||
cache->tcontext : sepgsql_avc_unlabeled(),
|
||||
cache->tclass,
|
||||
audited,
|
||||
audit_name);
|
||||
}
|
||||
|
||||
if (abort && !result)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("SELinux: security policy violation")));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
sepgsql_avc_check_perms(const ObjectAddress *tobject,
|
||||
uint16 tclass, uint32 required,
|
||||
const char *audit_name, bool abort)
|
||||
{
|
||||
char *tcontext = GetSecurityLabel(tobject, SEPGSQL_LABEL_TAG);
|
||||
bool rc;
|
||||
|
||||
rc = sepgsql_avc_check_perms_label(tcontext,
|
||||
tclass, required,
|
||||
audit_name, abort);
|
||||
if (tcontext)
|
||||
pfree(tcontext);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_trusted_proc
|
||||
*
|
||||
* It returns a security label to be switched on execution of the supplied
|
||||
* procedure, if it was configured as a trusted procedure. Otherwise, NULL
|
||||
* shall be returned.
|
||||
*/
|
||||
char *
|
||||
sepgsql_avc_trusted_proc(Oid functionId)
|
||||
{
|
||||
char *scontext = sepgsql_get_client_label();
|
||||
char *tcontext;
|
||||
ObjectAddress tobject;
|
||||
avc_cache *cache;
|
||||
|
||||
tobject.classId = ProcedureRelationId;
|
||||
tobject.objectId = functionId;
|
||||
tobject.objectSubId = 0;
|
||||
tcontext = GetSecurityLabel(&tobject, SEPGSQL_LABEL_TAG);
|
||||
|
||||
sepgsql_avc_check_valid();
|
||||
do {
|
||||
if (tcontext)
|
||||
cache = sepgsql_avc_lookup(scontext, tcontext,
|
||||
SEPG_CLASS_DB_PROCEDURE);
|
||||
else
|
||||
cache = sepgsql_avc_lookup(scontext, sepgsql_avc_unlabeled(),
|
||||
SEPG_CLASS_DB_PROCEDURE);
|
||||
} while (!sepgsql_avc_check_valid());
|
||||
|
||||
return cache->ncontext;
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_exit
|
||||
*
|
||||
* It clean up userspace avc stuff on process exit
|
||||
*/
|
||||
static void
|
||||
sepgsql_avc_exit(int code, Datum arg)
|
||||
{
|
||||
selinux_status_close();
|
||||
}
|
||||
|
||||
/*
|
||||
* sepgsql_avc_init
|
||||
*
|
||||
* It shall be invoked at once from _PG_init routine to initialize
|
||||
* userspace access vector cache stuff.
|
||||
*/
|
||||
void
|
||||
sepgsql_avc_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* All the avc stuff shall be allocated on avc_mem_cxt
|
||||
*/
|
||||
avc_mem_cxt = AllocSetContextCreate(TopMemoryContext,
|
||||
"userspace access vector cache",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
memset(avc_slots, 0, sizeof(avc_slots));
|
||||
avc_num_caches = 0;
|
||||
avc_lru_hint = 0;
|
||||
avc_threshold = AVC_DEF_THRESHOLD;
|
||||
|
||||
/*
|
||||
* SELinux allows to mmap(2) its kernel status page in read-only mode
|
||||
* to inform userspace applications its status updating (such as
|
||||
* policy reloading) without system-call invocations.
|
||||
* This feature is only supported in Linux-2.6.38 or later, however,
|
||||
* libselinux provides a fallback mode to know its status using
|
||||
* netlink sockets.
|
||||
*/
|
||||
rc = selinux_status_open(1);
|
||||
if (rc < 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("SELinux: could not open selinux status : %m")));
|
||||
else if (rc > 0)
|
||||
ereport(LOG,
|
||||
(errmsg("SELinux: kernel status page uses fallback mode")));
|
||||
|
||||
/*
|
||||
* To close selinux status page on process exit
|
||||
*/
|
||||
on_proc_exit(sepgsql_avc_exit, 0);
|
||||
}
|
@ -64,7 +64,7 @@
|
||||
or higher with <productname>SELinux</productname> enabled. It is not
|
||||
available on any other platform, and must be explicitly enabled using
|
||||
<literal>--with-selinux</>. You will also need <productname>libselinux</>
|
||||
2.0.93 or higher and <productname>selinux-policy</> 3.9.13 or higher
|
||||
2.0.99 or higher and <productname>selinux-policy</> 3.9.13 or higher
|
||||
(some distributions may backport the necessary rules into older policy
|
||||
versions).
|
||||
</para>
|
||||
@ -473,16 +473,6 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
|
||||
<title>Limitations</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Userspace access vector cache</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<productname>sepgsql</> does not yet support an access vector cache.
|
||||
This would likely improve performance.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Data Definition Language (DDL) Permissions</term>
|
||||
<listitem>
|
||||
|
Loading…
x
Reference in New Issue
Block a user