contrib/sepgsql/expected/create.out | 4 +++ contrib/sepgsql/hooks.c | 7 ++++- contrib/sepgsql/schema.c | 49 +++++++++++++++++++++++++--------- contrib/sepgsql/sepgsql.h | 3 +- contrib/sepgsql/sql/create.sql | 6 ++++ src/backend/catalog/namespace.c | 17 +++++++++--- src/backend/catalog/pg_namespace.c | 7 +++-- src/backend/commands/schemacmds.c | 7 ++++- src/include/catalog/objectaccess.h | 19 +++++++++++++ src/include/catalog/pg_namespace.h | 3 +- 10 files changed, 98 insertions(+), 24 deletions(-) diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out index 95fc4f1..3293a73 100644 --- a/contrib/sepgsql/expected/create.out +++ b/contrib/sepgsql/expected/create.out @@ -13,7 +13,11 @@ 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" +CREATE SCHEMA regtest_schema; +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +SET search_path = regtest_schema, public; -- -- clean-up -- DROP DATABASE IF EXISTS regtest_sepgsql_test_database; +DROP SCHEMA IF EXISTS regtest_schema CASCADE; diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 886f1fa..2b62a75 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -141,6 +141,11 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId, args->pg_database.tablespaceId); break; + case NamespaceRelationId: + cinfo->ncontext = + sepgsql_schema_prep_create(args->pg_namespace.nspname); + break; + default: /* Ignore unsupported object classes */ break; @@ -171,7 +176,7 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId, break; case NamespaceRelationId: - sepgsql_schema_post_create(objectId); + sepgsql_schema_post_create(objectId, cinfo->ncontext); break; case RelationRelationId: diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c index a167be1..07dbf80 100644 --- a/contrib/sepgsql/schema.c +++ b/contrib/sepgsql/schema.c @@ -20,38 +20,61 @@ #include "sepgsql.h" /* - * sepgsql_schema_post_create + * sepgsql_schema_prep_create * - * This routine assigns a default security label on a newly defined - * schema. + * It computes a default security label of schema object, and checks + * permission to create a new schema object. */ -void -sepgsql_schema_post_create(Oid namespaceId) +const char * +sepgsql_schema_prep_create(const char *nspname) { - char *scontext; + char *scontext = sepgsql_get_client_label(); char *tcontext; char *ncontext; - ObjectAddress object; + char audit_name[NAMEDATALEN + 16]; /* * Compute a default security label when we create a new schema object * under the working database. */ - scontext = sepgsql_get_client_label(); tcontext = sepgsql_get_label(DatabaseRelationId, MyDatabaseId, 0); ncontext = sepgsql_compute_create(scontext, tcontext, SEPG_CLASS_DB_SCHEMA); /* - * Assign the default security label on a new procedure + * Check db_schema:{create} permission */ + if (strncmp(nspname, "pg_temp_", 8) == 0) + snprintf(audit_name, sizeof(audit_name), "schema pg_temp"); + else + snprintf(audit_name, sizeof(audit_name), "schema %s", nspname); + + sepgsql_avc_check_perms_label(ncontext, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__CREATE, + audit_name, + true); + + pfree(tcontext); + + return ncontext; +} + + +/* + * sepgsql_schema_post_create + * + * This routine assigns a default security label on a newly defined schema. + */ +void +sepgsql_schema_post_create(Oid namespaceId, const char *seclabel) +{ + ObjectAddress object; + object.classId = NamespaceRelationId; object.objectId = namespaceId; object.objectSubId = 0; - SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); - - pfree(ncontext); - pfree(tcontext); + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, seclabel); } /* diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index 0d0eead..b4a3e4d 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -313,7 +313,8 @@ extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel); /* * schema.c */ -extern void sepgsql_schema_post_create(Oid namespaceId); +extern const char *sepgsql_schema_prep_create(const char *nspname); +extern void sepgsql_schema_post_create(Oid namespaceId, const char *seclabel); extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel); /* diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql index e75d57b..9ce1db9 100644 --- a/contrib/sepgsql/sql/create.sql +++ b/contrib/sepgsql/sql/create.sql @@ -9,7 +9,13 @@ SET client_min_messages = LOG; CREATE DATABASE regtest_sepgsql_test_database; +CREATE SCHEMA regtest_schema; + +SET search_path = regtest_schema, public; + -- -- clean-up -- DROP DATABASE IF EXISTS regtest_sepgsql_test_database; + +DROP SCHEMA IF EXISTS regtest_schema CASCADE; diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index fcc90fe..3a0238f 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -21,6 +21,7 @@ #include "access/xact.h" #include "catalog/dependency.h" +#include "catalog/objectaccess.h" #include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" @@ -3384,9 +3385,12 @@ InitTempTableNamespace(void) char namespaceName[NAMEDATALEN]; Oid namespaceId; Oid toastspaceId; + Datum hook_private = 0; Assert(!OidIsValid(myTempNamespace)); + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); + /* * First, do permission check to see if we are authorized to make temp * tables. We use a nonstandard error message here since "databasename: @@ -3404,6 +3408,9 @@ InitTempTableNamespace(void) errmsg("permission denied to create temporary tables in database \"%s\"", get_database_name(MyDatabaseId)))); + /* Prep-creation hook for new temp-schema */ + InvokePrepCreateSchemaHook(&hook_private, namespaceName); + /* * Do not allow a Hot Standby slave session to make temp tables. Aside * from problems with modifying the system catalogs, there is a naming @@ -3419,8 +3426,6 @@ InitTempTableNamespace(void) (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("cannot create temporary tables during recovery"))); - snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); - namespaceId = get_namespace_oid(namespaceName, true); if (!OidIsValid(namespaceId)) { @@ -3432,7 +3437,9 @@ InitTempTableNamespace(void) * temp tables. This works because the places that access the temp * namespace for my own backend skip permissions checks on it. */ - namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + namespaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + hook_private); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } @@ -3456,7 +3463,9 @@ InitTempTableNamespace(void) toastspaceId = get_namespace_oid(namespaceName, true); if (!OidIsValid(toastspaceId)) { - toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + toastspaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + hook_private); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index ceebac2..48b9fc9 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -29,7 +29,7 @@ * --------------- */ Oid -NamespaceCreate(const char *nspName, Oid ownerId) +NamespaceCreate(const char *nspName, Oid ownerId, Datum hook_private) { Relation nspdesc; HeapTuple tup; @@ -86,7 +86,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new schema */ - InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0); - + InvokeObjectAccessHookArg(OAT_POST_CREATE, + NamespaceRelationId, nspoid, 0, + hook_private); return nspoid; } diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index 8daa9d0..d6f1927 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -19,6 +19,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" #include "commands/schemacmds.h" @@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) Oid owner_uid; Oid saved_uid; int save_sec_context; + Datum hook_private = 0; AclResult aclresult; GetUserIdAndSecContext(&saved_uid, &save_sec_context); @@ -74,6 +76,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) check_is_member_of_role(saved_uid, owner_uid); + /* Prep-creation hook for new schema */ + InvokePrepCreateSchemaHook(&hook_private, schemaName); + /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, @@ -94,7 +99,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) save_sec_context | SECURITY_LOCAL_USERID_CHANGE); /* Create the schema's namespace */ - namespaceId = NamespaceCreate(schemaName, owner_uid); + namespaceId = NamespaceCreate(schemaName, owner_uid, hook_private); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index 0531327..cb5c396 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -51,6 +51,10 @@ typedef union Oid datsourceId; /* oid of source database */ Oid tablespaceId; /* oid of default tablespace */ } pg_database; + struct { + Datum *private; /* common */ + const char *nspname; /* name of new schema */ + } pg_namespace; } ObjectAccessCreateObjectArgs; /* @@ -102,4 +106,19 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook; } \ } while(0) +#define InvokePrepCreateSchemaHook(_private,_nspname) \ + do { \ + if (object_access_hook) \ + { \ + ObjectAccessCreateObjectArgs __args; \ + \ + __args.pg_namespace.private = (_private); \ + __args.pg_namespace.nspname = (_nspname); \ + \ + (*object_access_hook)(OAT_PREP_CREATE, \ + NamespaceRelationId, InvalidOid, 0, \ + PointerGetDatum(&__args)); \ + } \ + } while(0) + #endif /* OBJECTACCESS_H */ diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h index 680802d7..eedcf1b 100644 --- a/src/include/catalog/pg_namespace.h +++ b/src/include/catalog/pg_namespace.h @@ -77,6 +77,7 @@ DESCR("standard public schema"); /* * prototypes for functions in pg_namespace.c */ -extern Oid NamespaceCreate(const char *nspName, Oid ownerId); +extern Oid NamespaceCreate(const char *nspName, Oid ownerId, + Datum hook_private); #endif /* PG_NAMESPACE_H */