pgsql-v9.2-prep-creation-hook-part-1.v1.patch

application/octet-stream

Filename: pgsql-v9.2-prep-creation-hook-part-1.v1.patch
Type: application/octet-stream
Part: 0
Message: Re: [v9.2] Object access hooks with arguments support (v1)

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v9
File+
contrib/sepgsql/database.c 50 18
contrib/sepgsql/expected/create.out 19 0
contrib/sepgsql/hooks.c 92 31
contrib/sepgsql/sepgsql.h 22 1
contrib/sepgsql/sql/create.sql 15 0
contrib/sepgsql/test_sepgsql 1 1
src/backend/commands/dbcommands.c 8 1
src/include/catalog/objectaccess.h 65 6
 contrib/sepgsql/database.c          |   68 ++++++++++++++-----
 contrib/sepgsql/expected/create.out |   19 ++++++
 contrib/sepgsql/hooks.c             |  123 ++++++++++++++++++++++++++---------
 contrib/sepgsql/sepgsql.h           |   23 ++++++-
 contrib/sepgsql/sql/create.sql      |   15 ++++
 contrib/sepgsql/test_sepgsql        |    2 +-
 src/backend/commands/dbcommands.c   |    9 ++-
 src/include/catalog/objectaccess.h  |   71 ++++++++++++++++++--
 8 files changed, 272 insertions(+), 58 deletions(-)

diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index 7f15d9c..ed3817b 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -15,40 +15,72 @@
 #include "commands/seclabel.h"
 #include "sepgsql.h"
 
