2011/7/22 Yeb Havinga <yebhavi...@gmail.com>:
> On 2011-07-22 11:55, Kohei Kaigai wrote:
>>
>>> 2) Also I thought if it could work to not remember tcontext is valid, but
>>> instead remember the consequence,
>>> which is that it is replaced by "unlabeled". It makes the avc_cache
>>> struct shorter and the code somewhat
>>> simpler.
>>>
>> Here is a reason why we hold tcontext, even if it is not valid.
>> The hash key of avc_cache is combination of scontext, tcontext and tclass.
>> Thus, if we replaced an invalid
>> tcontext by unlabeled context, it would always make cache mishit and
>> performance loss.
>
> I see that now, thanks.
>
> I have no further comments, and I think that the patch in it's current
> status is ready for committer.
>
Thanks for your reviewing.

The attached patch is a revised one according to your suggestion to
include fallback for 'unlabeled' label within sepgsql_avc_lookup().
And I found a noise in regression test results, so eliminated it from v5.
-- 
KaiGai Kohei <kai...@kaigai.gr.jp>
 configure.in               |    4 +-
 contrib/sepgsql/Makefile   |    2 +-
 contrib/sepgsql/dml.c      |   59 +++---
 contrib/sepgsql/hooks.c    |   64 +++---
 contrib/sepgsql/label.c    |   13 +-
 contrib/sepgsql/proc.c     |   79 ++-----
 contrib/sepgsql/relation.c |   93 ++++-----
 contrib/sepgsql/schema.c   |   39 ++--
 contrib/sepgsql/selinux.c  |    2 +-
 contrib/sepgsql/sepgsql.h  |   20 ++-
 contrib/sepgsql/uavc.c     |  510 ++++++++++++++++++++++++++++++++++++++++++++
 doc/src/sgml/sepgsql.sgml  |   12 +-
 12 files changed, 673 insertions(+), 224 deletions(-)

diff --git a/configure.in b/configure.in
index b1f15f1..8e3bc62 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
index bc995dd..5794921 100644
--- a/contrib/sepgsql/Makefile
+++ b/contrib/sepgsql/Makefile
@@ -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
 REGRESS = label dml misc
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 22666b7..3199337 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -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)
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 7797ccb..ca6ce99 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -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);
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index 669ee35..8a73641 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -19,6 +19,7 @@
 #include "catalog/pg_class.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_seclabel.h"
 #include "commands/dbcommands.h"
 #include "commands/seclabel.h"
 #include "libpq/libpq-be.h"
@@ -27,6 +28,7 @@
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
+#include "utils/syscache.h"
 #include "utils/tqual.h"
 
 #include "sepgsql.h"
@@ -64,16 +66,11 @@ sepgsql_set_client_label(char *new_label)
  * shall be returned.
  */
 char *
-sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
+sepgsql_get_label(const ObjectAddress *tobject)
 {
-	ObjectAddress object;
-	char	   *label;
+	char   *label;
 
-	object.classId = classId;
-	object.objectId = objectId;
-	object.objectSubId = subId;
-
-	label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+	label = GetSecurityLabel(tobject, SEPGSQL_LABEL_TAG);
 	if (!label || security_check_context_raw((security_context_t) label))
 	{
 		security_context_t unlabeled;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index 3b8bf23..23d82b2 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -39,7 +39,6 @@ sepgsql_proc_post_create(Oid functionId)
 	HeapTuple	tuple;
 	Oid			namespaceId;
 	ObjectAddress object;
-	char	   *scontext;
 	char	   *tcontext;
 	char	   *ncontext;
 
@@ -70,10 +69,12 @@ sepgsql_proc_post_create(Oid functionId)
 	 * 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);
+	object.classId = NamespaceRelationId;
+	object.objectId = namespaceId;
+	object.objectSubId = 0;
+	tcontext = sepgsql_get_label(&object);
+	ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
+									  tcontext, SEPG_CLASS_DB_PROCEDURE);
 
 	/*
 	 * Assign the default security label on a new procedure
@@ -96,64 +97,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;
-}
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index 963cfdf..90876a8 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -48,21 +48,22 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
 	if (get_rel_relkind(relOid) != RELKIND_RELATION)
 		return;
 
+	object.classId = RelationRelationId;
+	object.objectId = relOid;
+	object.objectSubId = attnum;
+
 	/*
 	 * 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);
+	tcontext = sepgsql_get_label(&object);
 	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);
@@ -79,10 +80,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 +96,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);
 }
 
@@ -135,7 +128,6 @@ sepgsql_relation_post_create(Oid relOid)
 	Form_pg_class classForm;
 	ObjectAddress object;
 	uint16		tclass;
-	char	   *scontext;		/* subject */
 	char	   *tcontext;		/* schema */
 	char	   *rcontext;		/* relation */
 	char	   *ccontext;		/* column */
@@ -173,10 +165,12 @@ sepgsql_relation_post_create(Oid relOid)
 	 * 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);
+	object.classId = NamespaceRelationId;
+	object.objectId = classForm->relnamespace;
+	object.objectSubId = 0;
+	tcontext = sepgsql_get_label(&object);
+	rcontext = sepgsql_compute_create(sepgsql_get_client_label(),
+									  tcontext, tclass);
 
 	/*
 	 * Assign the default security label on the new relation
@@ -194,8 +188,8 @@ sepgsql_relation_post_create(Oid relOid)
 	{
 		AttrNumber	index;
 
-		ccontext = sepgsql_compute_create(scontext, rcontext,
-										  SEPG_CLASS_DB_COLUMN);
+		ccontext = sepgsql_compute_create(sepgsql_get_client_label(),
+										  rcontext, SEPG_CLASS_DB_COLUMN);
 		for (index = FirstLowInvalidHeapAttributeNumber + 1;
 			 index <= classForm->relnatts;
 			 index++)
@@ -227,8 +221,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 +239,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);
 }
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 0de8997..aae68ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -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);
 }
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index 1f5a97e..d693d63 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -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;
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index 71688ab..81cb669 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -15,6 +15,7 @@
 #include "fmgr.h"
 
 #include <selinux/selinux.h>
+#include <selinux/avc.h>
 
 /*
  * SE-PostgreSQL Label Tag
@@ -245,13 +246,29 @@ 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
  */
 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 char *sepgsql_get_label(const ObjectAddress *tobject);
 
 extern void sepgsql_object_relabel(const ObjectAddress *object,
 					   const char *seclabel);
@@ -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 */
diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c
new file mode 100644
index 0000000..99ca5a9
--- /dev/null
+++ b/contrib/sepgsql/uavc.c
@@ -0,0 +1,510 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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;
+
+	/*
+	 * tcontext == NULL means the target object has no label,
+	 * so we assume like as it has system 'unlabeled' label.
+	 */
+	if (!tcontext)
+		tcontext = sepgsql_avc_unlabeled();
+
+	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. Elsewhere,  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;
+
+		/*
+		 * Look up userspace avc, towards the supplied scontext,
+		 * tcontext and tclass.
+		 */
+		cache = sepgsql_avc_lookup(scontext, tcontext, 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. Elsewhere, 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 {
+		cache = sepgsql_avc_lookup(scontext, tcontext,
+								   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);
+}
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index fa42c19..81c1292 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -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>
@@ -474,16 +474,6 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
 
   <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>
      <para>
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to