*** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 209,214 **** --- 209,219 ---- + pg_seclabel + security labels on local database objects + + + pg_shdepend dependencies on shared objects *************** *** 4227,4232 **** --- 4232,4322 ---- + + <structname>pg_seclabel</structname> + + + pg_seclabel + + + + The catalog pg_seclabel stores the security + label of database objects. This information allows external security + providers to apply label based mandatory access controls. + + + + When external security providers with label based mandatory access + control are installed, its security label shall be assigned on + creation of database obejcts. + (Please note that only relations are supported right now.) + + + The security labels shall be automatically cleaned up when the database + object being labeled is dropped. + + + See also SECURITY LABEL, + which provides a functionality to relabel a certain database object, + as long as user has enough privileges. + + + + <structname>pg_seclabel</structname> Columns + + + + + Name + Type + References + Description + + + + + + reloid + oid + pg_class.oid + The OID of the system catalog this object appears in + + + + objoid + oid + any OID column + The OID of the object this security label assigned on + + + + subid + int4 + + For a security label on a table column, this is the column number + (the objoid and classoid + refer to the table itself). For all other object types, this column is + zero + + + + + tag + text + + identifier of external security provider + + + + label + text + + security label in text format + + + +
+
<structname>pg_shdepend</structname> *** /dev/null --- b/doc/src/sgml/esp.sgml *************** *** 0 **** --- 1,249 ---- + + External Security Provider + + External security provider (ESP) is a plugin which makes access control + decision based on its security model and policy. + + + PostgreSQL version 9.1 or later provides a set of + facilities to enhance access control features using ESP, in addition to the + default database privilege mechanism introduced + as the . + + + This chapter introduces brief overview of ESP concept and corresponding + features, but it does not introduce details of individual ESP plugins. + If you need to know something about a certain ESP plugin, please reference + documentation of the plugin. + + + + Overview + + The ESP concept was designed to integrate various kind of security + models into PostgreSQL. + + It allows third-party modules to perform as security provider which + makes access control decision on accesses to database objects; + such as tables, functions and so on. + + Please note that it never replaces anything in the default database + privilege mechanism. It just installs additional access control stuffs. + + + Access control is a mechanism that decides what actions are allowed + or denied on a pair of certain user and object to be referenced, + then applies the decision on any requests come from clients. + + + The default database privilege mechanism makes its access control + decision based on the authenticated user identifier and permissions + of database objects to be referenced. + + + Here are some cases that we hope to make and apply access control + decisions based on a different basis from the default one. + + For example, one significant goal of ESP is integration of mandatory + access control that only allows security administrator to set up + access rights of database objects, unlike the default one which + allows owner of them to set up their access rights. + + + At the implementation level, PostgreSQL 9.1 + or later provides a series of security hooks which are put on strategic + points of core routines; such as a routine that checks permissions of + tables and columns for the supplied DML statement. + + + ESP modules shall be invoked via the security hooks, then it can make + its access control decision based on the security model and supplied + information about user's request; such as OID of tables to be referenced. + + + Expected behaviors are depending on every security hooks. + + One hook may assume ESP raises an error, if access control violation. + One other hook may assume ESP returns a bool value which informs the + caller routine whether its decision was 'allowed' or 'denied'. + + + These hooks shall be filled up when we load plugins. + + Administrator can add a configuration of + shared_preload_libraries on which we specify a few + plugins to be loaded on starting up the server process, as follows: + + + shared_preload_libraries = 'sepgsql' # (change requires restart) + + + You should never use local_preload_libraries instead, + even if the plugin is well verified without any vulnerabilities, because + it can be overridden by GUC variable settings passed in startup packet. + + + Please note that ESP framework has quite-limited coverage of access control + compared to the default database privilege mechanism right now. + In this release, it only hooks on execution of DML, a part of DDL and + post client authentication process. + + + + + Mandatory Access Control + + It is a major of ESP to support mandatory access control (MAC) policy + in PostgreSQL. + + + Unlike discretionary access control (DAC), only a security administrator + is allowed to control centralized MAC policy and access rights on + individual objects; it means any other users don't have ability to change + the access rights on database objects, even if they have ownership of the + object. + + + This characteristic enables to confine classified information in certain + domains, even if owner of data tries to leak it somewhere, because the + owner cannot change its access rights as he like. + + + Here is a a traditional rule for instance. It prevents people who can + read both of classified and unclassified data object to write unclassified + data object. It means infomation moves only single direction; from + unclassified to classified. Since this rule is applied to everybody, + noboby can leak classified information into unclassified ones, even if + a malicious internal tries to do. + + + MAC is designed to tackle such a strict security requirement. + + + + Security Label + + In the default database privileges, GRANT and + REVOKE commands allow superusers and owner of the + database obejcts to set up access permissions, then it makes access + control decision based on these properties. + + + On the other hand, MAC model also has a property that characterizes + database objects from the viewpoint of MAC. A typical MAC feature uses + security label to identify the objects. It is a short text with a format + according to the security model. + + + The following examples are security label in SELinux. + It contains all the needed properties to make access control decision + from the viewpoint of SELinux, like a pair of access permissions and + owner identifier of the database object. + + dbadm_u:system_r:postgresql_t:s0 + + system_u:object_r:sepgsql_table_t:s0:c0 + + + + It also means MAC model cannot make its access control decision on accesses + to database objects without security labels. So, we need to assign a certain + security label on database objects to be referenced. + + + In addition, MAC model also represents privilege set of user as a security + label, instead of user identifier. Mostly, security label of the users are + assigned according to the result of authentication. + + + In other words, MAC mechanism is something like a function which returns + a binary state ('allowed' or 'denied') for the supplied actions and + security labels of user and object to be referenced. + + + DAC mechanism (ie; the default database privilege mechanism) is also like + a function which returns a binary state for the supplied actions, user + identifier and permissions on the object to be referenced, so they are not + fundamentally difference. + + + + Management of security label + + As we introduced above, MAC mechanism needs both of user and object to be + labeled, to make its access control decision. + + + For database objects, PostgreSQL provides + a facility that enables to assign a security label on them. + + When an ESP plugin of MAC is installed, it shall assign a default security + label of a new database object on its creation time. + + In addition, user can relabel security label of database objects using + command later, as long as this + operation is allowed by security mechanisms. + + + The following example creates a new table tbl on the + system with SELinux support using ESP. + + postgres=> CREATE TABLE tbl (x int, y text); + CREATE TABLE + postgres=> SELECT * FROM pg_seclabel WHERE objoid = 'tbl'::regclass and subid=0; + reloid | objoid | subid | tag | label + --------+--------+-------+---------+-------------------------------------- + 1259 | 24600 | 0 | selinux | system_u:object_r:sepgsql_table_t:s0 + (1 row) + + It shows us that system_u:object_r:sepgsql_table_t:s0 + was assigned on the creation time of table tbl. + + postgres=> SECURITY LABEL ON TABLE tbl IS 'system_u:object_r:sepgsql_ro_table_t:s0'; + SECURITY LABEL + postgres=> SELECT * FROM pg_seclabel WHERE objoid = 'tbl'::regclass and subid=0; + reloid | objoid | subid | tag | label + --------+--------+-------+---------+----------------------------------------- + 1259 | 24600 | 0 | selinux | system_u:object_r:sepgsql_ro_table_t:s0 + (1 row) + + It also shows us SECURITY LABEL command allows to relabel + the table to system_u:object_r:sepgsql_ro_table_t:s0. + + + The + + pg_seclabel + + is a system catalog to store + security labels, and ESP plugins shall reference the catalog to fetch + them on making its access control decisions. + + + On the other hand, the way to assign a security label of user is depending + on the type of ESP plugin. + + Perhaps, one plugin may associates a security label with authenticated + database user, and other plugin assigns a security label which is retrieved + from the peer process using system API. + + So, PostgreSQL does not constrain ESP plugins + a certain way to retrive security label of users. + It provides a hook just after the database authentication, ESP plugin can + assign a security label on the current session with its own way. + + + When we backup and restore databases, security labels are also properties + of database objects to be handled correctly. + + The pg_dump and pg_dumpall support + --security-label option that enables to include + security label of the database objects into archives. + + And the pg_restore also supports + --no-security-label option that enables to ignore + security labels, even if the archives contains labels. + + + + *** a/doc/src/sgml/external-projects.sgml --- b/doc/src/sgml/external-projects.sgml *************** *** 250,254 **** --- 250,266 ---- pgAdmin III, and there are several commercially available ones as well. + + + The introduces how PostgreSQL + integrate additional security features using external security + providers. + + + SE-PostgreSQL + is an external security provider which + applies mandatory access control based on the centralized + security policy of operation system. + *** a/doc/src/sgml/filelist.sgml --- b/doc/src/sgml/filelist.sgml *************** *** 46,51 **** --- 46,52 ---- + *** a/doc/src/sgml/postgres.sgml --- b/doc/src/sgml/postgres.sgml *************** *** 150,155 **** --- 150,156 ---- &config; &client-auth; &user-manag; + &esp; &manage-ag; &charset; &maintenance; *** a/doc/src/sgml/ref/allfiles.sgml --- b/doc/src/sgml/ref/allfiles.sgml *************** *** 132,137 **** Complete list of usable sgml source files in this directory. --- 132,138 ---- + *** a/doc/src/sgml/ref/pg_dump.sgml --- b/doc/src/sgml/ref/pg_dump.sgml *************** *** 778,783 **** PostgreSQL documentation --- 778,793 ---- + + + + + + With this option, it also outputs security labels of database + objects to be dumped, if labeled. + + + *** a/doc/src/sgml/ref/pg_dumpall.sgml --- b/doc/src/sgml/ref/pg_dumpall.sgml *************** *** 493,498 **** PostgreSQL documentation --- 493,507 ---- + + + + + With this option, it also outputs security labels of database + objects to be dumped, if labeled. + + + *** a/doc/src/sgml/ref/pg_restore.sgml --- b/doc/src/sgml/ref/pg_restore.sgml *************** *** 329,334 **** --- 329,347 ---- + + + + Do not output commands to relabel database objects, + even if the archive contains them. + With this option, all objects will be created with a default + security label, or without any labels if no label based + security feature is not installed. + + + + + *** /dev/null --- b/doc/src/sgml/ref/seclabel.sgml *************** *** 0 **** --- 1,123 ---- + + + SECURITY LABEL + 7 + SQL - Language Statements + + + + SECURITY LABEL + relabel the security label of an object + + + + SECURITY LABEL + + + + + SECURITY LABEL [ FOR 'esp_tag' ] ON + { + TABLE object_name | + COLUMN table_name.column_name | + SEQUENCE object_name | + VIEW object_name + } IS 'security_label'; + + + + + Description + + + SECURITY LABEL relabels a security label of a database object. + + + + We can assign individual security labels on a certain database object for + each security providers, using SECURITY LABEL command. + + + Security labels are automatically assigned when the object is created, + and also automatically dropped when the object is dropped. + + + + + + Parameters + + + object_name + table_name + table_name.column_name + + + The name of the object to be relabeled. Names of tables, columns, + sequences and views can be schema-qualified. + + + + + + security_label + + + The new security label as a string literal. + + + It shall be validated by one of the label based security features, + in addition to its permission checks relabeling on the specified + database objects. + + + + + + esp_tag + + + The identifier string of external security provider. + + + When we install just one provider, we can omit this clause because + it is obvious which provider shall handle the given security label. + Elsewhere, when we install two or more providers concurrently, + we need to identify a certain external security provider. + + + + + + + + Notes + + This feature requires one label based mandatory access control feature + to be installed at least, because a security label is specific for + each providers, so it has to be validated when we relabel it. + + + + + Examples + + + Relabel a security label on the table to 'system_u:object_r:sepgsql_table_t:s0'. + + + SECURITY LABEL FOR 'selinux' ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0'; + + + + + + Compatibility + + There is no SECURITY LABEL command in the SQL standard. + + + + + + *** a/doc/src/sgml/reference.sgml --- b/doc/src/sgml/reference.sgml *************** *** 160,165 **** --- 160,166 ---- &rollbackPrepared; &rollbackTo; &savepoint; + &securityLabel; &select; &selectInto; &set; *** a/src/backend/catalog/Makefile --- b/src/backend/catalog/Makefile *************** *** 38,44 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ ! pg_default_acl.h \ toasting.h indexing.h \ ) --- 38,44 ---- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ ! pg_default_acl.h pg_seclabel.h \ toasting.h indexing.h \ ) *** a/src/backend/catalog/dependency.c --- b/src/backend/catalog/dependency.c *************** *** 57,62 **** --- 57,63 ---- #include "commands/defrem.h" #include "commands/proclang.h" #include "commands/schemacmds.h" + #include "commands/seclabel.h" #include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/typecmds.h" *************** *** 1010,1015 **** deleteOneObject(const ObjectAddress *object, Relation depRel) --- 1011,1023 ---- DeleteComments(object->objectId, object->classId, object->objectSubId); /* + * Delete any security labels associated with this object. (This is also + * a convenient place to do it instead of having every object type know + * to do it.) + */ + DeleteSecurityLabel(object); + + /* * CommandCounterIncrement here to ensure that preceding changes are all * visible to the next deletion step. */ *** a/src/backend/commands/Makefile --- b/src/backend/commands/Makefile *************** *** 17,23 **** OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ portalcmds.o prepare.o proclang.o \ ! schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \ tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \ variable.o view.o --- 17,23 ---- dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ portalcmds.o prepare.o proclang.o \ ! schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \ variable.o view.o *** /dev/null --- b/src/backend/commands/seclabel.c *************** *** 0 **** --- 1,441 ---- + /* ------------------------------------------------------------------------- + * + * seclabel.c + * routines to support security label feature. + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * ------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/genam.h" + #include "access/heapam.h" + #include "catalog/catalog.h" + #include "catalog/indexing.h" + #include "catalog/pg_seclabel.h" + #include "commands/seclabel.h" + #include "miscadmin.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/tqual.h" + + /* + * GetSecurityLabel + * + * It tries to look up a security label entry for the given OID of + * the catalog, object itself and sub identifier (if needed) from + * the pg_selabel system catalog. + * It can return NULL, if no valid entry. Elsewhere, it returns + * a security label of the specified database object. + */ + char * + GetSecurityLabel(const ObjectAddress *object, const char *tag) + { + Relation pg_seclabel; + ScanKeyData keys[4]; + SysScanDesc scan; + HeapTuple tuple; + Datum datum; + bool isnull; + char *seclabel = NULL; + + Assert(!IsSharedRelation(object->classId)); + + ScanKeyInit(&keys[0], + Anum_pg_seclabel_reloid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + ScanKeyInit(&keys[1], + Anum_pg_seclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + ScanKeyInit(&keys[2], + Anum_pg_seclabel_subid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(object->objectSubId)); + ScanKeyInit(&keys[3], + Anum_pg_seclabel_tag, + BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(tag)); + + pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock); + + scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, + SnapshotNow, 4, keys); + + tuple = systable_getnext(scan); + if (HeapTupleIsValid(tuple)) + { + datum = heap_getattr(tuple, Anum_pg_seclabel_label, + RelationGetDescr(pg_seclabel), &isnull); + if (!isnull) + seclabel = TextDatumGetCString(datum); + } + systable_endscan(scan); + + heap_close(pg_seclabel, AccessShareLock); + + return seclabel; + } + + /* + * SetSecurityLabel + * + * It tries to insert/update/delete a security label for the given OID + * of the catalog, object itself and sub identifier (if needed) on the + * pg_seclabel system catalog. + * If given 'seclabel' is NULL, it tries to delete the specified entry. + * Elsewhere, it tries to insert (if no specified entry now) or updata + * security label of the specified entry. + */ + void + SetSecurityLabel(const ObjectAddress *object, + const char *tag, const char *seclabel) + { + Relation pg_seclabel; + ScanKeyData keys[4]; + SysScanDesc scan; + HeapTuple oldtup; + HeapTuple newtup = NULL; + Datum values[Natts_pg_seclabel]; + bool nulls[Natts_pg_seclabel]; + bool replaces[Natts_pg_seclabel]; + + Assert(!IsSharedRelation(object->classId)); + + ScanKeyInit(&keys[0], + Anum_pg_seclabel_reloid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + ScanKeyInit(&keys[1], + Anum_pg_seclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + ScanKeyInit(&keys[2], + Anum_pg_seclabel_subid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(object->objectSubId)); + ScanKeyInit(&keys[3], + Anum_pg_seclabel_tag, + BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(tag)); + + pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, + SnapshotNow, 4, keys); + + oldtup = systable_getnext(scan); + if (HeapTupleIsValid(oldtup)) + { + if (seclabel != NULL) + { + /* + * update the specified security label entry + */ + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + + replaces[Anum_pg_seclabel_label - 1] = true; + values[Anum_pg_seclabel_label - 1] + = CStringGetTextDatum(seclabel); + + newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel), + values, nulls, replaces); + simple_heap_update(pg_seclabel, &oldtup->t_self, newtup); + + CatalogUpdateIndexes(pg_seclabel, newtup); + + heap_freetuple(newtup); + } + else + { + /* + * when seclabel = NULL, it means to remove the matched + * entry from pg_seclabel. + */ + simple_heap_delete(pg_seclabel, &oldtup->t_self); + } + } + else if (seclabel != NULL) + { + /* + * insert a new security label entry + */ + memset(nulls, false, sizeof(nulls)); + values[Anum_pg_seclabel_reloid - 1] = ObjectIdGetDatum(object->classId); + values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId); + values[Anum_pg_seclabel_subid - 1] = Int32GetDatum(object->objectSubId); + values[Anum_pg_seclabel_tag - 1] = CStringGetTextDatum(tag); + values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel); + + newtup = heap_form_tuple(RelationGetDescr(pg_seclabel), + values, nulls); + simple_heap_insert(pg_seclabel, newtup); + + CatalogUpdateIndexes(pg_seclabel, newtup); + + heap_freetuple(newtup); + } + systable_endscan(scan); + + heap_close(pg_seclabel, RowExclusiveLock); + } + + /* + * DeleteSecurityLabel + * + * It tries to delete entries of security labels for given OID of + * the catalog, object itself and sub identifier (if needed) on + * the pg_seclabel system catalog. + * If given 'objectSubId' is 0, all the security labels matching + * with classId and objectId will be removed. + */ + void + DeleteSecurityLabel(const ObjectAddress *object) + { + Relation pg_seclabel; + ScanKeyData keys[3]; + SysScanDesc scan; + HeapTuple oldtup; + int nkeys = 2; + + /* + * right now, we have obviously nothing to do on shared database + * object deletion + */ + if (IsSharedRelation(object->classId)) + return; + + ScanKeyInit(&keys[0], + Anum_pg_seclabel_reloid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + ScanKeyInit(&keys[1], + Anum_pg_seclabel_objoid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + if (object->objectSubId != 0) + { + ScanKeyInit(&keys[2], + Anum_pg_seclabel_subid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(object->objectSubId)); + nkeys = 3; + } + + pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, + SnapshotNow, nkeys, keys); + while (HeapTupleIsValid(oldtup = systable_getnext(scan))) + { + simple_heap_delete(pg_seclabel, &oldtup->t_self); + } + systable_endscan(scan); + + heap_close(pg_seclabel, RowExclusiveLock); + } + + /* + * check_relation_relabel + * + * It checks whether the user is allowed to relabel this relation + */ + static void + check_relation_relabel(int objtype, Relation relation) + { + /* + * check object ownership + */ + if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + + /* + * next, verify that the relation type matches the intent + */ + switch (objtype) + { + case OBJECT_TABLE: + case OBJECT_COLUMN: + if (relation->rd_rel->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(relation)))); + break; + + case OBJECT_SEQUENCE: + if (relation->rd_rel->relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + RelationGetRelationName(relation)))); + break; + + case OBJECT_VIEW: + if (relation->rd_rel->relkind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a view", + RelationGetRelationName(relation)))); + break; + } + } + + /* + * Registration of an external security provider hook + */ + static List *esp_relabel_hook_list = NIL; + + typedef struct + { + const char *tag; + check_object_relabel_type hook; + } esp_relabel_hook_entry; + + void + register_object_relabel_hook(const char *tag, check_object_relabel_type hook) + { + esp_relabel_hook_entry *entry; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + entry = palloc(sizeof(esp_relabel_hook_entry)); + entry->tag = pstrdup(tag); + entry->hook = hook; + + esp_relabel_hook_list = lappend(esp_relabel_hook_list, entry); + + MemoryContextSwitchTo(oldcxt); + } + + /* + * ExecSecLabelStmt + * + * SECURITY LABEL [FOR ] ON IS + */ + void + ExecSecLabelStmt(SecLabelStmt *stmt) + { + esp_relabel_hook_entry *entry; + ObjectAddress address; + Relation relation; + ListCell *cell; + const char *tag = NULL; + + /* + * SECURITY LABEL statement needs one label based security feature + * being available at least. + */ + if (list_length(esp_relabel_hook_list) == 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("No label based security is available now"))); + + /* + * Determine what ESP needs to own the supplied label + */ + if (!stmt->tag) + { + /* + * User can omit FOR clause, only when one ESP is + * installed, because it is obvious what ESP needs to own the + * supplied security label. + * But we prohibit to omit it when multiple ESPs are installed. + */ + if (list_length(esp_relabel_hook_list) > 1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("No FOR clause in spite of multiple " + "label based security features"))); + entry = linitial(esp_relabel_hook_list); + } + else + { + /* + * If user explicitly specified a security provider, + * it has to be installed of course. + */ + foreach (cell, esp_relabel_hook_list) + { + entry = lfirst(cell); + + if (strcmp(stmt->tag, entry->tag) == 0) + goto found; + } + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("No label based security feature for \"%s\"", + stmt->tag))); + } + found: + Assert(entry != NULL); + tag = entry->tag; + + /* + * Translate the parser representation which identifies this object + * into ObjectAddress. get_object_address() will throw an error if + * the object does not exist, and will also acquire a lock on the + * target to guard against concurrent DROP operations. + */ + address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, + &relation, ShareUpdateExclusiveLock); + + /* + * Privilege and integrity checks + */ + switch (stmt->objtype) + { + case OBJECT_SEQUENCE: + case OBJECT_TABLE: + case OBJECT_VIEW: + case OBJECT_COLUMN: + check_relation_relabel(stmt->objtype, relation); + break; + + default: + elog(ERROR, "unrecognized object type: %d", + (int)stmt->objtype); + } + + /* + * Privilege and validation checks by external security provider + */ + foreach (cell, esp_relabel_hook_list) + { + entry = lfirst(cell); + + Assert(entry->hook); + /* + * If the supplied FOR clause is not matched with + * this entry, we call the security hook without new security + * label. The ESP shall check the privilege to update one of + * properties of the supecified database object. + */ + if (strcmp(tag, entry->tag) == 0) + (*entry->hook)(&address, stmt->seclabel); + else + (*entry->hook)(&address, NULL); + } + + /* + * Do actual relabeling. + */ + SetSecurityLabel(&address, tag, stmt->seclabel); + + /* + * If get_object_address() opened the relation for us, we close it to keep + * the reference count correct - but we retain any locks acquired by + * get_object_address() until commit time, to guard against concurrent + * activity. + */ + if (relation != NULL) + relation_close(relation, NoLock); + } *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 2607,2612 **** _copyCommentStmt(CommentStmt *from) --- 2607,2626 ---- return newnode; } + static SecLabelStmt * + _copySecLabelStmt(SecLabelStmt *from) + { + SecLabelStmt *newnode = makeNode(SecLabelStmt); + + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(objname); + COPY_NODE_FIELD(objargs); + COPY_STRING_FIELD(tag); + COPY_STRING_FIELD(seclabel); + + return newnode; + } + static FetchStmt * _copyFetchStmt(FetchStmt *from) { *************** *** 3958,3963 **** copyObject(void *from) --- 3972,3980 ---- case T_CommentStmt: retval = _copyCommentStmt(from); break; + case T_SecLabelStmt: + retval = _copySecLabelStmt(from); + break; case T_FetchStmt: retval = _copyFetchStmt(from); break; *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 1164,1169 **** _equalCommentStmt(CommentStmt *a, CommentStmt *b) --- 1164,1181 ---- } static bool + _equalSecLabelStmt(SecLabelStmt *a, SecLabelStmt *b) + { + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(objname); + COMPARE_NODE_FIELD(objargs); + COMPARE_STRING_FIELD(tag); + COMPARE_STRING_FIELD(seclabel); + + return true; + } + + static bool _equalFetchStmt(FetchStmt *a, FetchStmt *b) { COMPARE_SCALAR_FIELD(direction); *************** *** 2624,2629 **** equal(void *a, void *b) --- 2636,2644 ---- case T_CommentStmt: retval = _equalCommentStmt(a, b); break; + case T_SecLabelStmt: + retval = _equalSecLabelStmt(a, b); + break; case T_FetchStmt: retval = _equalFetchStmt(a, b); break; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 204,210 **** static TypeName *TableFuncTypeName(List *columns); CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt RuleActionStmt RuleActionStmtOrEmpty RuleStmt ! SelectStmt TransactionStmt TruncateStmt UnlistenStmt UpdateStmt VacuumStmt VariableResetStmt VariableSetStmt VariableShowStmt ViewStmt CheckPointStmt CreateConversionStmt --- 204,210 ---- CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt RuleActionStmt RuleActionStmtOrEmpty RuleStmt ! SecLabelStmt SelectStmt TransactionStmt TruncateStmt UnlistenStmt UpdateStmt VacuumStmt VariableResetStmt VariableSetStmt VariableShowStmt ViewStmt CheckPointStmt CreateConversionStmt *************** *** 422,427 **** static TypeName *TableFuncTypeName(List *columns); --- 422,429 ---- %type OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option + %type label_item opt_esp + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone *************** *** 499,505 **** static TypeName *TableFuncTypeName(List *columns); KEY ! LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P --- 501,507 ---- KEY ! LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P *************** *** 737,742 **** stmt : --- 739,745 ---- | RevokeStmt | RevokeRoleStmt | RuleStmt + | SecLabelStmt | SelectStmt | TransactionStmt | TruncateStmt *************** *** 4327,4332 **** comment_text: --- 4330,4390 ---- | NULL_P { $$ = NULL; } ; + + /***************************************************************************** + * + * ALTER THING name SECURITY LABEL TO label + * + *****************************************************************************/ + + SecLabelStmt: SECURITY LABEL opt_esp ON TABLE any_name IS label_item + { + SecLabelStmt *n = makeNode(SecLabelStmt); + + n->objtype = OBJECT_TABLE; + n->objname = $6; + n->tag = $3; + n->seclabel = $8; + $$ = (Node *)n; + } + | SECURITY LABEL opt_esp ON COLUMN any_name IS label_item + { + SecLabelStmt *n = makeNode(SecLabelStmt); + + n->objtype = OBJECT_COLUMN; + n->objname = $6; + n->tag = $3; + n->seclabel = $8; + $$ = (Node *)n; + } + | SECURITY LABEL opt_esp ON SEQUENCE any_name IS label_item + { + SecLabelStmt *n = makeNode(SecLabelStmt); + + n->objtype = OBJECT_SEQUENCE; + n->objname = $6; + n->tag = $3; + n->seclabel = $8; + $$ = (Node *)n; + } + | SECURITY LABEL opt_esp ON VIEW any_name IS label_item + { + SecLabelStmt *n = makeNode(SecLabelStmt); + + n->objtype = OBJECT_VIEW; + n->objname = $6; + n->tag = $3; + n->seclabel = $8; + $$ = (Node *)n; + } + ; + + label_item: Sconst { $$ = $1; } + + opt_esp: FOR Sconst { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + /***************************************************************************** * * QUERY: *************** *** 10993,10998 **** unreserved_keyword: --- 11051,11057 ---- | INVOKER | ISOLATION | KEY + | LABEL | LANGUAGE | LARGE_P | LAST_P *** a/src/backend/tcop/utility.c --- b/src/backend/tcop/utility.c *************** *** 218,223 **** check_xact_readonly(Node *parsetree) --- 218,224 ---- case T_AlterUserMappingStmt: case T_DropUserMappingStmt: case T_AlterTableSpaceOptionsStmt: + case T_SecLabelStmt: PreventCommandIfReadOnly(CreateCommandTag(parsetree)); break; default: *************** *** 663,668 **** standard_ProcessUtility(Node *parsetree, --- 664,673 ---- CommentObject((CommentStmt *) parsetree); break; + case T_SecLabelStmt: + ExecSecLabelStmt((SecLabelStmt *) parsetree); + break; + case T_CopyStmt: { uint64 processed; *************** *** 1592,1597 **** CreateCommandTag(Node *parsetree) --- 1597,1606 ---- tag = "COMMENT"; break; + case T_SecLabelStmt: + tag = "SECURITY LABEL"; + break; + case T_CopyStmt: tag = "COPY"; break; *************** *** 2314,2319 **** GetCommandLogLevel(Node *parsetree) --- 2323,2332 ---- lev = LOGSTMT_DDL; break; + case T_SecLabelStmt: + lev = LOGSTMT_DDL; + break; + case T_CopyStmt: if (((CopyStmt *) parsetree)->is_from) lev = LOGSTMT_MOD; *** a/src/bin/pg_dump/pg_backup.h --- b/src/bin/pg_dump/pg_backup.h *************** *** 103,108 **** typedef struct _restoreOptions --- 103,109 ---- * restore */ int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands * instead of OWNER TO */ + int skip_seclabel; /* Skip security label entries */ char *superuser; /* Username to use as superuser */ char *use_role; /* Issue SET ROLE to this */ int dataOnly; *** a/src/bin/pg_dump/pg_backup_archiver.c --- b/src/bin/pg_dump/pg_backup_archiver.c *************** *** 2275,2280 **** _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) --- 2275,2284 ---- if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) return 0; + /* If it's security labels, maybe ignore it */ + if (ropt->skip_seclabel && strcmp(te->desc, "LABEL") == 0) + return 0; + /* Ignore DATABASE entry unless we should create it */ if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0) return 0; *************** *** 2341,2346 **** _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) --- 2345,2352 ---- (strcmp(te->desc, "ACL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || (strcmp(te->desc, "COMMENT") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || + (strcmp(te->desc, "LABEL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) res = res & REQ_DATA; else *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 125,130 **** static int binary_upgrade = 0; --- 125,131 ---- static int disable_dollar_quoting = 0; static int dump_inserts = 0; static int column_inserts = 0; + static int security_label = 0; static void help(const char *progname); *************** *** 183,188 **** static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, --- 184,194 ---- const char *tag, const char *nspname, const char *owner, const char *acls); + static void dumpSecLabel(Archive *fout, + CatalogId objCatId, DumpId objDumpId, + const char *type, const char *target, + const char *nspname, const char *owner); + static void getDependencies(void); static void getDomainConstraints(TypeInfo *tyinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); *************** *** 300,305 **** main(int argc, char **argv) --- 306,312 ---- {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-label", no_argument, &security_label, 1}, {NULL, 0, NULL, 0} }; *************** *** 448,453 **** main(int argc, char **argv) --- 455,462 ---- outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-label") == 0) + security_label = 1; else { fprintf(stderr, *************** *** 643,648 **** main(int argc, char **argv) --- 652,666 ---- do_sql_command(g_conn, "SET quote_all_identifiers = true"); /* + * Disables security label support if server version < v9.1.x + */ + if (security_label && g_fout->remoteVersion < 90100) + { + write_msg(NULL, "Server does not support security labels\n"); + security_label = 0; + } + + /* * Start serializable transaction to dump consistent data. */ do_sql_command(g_conn, "BEGIN"); *************** *** 839,844 **** help(const char *progname) --- 857,863 ---- printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); + printf(_(" --security-label also dump security labels\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); *************** *** 10435,10440 **** dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, --- 10454,10564 ---- } /* + * dumpSecLabel + * + * It dumps any security labels associated with the object handed + * to this routine. The routine takes a constant character string + * for the target part of the security-label command, plus the + * namespace and owner of the object (for labeling the ArchiveEntry), + * plus catalogId which is lookup key for pg_seclabel, and dumpId + * for the object. + * If a matching pg_seclabel entry is found, it is dumped. + */ + static void + dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId, + const char *type, const char *target, + const char *nspname, const char *owner) + { + PGresult *res; + PQExpBuffer lquery; + PQExpBuffer cquery; + PQExpBuffer tgbuf; + int i_attname; + int i_tag; + int i_label; + int i, ntups; + + /* do nothing, if security label dump is not enabled */ + if (!security_label) + return; + + /* --data-only skips security labels *except* large object */ + if (dataOnly && strcmp(type, "LARGE OBJECT") != 0) + return; + + /* + * Fetch security labels associated with the object + */ + lquery = createPQExpBuffer(); + cquery = createPQExpBuffer(); + tgbuf = createPQExpBuffer(); + + appendPQExpBuffer(lquery, + "SELECT a.attname, s.tag, s.label" + " FROM pg_catalog.pg_seclabel s" + " LEFT OUTER JOIN" + " pg_catalog.pg_attribute a" + " ON s.objoid = a.attrelid AND" + " s.subid = a.attnum" + " WHERE reloid = %u AND objoid = %u", + objCatId.tableoid, objCatId.oid); + + res = PQexec(g_conn, lquery->data); + check_sql_result(res, g_conn, lquery->data, PGRES_TUPLES_OK); + + i_attname = PQfnumber(res, "attname"); + i_tag = PQfnumber(res, "tag"); + i_label = PQfnumber(res, "label"); + + ntups = PQntuples(res); + + for (i = 0; i < ntups; i++) + { + if (strcmp(type, "TABLE") == 0 && + !PQgetisnull(res, i, i_attname)) + { + appendPQExpBuffer(cquery, + "SECURITY LABEL FOR '%s'" + " ON COLUMN %s.%s IS '%s';\n", + PQgetvalue(res, i, i_tag), + target, + fmtId(PQgetvalue(res, i, i_attname)), + PQgetvalue(res, i, i_label)); + continue; + } + + /* + * a.attname can be available only when type = "TABLE", + * so we expect it will be always NULL. Elsewhere, we assume + * it as a data corruption, so simply ignored. + */ + if (!PQgetisnull(res, i, i_attname)) + continue; + + appendPQExpBuffer(cquery, + "SECURITY LABEL FOR '%s' ON %s %s IS '%s';\n", + PQgetvalue(res, i, i_tag), + type, target, + PQgetvalue(res, i, i_label)); + } + PQclear(res); + + if (cquery->len > 0) + { + appendPQExpBuffer(tgbuf, "%s %s", type, target); + ArchiveEntry(fout, nilCatalogId, createDumpId(), + tgbuf->data, nspname, NULL, owner, + false, "LABEL", SECTION_NONE, + cquery->data, "", NULL, + &(objDumpId), 1, + NULL, NULL); + } + destroyPQExpBuffer(lquery); + destroyPQExpBuffer(cquery); + destroyPQExpBuffer(tgbuf); + } + + /* * dumpTable * write out to fout the declarations (not data) of a user-defined table */ *************** *** 10458,10463 **** dumpTable(Archive *fout, TableInfo *tbinfo) --- 10582,10594 ---- tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->relacl); + /* Handle the security label here */ + dumpSecLabel(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, + (tbinfo->relkind == RELKIND_SEQUENCE ? "SEQUENCE" : + (tbinfo->relkind == RELKIND_VIEW ? "VIEW" : "TABLE")), + namecopy, + tbinfo->dobj.namespace->dobj.name, + tbinfo->rolname); /* * Handle column ACLs, if any. Note: we pull these with a separate * query rather than trying to fetch them during getTableAttrs, so *** a/src/bin/pg_dump/pg_dumpall.c --- b/src/bin/pg_dump/pg_dumpall.c *************** *** 69,74 **** static int disable_triggers = 0; --- 69,75 ---- static int inserts = 0; static int no_tablespaces = 0; static int use_setsessauth = 0; + static int security_label = 0; static int server_version; static FILE *OPF; *************** *** 133,138 **** main(int argc, char *argv[]) --- 134,140 ---- {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-label", no_argument, &security_label, 1}, {NULL, 0, NULL, 0} }; *************** *** 286,291 **** main(int argc, char *argv[]) --- 288,295 ---- no_tablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-label") == 0) + security_label = 1; else { fprintf(stderr, *************** *** 371,376 **** main(int argc, char *argv[]) --- 375,382 ---- appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); + if (security_label) + appendPQExpBuffer(pgdumpopts, " --security-label"); /* * If there was a database specified on the command line, use that, *************** *** 567,572 **** help(void) --- 573,579 ---- printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); + printf(_(" --security-label also dump security labels\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); *** a/src/bin/pg_dump/pg_restore.c --- b/src/bin/pg_dump/pg_restore.c *************** *** 76,81 **** main(int argc, char **argv) --- 76,82 ---- static int no_data_for_failed_tables = 0; static int outputNoTablespaces = 0; static int use_setsessauth = 0; + static int skip_seclabel = 0; struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, *************** *** 116,121 **** main(int argc, char **argv) --- 117,123 ---- {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 2}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"no-security-label", no_argument, &skip_seclabel, 1}, {NULL, 0, NULL, 0} }; *************** *** 262,267 **** main(int argc, char **argv) --- 264,271 ---- outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "no-security-label") == 0) + skip_seclabel = 1; else { fprintf(stderr, *************** *** 337,342 **** main(int argc, char **argv) --- 341,347 ---- opts->noDataForFailedTables = no_data_for_failed_tables; opts->noTablespace = outputNoTablespaces; opts->use_setsessauth = use_setsessauth; + opts->skip_seclabel = skip_seclabel; if (opts->formatName) { *************** *** 442,447 **** usage(const char *progname) --- 447,453 ---- " do not restore data of tables that could not be\n" " created\n")); printf(_(" --no-tablespaces do not restore tablespace assignments\n")); + printf(_(" --no-security-label do not restore security labels\n")); printf(_(" --role=ROLENAME do SET ROLE before restore\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" *** a/src/include/catalog/indexing.h --- b/src/include/catalog/indexing.h *************** *** 281,286 **** DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre --- 281,289 ---- DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase oid_ops, setrole oid_ops)); #define DbRoleSettingDatidRolidIndexId 2965 + DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3038, on pg_seclabel using btree(reloid oid_ops, objoid oid_ops, subid int4_ops, tag text_ops)); + #define SecLabelObjectIndexId 3038 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES *** /dev/null --- b/src/include/catalog/pg_seclabel.h *************** *** 0 **** --- 1,43 ---- + /* ------------------------------------------------------------------------- + * + * pg_seclabel.h + * definition of the system "security label" relation (pg_seclabel) + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * ------------------------------------------------------------------------- + */ + #ifndef PG_SECLABEL_H + #define PG_SECLABEL_H + + #include "catalog/genbki.h" + + /* ---------------- + * pg_seclabel definition. cpp turns this into + * typedef struct FormData_pg_seclabel + * ---------------- + */ + #define SecLabelRelationId 3037 + + CATALOG(pg_seclabel,3037) BKI_WITHOUT_OIDS + { + Oid reloid; /* OID of table containing the object */ + Oid objoid; /* OID of the object itself */ + int4 subid; /* column number, or 0 if not used */ + text tag; /* identifier of external security provider */ + text label; /* security label of the object */ + } FormData_pg_seclabel; + + /* ---------------- + * compiler constants for pg_seclabel + * ---------------- + */ + #define Natts_pg_seclabel 5 + #define Anum_pg_seclabel_reloid 1 + #define Anum_pg_seclabel_objoid 2 + #define Anum_pg_seclabel_subid 3 + #define Anum_pg_seclabel_tag 4 + #define Anum_pg_seclabel_label 5 + + #endif /* PG_SECLABEL_H */ *** a/src/include/catalog/toasting.h --- b/src/include/catalog/toasting.h *************** *** 45,50 **** DECLARE_TOAST(pg_constraint, 2832, 2833); --- 45,51 ---- DECLARE_TOAST(pg_description, 2834, 2835); DECLARE_TOAST(pg_proc, 2836, 2837); DECLARE_TOAST(pg_rewrite, 2838, 2839); + DECLARE_TOAST(pg_seclabel, 3039, 3040); DECLARE_TOAST(pg_statistic, 2840, 2841); DECLARE_TOAST(pg_trigger, 2336, 2337); *** /dev/null --- b/src/include/commands/seclabel.h *************** *** 0 **** --- 1,36 ---- + /* + * seclabel.h + * + * Prototypes for functions in commands/seclabel.c + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #ifndef SECLABEL_H + #define SECLABEL_H + + #include "catalog/objectaddress.h" + #include "nodes/primnodes.h" + #include "nodes/parsenodes.h" + + /* + * Internal APIs + */ + extern char *GetSecurityLabel(const ObjectAddress *object, + const char *tag); + extern void SetSecurityLabel(const ObjectAddress *object, + const char *tag, + const char *seclabel); + extern void DeleteSecurityLabel(const ObjectAddress *object); + + /* + * Statement and ESP hook support + */ + extern void ExecSecLabelStmt(SecLabelStmt *stmt); + + typedef void (*check_object_relabel_type)(const ObjectAddress *object, + const char *seclabel); + extern void register_object_relabel_hook(const char *tag, + check_object_relabel_type hook); + + #endif /* SECLABEL_H */ *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** *** 347,352 **** typedef enum NodeTag --- 347,353 ---- T_AlterUserMappingStmt, T_DropUserMappingStmt, T_AlterTableSpaceOptionsStmt, + T_SecLabelStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 1850,1855 **** typedef struct CommentStmt --- 1850,1869 ---- } CommentStmt; /* ---------------------- + * SECURITY LABEL Statemetn + * ---------------------- + */ + typedef struct SecLabelStmt + { + NodeTag type; + ObjectType objtype; /* Object's type */ + List *objname; /* Qualified name of the object */ + List *objargs; /* Arguments if needed (eg, for functions) */ + char *tag; /* Identifier of ESP, if specified */ + char *seclabel; /* New security label to be assigned */ + } SecLabelStmt; + + /* ---------------------- * Declare Cursor Statement * * Note: the "query" field of DeclareCursorStmt is only used in the raw grammar *** a/src/include/parser/kwlist.h --- b/src/include/parser/kwlist.h *************** *** 208,213 **** PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) --- 208,214 ---- PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) + PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) *** a/src/include/utils/errcodes.h --- b/src/include/utils/errcodes.h *************** *** 260,265 **** --- 260,266 ---- #define ERRCODE_DATATYPE_MISMATCH MAKE_SQLSTATE('4','2', '8','0','4') #define ERRCODE_INDETERMINATE_DATATYPE MAKE_SQLSTATE('4','2', 'P','1','8') #define ERRCODE_WRONG_OBJECT_TYPE MAKE_SQLSTATE('4','2', '8','0','9') + #define ERRCODE_INVALID_SECLABEL MAKE_SQLSTATE('4','2', 'P','2','1') /* * Note: for ERRCODE purposes, we divide namable objects into these categories: * databases, schemas, prepared statements, cursors, tables, columns, *** a/src/test/regress/expected/sanity_check.out --- b/src/test/regress/expected/sanity_check.out *************** *** 114,119 **** SELECT relname, relhasindex --- 114,120 ---- pg_pltemplate | t pg_proc | t pg_rewrite | t + pg_seclabel | t pg_shdepend | t pg_shdescription | t pg_statistic | t *************** *** 153,159 **** SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (142 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 154,160 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (143 rows) -- -- another sanity check: every system catalog that has OIDs should have