-void
-sepgsql_database_post_create(Oid databaseId)
+/*
+ * sepgsql_database_prep_create
+ *
+ * This routine computes default security label and checks permissions to
+ * create a new database.
+ */
+const char *
+sepgsql_database_prep_create(const char *datname,
+							 Oid datsourceId,
+							 Oid tablespaceId)
 {
 	char   *scontext = sepgsql_get_client_label();
 	char   *tcontext;
 	char   *ncontext;
+	char	audit_name[NAMEDATALEN + 16];
 	ObjectAddress	object;
 
-	/*
-	 * Compute a default security label of the newly created database
-	 * based on a pair of security label of client and source database.
-	 *
-	 * XXX - Right now, this logic uses "template1" as its source, because
-	 * here is no way to know the Oid of source database.
-	 */
+	/* check db_database:{getattr} permission on the source database */
 	object.classId = DatabaseRelationId;
-	object.objectId = TemplateDbOid;
+	object.objectId = datsourceId;;
 	object.objectSubId = 0;
-	tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+	sepgsql_avc_check_perms(&object,
+							SEPG_CLASS_DB_DATABASE,
+							SEPG_DB_DATABASE__GETATTR,
+							getObjectDescription(&object),
+							true);
+	/*
+	 * Compute a default security label of the new database being
+	 * constructed; based on a pair of security label of client and
+	 * source database.
+	 */
+	tcontext = sepgsql_get_label(DatabaseRelationId, datsourceId, 0);
 
+	/*
+	 * XXX - upcoming libselinux supports case handling when
+	 * a new object that has a particular name.
+	 */
 	ncontext = sepgsql_compute_create(scontext, tcontext,
 									  SEPG_CLASS_DB_DATABASE);
 
-	/*
-	 * Assign the default security label on the new database
-	 */
+	/* check db_database:{create} permission */
+	snprintf(audit_name, sizeof(audit_name), "database: %s", datname);
+	sepgsql_avc_check_perms_label(ncontext,
+								  SEPG_CLASS_DB_DATABASE,
+								  SEPG_DB_DATABASE__CREATE,
+								  audit_name,
+								  true);
+
+	return ncontext;
+}
+
+/*
+ * sepgsql_database_post_create
+ *
+ * This routine assigns default security label of the database.
+ */
+void
+sepgsql_database_post_create(Oid databaseId, const char *seclabel)
+{
+	ObjectAddress	object;
+
 	object.classId = DatabaseRelationId;
 	object.objectId = databaseId;
 	object.objectSubId = 0;
 
-	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
-
-	pfree(ncontext);
-	pfree(tcontext);
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, seclabel);
 }
 
 /*
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
new file mode 100644
index 0000000..95fc4f1
--- /dev/null
+++ b/contrib/sepgsql/expected/create.out
@@ -0,0 +1,19 @@
+--
+-- Regression Test for Creation of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon();	-- confirm client privilege
+              sepgsql_getcon               
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG:  SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database: regtest_sepgsql_test_database"
+--
+-- clean-up 
+--
+DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 331bbd7..886f1fa 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -107,6 +107,91 @@ sepgsql_client_auth(Port *port, int status)
 }
 
 /*
+ * sepgsql_object_prep_create
+ *
+ * Entrypoint of OAT_PREP_CREATE event via object_access_hook.
+ */
+static void
+sepgsql_object_prep_create(Oid classId, Oid objectId, int subId,
+						   Datum argument)
+{
+	ObjectAccessCreateObjectArgs   *args
+		= (ObjectAccessCreateObjectArgs *)DatumGetPointer(argument);
+	sepgsql_creation_info		   *cinfo;
+
+	if (next_object_access_hook)
+		(*next_object_access_hook) (OAT_PREP_CREATE,
+									classId, objectId, subId,
+									argument);
+
+	/*
+	 * create sepgsql's private data to be delivered to
+	 * the post-creation hook.
+	 */
+	cinfo = palloc(sizeof(sepgsql_creation_info));
+	cinfo->classId = classId;
+	cinfo->private_next = *(args->common.private);
+
+	switch (classId)
+	{
+		case DatabaseRelationId:
+			cinfo->ncontext =
+				sepgsql_database_prep_create(args->pg_database.datname,
+											 args->pg_database.datsourceId,
+											 args->pg_database.tablespaceId);
+			break;
+
+		default:
+			/* Ignore unsupported object classes */
+			break;
+	}
+	*(args->common.private) = PointerGetDatum(cinfo);
+}
+
+/*
+ * sepgsql_object_post_create
+ *
+ * Entrypoint of OAT_POST_CREATE event via object_access_hook.
+ */
+static void
+sepgsql_object_post_create(Oid classId, Oid objectId, int subId,
+						   Datum argument)
+{
+	sepgsql_creation_info  *cinfo
+		= (sepgsql_creation_info *)DatumGetPointer(argument);
+
+	if (next_object_access_hook)
+		(*next_object_access_hook) (OAT_POST_CREATE,
+									classId, objectId, subId,
+									!cinfo ? 0 : cinfo->private_next);
+	switch (classId)
+	{
+		case DatabaseRelationId:
+			sepgsql_database_post_create(objectId, cinfo->ncontext);
+			break;
+
+		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;
+	}
+}
+
+/*
  * sepgsql_object_access
  *
  * Entrypoint of the object_access_hook. This routine performs as
@@ -114,41 +199,17 @@ sepgsql_client_auth(Port *port, int status)
  */
 static void
 sepgsql_object_access(ObjectAccessType access,
-					  Oid classId,
-					  Oid objectId,
-					  int subId)
+					  Oid classId, Oid objectId, int subId,
+					  Datum argument)
 {
-	if (next_object_access_hook)
-		(*next_object_access_hook) (access, classId, objectId, subId);
-
 	switch (access)
 	{
+		case OAT_PREP_CREATE:
+			sepgsql_object_prep_create(classId, objectId, subId, argument);
+			break;
+
 		case OAT_POST_CREATE:
-			switch (classId)
-			{
-				case DatabaseRelationId:
-					sepgsql_database_post_create(objectId);
-					break;
-
-				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;
-			}
+			sepgsql_object_post_create(classId, objectId, subId, argument);
 			break;
 
 		default:
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index b4c1dfd..0d0eead 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -211,6 +211,24 @@
 #define SEPG_DB_VIEW__EXPAND				(1<<6)
 
 /*
+ * sepgsql_creation_info
+ *
+ * A data structure to store contextual information between prep-creation
+ * and post-creation hooks; mostly a security label to be assigned on the
+ * newly constructed object.
+ */
+typedef struct {
+	/* oid of the catalog that stores this new object */
+	Oid			classId;
+
+	/* private field for stacking module */
+	Datum		private_next;
+
+	/* a default security label to be assigned on */
+	const char *ncontext;
+} sepgsql_creation_info;
+
+/*
  * hooks.c
  */
 extern bool sepgsql_get_permissive(void);
@@ -286,7 +304,10 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
 /*
  * database.c
  */
-extern void sepgsql_database_post_create(Oid databaseId);
+extern const char *sepgsql_database_prep_create(const char *datname,
+												Oid datsourceId,
+												Oid tablespaceId);
+extern void sepgsql_database_post_create(Oid databaseId, const char *seclabel);
 extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
 
 /*
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
new file mode 100644
index 0000000..e75d57b
--- /dev/null
+++ b/contrib/sepgsql/sql/create.sql
@@ -0,0 +1,15 @@
+--
+-- Regression Test for Creation of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+CREATE DATABASE regtest_sepgsql_test_database;
+
+--
+-- clean-up 
+--
+DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 9b7262a..52237e6 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
 echo
 echo "============== running sepgsql regression tests       =============="
 
-make REGRESS="label dml misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
 
 # exit with the exit code provided by "make"
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 4551db7..361a172 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -127,6 +127,7 @@ createdb(const CreatedbStmt *stmt)
 	int			dbconnlimit = -1;
 	int			notherbackends;
 	int			npreparedxacts;
+	Datum		hook_private = 0;
 	createdb_failure_params fparms;
 
 	/* Extract options from the statement node tree */
@@ -424,6 +425,10 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* Prep-creation hook for new database */
+	InvokePrepCreateDatabaseHook(&hook_private, dbname, src_dboid,
+								 dst_deftablespace);
+
 	/*
 	 * Check for db name conflict.	This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -515,7 +520,9 @@ createdb(const CreatedbStmt *stmt)
 	copyTemplateDependencies(src_dboid, dboid);
 
 	/* Post creation hook for new database */
-	InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+	InvokeObjectAccessHookArg(OAT_POST_CREATE,
+							  DatabaseRelationId, dboid, 0,
+							  hook_private);
 
 	/*
 	 * Force a checkpoint before starting the copy. This will force dirty
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 2925475..0531327 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -15,32 +15,91 @@
  * performing certain actions on a SQL object.	This is intended as
  * infrastructure for security or logging pluggins.
  *
+ * OAT_PREP_CREATE should be invoked around permission check of the core
+ * with arguments that describes about new object being constructed.
+ * Its private field can be used to deliver contextual information to
+ * post-creation hook.
+ *
  * OAT_POST_CREATE should be invoked just after the the object is created.
  * Typically, this is done after inserting the primary catalog records and
- * associated dependencies.
+ * associated dependencies. If prep-creation hook was invoked in same
+ * context, its private valud shall be provided to the modules.
  *
  * Other types may be added in the future.
  */
 typedef enum ObjectAccessType
 {
+	OAT_PREP_CREATE,
 	OAT_POST_CREATE,
 } ObjectAccessType;
 
 /*
- * Hook, and a macro to invoke it.
+ * ObjectAccessCreateObjectInfo
+ *
+ * It shall be used as argument of OAT_PREP_CREATE hook to inform about
+ * new object being constructed; prior to updates of system catalogs.
+ */
+typedef union
+{
+	struct {
+		Datum	   *private;	/* private data to deliver contextual
+								 * information into post creation hook */
+	} common;
+	struct {
+		Datum	   *private;	/* common */
+		const char *datname;	/* name of new database */
+		Oid			datsourceId;	/* oid of source database */
+		Oid			tablespaceId;	/* oid of default tablespace */
+	} pg_database;
+} ObjectAccessCreateObjectArgs;
+
+/*
+ * Hook, and macros to invoke it.
  */
 
 typedef void (*object_access_hook_type) (ObjectAccessType access,
-													 Oid classId,
-													 Oid objectId,
-													 int subId);
+										 Oid classId,
+										 Oid objectId,
+										 int subId,
+										 Datum argument);
 
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
 
 #define InvokeObjectAccessHook(access,classId,objectId,subId)			\
 	do {																\
 		if (object_access_hook)											\
-			(*object_access_hook)((access),(classId),(objectId),(subId)); \
+			(*object_access_hook)((access),								\
+								  (classId),(objectId),(subId),			\
+								  (Datum) 0);							\
+	} while(0)
+
+#define InvokeObjectAccessHookArg(access,classId,objectId,subId,argument) \
+	do {																\
+		if (object_access_hook)											\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId),			\
+								  (argument));							\
+	} while(0)
+
+/*
+ * OAT_PREP_CREATE specific macros, because it takes various arguments
+ * depending on object classes.
+ */
+#define InvokePrepCreateDatabaseHook(_private,_datname,_source,_tablespace) \
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessCreateObjectArgs	__args;						\
+																		\
+			__args.pg_database.private = (_private);					\
+			__args.pg_database.datname = (_datname);					\
+			__args.pg_database.datsourceId = (_source);					\
+			__args.pg_database.tablespaceId = (_tablespace);			\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  DatabaseRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__args));			\
+		}																\
 	} while(0)
 
 #endif   /* OBJECTACCESS_H */