contrib/sepgsql/Makefile | 2 +- contrib/sepgsql/database.c | 105 +++++++++++++++ contrib/sepgsql/hooks.c | 5 + contrib/sepgsql/label.c | 23 +++- contrib/sepgsql/schema.c | 18 ++-- contrib/sepgsql/sepgsql.h | 6 + doc/src/sgml/catalogs.sgml | 65 ++++++++++ doc/src/sgml/ref/security_label.sgml | 3 + src/backend/catalog/Makefile | 2 +- src/backend/catalog/catalog.c | 5 + src/backend/catalog/system_views.sql | 32 +++++- src/backend/commands/seclabel.c | 176 +++++++++++++++++++++++++- src/backend/parser/gram.y | 3 + src/bin/pg_dump/pg_dumpall.c | 108 ++++++++++++---- src/include/catalog/indexing.h | 3 + src/include/catalog/pg_shseclabel.h | 41 ++++++ src/include/catalog/toasting.h | 3 + src/test/regress/expected/rules.out | 6 +- src/test/regress/expected/sanity_check.out | 3 +- src/test/regress/input/security_label.source | 32 +++++- 20 files changed, 590 insertions(+), 51 deletions(-) diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile index bc995dd..6f77239 100644 --- a/contrib/sepgsql/Makefile +++ b/contrib/sepgsql/Makefile @@ -2,7 +2,7 @@ MODULE_big = sepgsql OBJS = hooks.o selinux.o label.o dml.o \ - schema.o relation.o proc.o + database.o schema.o relation.o proc.o DATA_built = sepgsql.sql REGRESS = label dml misc EXTRA_CLEAN = -r tmp *.pp sepgsql-regtest.if sepgsql-regtest.fc diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c new file mode 100644 index 0000000..efac6a2 --- /dev/null +++ b/contrib/sepgsql/database.c @@ -0,0 +1,105 @@ +/* ------------------------------------------------------------------------- + * + * contrib/sepgsql/database.c + * + * Routines corresponding to database objects + * + * Copyright (c) 2010-2011, PostgreSQL Global Development Group + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/dependency.h" +#include "catalog/pg_database.h" +#include "commands/seclabel.h" +#include "miscadmin.h" +#include "utils/lsyscache.h" + +#include "sepgsql.h" + +/* + * sepgsql_database_post_create + * + * This routine assigns a default security label on a newly defined + * database + */ +void +sepgsql_database_post_create(Oid databaseId) +{ + char *scontext = sepgsql_get_client_label(); + char *tcontext; + char *ncontext; + ObjectAddress object; + + /* + * Compute a default security label when we create a new database + * object according to the specified template database. + * + * FIXME - In correctly, we'd like to inherit security label of + * the template database object, however, we cannot see which was + * used to as a template. So, we need a facility to deliver an + * opaque datum from the prep-creation hook in the future fixups. + */ + object.classId = DatabaseRelationId; + object.objectId = TemplateDbOid; + object.objectSubId = 0; + tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG); + + ncontext = sepgsql_compute_create(scontext, tcontext, + SEPG_CLASS_DB_DATABASE); + + /* + * Assign the default security label on a new database + */ + object.classId = DatabaseRelationId; + object.objectId = databaseId; + object.objectSubId = 0; + + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); + + pfree(ncontext); + pfree(tcontext); +} + +/* + * sepgsql_database_relabel + * + * It checks privileges to relabel the supplied database + * by the `seclabel'. + */ +void +sepgsql_database_relabel(Oid databaseId, const char *seclabel) +{ + char *scontext = sepgsql_get_client_label(); + char *tcontext; + char *audit_name; + + audit_name = getObjectDescriptionOids(DatabaseRelationId, databaseId); + + /* + * check db_database:{setattr relabelfrom} permission + */ + tcontext = sepgsql_get_label(DatabaseRelationId, databaseId, 0); + + sepgsql_check_perms(scontext, + tcontext, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__SETATTR | + SEPG_DB_DATABASE__RELABELFROM, + audit_name, + true); + + /* + * check db_database:{relabelto} permission + */ + sepgsql_check_perms(scontext, + seclabel, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__RELABELTO, + audit_name, + true); + + pfree(tcontext); + pfree(audit_name); +} diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 7797ccb..ca0d5f4 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -12,6 +12,7 @@ #include "catalog/objectaccess.h" #include "catalog/pg_class.h" +#include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "commands/seclabel.h" @@ -125,6 +126,10 @@ sepgsql_object_access(ObjectAccessType access, case OAT_POST_CREATE: switch (classId) { + case DatabaseRelationId: + sepgsql_database_post_create(objectId); + break; + case NamespaceRelationId: sepgsql_schema_post_create(objectId); break; diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c index 669ee35..c0503f7 100644 --- a/contrib/sepgsql/label.c +++ b/contrib/sepgsql/label.c @@ -17,6 +17,7 @@ #include "catalog/indexing.h" #include "catalog/pg_attribute.h" #include "catalog/pg_class.h" +#include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "commands/dbcommands.h" @@ -121,6 +122,9 @@ sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel) */ switch (object->classId) { + case DatabaseRelationId: + sepgsql_database_relabel(object->objectId, seclabel); + break; case NamespaceRelationId: sepgsql_schema_relabel(object->objectId, seclabel); break; @@ -315,6 +319,7 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId) SnapshotNow, 0, NULL); while (HeapTupleIsValid(tuple = systable_getnext(sscan))) { + Form_pg_database datForm; Form_pg_namespace nspForm; Form_pg_class relForm; Form_pg_attribute attForm; @@ -330,6 +335,19 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId) */ switch (catalogId) { + case DatabaseRelationId: + datForm = (Form_pg_database) GETSTRUCT(tuple); + + objtype = SELABEL_DB_DATABASE; + + objname = quote_object_name(NameStr(datForm->datname), + NULL, NULL, NULL); + + object.classId = DatabaseRelationId; + object.objectId = HeapTupleGetOid(tuple); + object.objectSubId = 0; + break; + case NamespaceRelationId: nspForm = (Form_pg_namespace) GETSTRUCT(tuple); @@ -506,10 +524,7 @@ sepgsql_restorecon(PG_FUNCTION_ARGS) errmsg("SELinux: failed to initialize labeling handle: %m"))); PG_TRY(); { - /* - * Right now, we have no support labeling on the shared database - * objects, such as database, role, or tablespace. - */ + exec_object_restorecon(sehnd, DatabaseRelationId); exec_object_restorecon(sehnd, NamespaceRelationId); exec_object_restorecon(sehnd, RelationRelationId); exec_object_restorecon(sehnd, AttributeRelationId); diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c index 0de8997..131a4f6 100644 --- a/contrib/sepgsql/schema.c +++ b/contrib/sepgsql/schema.c @@ -11,8 +11,10 @@ #include "postgres.h" #include "catalog/dependency.h" +#include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "commands/seclabel.h" +#include "miscadmin.h" #include "utils/lsyscache.h" #include "sepgsql.h" @@ -32,16 +34,15 @@ sepgsql_schema_post_create(Oid namespaceId) ObjectAddress object; /* - * FIXME: Right now, we assume pg_database object has a fixed security - * label, because pg_seclabel does not support to store label of shared - * database objects. + * Compute a default security label when we create a new schema + * object under the working database. */ - tcontext = "system_u:object_r:sepgsql_db_t:s0"; + object.classId = DatabaseRelationId; + object.objectId = MyDatabaseId; + object.objectSubId = 0; + + tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG); - /* - * Compute a default security label when we create a new schema object - * under the working database. - */ ncontext = sepgsql_compute_create(scontext, tcontext, SEPG_CLASS_DB_SCHEMA); @@ -54,6 +55,7 @@ sepgsql_schema_post_create(Oid namespaceId) SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext); pfree(ncontext); + pfree(tcontext); } /* diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index 71688ab..5045ddd 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -267,6 +267,12 @@ extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS); extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort); /* + * database.c + */ +extern void sepgsql_database_post_create(Oid databaseId); +extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel); + +/* * schema.c */ extern void sepgsql_schema_post_create(Oid namespaceId); diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 7b62818..707fad8 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -239,6 +239,11 @@ + pg_shseclabel + security labels on shared database objects + + + pg_statistic planner statistics @@ -4672,6 +4677,13 @@ statement. + + See also pg_shseclabel, + which performs a similar function for security labels of database objects that + are shared across a database cluster. + + + <structname>pg_seclabel</structname> Columns @@ -4950,6 +4962,59 @@ + + <structname>pg_shseclabel</structname> + + + pg_shseclabel + + + + The catalog pg_shseclabel stores security + labels on shared database objects across a database cluster. + See the statement. + + +
+ <structname>pg_shseclabel</structname> Columns + + + + Name + Type + References + Description + + + + + objoid + oid + any OID column + The OID of the object this security label pertains to + + + classoid + oid + pg_class.oid + The OID of the system catalog this object appears in + + + provider + text + + The label provider associated with this label. + + + label + text + + The security label applied to this object. + + + +
+ <structname>pg_statistic</structname> diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml index 8a01b94..78e3f45 100644 --- a/doc/src/sgml/ref/security_label.sgml +++ b/doc/src/sgml/ref/security_label.sgml @@ -26,13 +26,16 @@ SECURITY LABEL [ FOR provider ] ON TABLE object_name | COLUMN table_name.column_name | AGGREGATE agg_name (agg_type [, ...] ) | + DATABASE object_name | DOMAIN object_name | FOREIGN TABLE object_name FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | LARGE OBJECT large_object_oid | [ PROCEDURAL ] LANGUAGE object_name | + ROLE object_name | SCHEMA object_name | SEQUENCE object_name | + TABLESPACE object_name | TYPE object_name | VIEW object_name } IS 'label' diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 3a83461..7e0b7d6 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_table.h \ - pg_default_acl.h pg_seclabel.h pg_collation.h \ + pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h \ toasting.h indexing.h \ ) diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index cbce007..a8995de 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -34,6 +34,7 @@ #include "catalog/pg_db_role_setting.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" +#include "catalog/pg_shseclabel.h" #include "catalog/pg_tablespace.h" #include "catalog/toasting.h" #include "miscadmin.h" @@ -380,6 +381,7 @@ IsSharedRelation(Oid relationId) relationId == PLTemplateRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || + relationId == SharedSecLabelRelationId || relationId == TableSpaceRelationId || relationId == DbRoleSettingRelationId) return true; @@ -394,6 +396,7 @@ IsSharedRelation(Oid relationId) relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || + relationId == SharedSecLabelObjectIndexId || relationId == TablespaceOidIndexId || relationId == TablespaceNameIndexId || relationId == DbRoleSettingDatidRolidIndexId) @@ -403,6 +406,8 @@ IsSharedRelation(Oid relationId) relationId == PgDatabaseToastIndex || relationId == PgShdescriptionToastTable || relationId == PgShdescriptionToastIndex || + relationId == PgShSecLabelToastTable || + relationId == PgShSecLabelToastIndex || relationId == PgDbRoleSettingToastTable || relationId == PgDbRoleSettingToastIndex) return true; diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 325d452..2253ca8 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -283,7 +283,37 @@ FROM pg_seclabel l JOIN pg_namespace nsp ON l.classoid = nsp.tableoid AND l.objoid = nsp.oid WHERE - l.objsubid = 0; + l.objsubid = 0 +UNION ALL +SELECT + l.objoid, l.classoid, 0::int4 AS objsubid, + 'database'::text AS objtype, + NULL::oid AS objnamespace, + quote_ident(dat.datname) AS objname, + l.provider, l.label +FROM + pg_shseclabel l + JOIN pg_database dat ON l.classoid = dat.tableoid AND l.objoid = dat.oid +UNION ALL +SELECT + l.objoid, l.classoid, 0::int4 AS objsubid, + 'tablespace'::text AS objtype, + NULL::oid AS objnamespace, + quote_ident(spc.spcname) AS objname, + l.provider, l.label +FROM + pg_shseclabel l + JOIN pg_tablespace spc ON l.classoid = spc.tableoid AND l.objoid = spc.oid +UNION ALL +SELECT + l.objoid, l.classoid, 0::int4 AS objsubid, + 'role'::text AS objtype, + NULL::oid AS objnamespace, + quote_ident(rol.rolname) AS objname, + l.provider, l.label +FROM + pg_shseclabel l + JOIN pg_authid rol ON l.classoid = rol.tableoid AND l.objoid = rol.oid; CREATE VIEW pg_settings AS SELECT * FROM pg_show_all_settings() AS A; diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 7afb713..63d2818 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -16,6 +16,7 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_seclabel.h" +#include "catalog/pg_shseclabel.h" #include "commands/seclabel.h" #include "miscadmin.h" #include "utils/acl.h" @@ -134,6 +135,54 @@ ExecSecLabelStmt(SecLabelStmt *stmt) } /* + * GetSharedSecurityLabel is a helper function of GetSecurityLabel to + * handle shared database objects. + */ +static char * +GetSharedSecurityLabel(const ObjectAddress *object, const char *provider) +{ + Relation pg_shseclabel; + ScanKeyData keys[3]; + SysScanDesc scan; + HeapTuple tuple; + Datum datum; + bool isnull; + char *seclabel = NULL; + + ScanKeyInit(&keys[0], + Anum_pg_shseclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + ScanKeyInit(&keys[1], + Anum_pg_shseclabel_classoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + ScanKeyInit(&keys[2], + Anum_pg_shseclabel_provider, + BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(provider)); + + pg_shseclabel = heap_open(SharedSecLabelRelationId, AccessShareLock); + + scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true, + SnapshotNow, 3, keys); + + tuple = systable_getnext(scan); + if (HeapTupleIsValid(tuple)) + { + datum = heap_getattr(tuple, Anum_pg_shseclabel_label, + RelationGetDescr(pg_shseclabel), &isnull); + if (!isnull) + seclabel = TextDatumGetCString(datum); + } + systable_endscan(scan); + + heap_close(pg_shseclabel, AccessShareLock); + + return seclabel; +} + +/* * GetSecurityLabel returns the security label for a database object for a * given provider, or NULL if there is no such label. */ @@ -148,7 +197,9 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider) bool isnull; char *seclabel = NULL; - Assert(!IsSharedRelation(object->classId)); + /* Special handling for shared database objects */ + if (IsSharedRelation(object->classId)) + return GetSharedSecurityLabel(object, provider); ScanKeyInit(&keys[0], Anum_pg_seclabel_objoid, @@ -187,6 +238,84 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider) return seclabel; } +/* + * SetSharedSecurityLabel is a helper function of SetSecurityLabel to + * handle shared database objects. + */ +static void +SetSharedSecurityLabel(const ObjectAddress *object, + const char *provider, const char *label) +{ + Relation pg_shseclabel; + ScanKeyData keys[4]; + SysScanDesc scan; + HeapTuple oldtup; + HeapTuple newtup = NULL; + Datum values[Natts_pg_shseclabel]; + bool nulls[Natts_pg_shseclabel]; + bool replaces[Natts_pg_shseclabel]; + + /* Prepare to form or update a tuple, if necessary. */ + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId); + values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId); + values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider); + if (label != NULL) + values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label); + + /* Use the index to search for a matching old tuple */ + ScanKeyInit(&keys[0], + Anum_pg_shseclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + ScanKeyInit(&keys[1], + Anum_pg_shseclabel_classoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + ScanKeyInit(&keys[2], + Anum_pg_shseclabel_provider, + BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(provider)); + + pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true, + SnapshotNow, 3, keys); + + oldtup = systable_getnext(scan); + if (HeapTupleIsValid(oldtup)) + { + if (label == NULL) + simple_heap_delete(pg_shseclabel, &oldtup->t_self); + else + { + replaces[Anum_pg_shseclabel_label - 1] = true; + newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel), + values, nulls, replaces); + simple_heap_update(pg_shseclabel, &oldtup->t_self, newtup); + } + } + systable_endscan(scan); + + /* If we didn't find an old tuple, insert a new one */ + if (newtup == NULL && label != NULL) + { + newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel), + values, nulls); + simple_heap_insert(pg_shseclabel, newtup); + } + + /* Update indexes, if necessary */ + if (newtup != NULL) + { + CatalogUpdateIndexes(pg_shseclabel, newtup); + heap_freetuple(newtup); + } + + heap_close(pg_shseclabel, RowExclusiveLock); +} + /* * SetSecurityLabel attempts to set the security label for the specified * provider on the specified object to the given value. NULL means that any @@ -205,8 +334,12 @@ SetSecurityLabel(const ObjectAddress *object, bool nulls[Natts_pg_seclabel]; bool replaces[Natts_pg_seclabel]; - /* Security labels on shared objects are not supported. */ - Assert(!IsSharedRelation(object->classId)); + /* Special handling for security labels on shared objects */ + if (IsSharedRelation(object->classId)) + { + SetSharedSecurityLabel(object, provider, label); + return; + } /* Prepare to form or update a tuple, if necessary. */ memset(nulls, false, sizeof(nulls)); @@ -275,6 +408,38 @@ SetSecurityLabel(const ObjectAddress *object, } /* + * DeleteSharedSecurityLabel is a helper function of DeleteSecurityLabel + * to handle shared database objects. + */ +static void +DeleteSharedSecurityLabel(const ObjectAddress *object) +{ + Relation pg_shseclabel; + ScanKeyData skey[2]; + SysScanDesc scan; + HeapTuple oldtup; + + ScanKeyInit(&skey[0], + Anum_pg_shseclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + ScanKeyInit(&skey[1], + Anum_pg_shseclabel_classoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + + pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true, + SnapshotNow, 2, skey); + while (HeapTupleIsValid(oldtup = systable_getnext(scan))) + simple_heap_delete(pg_shseclabel, &oldtup->t_self); + systable_endscan(scan); + + heap_close(pg_shseclabel, RowExclusiveLock); +} + +/* * DeleteSecurityLabel removes all security labels for an object (and any * sub-objects, if applicable). */ @@ -287,9 +452,12 @@ DeleteSecurityLabel(const ObjectAddress *object) HeapTuple oldtup; int nkeys; - /* Security labels on shared objects are not supported. */ + /* Special handling for security labels on shared objects */ if (IsSharedRelation(object->classId)) + { + DeleteSharedSecurityLabel(object); return; + } ScanKeyInit(&skey[0], Anum_pg_seclabel_objoid, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd95961..6eac2c2 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -5073,11 +5073,14 @@ opt_provider: FOR ColId_or_Sconst { $$ = $2; } security_label_type: COLUMN { $$ = OBJECT_COLUMN; } + | DATABASE { $$ = OBJECT_DATABASE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | SCHEMA { $$ = OBJECT_SCHEMA; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | TABLE { $$ = OBJECT_TABLE; } | DOMAIN_P { $$ = OBJECT_TYPE; } + | ROLE { $$ = OBJECT_ROLE; } + | TABLESPACE { $$ = OBJECT_TABLESPACE; } | TYPE_P { $$ = OBJECT_TYPE; } | VIEW { $$ = OBJECT_VIEW; } ; diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 41a3307..62f6e91 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -52,6 +52,9 @@ static void dumpTimestamp(char *msg); static void doShellQuoting(PQExpBuffer buf, const char *str); static int runPgDump(const char *dbname); +static void buildShSecLabels(PGconn *conn, const char *objtype, + uint32 objectId, PQExpBuffer buffer, + const char *target, const char *objname); static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport, const char *pguser, enum trivalue prompt_password, bool fail_on_error); static PGresult *executeQuery(PGconn *conn, const char *query); @@ -718,15 +721,15 @@ dumpRoles(PGconn *conn) for (i = 0; i < PQntuples(res); i++) { const char *rolename; + Oid auth_oid; + auth_oid = atooid(PQgetvalue(res, i, i_oid)); rolename = PQgetvalue(res, i, i_rolname); resetPQExpBuffer(buf); if (binary_upgrade) { - Oid auth_oid = atooid(PQgetvalue(res, i, i_oid)); - appendPQExpBuffer(buf, "\n-- For binary upgrade, must preserve pg_authid.oid\n"); appendPQExpBuffer(buf, "SELECT binary_upgrade.set_next_pg_authid_oid('%u'::pg_catalog.oid);\n\n", @@ -796,6 +799,10 @@ dumpRoles(PGconn *conn) appendPQExpBuffer(buf, ";\n"); } + if (!no_security_label && server_version >= 90200) + buildShSecLabels(conn, "role", auth_oid, + buf, "ROLE", fmtId(rolename)); + fprintf(OPF, "%s", buf->data); if (server_version >= 70300) @@ -981,7 +988,7 @@ dumpTablespaces(PGconn *conn) * pg_xxx) */ if (server_version >= 90000) - res = executeQuery(conn, "SELECT spcname, " + res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "array_to_string(spcoptions, ', ')," @@ -990,7 +997,7 @@ dumpTablespaces(PGconn *conn) "WHERE spcname !~ '^pg_' " "ORDER BY 1"); else if (server_version >= 80200) - res = executeQuery(conn, "SELECT spcname, " + res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, null, " "pg_catalog.shobj_description(oid, 'pg_tablespace') " @@ -998,7 +1005,7 @@ dumpTablespaces(PGconn *conn) "WHERE spcname !~ '^pg_' " "ORDER BY 1"); else - res = executeQuery(conn, "SELECT spcname, " + res = executeQuery(conn, "SELECT oid, spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "null, null " @@ -1012,12 +1019,13 @@ dumpTablespaces(PGconn *conn) for (i = 0; i < PQntuples(res); i++) { PQExpBuffer buf = createPQExpBuffer(); - char *spcname = PQgetvalue(res, i, 0); - char *spcowner = PQgetvalue(res, i, 1); - char *spclocation = PQgetvalue(res, i, 2); - char *spcacl = PQgetvalue(res, i, 3); - char *spcoptions = PQgetvalue(res, i, 4); - char *spccomment = PQgetvalue(res, i, 5); + uint32 spcoid = atooid(PQgetvalue(res, i, 0)); + char *spcname = PQgetvalue(res, i, 1); + char *spcowner = PQgetvalue(res, i, 2); + char *spclocation = PQgetvalue(res, i, 3); + char *spcacl = PQgetvalue(res, i, 4); + char *spcoptions = PQgetvalue(res, i, 5); + char *spccomment = PQgetvalue(res, i, 6); char *fspcname; /* needed for buildACLCommands() */ @@ -1051,6 +1059,10 @@ dumpTablespaces(PGconn *conn) appendPQExpBuffer(buf, ";\n"); } + if (!no_security_label && server_version >= 90200) + buildShSecLabels(conn, "tablespace", spcoid, + buf, "TABLESPACE", fspcname); + fprintf(OPF, "%s", buf->data); free(fspcname); @@ -1178,7 +1190,7 @@ dumpCreateDB(PGconn *conn) /* Now collect all the information about databases to dump */ if (server_version >= 80400) res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datcollate, datctype, datfrozenxid, " @@ -1188,7 +1200,7 @@ dumpCreateDB(PGconn *conn) "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80100) res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " @@ -1198,7 +1210,7 @@ dumpCreateDB(PGconn *conn) "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80000) res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " @@ -1208,7 +1220,7 @@ dumpCreateDB(PGconn *conn) "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70300) res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " @@ -1218,7 +1230,7 @@ dumpCreateDB(PGconn *conn) "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70100) res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " @@ -1235,7 +1247,7 @@ dumpCreateDB(PGconn *conn) * with getting a NULL by not printing any OWNER clause. */ res = executeQuery(conn, - "SELECT datname, " + "SELECT d.oid, datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " @@ -1248,16 +1260,17 @@ dumpCreateDB(PGconn *conn) for (i = 0; i < PQntuples(res); i++) { - char *dbname = PQgetvalue(res, i, 0); - char *dbowner = PQgetvalue(res, i, 1); - char *dbencoding = PQgetvalue(res, i, 2); - char *dbcollate = PQgetvalue(res, i, 3); - char *dbctype = PQgetvalue(res, i, 4); - uint32 dbfrozenxid = atooid(PQgetvalue(res, i, 5)); - char *dbistemplate = PQgetvalue(res, i, 6); - char *dbacl = PQgetvalue(res, i, 7); - char *dbconnlimit = PQgetvalue(res, i, 8); - char *dbtablespace = PQgetvalue(res, i, 9); + uint32 dboid = atooid(PQgetvalue(res, i, 0)); + char *dbname = PQgetvalue(res, i, 1); + char *dbowner = PQgetvalue(res, i, 2); + char *dbencoding = PQgetvalue(res, i, 3); + char *dbcollate = PQgetvalue(res, i, 4); + char *dbctype = PQgetvalue(res, i, 5); + uint32 dbfrozenxid = atooid(PQgetvalue(res, i, 6)); + char *dbistemplate = PQgetvalue(res, i, 7); + char *dbacl = PQgetvalue(res, i, 8); + char *dbconnlimit = PQgetvalue(res, i, 9); + char *dbtablespace = PQgetvalue(res, i, 10); char *fdbname; fdbname = strdup(fmtId(dbname)); @@ -1344,6 +1357,10 @@ dumpCreateDB(PGconn *conn) exit(1); } + if (!no_security_label && server_version >= 90200) + buildShSecLabels(conn, "database", dboid, + buf, "DATABASE", fdbname); + fprintf(OPF, "%s", buf->data); if (server_version >= 70300) @@ -1615,6 +1632,43 @@ runPgDump(const char *dbname) return ret; } +/* + * buildShSecLabels + * + * Build SECURITY LABEL command(s) for an shared object + * + * The caller has to provide object type and identifier to select security + * labels from pg_seclabels system view. + */ +static void +buildShSecLabels(PGconn *conn, const char *objtype, uint32 objectId, + PQExpBuffer buffer, const char *target, const char *objname) +{ + PQExpBuffer sql = createPQExpBuffer(); + PGresult *res; + int i; + + appendPQExpBuffer(sql, + "SELECT provider, label FROM pg_seclabels " + "WHERE objtype = '%s' AND objoid = %u", + objtype, objectId); + + res = executeQuery(conn, sql->data); + + for (i = 0; i < PQntuples(res); i++) + { + char *provider = PQgetvalue(res, i, 0); + char *label = PQgetvalue(res, i, 1); + + appendPQExpBuffer(buffer, + "SECURITY LABEL FOR %s ON %s %s IS ", + fmtId(provider), target, objname); + appendStringLiteralConn(buffer, label, conn); + appendPQExpBuffer(buffer, ";\n"); + } + PQclear(res); + destroyPQExpBuffer(sql); +} /* * Make a database connection with the given parameters. An diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 4118e64..9a8e6ff 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -294,6 +294,9 @@ DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops)); #define SecLabelObjectIndexId 3597 +DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops, provider text_ops)); +#define SharedSecLabelObjectIndexId 3593 + DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops)); #define ExtensionOidIndexId 3080 diff --git a/src/include/catalog/pg_shseclabel.h b/src/include/catalog/pg_shseclabel.h new file mode 100644 index 0000000..d1b07da --- /dev/null +++ b/src/include/catalog/pg_shseclabel.h @@ -0,0 +1,41 @@ +/* ------------------------------------------------------------------------- + * + * pg_shseclabel.h + * definition of the system "security label" relation (pg_shseclabel) + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * ------------------------------------------------------------------------- + */ +#ifndef PG_SHSECLABEL_H +#define PG_SHSECLABEL_H + +#include "catalog/genbki.h" + +/* ---------------- + * pg_shseclabel definition. cpp turns this into + * typedef struct FormData_pg_shseclabel + * ---------------- + */ +#define SharedSecLabelRelationId 3592 + +CATALOG(pg_shseclabel,3592) BKI_SHARED_RELATION BKI_WITHOUT_OIDS +{ + Oid objoid; /* OID of the shared object itself */ + Oid classoid; /* OID of table containing the shared object */ + text provider; /* name of label provider */ + text label; /* security label of the object */ +} FormData_pg_shseclabel; + +/* ---------------- + * compiler constants for pg_shseclabel + * ---------------- + */ +#define Natts_pg_shseclabel 4 +#define Anum_pg_shseclabel_objoid 1 +#define Anum_pg_shseclabel_classoid 2 +#define Anum_pg_shseclabel_provider 3 +#define Anum_pg_shseclabel_label 4 + +#endif /* PG_SHSECLABEL_H */ diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index de3623ac..41cc0a1 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -56,6 +56,9 @@ DECLARE_TOAST(pg_database, 2844, 2845); DECLARE_TOAST(pg_shdescription, 2846, 2847); #define PgShdescriptionToastTable 2846 #define PgShdescriptionToastIndex 2847 +DECLARE_TOAST(pg_shseclabel, 3594, 3595); +#define PgShSecLabelToastTable 3594 +#define PgShSecLabelToastIndex 3595 DECLARE_TOAST(pg_db_role_setting, 2966, 2967); #define PgDbRoleSettingToastTable 2966 #define PgDbRoleSettingToastIndex 2967 diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 20cdc39..63253dc 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1276,8 +1276,8 @@ drop table cchild; -- Check that ruleutils are working -- SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; - viewname | definition ----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + viewname | definition +---------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, e.relocatable, e.schema, e.requires, e.comment FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion)))); pg_available_extensions | SELECT e.name, e.default_version, x.extversion AS installed_version, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); @@ -1289,7 +1289,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))); pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); - pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0); + pg_seclabels | ((((((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'database'::text AS objtype, NULL::oid AS objnamespace, quote_ident((dat.datname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_database dat ON (((l.classoid = dat.tableoid) AND (l.objoid = dat.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'tablespace'::text AS objtype, NULL::oid AS objnamespace, quote_ident((spc.spcname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_tablespace spc ON (((l.classoid = spc.tableoid) AND (l.objoid = spc.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'role'::text AS objtype, NULL::oid AS objnamespace, quote_ident((rol.rolname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid)))); pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_hostname, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_hostname, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid)); diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index ab9e891..d42b0ea 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -120,6 +120,7 @@ SELECT relname, relhasindex pg_seclabel | t pg_shdepend | t pg_shdescription | t + pg_shseclabel | t pg_statistic | t pg_tablespace | t pg_trigger | t @@ -157,7 +158,7 @@ SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f -(146 rows) +(147 rows) -- -- another sanity check: every system catalog that has OIDs should have diff --git a/src/test/regress/input/security_label.source b/src/test/regress/input/security_label.source index 810a721..e556d23 100644 --- a/src/test/regress/input/security_label.source +++ b/src/test/regress/input/security_label.source @@ -12,7 +12,7 @@ DROP TABLE IF EXISTS seclabel_tbl1; DROP TABLE IF EXISTS seclabel_tbl2; DROP TABLE IF EXISTS seclabel_tbl3; -CREATE USER seclabel_user1; +CREATE USER seclabel_user1 WITH CREATEROLE; CREATE USER seclabel_user2; CREATE TABLE seclabel_tbl1 (a int, b text); @@ -34,6 +34,11 @@ SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail +SECURITY LABEL ON ROLE seclabel_user1 IS 'classified'; -- fail +SECURITY LABEL FOR 'dummy' ON ROLE seclabel_user1 IS 'classified'; -- fail +SECURITY LABEL ON ROLE seclabel_user1 IS '...invalid label...'; -- fail +SECURITY LABEL ON ROLE seclabel_user3 IS 'unclassified'; -- fail + -- Load dummy external security provider LOAD '@libdir@/dummy_seclabel@DLSUFFIX@'; @@ -55,6 +60,26 @@ SET SESSION AUTHORIZATION seclabel_user2; SECURITY LABEL ON TABLE seclabel_tbl1 IS 'unclassified'; -- fail SECURITY LABEL ON TABLE seclabel_tbl2 IS 'classified'; -- OK +-- +-- Test for shared database object +-- +SET SESSION AUTHORIZATION seclabel_user1; + +SECURITY LABEL ON ROLE seclabel_user1 IS 'classified'; -- OK +SECURITY LABEL ON ROLE seclabel_user1 IS '...invalid label...'; -- fail +SECURITY LABEL FOR 'dummy' ON ROLE seclabel_user2 IS 'unclassified'; -- OK +SECURITY LABEL FOR 'unknown_seclabel' ON ROLE seclabel_user1 IS 'unclassified'; -- fail +SECURITY LABEL ON ROLE seclabel_user1 IS 'secret'; -- fail (not superuser) +SECURITY LABEL ON ROLE seclabel_user3 IS 'unclassified'; -- fail (not found) + +SET SESSION AUTHORIZATION seclabel_user2; +SECURITY LABEL ON ROLE seclabel_user2 IS 'unclassified'; -- fail (not privileged) + +RESET SESSION AUTHORIZATION; + +-- +-- Test for various types of object +-- RESET SESSION AUTHORIZATION; SECURITY LABEL ON TABLE seclabel_tbl1 IS 'top secret'; -- OK @@ -63,12 +88,17 @@ SECURITY LABEL ON FUNCTION seclabel_four() IS 'classified'; -- OK SECURITY LABEL ON DOMAIN seclabel_domain IS 'classified'; -- OK SECURITY LABEL ON LANGUAGE plpgsql IS 'unclassified'; -- OK SECURITY LABEL ON SCHEMA public IS 'unclassified'; -- OK +SECURITY LABEL ON DATABASE postgres IS 'secret'; -- OK +SECURITY LABEL ON TABLESPACE pg_default IS 'classified'; -- OK SELECT objtype, objname, provider, label FROM pg_seclabels ORDER BY objtype, objname; +-- clean up labels SECURITY LABEL ON LANGUAGE plpgsql IS NULL; -- OK SECURITY LABEL ON SCHEMA public IS NULL; -- OK +SECURITY LABEL ON DATABASE postgres IS NULL; -- OK +SECURITY LABEL ON TABLESPACE pg_default IS NULL; -- OK -- clean up objects DROP FUNCTION seclabel_four();