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
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 */