security label support, part.2
The attached patch is a part of efforts to support security label
on database objects.
It adds statement support to manage security label of relations.
Right now, object labeling except for relations/columns are not
supported, because the DML permission hook is the only chance to
apply access control decision of ESP module.
It has the following syntax:
ALTER TABLE <relation_expr> [ALTER [COLUMN] <colmu_name>]
SECURITY LABEL TO '<label>';
I believe Robert's refactoring on COMMENT ON code also helps to
implement security label support for various kind of object classes.
However, we need to handle relabeling on the tables particularly
because of table's inheritances, unlike any other object classes.
So, I considered we can make progress these works in progress, then
we can integrated them later.
Example:
postgres=# CREATE TABLE t (a int, b text);
CREATE TABLE
postgres=# ALTER TABLE t SECURITY LABEL TO 'system_u:object_r:sepgsql_table_t:s0';
ALTER TABLE
postgres=# ALTER TABLE t ALTER a SECURITY LABEL TO 'system_u:object_r:sepgsql_table_t:s0';
ALTER TABLE
postgres=# ALTER TABLE t ALTER b SECURITY LABEL TO 'system_u:object_r:sepgsql_table_t:s0:c1';
ALTER TABLE
[kaigai@saba ~]$ runcon -l s0 psql postgres
psql (9.1devel)
Type "help" for help.
postgres=# set client_min_messages = log;
SET
postgres=# SELECT * FROM t;
LOG: SELinux: denied { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_table_t:s0:c1 tclass=db_column name=t.b
ERROR: SELinux: security policy violation
postgres=# SELECT a FROM t;
a
---
(0 rows)
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-v9.1-security-label-2.v1.patchapplication/octect-stream; name=pgsql-v9.1-security-label-2.v1.patchDownload
*** a/src/backend/commands/seclabel.c
--- b/src/backend/commands/seclabel.c
***************
*** 14,27 ****
--- 14,39 ----
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/indexing.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_inherits_fn.h"
#include "catalog/pg_seclabel.h"
#include "commands/seclabel.h"
+ #include "miscadmin.h"
+ #include "parser/parse_clause.h"
+ #include "storage/lmgr.h"
+ #include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/tqual.h"
/*
+ * External security providers
+ */
+ check_relation_relabel_type check_relation_relabel_hook = NULL;
+
+ /*
* GetLocalSecLabel
*
* It tries to look up a security label entry for the given OID of the
***************
*** 257,259 **** DeleteSecurityLabel(Oid classOid, Oid objOid, int4 subId)
--- 269,428 ----
DeleteLocalSecLabel(classOid, objOid, subId);
}
+
+ /*
+ * ExecAlterRelationLabel
+ *
+ * It relabels a relation and its child tables.
+ */
+ static void
+ ExecAlterRelationLabel(RangeVar *relation, const char *attname,
+ ObjectType objtype, const char *seclabel)
+ {
+ Oid relOid = RangeVarGetRelid(relation, false);
+ bool recurse = interpretInhOption(relation->inhOpt);
+ char relkind;
+ List *child_oids;
+ List *child_numparents;
+ ListCell *lo, *li;
+
+ /*
+ * Sanity checks for relation types
+ */
+ relkind = get_rel_relkind(relOid);
+ switch (objtype)
+ {
+ case OBJECT_TABLE:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_RELATION &&
+ relkind != RELKIND_SEQUENCE &&
+ relkind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table, sequence or view",
+ relation->relname)));
+ break;
+
+ case OBJECT_SEQUENCE:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence", relation->relname)));
+ break;
+
+ case OBJECT_VIEW:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a view", relation->relname)));
+ break;
+
+ case OBJECT_COLUMN:
+ Assert(attname != NULL);
+ if (relkind != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s.%s\" is not a column of tables",
+ relation->relname, attname)));
+ break;
+
+ default:
+ elog(ERROR, "unexpected object type %d", (int)objtype);
+ break;
+ }
+
+ /*
+ * Build a list of relations to be altered.
+ * Even if relation->inhOpt is not true, we make a dummy list
+ * and lock the relation to simplify the logic.
+ */
+ if (recurse)
+ {
+ child_oids = find_all_inheritors(relOid,
+ AccessExclusiveLock,
+ &child_numparents);
+ }
+ else
+ {
+ child_oids = list_make1_oid(relOid);
+ child_numparents = list_make1_int(0);
+ /* make sure the relation being already locked */
+ LockRelationOid(relOid, AccessExclusiveLock);
+ }
+
+ /*
+ * Relabel each tables being listed
+ */
+ forboth (lo, child_oids, li, child_numparents)
+ {
+ Oid tableOid = lfirst_oid(lo);
+ Oid numParents = lfirst_int(li);
+ Oid namespaceOid = get_rel_namespace(tableOid);
+ AttrNumber attnum = InvalidAttrNumber;
+
+ /*
+ * Verify existence of attribute
+ */
+ if (attname != NULL)
+ {
+ attnum = get_attnum(tableOid, attname);
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attname, get_rel_name(tableOid))));
+ }
+ /*
+ * Prevent to relabel system catalogs
+ */
+ if (!allowSystemTableMods &&
+ (IsSystemNamespace(namespaceOid) || IsToastNamespace(namespaceOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ get_rel_name(tableOid))));
+ /*
+ * Permission checks
+ */
+ if (!pg_class_ownercheck(tableOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ get_rel_name(tableOid));
+
+ if (check_relation_relabel_hook)
+ (*check_relation_relabel_hook)(tableOid, attnum, seclabel,
+ recurse, numParents);
+ /*
+ * Do the actual relabeling
+ */
+ SetSecurityLabel(RelationRelationId, tableOid, attnum, seclabel);
+ }
+ }
+
+ /*
+ * ExecAlterLabelStmt
+ *
+ * ALTER <thing> <name> SECURITY LABEL TO <seclabel> statement.
+ */
+ void
+ ExecAlterLabelStmt(AlterLabelStmt *stmt)
+ {
+ const char *seclabel = strVal(stmt->seclabel);
+
+ switch (stmt->objectType)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_COLUMN:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ ExecAlterRelationLabel(stmt->relation, stmt->addname,
+ stmt->objectType, seclabel);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized object type: %d", (int)stmt->objectType);
+ break;
+ }
+ }
+
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 182,188 **** static TypeName *TableFuncTypeName(List *columns);
%type <node> stmt schema_stmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
--- 182,188 ----
%type <node> stmt schema_stmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
! AlterForeignServerStmt AlterGroupStmt AlterLabelStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
***************
*** 422,427 **** static TypeName *TableFuncTypeName(List *columns);
--- 422,429 ----
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+ %type <value> label_item
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
***************
*** 498,504 **** 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
--- 500,506 ----
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
***************
*** 652,657 **** stmt :
--- 654,660 ----
| AlterForeignServerStmt
| AlterFunctionStmt
| AlterGroupStmt
+ | AlterLabelStmt
| AlterObjectSchemaStmt
| AlterOwnerStmt
| AlterSeqStmt
***************
*** 6026,6031 **** AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
--- 6029,6084 ----
}
;
+ /*****************************************************************************
+ *
+ * ALTER THING name SECURITY LABEL TO label
+ *
+ *****************************************************************************/
+
+ AlterLabelStmt: ALTER TABLE relation_expr label_item
+ {
+ AlterLabelStmt *n = makeNode(AlterLabelStmt);
+
+ n->objectType = OBJECT_TABLE;
+ n->relation = $3;
+ n->seclabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER TABLE relation_expr ALTER opt_column ColId label_item
+ {
+ AlterLabelStmt *n = makeNode(AlterLabelStmt);
+
+ n->objectType = OBJECT_COLUMN;
+ n->relation = $3;
+ n->addname = $6;
+ n->seclabel = $7;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE qualified_name label_item
+ {
+ AlterLabelStmt *n = makeNode(AlterLabelStmt);
+
+ n->objectType = OBJECT_SEQUENCE;
+ n->relation = $3;
+ n->seclabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER VIEW qualified_name label_item
+ {
+ AlterLabelStmt *n = makeNode(AlterLabelStmt);
+
+ n->objectType = OBJECT_VIEW;
+ n->relation = $3;
+ n->seclabel = $4;
+ $$ = (Node *)n;
+ }
+ ;
+
+ label_item: SECURITY LABEL TO Sconst
+ {
+ $$ = makeString($4);
+ }
+ ;
/*****************************************************************************
*
***************
*** 10921,10926 **** unreserved_keyword:
--- 10974,10980 ----
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 161,166 **** check_xact_readonly(Node *parsetree)
--- 161,167 ----
case T_AlterDatabaseSetStmt:
case T_AlterDomainStmt:
case T_AlterFunctionStmt:
+ case T_AlterLabelStmt:
case T_AlterRoleStmt:
case T_AlterRoleSetStmt:
case T_AlterObjectSchemaStmt:
***************
*** 696,701 **** standard_ProcessUtility(Node *parsetree,
--- 697,706 ----
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
break;
+ case T_AlterLabelStmt:
+ ExecAlterLabelStmt((AlterLabelStmt *) parsetree);
+ break;
+
case T_AlterTableStmt:
{
List *stmts;
***************
*** 1760,1765 **** CreateCommandTag(Node *parsetree)
--- 1765,1789 ----
}
break;
+ case T_AlterLabelStmt:
+ switch (((AlterLabelStmt *) parsetree)->objectType)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_COLUMN:
+ tag = "ALTER TABLE";
+ break;
+ case OBJECT_SEQUENCE:
+ tag = "ALTER SEQUENCE";
+ break;
+ case OBJECT_VIEW:
+ tag = "ALTER VIEW";
+ break;
+ default:
+ tag = "???";
+ break;
+ }
+ break;
+
case T_AlterTableStmt:
switch (((AlterTableStmt *) parsetree)->relkind)
{
***************
*** 2352,2357 **** GetCommandLogLevel(Node *parsetree)
--- 2376,2385 ----
lev = LOGSTMT_DDL;
break;
+ case T_AlterLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterTableStmt:
lev = LOGSTMT_DDL;
break;
*** a/src/include/commands/seclabel.h
--- b/src/include/commands/seclabel.h
***************
*** 9,14 ****
--- 9,31 ----
#ifndef SECLABEL_H
#define SECLABEL_H
+ #include "nodes/primnodes.h"
+ #include "nodes/parsenodes.h"
+
+ /*
+ * External security provider hooks
+ */
+
+ /*
+ * check_relation_relabel_hook
+ *
+ * It enables ESP module to make its access control decision on relabeling
+ * the relation/attribute. If violated, it can raise an error to prevent.
+ */
+ typedef void (*check_relation_relabel_type)(Oid, AttrNumber, const char *, bool, int);
+
+ extern check_relation_relabel_type check_relation_relabel_hook;
+
/*
* Internal APIs
*/
***************
*** 17,21 **** extern void SetSecurityLabel(Oid classOid, Oid objOid, int4 subId,
const char *seclabel);
extern void DeleteSecurityLabel(Oid classOid, Oid objOid, int4 subId);
! #endif /* SECLABEL_H */
--- 34,42 ----
const char *seclabel);
extern void DeleteSecurityLabel(Oid classOid, Oid objOid, int4 subId);
! /*
! * Statement support
! */
! extern void ExecAlterLabelStmt(AlterLabelStmt *stmt);
+ #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_AlterLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2073,2078 **** typedef struct AlterOwnerStmt
--- 2073,2092 ----
char *newowner; /* the new owner */
} AlterOwnerStmt;
+ /* ----------------------
+ * ALTER object SECURITY LABEL TO Statement
+ * ----------------------
+ */
+ typedef struct AlterLabelStmt
+ {
+ NodeTag type;
+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */
+ RangeVar *relation; /* in case it's a table */
+ List *object; /* in case it's some other object */
+ List *objarg; /* argument types, if applicable */
+ char *addname; /* additional name if needed */
+ Value *seclabel; /* the new security label */
+ } AlterLabelStmt;
/* ----------------------
* Create Rule Statement
*** 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)
2010/7/14 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a part of efforts to support security label
on database objects.
This is similar to what I had in mind as a design for this feature,
but I have some gripes:
1. I am inclined to suggest the syntax SECURITY LABEL ON ... IS ...,
following COMMENT ON (it's also somewhat similar to the GRANT syntax).
While the hook in ExecCheckRTPerms() will only allow meaningful
permissions checks on the use of relations, there will presumably be
ongoing demand to add additional hooks to cover other types of
objects, and I'd rather add label support all at once rather than
bit-by-bit. I also think that the SECURITY LABEL syntax is more
future-proof; we don't need to worry about conflicts in other parts of
the grammar.
2. Similarly, the design of the hook in secabel.h is way too
short-sighted and won't scale to any other object type. We don't need
or want one hook per object type here. Use an ObjectAddress.
3. I am firmly of the view that we want to allow multiple security
providers. I think the way this should work here is that we should
keep a list somewhere of security providers known to the system, which
loadable modules should add themselves to. Each such security
provider should be represented by a struct containing, at least, a
name and a function that gets called on relabel. The labels should be
stored in the catalog. That way there is never any possibility of one
security provider getting a label that was originally applied by some
other security provider. If we were storing these labels in pg_class
or pg_attribute or similar, the storage cost for this might be worth
worrying about, but as this is a separate catalog, it's not.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Thanks for your reviewing.
(2010/07/23 0:54), Robert Haas wrote:
2010/7/14 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patch is a part of efforts to support security label
on database objects.This is similar to what I had in mind as a design for this feature,
but I have some gripes:1. I am inclined to suggest the syntax SECURITY LABEL ON ... IS ...,
following COMMENT ON (it's also somewhat similar to the GRANT syntax).
While the hook in ExecCheckRTPerms() will only allow meaningful
permissions checks on the use of relations, there will presumably be
ongoing demand to add additional hooks to cover other types of
objects, and I'd rather add label support all at once rather than
bit-by-bit. I also think that the SECURITY LABEL syntax is more
future-proof; we don't need to worry about conflicts in other parts of
the grammar.
Hmm. Indeed, we cannot deny the possibility to conflict with other part
in the future, if we use ALTER xxx statement here.
But, right now, we have no statement that starts in noun, rather than verb.
The "comment" is a noun, but "comment on" is a phrasal-verb, isn't it?
How about RELABEL <object> TO <label>, instead?
The "relabel" is a transitive-verb, we don't need "ON" between RELABEL
and <object>. And, it tries to change a property of the object, so
it seems to me "TO" is more appropriate than "IS".
2. Similarly, the design of the hook in secabel.h is way too
short-sighted and won't scale to any other object type. We don't need
or want one hook per object type here. Use an ObjectAddress.
I think the relation type is an exceptional object class, because of
the recursion due to the table inheritances.
For other object classes, I also think one security hook which takes
ObjectAddress as an argument is enough to implement.
So, I expect we need two hooks on relabeling eventually.
(One for relation, one for other object classes)
3. I am firmly of the view that we want to allow multiple security
providers. I think the way this should work here is that we should
keep a list somewhere of security providers known to the system, which
loadable modules should add themselves to. Each such security
provider should be represented by a struct containing, at least, a
name and a function that gets called on relabel. The labels should be
stored in the catalog. That way there is never any possibility of one
security provider getting a label that was originally applied by some
other security provider. If we were storing these labels in pg_class
or pg_attribute or similar, the storage cost for this might be worth
worrying about, but as this is a separate catalog, it's not.
What I'm worrying about is that we cannot estimate amount of works when
we expand the concept to row-level security. We will need to revise the
implementation, if individual user tuples have its security label in the
future version.
If we don't support multiple labels right now, it will not be feature
degradation, even if it will be hard to implement multiple label support
for each user tuples. :(
I don't deny worth of multiple security providers concurrently, however,
I doubt whether it should be supported from the beginning, or not.
It seems to me it is not too late after we can find out the way to label
individual user tuples.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/7/22 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Thanks for your reviewing.
1. I am inclined to suggest the syntax SECURITY LABEL ON ... IS ...,
following COMMENT ON (it's also somewhat similar to the GRANT syntax).
While the hook in ExecCheckRTPerms() will only allow meaningful
permissions checks on the use of relations, there will presumably be
ongoing demand to add additional hooks to cover other types of
objects, and I'd rather add label support all at once rather than
bit-by-bit. I also think that the SECURITY LABEL syntax is more
future-proof; we don't need to worry about conflicts in other parts of
the grammar.Hmm. Indeed, we cannot deny the possibility to conflict with other part
in the future, if we use ALTER xxx statement here.But, right now, we have no statement that starts in noun, rather than verb.
The "comment" is a noun, but "comment on" is a phrasal-verb, isn't it?How about RELABEL <object> TO <label>, instead?
Well, I like SECURITY LABEL better because it's more clear about what
kind of label we're talking about, but if there's consensus on some
other option it's OK with me. Actually, we need to work the security
provider name in there too, I think, so perhaps SECURITY LABEL FOR
provider ON object IS labeltext. I realize it's slightly odd
grammatically, but it's no worse than the COMMENT syntax.
2. Similarly, the design of the hook in secabel.h is way too
short-sighted and won't scale to any other object type. We don't need
or want one hook per object type here. Use an ObjectAddress.I think the relation type is an exceptional object class, because of
the recursion due to the table inheritances.
For other object classes, I also think one security hook which takes
ObjectAddress as an argument is enough to implement.So, I expect we need two hooks on relabeling eventually.
(One for relation, one for other object classes)
Please explain in more detail.
3. I am firmly of the view that we want to allow multiple security
providers. I think the way this should work here is that we should
keep a list somewhere of security providers known to the system, which
loadable modules should add themselves to. Each such security
provider should be represented by a struct containing, at least, a
name and a function that gets called on relabel. The labels should be
stored in the catalog. That way there is never any possibility of one
security provider getting a label that was originally applied by some
other security provider. If we were storing these labels in pg_class
or pg_attribute or similar, the storage cost for this might be worth
worrying about, but as this is a separate catalog, it's not.What I'm worrying about is that we cannot estimate amount of works when
we expand the concept to row-level security. We will need to revise the
implementation, if individual user tuples have its security label in the
future version.
If we don't support multiple labels right now, it will not be feature
degradation, even if it will be hard to implement multiple label support
for each user tuples. :(
I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/07/23 10:05), Robert Haas wrote:
2010/7/22 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Thanks for your reviewing.
1. I am inclined to suggest the syntax SECURITY LABEL ON ... IS ...,
following COMMENT ON (it's also somewhat similar to the GRANT syntax).
While the hook in ExecCheckRTPerms() will only allow meaningful
permissions checks on the use of relations, there will presumably be
ongoing demand to add additional hooks to cover other types of
objects, and I'd rather add label support all at once rather than
bit-by-bit. I also think that the SECURITY LABEL syntax is more
future-proof; we don't need to worry about conflicts in other parts of
the grammar.Hmm. Indeed, we cannot deny the possibility to conflict with other part
in the future, if we use ALTER xxx statement here.But, right now, we have no statement that starts in noun, rather than verb.
The "comment" is a noun, but "comment on" is a phrasal-verb, isn't it?How about RELABEL<object> TO<label>, instead?
Well, I like SECURITY LABEL better because it's more clear about what
kind of label we're talking about, but if there's consensus on some
other option it's OK with me. Actually, we need to work the security
provider name in there too, I think, so perhaps SECURITY LABEL FOR
provider ON object IS labeltext. I realize it's slightly odd
grammatically, but it's no worse than the COMMENT syntax.
The "FOR <provider>" clause should be optional. I expect most use
cases installs only one security provider, rather than multiple.
If no explicit <provider> is specified, all the security providers
check the given security label. If two or more providers are here,
of course, either of them will raise an error, because they have
different label formats. It is right.
Anyway, I'd like to implement according to the idea.
SECURITY LABEL [FOR <provider>] ON <object> IS <label>;
2. Similarly, the design of the hook in secabel.h is way too
short-sighted and won't scale to any other object type. We don't need
or want one hook per object type here. Use an ObjectAddress.I think the relation type is an exceptional object class, because of
the recursion due to the table inheritances.
For other object classes, I also think one security hook which takes
ObjectAddress as an argument is enough to implement.So, I expect we need two hooks on relabeling eventually.
(One for relation, one for other object classes)Please explain in more detail.
For relations, one SECURITY LABEL statement may relabel multiple tables
when it has child tables, if ONLY clause was not given.
So, we need to obtain oids to be relabeled using find_all_inheritors(),
and need to ask providers whether it allows, or not.
But, obviously, it is specific for relations.
For other object class, the target object to be relabeled is identified
by <object> in SECURITY LABEL statement. It will be parsed by the upcoming
parse_object.c feature, then it solves the object name to ObjectAddress.
So, we can apply access controls after setting up the ObjectAddress with
common hooks for object classes except for relations, like:
void check_object_relabel(ObjectAddress object, const char *new_label);
3. I am firmly of the view that we want to allow multiple security
providers. I think the way this should work here is that we should
keep a list somewhere of security providers known to the system, which
loadable modules should add themselves to. Each such security
provider should be represented by a struct containing, at least, a
name and a function that gets called on relabel. The labels should be
stored in the catalog. That way there is never any possibility of one
security provider getting a label that was originally applied by some
other security provider. If we were storing these labels in pg_class
or pg_attribute or similar, the storage cost for this might be worth
worrying about, but as this is a separate catalog, it's not.What I'm worrying about is that we cannot estimate amount of works when
we expand the concept to row-level security. We will need to revise the
implementation, if individual user tuples have its security label in the
future version.
If we don't support multiple labels right now, it will not be feature
degradation, even if it will be hard to implement multiple label support
for each user tuples. :(I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)
Hmm. Are you saying we may degrade the feature when we switch to the
completely separate infrastructure? Is it preferable??
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/7/22 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Well, I like SECURITY LABEL better because it's more clear about what
kind of label we're talking about, but if there's consensus on some
other option it's OK with me. Actually, we need to work the security
provider name in there too, I think, so perhaps SECURITY LABEL FOR
provider ON object IS labeltext. I realize it's slightly odd
grammatically, but it's no worse than the COMMENT syntax.The "FOR <provider>" clause should be optional. I expect most use
cases installs only one security provider, rather than multiple.If no explicit <provider> is specified, all the security providers
check the given security label. If two or more providers are here,
of course, either of them will raise an error, because they have
different label formats. It is right.
Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's >1 loaded, we just throw an
error saying you didn't specify whose label it is.
So, I expect we need two hooks on relabeling eventually.
(One for relation, one for other object classes)Please explain in more detail.
For relations, one SECURITY LABEL statement may relabel multiple tables
when it has child tables, if ONLY clause was not given.
So, we need to obtain oids to be relabeled using find_all_inheritors(),
and need to ask providers whether it allows, or not.
But, obviously, it is specific for relations.For other object class, the target object to be relabeled is identified
by <object> in SECURITY LABEL statement. It will be parsed by the upcoming
parse_object.c feature, then it solves the object name to ObjectAddress.
So, we can apply access controls after setting up the ObjectAddress with
common hooks for object classes except for relations, like:void check_object_relabel(ObjectAddress object, const char *new_label);
So just construct an ObjectAddress for each relation and call the
check function once for each.
I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)Hmm. Are you saying we may degrade the feature when we switch to the
completely separate infrastructure? Is it preferable??
Uh... no, not really. I'm saying that I don't think we're backing
ourselves into a corner. What makes you think we are?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/07/23 12:56), Robert Haas wrote:
2010/7/22 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Well, I like SECURITY LABEL better because it's more clear about what
kind of label we're talking about, but if there's consensus on some
other option it's OK with me. Actually, we need to work the security
provider name in there too, I think, so perhaps SECURITY LABEL FOR
provider ON object IS labeltext. I realize it's slightly odd
grammatically, but it's no worse than the COMMENT syntax.The "FOR<provider>" clause should be optional. I expect most use
cases installs only one security provider, rather than multiple.If no explicit<provider> is specified, all the security providers
check the given security label. If two or more providers are here,
of course, either of them will raise an error, because they have
different label formats. It is right.Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's>1 loaded, we just throw an
error saying you didn't specify whose label it is.
Perhaps, we need to return the caller a state whether one provider checked
the given label at least, or not.
If invalid <provider> was specified so nobody checked it, nobody returns
the caller a state of "checked", then it raises an error to notice invalid
security provider.
If valid <provider> was specified, only specified provider checks the given
label, and returns the caller a state of "it was checked by xxxx".
If it was omitted, all the providers try to check the given label, but it
has mutually different format, so one of providers will raise an error at
least.
It means we have to specify the provider when two or more providers are
loaded, but not necessary when just one provider.
So, I expect we need two hooks on relabeling eventually.
(One for relation, one for other object classes)Please explain in more detail.
For relations, one SECURITY LABEL statement may relabel multiple tables
when it has child tables, if ONLY clause was not given.
So, we need to obtain oids to be relabeled using find_all_inheritors(),
and need to ask providers whether it allows, or not.
But, obviously, it is specific for relations.For other object class, the target object to be relabeled is identified
by<object> in SECURITY LABEL statement. It will be parsed by the upcoming
parse_object.c feature, then it solves the object name to ObjectAddress.
So, we can apply access controls after setting up the ObjectAddress with
common hooks for object classes except for relations, like:void check_object_relabel(ObjectAddress object, const char *new_label);
So just construct an ObjectAddress for each relation and call the
check function once for each.
OK, I'll revise it.
I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)Hmm. Are you saying we may degrade the feature when we switch to the
completely separate infrastructure? Is it preferable??Uh... no, not really. I'm saying that I don't think we're backing
ourselves into a corner. What makes you think we are?
Sorry, meaning of the last question was unclear for me.... Is it a idiom?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/7/23 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's>1 loaded, we just throw an
error saying you didn't specify whose label it is.Perhaps, we need to return the caller a state whether one provider checked
the given label at least, or not.
Return to the caller? This is an SQL command. You either get an
error, or you don't.
If it was omitted, all the providers try to check the given label, but it
has mutually different format, so one of providers will raise an error at
least.
Yeah, but it won't be a very clear error, and what if you have, say, a
provider that allows arbitrary strings as labels? Since this is a
security feature, I think it's a pretty bad idea to allow the user to
do anything that might be ambiguous.
It means we have to specify the provider when two or more providers are
loaded, but not necessary when just one provider.
But that should be fine. Loading multiple providers should, as you
say, be fairly rare.
I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)Hmm. Are you saying we may degrade the feature when we switch to the
completely separate infrastructure? Is it preferable??Uh... no, not really. I'm saying that I don't think we're backing
ourselves into a corner. What makes you think we are?Sorry, meaning of the last question was unclear for me.... Is it a idiom?
I don't understand why we wouldn't be able to support multiple
providers for row-level security. Why do you think that's a problem?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
* Robert Haas (robertmhaas@gmail.com) wrote:
I don't understand why we wouldn't be able to support multiple
providers for row-level security. Why do you think that's a problem?
My guess would be that he's concerned about only having space in the
tuple header for 1 label. I see two answers- only allow 1 provider for
a given relation (doesn't strike me as a horrible limitation), or handle
labels as extra columns where you could have more than one.
Thanks,
Stephen
On Fri, Jul 23, 2010 at 8:32 AM, Stephen Frost <sfrost@snowman.net> wrote:
* Robert Haas (robertmhaas@gmail.com) wrote:
I don't understand why we wouldn't be able to support multiple
providers for row-level security. Why do you think that's a problem?My guess would be that he's concerned about only having space in the
tuple header for 1 label. I see two answers- only allow 1 provider for
a given relation (doesn't strike me as a horrible limitation), or handle
labels as extra columns where you could have more than one.
I think we've been pretty clear in previous discussions that any
row-level security implementation should be a general one, and
SE-Linux or whatever can integrate with that to do what it needs to
do. So I'm pretty sure we'll be using regular columns rather than
cramming anything into the tuple header. There are pretty substantial
performance benefits to such an implementation, as well.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/07/23 20:44), Robert Haas wrote:
2010/7/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's>1 loaded, we just throw an
error saying you didn't specify whose label it is.Perhaps, we need to return the caller a state whether one provider checked
the given label at least, or not.Return to the caller? This is an SQL command. You either get an
error, or you don't.
Ahh, I was talked about relationship between the core PG code and ESP module.
It means the security hook returns a state which informs the core PG code
whether one provider checked the given label, then the core PG code can
decide whether it raise an actual error to users, or not.
In other words, I'd like to suggest the security hook which returns a tag
of ESP module, as follows:
const char *
check_object_relabel_hook(const ObjectAddress *object,
const char *provider,
const char *seclabel);
The second argument reflects "FOR <provider>" clause. It informs ESP modules
what provider is specified. If omitted, it will be NULL.
Then, ESP module which checked the given security label must return its tag.
Maybe, "selinux", if SE-PostgreSQL. Or, NULL will be returned if nobody
checked it. If NULL or incorrect tag is returned, the core PG code can know
the given seclabel is not checked/validated, then it will raise an error to
users.
Elsewhere, the validated label will be stored with the returned tag.
It enables to recognize what label is validated by SELinux, and what label
is not.
If it was omitted, all the providers try to check the given label, but it
has mutually different format, so one of providers will raise an error at
least.Yeah, but it won't be a very clear error, and what if you have, say, a
provider that allows arbitrary strings as labels? Since this is a
security feature, I think it's a pretty bad idea to allow the user to
do anything that might be ambiguous.
It is provider's job to validate the given security label.
So, if we install such a security module which accept arbitrary strings
as label, the core PG code also need to believe the ESP module.
But the arbitrary label will be tagged with something other than "selinux",
so it does not confuse other module, according to the above idea.
It means we have to specify the provider when two or more providers are
loaded, but not necessary when just one provider.But that should be fine. Loading multiple providers should, as you
say, be fairly rare.I think it is 100% clear that row-level security will require
completely separate infrastructure, and therefore I'm not even a tiny
bit worried about this. :-)Hmm. Are you saying we may degrade the feature when we switch to the
completely separate infrastructure? Is it preferable??Uh... no, not really. I'm saying that I don't think we're backing
ourselves into a corner. What makes you think we are?Sorry, meaning of the last question was unclear for me.... Is it a idiom?
I don't understand why we wouldn't be able to support multiple
providers for row-level security. Why do you think that's a problem?
I don't have any clear reason why we wouldn't be able to support multiple
labels on user tuples, but it is intangible anxiety, because I have not
implemented it as a working example yet.
(So, I never think it is impossible.)
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Fri, Jul 23, 2010 at 8:59 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
(2010/07/23 20:44), Robert Haas wrote:
2010/7/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's>1 loaded, we just throw an
error saying you didn't specify whose label it is.Perhaps, we need to return the caller a state whether one provider
checked
the given label at least, or not.Return to the caller? This is an SQL command. You either get an
error, or you don't.Ahh, I was talked about relationship between the core PG code and ESP
module.
It means the security hook returns a state which informs the core PG code
whether one provider checked the given label, then the core PG code can
decide whether it raise an actual error to users, or not.In other words, I'd like to suggest the security hook which returns a tag
of ESP module, as follows:const char *
check_object_relabel_hook(const ObjectAddress *object,
const char *provider,
const char *seclabel);
I don't think that's a very good design. What I had in mind was a
simple API for security providers to register themselves (including
their names), and then the core code will only call the relevant
security provider. I did try to explain this in point #3 of my
original review.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
The attached patches are revised ones, as follows.
* A new SECURITY LABEL statement replaced the previous ALTER TABLE statement
with SECURITY LABEL TO option. It has the following syntax.
SECURITY LABEL [ FOR <provider> ] ON <object class> <object name> IS '<label>';
E.g) SECURITY LABEL ON TABLE t1 IS 'system_u:object_r:sepgsql_table_t:s0';
* It supports multiple security providers to assign its security label on
a database object. The pg_seclabel catalog was modified as follows:
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;
The new 'tag' field identifies which security provider manages this
security label. For example, SE-PostgreSQL uses "selinux" for its
identifier.
* The security hook to check relabeling become to be registered using
register_object_relabel_hook() which takes a tag of ESP module and
a function pointer to the security hook.
ExecSecLabelStmt() picks up an appropriate security hook, then it
shall be invoked even if multiple modules are loaded.
* Add _copySecLabelStmt() on nodes/copyfuncs.c and _equalSecLabelStmt()
on nodes/equalfuncs.c, because I forgot to add them, although new
parsenode (SecLabelStmt) was added.
* Add descriptions about pg_seclabel catalog and SECURITY LABEL statement
on the documentation.
Thanks,
(2010/07/23 22:36), Robert Haas wrote:
On Fri, Jul 23, 2010 at 8:59 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
(2010/07/23 20:44), Robert Haas wrote:
2010/7/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Hmm. How about if there's just one provider loaded, you can omit it,
but if you fail to specify it and there's>1 loaded, we just throw an
error saying you didn't specify whose label it is.Perhaps, we need to return the caller a state whether one provider
checked
the given label at least, or not.Return to the caller? This is an SQL command. You either get an
error, or you don't.Ahh, I was talked about relationship between the core PG code and ESP
module.
It means the security hook returns a state which informs the core PG code
whether one provider checked the given label, then the core PG code can
decide whether it raise an actual error to users, or not.In other words, I'd like to suggest the security hook which returns a tag
of ESP module, as follows:const char *
check_object_relabel_hook(const ObjectAddress *object,
const char *provider,
const char *seclabel);I don't think that's a very good design. What I had in mind was a
simple API for security providers to register themselves (including
their names), and then the core code will only call the relevant
security provider. I did try to explain this in point #3 of my
original review.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-v9.1-security-label-2.v2.patchapplication/octect-stream; name=pgsql-v9.1-security-label-2.v2.patchDownload
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7518c84..6f46356 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -132,6 +132,7 @@ Complete list of usable sgml source files in this directory.
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
+<!entity securityLabel system "seclabel.sgml">
<!entity select system "select.sgml">
<!entity selectInto system "select_into.sgml">
<!entity set system "set.sgml">
diff --git a/doc/src/sgml/ref/seclabel.sgml b/doc/src/sgml/ref/seclabel.sgml
new file mode 100644
index 0000000..9215255
--- /dev/null
+++ b/doc/src/sgml/ref/seclabel.sgml
@@ -0,0 +1,123 @@
+<refentry id="SQL-SECURITY-LABEL">
+ <refmeta>
+ <refentrytitle>SECURITY LABEL</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SECURITY LABEL</refname>
+ <refpurpose>relabel the security label of an object</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-security-label">
+ <primary>SECURITY LABEL</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+SECURITY LABEL [ FOR '<replaceable class="PARAMETER">esp_tag</replaceable>' ] ON
+{
+ TABLE <replaceable class="PARAMETER">object_name</replaceable> |
+ COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
+ SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+ VIEW <replaceable class="PARAMETER">object_name</replaceable>
+} IS '<replaceable class="PARAMETER">security_label</replaceable>';
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>SECURITY LABEL</command> relabels a security label of a database object.
+ </para>
+
+ <para>
+ We can assign individual security labels on a certain database object for
+ each security providers, using <command>SECURITY LABEL</command> command.
+ </para>
+ <para>
+ Security labels are automatically assigned when the object is created,
+ and also automatically dropped when the object is dropped.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">object_name</replaceable></term>
+ <term><replaceable class="parameter">table_name</replaceable></term>
+ <term><replaceable class="parameter">table_name.column_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the object to be relabeled. Names of tables, columns,
+ sequences and views can be schema-qualified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">security_label</replaceable></term>
+ <listitem>
+ <para>
+ The new security label as a string literal.
+ </para>
+ <para>
+ It shall be validated by one of the label based security features,
+ in addition to its permission checks relabeling on the specified
+ database objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">esp_tag</replaceable></term>
+ <listitem>
+ <para>
+ The identifier string of external security provider.
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+ 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.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Relabel a security label on the table to '<literal>system_u:object_r:sepgsql_table_t:s0</literal>'.
+
+<programlisting>
+SECURITY LABEL FOR 'selinux' ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+ <para>
+ There is no <command>SECURITY LABEL</command> command in the SQL standard.
+ </para>
+ </refsect1>
+</refentry>
+
+
+
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index c33d883..1448f47 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -160,6 +160,7 @@
&rollbackPrepared;
&rollbackTo;
&savepoint;
+ &securityLabel;
&select;
&selectInto;
&set;
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 5f914fa..0947376 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -15,10 +15,18 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_seclabel.h"
#include "commands/seclabel.h"
+#include "miscadmin.h"
+#include "parser/parse_clause.h"
+#include "storage/lmgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
@@ -269,3 +277,249 @@ DeleteSecurityLabel(const ObjectAddress *object)
DeleteLocalSecLabel(object);
}
+
+/*
+ * 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;
+
+ if (!process_shared_preload_libraries_in_progress)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unavailable to register security hooks now")));
+
+ 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);
+}
+
+/*
+ * ExecRelationSecLabel
+ *
+ * It relabels relabel/attribute
+ *
+ */
+static void
+ExecRelationSecLabel(ObjectType objtype, List *nameList,
+ const char *seclabel,
+ esp_relabel_hook_entry *esp_entry)
+{
+ List *relname = nameList;
+ char *attname = NULL;
+ RangeVar *rel;
+ Oid relOid;
+ List *child_oids;
+ List *child_numparents;
+ ListCell *lo, *li;
+
+ if (objtype == OBJECT_COLUMN)
+ {
+ int nnames = list_length(nameList);
+
+ if (nnames < 2)
+ elog(ERROR, "must specify relation and attribute");
+
+ attname = strVal(lfirst(list_tail(nameList)));
+ relname = list_truncate(list_copy(nameList), nnames - 1);
+ }
+
+ /*
+ * Resolve the specified relation
+ */
+ rel = makeRangeVarFromNameList(relname);
+ relOid = RangeVarGetRelid(rel, false);
+
+ /*
+ * Build a list of inherited relations, if exists.
+ * Note that 'child_oids' contains relOid.
+ */
+ child_oids = find_all_inheritors(relOid,
+ AccessExclusiveLock,
+ &child_numparents);
+
+ /*
+ * Relabel each tables being listed
+ */
+ forboth (lo, child_oids, li, child_numparents)
+ {
+ ObjectAddress object;
+ Oid tableOid = lfirst_oid(lo);
+ Oid namespaceOid = get_rel_namespace(tableOid);
+ int expected_parents = lfirst_int(li);
+ AttrNumber attnum = InvalidAttrNumber;
+
+ switch (objtype)
+ {
+ case OBJECT_TABLE:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_SEQUENCE:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_VIEW:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a view",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_COLUMN:
+ Assert(attname != NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ get_rel_name(tableOid))));
+ break;
+
+ default:
+ elog(ERROR, "unexpected object type %d", (int)objtype);
+ break;
+ }
+
+ /*
+ * Resolve the column name, if necessary
+ */
+ if (attname)
+ {
+ attnum = get_attnum(tableOid, attname);
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attname, get_rel_name(tableOid))));
+ }
+
+ /*
+ * Prevent relabeling system catalogs
+ */
+ if (!allowSystemTableMods &&
+ (IsSystemNamespace(namespaceOid) || IsToastNamespace(namespaceOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ get_rel_name(tableOid))));
+ /*
+ * Permission checks
+ */
+ if (!pg_class_ownercheck(tableOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ get_rel_name(tableOid));
+
+ object.classId = RelationRelationId;
+ object.objectId = tableOid;
+ object.objectSubId = attnum;
+
+ (*esp_entry->hook)(&object, seclabel, expected_parents);
+
+ /*
+ * Do actual relabeling
+ */
+ SetSecurityLabel(&object, esp_entry->tag, seclabel);
+ }
+}
+
+/*
+ * ExecSecLabelStmt
+ *
+ * SECURITY LABEL [FOR <esp>] ON <class> <name> IS <new label>
+ *
+ */
+void
+ExecSecLabelStmt(SecLabelStmt *stmt)
+{
+ esp_relabel_hook_entry *esp_entry = NULL;
+ ListCell *l;
+
+ /*
+ * SECURITY LABEL statement needs one or more label based security
+ * features are available at least.
+ */
+ if (esp_relabel_hook_list == NIL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("no label based security is available")));
+ }
+
+ /*
+ * when just one label based security is available, we can omit
+ * FOR <esp> clause because it is obvious which ESP module handle
+ * it. But, we have to specify the tag when multiple label based
+ * securities are available.
+ */
+ if (!stmt->tag)
+ {
+ if (list_length(esp_relabel_hook_list) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("multiple label based securities are available")));
+
+ esp_entry = linitial(esp_relabel_hook_list);
+ }
+ else
+ {
+ foreach (l, esp_relabel_hook_list)
+ {
+ esp_entry = lfirst(l);
+
+ if (strcmp(stmt->tag, esp_entry->tag) == 0)
+ goto found;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("no label based security for the tag: '%s'",
+ stmt->tag)));
+ }
+found:
+
+ switch (stmt->objtype)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_COLUMN:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ ExecRelationSecLabel(stmt->objtype,
+ stmt->objname,
+ stmt->seclabel,
+ esp_entry);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized object type: %d", (int)stmt->objtype);
+ break;
+ }
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9af1217..92d786a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2605,6 +2605,20 @@ _copyCommentStmt(CommentStmt *from)
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)
{
@@ -3955,6 +3969,9 @@ copyObject(void *from)
case T_CommentStmt:
retval = _copyCommentStmt(from);
break;
+ case T_SecLabelStmt:
+ retval = _copySecLabelStmt(from);
+ break;
case T_FetchStmt:
retval = _copyFetchStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 70b3c62..1b84a55 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1163,6 +1163,18 @@ _equalCommentStmt(CommentStmt *a, CommentStmt *b)
}
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);
@@ -2622,6 +2634,9 @@ equal(void *a, void *b)
case T_CommentStmt:
retval = _equalCommentStmt(a, b);
break;
+ case T_SecLabelStmt:
+ retval = _equalSecLabelStmt(a, b);
+ break;
case T_FetchStmt:
retval = _equalFetchStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1722036..0a92027 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -204,7 +204,7 @@ static TypeName *TableFuncTypeName(List *columns);
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
- SelectStmt TransactionStmt TruncateStmt
+ SecLabelStmt SelectStmt TransactionStmt TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
ViewStmt CheckPointStmt CreateConversionStmt
@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+%type <str> label_item opt_esp
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ 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
@@ -736,6 +738,7 @@ stmt :
| RevokeStmt
| RevokeRoleStmt
| RuleStmt
+ | SecLabelStmt
| SelectStmt
| TransactionStmt
| TruncateStmt
@@ -4324,6 +4327,61 @@ comment_text:
| 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:
@@ -10954,6 +11012,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 1815539..2d3324d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -218,6 +218,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
@@ -662,6 +663,10 @@ standard_ProcessUtility(Node *parsetree,
CommentObject((CommentStmt *) parsetree);
break;
+ case T_SecLabelStmt:
+ ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ break;
+
case T_CopyStmt:
{
uint64 processed;
@@ -1591,6 +1596,10 @@ CreateCommandTag(Node *parsetree)
tag = "COMMENT";
break;
+ case T_SecLabelStmt:
+ tag = "SECURITY LABEL";
+ break;
+
case T_CopyStmt:
tag = "COPY";
break;
@@ -2313,6 +2322,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_SecLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CopyStmt:
if (((CopyStmt *) parsetree)->is_from)
lev = LOGSTMT_MOD;
diff --git a/src/include/commands/seclabel.h b/src/include/commands/seclabel.h
index b69fc07..088180f 100644
--- a/src/include/commands/seclabel.h
+++ b/src/include/commands/seclabel.h
@@ -10,6 +10,8 @@
#define SECLABEL_H
#include "catalog/dependency.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
/*
* Internal APIs
@@ -21,5 +23,15 @@ extern void SetSecurityLabel(const ObjectAddress *object,
const char *seclabel);
extern void DeleteSecurityLabel(const ObjectAddress *object);
-#endif /* SECLABEL_H */
+/*
+ * Statement and ESP hook support
+ */
+extern void ExecSecLabelStmt(SecLabelStmt *stmt);
+typedef void (*check_object_relabel_type)(const ObjectAddress *object,
+ const char *seclabel,
+ int expected_parents);
+extern void register_object_relabel_hook(const char *tag,
+ check_object_relabel_type hook);
+
+#endif /* SECLABEL_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..bad1520 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -347,6 +347,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_SecLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fec8d3c..2cf6798 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1846,6 +1846,20 @@ typedef struct CommentStmt
} 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
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..13c88a1 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
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)
pgsql-v9.1-security-label-1.v2.patchapplication/octect-stream; name=pgsql-v9.1-security-label-1.v2.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index d2710db..e19cad6 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -209,6 +209,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+ <entry>security labels on local database objects</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
@@ -4204,6 +4209,91 @@
</table>
</sect1>
+ <sect1 id="catalog-pg-seclabel">
+ <title><structname>pg_seclabel</structname></title>
+
+ <indexterm zone="catalog-pg-seclabel">
+ <primary>pg_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seclabel</structname> stores the security
+ label of database objects. This information allows external security
+ providers to apply label based mandatory access controls.
+ </para>
+
+ <para>
+ 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.)
+ </para>
+ <para>
+ The security labels shall be automatically cleaned up when the database
+ object being labeled is dropped.
+ </para>
+ <para>
+ See also <link linkend="sql-security-label"><command>SECURITY LABEL</command></link>,
+ which provides a functionality to relabel a certain database object,
+ as long as user has enough privileges.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabel</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>reloid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+
+ <row>
+ <entry><structfield>subid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>For a security label on a table column, this is the column number
+ (the <structfield>objoid</structfield> and <structfield>classoid</structfield>
+ refer to the table itself). For all other object types, this column is
+ zero
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>tag</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>identifier of external security provider</entry>
+ </row>
+
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>security label in text format</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 82f761d..362b153 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -37,7 +37,7 @@ 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 \
+ pg_default_acl.h pg_seclabel.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 12b5d85..b171f01 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -57,6 +57,7 @@
#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,6 +1011,13 @@ deleteOneObject(const ObjectAddress *object, Relation depRel)
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.
*/
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index a2cc786..17ce32b 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -17,7 +17,7 @@ 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 \
+ 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
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
new file mode 100644
index 0000000..5f914fa
--- /dev/null
+++ b/src/backend/commands/seclabel.c
@@ -0,0 +1,271 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_seclabel.h"
+#include "commands/seclabel.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+/*
+ * GetLocalSecLabel
+ *
+ * It tries to look up a security label entry for the given OID of the
+ * relation, object itself and sub identifier (if needed) from the
+ * pg_seclabel system catalog.
+ * It returns NULL, if no valid entry. Elsewhere, it returns a security
+ * label of the specified object.
+ */
+static char *
+GetLocalSecLabel(const ObjectAddress *object, const char *tag)
+{
+ Relation pg_seclabel;
+ ScanKeyData keys[4];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull;
+ char *seclabel = NULL;
+
+ 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;
+}
+
+/*
+ * SetLocalSecLabel
+ *
+ * It tries to insert/update/delete a security label for the given OID
+ * of the relation, object itself and sub identifier (if needed) on the
+ * pg_seclabel system catalog.
+ * If given 'new_label' is NULL, it tries to delete the specified entry.
+ * Elsewhere, it tries to insert (if no specified entry now) or update
+ * security label of the specified entry.
+ */
+static void
+SetLocalSecLabel(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];
+
+ 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);
+}
+
+/*
+ * DeleteLocalSecLabel
+ *
+ * It tries to delete entries of security labels for the given OID
+ * of the relation, object itself and sub identifier (if needed) on
+ * the pg_seclabel system catalog.
+ * If given 'objectSubId' is 0, all security labels matching with
+ * classId and objectId will be removed.
+ */
+static void
+DeleteLocalSecLabel(const ObjectAddress *object)
+{
+ Relation pg_seclabel;
+ ScanKeyData keys[3];
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ int nkeys = 0;
+
+ ScanKeyInit(&keys[nkeys++],
+ Anum_pg_seclabel_reloid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&keys[nkeys++],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyInit(&keys[nkeys++],
+ Anum_pg_seclabel_subid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ }
+
+ 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);
+}
+
+/*
+ * GetSecurityLabel
+ *
+ * An internal API to get a security label of the specified object.
+ */
+char *
+GetSecurityLabel(const ObjectAddress *object, const char *tag)
+{
+ Assert(!IsSharedRelation(object->classId));
+
+ return GetLocalSecLabel(object, tag);
+}
+
+/*
+ * SetSecurityLabel
+ *
+ * An internal API to set a security label of the specified object.
+ */
+void
+SetSecurityLabel(const ObjectAddress *object,
+ const char *tag, const char *seclabel)
+{
+ Assert(!IsSharedRelation(object->classId));
+
+ SetLocalSecLabel(object, tag, seclabel);
+}
+
+/*
+ * DeleteSecurityLabel
+ *
+ * An internal API to delete all the security labels of the specified object.
+ */
+void
+DeleteSecurityLabel(const ObjectAddress *object)
+{
+ /* Right now, nothing to do for the shared relation */
+ if (IsSharedRelation(object->classId))
+ return;
+
+ DeleteLocalSecLabel(object);
+}
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 4f437fd..a297402 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -281,6 +281,9 @@ DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre
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
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
new file mode 100644
index 0000000..7c1255c
--- /dev/null
+++ b/src/include/catalog/pg_seclabel.h
@@ -0,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 */
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index fb6f678..fa47938 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -45,6 +45,7 @@ DECLARE_TOAST(pg_constraint, 2832, 2833);
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);
diff --git a/src/include/commands/seclabel.h b/src/include/commands/seclabel.h
new file mode 100644
index 0000000..b69fc07
--- /dev/null
+++ b/src/include/commands/seclabel.h
@@ -0,0 +1,25 @@
+/*
+ * 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/dependency.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);
+
+#endif /* SECLABEL_H */
+
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1d9e110..9596b0b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -114,6 +114,7 @@ SELECT relname, relhasindex
pg_pltemplate | t
pg_proc | t
pg_rewrite | t
+ pg_seclabel | t
pg_shdepend | t
pg_shdescription | t
pg_statistic | t
@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have
2010/7/26 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patches are revised ones, as follows.
I think this is pretty good, and I'm generally in favor of committing
it. Some concerns:
1. Since nobody has violently objected to the comment.c refactoring
patch I recently proposed, I'm hopeful that can go in. And if that's
the case, then I'd prefer to see that committed first, and then rework
this to use that code. That would eliminate some code here, and it
would also make it much easier to support labels on other types of
objects.
2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.
3. Is it really appropriate for ExecRelationSecLabel() to have an
"Exec" prefix? I don't think so.
4. Please get rid of the nkeys++ stuff in DeleteLocalSecLabel() and
just use fixed offsets as we do everywhere else.
5. Why do we think that the relabel hook needs to be passed the number
of expected parents?
6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.
8. Generally, the English in both the comments and documentation needs
work. However, we can address that problem when we're closer to
commit.
I am going to mark this Returned with Feedback because I don't believe
it's realistic to get the comment code committed in the next week,
rework this patch, and then get this patch committed also. However,
I'm feeling pretty good about this effort and I think we're making
good progress toward getting this done.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Thanks for your reviewing.
On Mon, 9 Aug 2010 16:02:12 -0400
Robert Haas <robertmhaas@gmail.com> wrote:
2010/7/26 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patches are revised ones, as follows.
I think this is pretty good, and I'm generally in favor of committing
it. Some concerns:1. Since nobody has violently objected to the comment.c refactoring
patch I recently proposed, I'm hopeful that can go in. And if that's
the case, then I'd prefer to see that committed first, and then rework
this to use that code. That would eliminate some code here, and it
would also make it much easier to support labels on other types of
objects.
It seems to me fair enough. This parse_object.c can also provide
a facility to resolve the name of object to be labeled.
2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.
In the feature, I plan to assign security labels on the shared database
objects such as pg_database. The "local" is a contradistinction
towards these "shared" objects.
3. Is it really appropriate for ExecRelationSecLabel() to have an
"Exec" prefix? I don't think so.
I don't have any preferences about
4. Please get rid of the nkeys++ stuff in DeleteLocalSecLabel() and
just use fixed offsets as we do everywhere else.
OK, I'll fix it.
5. Why do we think that the relabel hook needs to be passed the number
of expected parents?
We need to prevent relabeling on inherited relations/columns from
the multiple origin, like ALTER RENAME TO.
It requires to pass the expected parents into the provider, or
to check it in the caller.
6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.
Starting with "unlabeled" is wrong, because it does not distinguish
an object created by security sensitive users and insensitive users,
for example. It is similar to relation's relowner is InvalidOid.
I plan the security provider hook on the creation time works two things.
1. It checks user's privilege to create this object.
2. It returns security labels to be assigned.
Then, the caller assigns these returned labels on the new object,
if one or more valid labels are returned.
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.
OK, I'll try to describe with appropriate granularity.
Do we need an independent section in addition to the introduction of
SECURITY LABEL syntax?
8. Generally, the English in both the comments and documentation needs
work. However, we can address that problem when we're closer to
commit.
OK
BTW, I'll go on the area where internet unconnectable during next
two days. Perhaps, my reply will run late.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
2010/8/9 <kaigai@kaigai.gr.jp>:
2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.In the feature, I plan to assign security labels on the shared database
objects such as pg_database. The "local" is a contradistinction
towards these "shared" objects.
Oh, I see. I don't think that's entirely clear: and in any event it
seems a bit premature, since we're not at that point yet. Let's just
get rid of this stuff for now as I suggested.
5. Why do we think that the relabel hook needs to be passed the number
of expected parents?We need to prevent relabeling on inherited relations/columns from
the multiple origin, like ALTER RENAME TO.
It requires to pass the expected parents into the provider, or
to check it in the caller.
Please explain further. I don't understand.
6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.Starting with "unlabeled" is wrong, because it does not distinguish
an object created by security sensitive users and insensitive users,
for example. It is similar to relation's relowner is InvalidOid.I plan the security provider hook on the creation time works two things.
1. It checks user's privilege to create this object.
2. It returns security labels to be assigned.Then, the caller assigns these returned labels on the new object,
if one or more valid labels are returned.
OK, let's give that a try and see how it looks. I don't think that's
in this version of the patch, right?
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.OK, I'll try to describe with appropriate granularity.
Do we need an independent section in addition to the introduction of
SECURITY LABEL syntax?
I think so. I suggest a new chapter called "Enhanced Security
Providers" just after "Database Roles and Privileges".
BTW, I'll go on the area where internet unconnectable during next
two days. Perhaps, my reply will run late.
No problem.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/08/10 8:39), Robert Haas wrote:
2010/8/9<kaigai@kaigai.gr.jp>:
2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.In the feature, I plan to assign security labels on the shared database
objects such as pg_database. The "local" is a contradistinction
towards these "shared" objects.Oh, I see. I don't think that's entirely clear: and in any event it
seems a bit premature, since we're not at that point yet. Let's just
get rid of this stuff for now as I suggested.
OK. We can add this supportanytime we need it.
5. Why do we think that the relabel hook needs to be passed the number
of expected parents?We need to prevent relabeling on inherited relations/columns from
the multiple origin, like ALTER RENAME TO.
It requires to pass the expected parents into the provider, or
to check it in the caller.Please explain further. I don't understand.
Yep, rte->requiredPerms of inherited relations are cleared on the
expand_inherited_rtentry() since the v9.0, so we cannot know what
kind of accesses are required on the individual child relations.
It needs the inherited relations/columns being labeled with same
security label of their parent, because SE-PgSQL always makes same
access control decision on same security labels.
Thus, we want to check whether the relabeling operation breaks the
uniqueness of security label within a certain inheritance tree, or not.
Here is the logic to check relabeling on relation/column.
http://code.google.com/p/sepgsql/source/browse/trunk/sepgsql/hooks.c#254
It checks two things.
1) The given relation/column must be the origin of inheritance tree
when expected_parents = 0.
2) The given relation/column must not belong to multiple inheritance
tree.
So, the hook need to provide the expected_parents for each relations/columns.
6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.Starting with "unlabeled" is wrong, because it does not distinguish
an object created by security sensitive users and insensitive users,
for example. It is similar to relation's relowner is InvalidOid.I plan the security provider hook on the creation time works two things.
1. It checks user's privilege to create this object.
2. It returns security labels to be assigned.Then, the caller assigns these returned labels on the new object,
if one or more valid labels are returned.OK, let's give that a try and see how it looks. I don't think that's
in this version of the patch, right?
Yes, this version of the patch didn't support labeling on creation time
of database objects. It shall be added in separated patch.
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.OK, I'll try to describe with appropriate granularity.
Do we need an independent section in addition to the introduction of
SECURITY LABEL syntax?I think so. I suggest a new chapter called "Enhanced Security
Providers" just after "Database Roles and Privileges".
OK,
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
* KaiGai Kohei (kaigai@kaigai.gr.jp) wrote:
Yep, rte->requiredPerms of inherited relations are cleared on the
expand_inherited_rtentry() since the v9.0, so we cannot know what
kind of accesses are required on the individual child relations.
This is really a PG issue and decision, in my view. We're moving more
and more towards a decision that inherited relations are really just the
same relation but broken up per tables (ala "true" partitioning). As
such, PG has chosen to view them as the same wrt permissions checking.
I don't think we should make a different decision for security labels.
If you don't want people who have access to the parent to have access to
the children, then you shouldn't be making them children.
Thanks,
Stephen
(2010/08/15 9:16), Stephen Frost wrote:
* KaiGai Kohei (kaigai@kaigai.gr.jp) wrote:
Yep, rte->requiredPerms of inherited relations are cleared on the
expand_inherited_rtentry() since the v9.0, so we cannot know what
kind of accesses are required on the individual child relations.This is really a PG issue and decision, in my view. We're moving more
and more towards a decision that inherited relations are really just the
same relation but broken up per tables (ala "true" partitioning). As
such, PG has chosen to view them as the same wrt permissions checking.
I don't think we should make a different decision for security labels.
If you don't want people who have access to the parent to have access to
the children, then you shouldn't be making them children.
No, what I want to do is people have identical access rights on both of
the parent and children. If they have always same label, SE-PgSQL always
makes same access control decision. This behavior is suitable to the
standpoint that inherited relations are really just the same relation
of the parent. For this purpose, I want to enforce a unique label on
a certain inheritance tree.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
2010/8/14 KaiGai Kohei <kaigai@kaigai.gr.jp>:
(2010/08/15 9:16), Stephen Frost wrote:
* KaiGai Kohei (kaigai@kaigai.gr.jp) wrote:
Yep, rte->requiredPerms of inherited relations are cleared on the
expand_inherited_rtentry() since the v9.0, so we cannot know what
kind of accesses are required on the individual child relations.This is really a PG issue and decision, in my view. We're moving more
and more towards a decision that inherited relations are really just the
same relation but broken up per tables (ala "true" partitioning). As
such, PG has chosen to view them as the same wrt permissions checking.
I don't think we should make a different decision for security labels.
If you don't want people who have access to the parent to have access to
the children, then you shouldn't be making them children.No, what I want to do is people have identical access rights on both of
the parent and children. If they have always same label, SE-PgSQL always
makes same access control decision. This behavior is suitable to the
standpoint that inherited relations are really just the same relation
of the parent. For this purpose, I want to enforce a unique label on
a certain inheritance tree.
This seems like a bad idea to me, too. I think it's arguable whether
access to the children should be controlled by the parent's label or
the child's label, but enforcing that the entire inheritance hierarchy
is identically labeled seems like a pointless restriction. As Stephen
points out, it's also wildly inconsistent with the way we currently
handle it.
There's also the problem that the hooks we're talking about here are
inadequate to support such a restriction anyway. You'd need some kind
of a hook in ALTER TABLE ... [NO] INHERIT, at a minimum. As has been
mentioned many times before in reviewing many generations of
SE-PostgreSQL patches, we're not going to get into the business of
re-engineering our security architecture just because you would have
designed it differently. Inventing something that's randomly
different will not only make the code ugly and hard to maintain; it
will also be confusing and difficult to manage for end-users.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/08/15 9:55), Robert Haas wrote:
2010/8/14 KaiGai Kohei<kaigai@kaigai.gr.jp>:
(2010/08/15 9:16), Stephen Frost wrote:
* KaiGai Kohei (kaigai@kaigai.gr.jp) wrote:
Yep, rte->requiredPerms of inherited relations are cleared on the
expand_inherited_rtentry() since the v9.0, so we cannot know what
kind of accesses are required on the individual child relations.This is really a PG issue and decision, in my view. We're moving more
and more towards a decision that inherited relations are really just the
same relation but broken up per tables (ala "true" partitioning). As
such, PG has chosen to view them as the same wrt permissions checking.
I don't think we should make a different decision for security labels.
If you don't want people who have access to the parent to have access to
the children, then you shouldn't be making them children.No, what I want to do is people have identical access rights on both of
the parent and children. If they have always same label, SE-PgSQL always
makes same access control decision. This behavior is suitable to the
standpoint that inherited relations are really just the same relation
of the parent. For this purpose, I want to enforce a unique label on
a certain inheritance tree.This seems like a bad idea to me, too. I think it's arguable whether
access to the children should be controlled by the parent's label or
the child's label, but enforcing that the entire inheritance hierarchy
is identically labeled seems like a pointless restriction. As Stephen
points out, it's also wildly inconsistent with the way we currently
handle it.There's also the problem that the hooks we're talking about here are
inadequate to support such a restriction anyway. You'd need some kind
of a hook in ALTER TABLE ... [NO] INHERIT, at a minimum. As has been
mentioned many times before in reviewing many generations of
SE-PostgreSQL patches, we're not going to get into the business of
re-engineering our security architecture just because you would have
designed it differently. Inventing something that's randomly
different will not only make the code ugly and hard to maintain; it
will also be confusing and difficult to manage for end-users.
Indeed, our existing mechanism allows to assign individual privileges
on child tables, even if it is in a certain inheritance hierarchy.
The purpose of this restriction is to ensure an access control decision
using parent's label being also consistent on child tables.
If we control accesses on child tables using child's label, no need to
restrict an identical label within an entire inheritance hierarchy.
But it needs to provide the original rte->requiredPerms of child tables.
Now it is cleared at expand_inherited_rtentry(), so we have no way to
control accesses on child tables using child's label. :(
From viewpoint of MAC, both of the following SQLs should be denied,
when accesses on parent_tbl is allowed, but child_tbl is denied.
1) SELECT * FROM parent_tbl;
2) SELECT * FROM child_tbl;
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
KaiGai,
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
The purpose of this restriction is to ensure an access control decision
using parent's label being also consistent on child tables.
Robert and I understand the concern that you have. The answer, at least
for now, is that we don't agree with you. PG doesn't consider child
tables to be independent objects when they're being accessed through the
parent. As such, they don't have their own permissions checking.
If we control accesses on child tables using child's label, no need to
restrict an identical label within an entire inheritance hierarchy.
But it needs to provide the original rte->requiredPerms of child tables.
Now it is cleared at expand_inherited_rtentry(), so we have no way to
control accesses on child tables using child's label. :(
If you want to argue that we should care about the childs permissions,
or do something different with regard to inheritance, then you need to
make that argument for all of PG, not just try to do what you think is
right in the security definer framework.
From viewpoint of MAC, both of the following SQLs should be denied,
when accesses on parent_tbl is allowed, but child_tbl is denied.
KaiGai, this is not a MAC vs. DAC difference. This is a question of
"what is an object" and if a child table is really an independent object
from a parent table. In PG, we've decided they're not. We should
probably do more to make that clearer in PG, rather than have different
parts of the system treat them differently.
Thanks,
Stephen
Stephen Frost <sfrost@snowman.net> wrote:
PG doesn't consider child tables to be independent objects when
they're being accessed through the parent. As such, they don't
have their own permissions checking.
I've been thinking about this from the perspective of possible
eventual use by the Wisconsin Courts, and want to throw out my
perspective on long-term direction here, without venturing any
opinion on the immediate issue.
It wouldn't be totally insane for the courts to some day use
inheritance to deal with court cases. All court cases have much in
common and have the same basic structure, but specific case types
need to store some additional information. If we did that, we would
want different permissions on different case types -- for example,
juvenile cases are not open to the public as many case types are.
We would also need the ability to revoke public permissions on
specific rows, as judges can seal cases or various pieces of
information on a case (like the address of a stalker victim).
The point being, we would want a structure something like (picking a
few of our case types):
Case
\_ ChargeableCase
\_ FelonyCase
\_ MisdemeanorCase
\_ CivilForfeitureCase
\_ JuvenileCase
\_ NonchargableCase
\_ CivilCase
\_ SmallClaimsCase
\_ ProbateCase
\_ MentalCommitmentCase
Just because most of these case types are a matter of public record
and subject to open records law disclosure requests (which we
largely avoid by putting what we can on the web site), juvenile and
mental commitment cases are confidential; unless you need to handle
something related to such a case to support its progress through the
courts, you're not supposed to see anything beyond such sketchy
information as the existence of the case number, a filing date, and
a caption where names are replaced by initials (e.g., "In the
interest of E.B.") -- and even that information is held back from
the web site because of possible "data mining" attacks.
Many of the features KaiGai has discussed would fit nicely with
court requirements -- and might even be prerequisites for
considering moving security to the database level. Mandating
identical security for all tables in a hierarchy would be a problem.
We'd want to be able to `grant select on "Case" to public` and then
`revoke select on "JuvenileCase", "MentalCommitmentCase" from
public` and have those cases disappear from selects against the
ancestor levels unless the user has the appropriate permission. Or
less convenient, but still feasible, would be to grant nothing at
the ancestor levels, and grant what is appropriate at each child
level and have that affect the results of a query against the
ancestor.
Of course, if one was really careful, this could all be done by
adding views with appropriate permissions and blocking access to the
underlying ancestor tables, but that seems like a lot more work and
rather more error prone.
-Kevin
* Kevin Grittner (Kevin.Grittner@wicourts.gov) wrote:
Many of the features KaiGai has discussed would fit nicely with
court requirements -- and might even be prerequisites for
considering moving security to the database level. Mandating
identical security for all tables in a hierarchy would be a problem.
What you're describing isn't how inheiritance used to work in PG anyway,
so it's not really like we've made things worse. What used to happen is
that if your query against the parent table happened to hit a table you
didn't have access to, it'd fail outright with a permissions error, not
just skip over the things you didn't have access to. That certainly
wasn't ideal.
I think what you're really looking for is RLS (Row-Level Security),
which I think we would want to implement independently of the
inheiritance system (though it'd have to work with it, of course).
That's certainly something that I think would be great to have in PG and
would ideally be something which would address both of your "sometimes
everything is public except rows which look like X" and "all of these
types are non-public" situations.
I don't believe it's something that could be addressed *only* by
inheiritance though, in any case.
Thanks,
Stephen
(2010/08/16 22:14), Stephen Frost wrote:
KaiGai,
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
The purpose of this restriction is to ensure an access control decision
using parent's label being also consistent on child tables.Robert and I understand the concern that you have. The answer, at least
for now, is that we don't agree with you. PG doesn't consider child
tables to be independent objects when they're being accessed through the
parent. As such, they don't have their own permissions checking.If we control accesses on child tables using child's label, no need to
restrict an identical label within an entire inheritance hierarchy.
But it needs to provide the original rte->requiredPerms of child tables.
Now it is cleared at expand_inherited_rtentry(), so we have no way to
control accesses on child tables using child's label. :(If you want to argue that we should care about the childs permissions,
or do something different with regard to inheritance, then you need to
make that argument for all of PG, not just try to do what you think is
right in the security definer framework.From viewpoint of MAC, both of the following SQLs should be denied,
when accesses on parent_tbl is allowed, but child_tbl is denied.
KaiGai, this is not a MAC vs. DAC difference. This is a question of
"what is an object" and if a child table is really an independent object
from a parent table. In PG, we've decided they're not. We should
probably do more to make that clearer in PG, rather than have different
parts of the system treat them differently.
Ahh, yes, the question is "what is an object", not a "MAC vs DAC".
Indeed, PG does not try to handle child table as an independent object
from a parent table. However, if so, it seems to me strange that we can
assign individual ownership and access privileges on child tables.
If we stand on the perspective that child tables are a part of the
parent table, isn't it necessary to keep same ownership and access
privileges between parent and children? It seems to me the current
implementation is in the halfway from the perspective of child
tables as independent object to the perspective of child tables as
a part of parent table.
If PG can keep consistency of ownership and access privileges between
parent and children, it is quite natural we keep consistency of labels,
isn't it?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
Ahh, yes, the question is "what is an object", not a "MAC vs DAC".
Indeed, PG does not try to handle child table as an independent object
from a parent table. However, if so, it seems to me strange that we can
assign individual ownership and access privileges on child tables.
I tend to agree. Perhaps we should bring up, in an independent thread,
the question of if that really makes sense or if we should do something
to prevent it (or at least issue a warning when we detect it).
If we stand on the perspective that child tables are a part of the
parent table, isn't it necessary to keep same ownership and access
privileges between parent and children? It seems to me the current
implementation is in the halfway from the perspective of child
tables as independent object to the perspective of child tables as
a part of parent table.
I tend to agree- PG isn't doing this as cleanly as it should.
If PG can keep consistency of ownership and access privileges between
parent and children, it is quite natural we keep consistency of labels,
isn't it?
Yes, but that's something that should be dealt with in PG and not as
part of an external security infrastructure. For example, PG could just
force that the same label is applied to a child table when it's made a
child of a particular parent, perhaps with a check to the external
security system to see if there's a problem changing whatever label is
on the child table before it's changed to be that of the parent, but
once it's a child of the parent (if that operation was permitted and was
successful), it no longer has its own 'identity'.
Let's not build the complication of dealing with inheiritance/child
relations into the external security system when we're in the middle of
trying to get rid of that distinction in core PG though.
Thanks,
Stephen
(2010/08/17 9:51), Stephen Frost wrote:
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
Ahh, yes, the question is "what is an object", not a "MAC vs DAC".
Indeed, PG does not try to handle child table as an independent object
from a parent table. However, if so, it seems to me strange that we can
assign individual ownership and access privileges on child tables.I tend to agree. Perhaps we should bring up, in an independent thread,
the question of if that really makes sense or if we should do something
to prevent it (or at least issue a warning when we detect it).If we stand on the perspective that child tables are a part of the
parent table, isn't it necessary to keep same ownership and access
privileges between parent and children? It seems to me the current
implementation is in the halfway from the perspective of child
tables as independent object to the perspective of child tables as
a part of parent table.I tend to agree- PG isn't doing this as cleanly as it should.
If PG can keep consistency of ownership and access privileges between
parent and children, it is quite natural we keep consistency of labels,
isn't it?Yes, but that's something that should be dealt with in PG and not as
part of an external security infrastructure. For example, PG could just
force that the same label is applied to a child table when it's made a
child of a particular parent, perhaps with a check to the external
security system to see if there's a problem changing whatever label is
on the child table before it's changed to be that of the parent, but
once it's a child of the parent (if that operation was permitted and was
successful), it no longer has its own 'identity'.Let's not build the complication of dealing with inheiritance/child
relations into the external security system when we're in the middle of
trying to get rid of that distinction in core PG though.
I also agree the idea that PG core handles the recursion of inheritance
hierarchy and enforce same label between them. The reason why I handled
it within the module is the core does not enforce same labels.
OK, I'll rid 'expected_parents' argument from the security hook on
relabeling. Right now, it is incomplete, but should be fixed up in
the future.
In addition, I'll also post a design proposal to keep consistency of
ownership and access privileges between parent and children.
Please also wait for a while.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Stephen Frost <sfrost@snowman.net> writes:
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
Indeed, PG does not try to handle child table as an independent object
from a parent table. However, if so, it seems to me strange that we can
assign individual ownership and access privileges on child tables.
I tend to agree. Perhaps we should bring up, in an independent thread,
the question of if that really makes sense or if we should do something
to prevent it (or at least issue a warning when we detect it).
The reason there is still some value in setting permissions state on a
child table is that that controls what happens when you address the
child table directly, rather than implicitly by querying its parent.
regards, tom lane
(2010/08/17 11:58), Tom Lane wrote:
Stephen Frost<sfrost@snowman.net> writes:
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
Indeed, PG does not try to handle child table as an independent object
from a parent table. However, if so, it seems to me strange that we can
assign individual ownership and access privileges on child tables.I tend to agree. Perhaps we should bring up, in an independent thread,
the question of if that really makes sense or if we should do something
to prevent it (or at least issue a warning when we detect it).The reason there is still some value in setting permissions state on a
child table is that that controls what happens when you address the
child table directly, rather than implicitly by querying its parent.
However, isn't it strange if we stand on the perspective that child table
is a part of parent object? It means an object have multiple properties
depending on the context.
If we want to allow someone to reference a part of the table (= child table),
I think VIEW is more appropriate and flexible tool.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
On tis, 2010-08-17 at 13:00 +0900, KaiGai Kohei wrote:
However, isn't it strange if we stand on the perspective that child
table is a part of parent object? It means an object have multiple
properties depending on the context.
Such is the nature of inheritance, isn't it?
(2010/08/17 13:28), Peter Eisentraut wrote:
On tis, 2010-08-17 at 13:00 +0900, KaiGai Kohei wrote:
However, isn't it strange if we stand on the perspective that child
table is a part of parent object? It means an object have multiple
properties depending on the context.Such is the nature of inheritance, isn't it?
Yep, it will return different set of user data depending on the table
queried, when we reference either parent or child table.
But it seems to me too stretch interpretation to apply this behavior
on metadata of the tables also.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Stephen Frost <sfrost@snowman.net> wrote:
Let's not build the complication of dealing with inheiritance/
child relations into the external security system when we're in
the middle of trying to get rid of that distinction in core PG
though.
I didn't realize we were trying to do that. I know we're working on
making partitioning easier, but there hasn't been a decision to stop
supporting other uses of inheritance, has there?
-Kevin
* Kevin Grittner (Kevin.Grittner@wicourts.gov) wrote:
Stephen Frost <sfrost@snowman.net> wrote:
Let's not build the complication of dealing with inheiritance/
child relations into the external security system when we're in
the middle of trying to get rid of that distinction in core PG
though.I didn't realize we were trying to do that. I know we're working on
making partitioning easier, but there hasn't been a decision to stop
supporting other uses of inheritance, has there?
No.. and I'm not sure we ever would. What we *have* done is removed
all permissions checking on child tables when a parent is being
queried..
Stephen
Stephen Frost <sfrost@snowman.net> wrote:
No.. and I'm not sure we ever would. What we *have* done is
removed all permissions checking on child tables when a parent is
being queried..
OK, that clarifies things. Thanks.
So, essentially that means that you need to set all ancestor levels
to something at least as strict as the intersection of all the
permissions on lower levels to avoid exposing something through an
ancestor which is restricted in a descendant. And you'd better
trust the owner of any table you extend, because they can bypass any
attempt to restrict access to the table you create which extends
theirs.
I hope those security implications are well documented.
-Kevin
On Tue, Aug 17, 2010 at 1:50 PM, Stephen Frost <sfrost@snowman.net> wrote:
No.. and I'm not sure we ever would. What we *have* done is removed
all permissions checking on child tables when a parent is being
queried..
Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment. Anyway, I wonder if it would be sensible to try to
adjust the structure of the DAC permissions checks so enhanced
security providers can make their own decision about how to handle
this case.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
* Robert Haas (robertmhaas@gmail.com) wrote:
On Tue, Aug 17, 2010 at 1:50 PM, Stephen Frost <sfrost@snowman.net> wrote:
No.. and I'm not sure we ever would. What we *have* done is removed
all permissions checking on child tables when a parent is being
queried..Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment. Anyway, I wonder if it would be sensible to try to
adjust the structure of the DAC permissions checks so enhanced
security providers can make their own decision about how to handle
this case.
To be honest, I don't really like the way this is done at all. I'd
rather have it such that if and when a table is made a child of another
table, it should inherit the permissions of the parent and be kept that
way, or it should be completely independent (which is the situation we
used to have), or, last resort, we should complain when they don't
match.
Or we could just not error when we hit a child table that the caller
doesn't have access to (but also not return the records). The problem
is that we've got different users that want to use inheiritance for very
different purposes and we havn't got a way to address all of them. I do
worry that we're going to regret making the change to not check
permissions on child tables, but at the same time, any query which would
have been impacted by that would have failed, so that really begs the
question of "do people really use/want different permissions on child
tables than the parent?". I tend to think 'no', and would rather force
them and keep them the same, but maybe that's just me..
Thanks,
Stephen
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Aug 17, 2010 at 1:50 PM, Stephen Frost <sfrost@snowman.net> wrote:
No.. �and I'm not sure we ever would. �What we *have* done is removed
all permissions checking on child tables when a parent is being
queried..
Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment.
IIRC, the reason we did it was that we decided the SQL spec requires it.
So there's not a lot of point in debating the issue, unless you can
convince us we misread the spec.
regards, tom lane
(2010/08/18 3:07), Robert Haas wrote:
On Tue, Aug 17, 2010 at 1:50 PM, Stephen Frost<sfrost@snowman.net> wrote:
No.. and I'm not sure we ever would. What we *have* done is removed
all permissions checking on child tables when a parent is being
queried..Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment. Anyway, I wonder if it would be sensible to try to
adjust the structure of the DAC permissions checks so enhanced
security providers can make their own decision about how to handle
this case.
As long as we handle child tables in consistent way, here is no matter
for a MAC environment also. As Stephen mentioned, the question was
"what is an object". So, I want child tables being either a part of
parent table or an independent object from its parent.
In the first case, child tables need to have same security properties
(ownership, access privileges, security labels) with its parent.
In the later case, we need to check permissions on child tables also
when we query on the parent, but it is an old perspective now.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
* Tom Lane (tgl@sss.pgh.pa.us) wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment.IIRC, the reason we did it was that we decided the SQL spec requires it.
So there's not a lot of point in debating the issue, unless you can
convince us we misread the spec.
I've not gone through the spec with regard to this (yet..), but I think
we need to consider the whole 'principle of least surprise' here,
regardless of what the spec says. For one thing, this isn't how older
versions of PG behaved and while I doubt anyone intended to rely on that
behavior, it makes me nervous that someone, somewhere, unintentionally
relies on it.
What I'm thinking of is something like a warning if the permissions on
the child don't match those of the parent when the relationship is
created, or maybe forcibly setting the permissions on the child (with a
NOTICE), so it's at least clear what is going on. Or perhaps, set the
permissions on the child only if it doesn't have permissions (with the
NOTICE), and issue a WARNING if the child already has permissions set.
Perhaps also a WARNING if someone changes the permissions on a child
after the relationship has been created too, but let it happen in case
someone really wants it..
I dunno. None of the above makes me feel very comfortable from a
security perspective because I'm concerned any of the above could too
easily be overlooked by someone upgrading. It also doesn't really
address the concern that, at some point, a child table could have
different permissions than a parent table. If we forcibly set the
permissions on the child, or ERROR'd if the permissions weren't either
the same or empty on the child, and then ERROR'd if someone tried to
change the child's permissions later, I'd be happier.
I don't really want to force people doing routine partition additions
to have to set the permissions on the child before adding it, but I
also don't want to have to figure out "are these two sets of permissions
identical", since that's not really trivial to determine. We do have
default permissions now though, so maybe requiring the permissions be
the same from the get-go is the right idea. Of course, if we change the
permissions on the child when the inheiritance relationship is created,
we'll need to update those perms every time the parents perms are
changed.
Just my 2c.
Thanks,
Stephen
(2010/08/18 9:04), Stephen Frost wrote:
* Tom Lane (tgl@sss.pgh.pa.us) wrote:
Robert Haas<robertmhaas@gmail.com> writes:
Yeah. I'm not totally sure that is sensible for a MAC environment.
Heck, it's arguably incorrect (though perhaps quite convenient) in a
DAC environment.IIRC, the reason we did it was that we decided the SQL spec requires it.
So there's not a lot of point in debating the issue, unless you can
convince us we misread the spec.I've not gone through the spec with regard to this (yet..), but I think
we need to consider the whole 'principle of least surprise' here,
regardless of what the spec says. For one thing, this isn't how older
versions of PG behaved and while I doubt anyone intended to rely on that
behavior, it makes me nervous that someone, somewhere, unintentionally
relies on it.
I believed that table inheritance is a unique feature in PostgreSQL.
Does the SQL spec really mention about whether a child table is an
independent table object, or not?
Or, are you talking about the behavior that parent's permission also
controls accesses on child tables? If so, all of us already agreed.
What I'm thinking of is something like a warning if the permissions on
the child don't match those of the parent when the relationship is
created, or maybe forcibly setting the permissions on the child (with a
NOTICE), so it's at least clear what is going on. Or perhaps, set the
permissions on the child only if it doesn't have permissions (with the
NOTICE), and issue a WARNING if the child already has permissions set.
Perhaps also a WARNING if someone changes the permissions on a child
after the relationship has been created too, but let it happen in case
someone really wants it..I dunno. None of the above makes me feel very comfortable from a
security perspective because I'm concerned any of the above could too
easily be overlooked by someone upgrading. It also doesn't really
address the concern that, at some point, a child table could have
different permissions than a parent table. If we forcibly set the
permissions on the child, or ERROR'd if the permissions weren't either
the same or empty on the child, and then ERROR'd if someone tried to
change the child's permissions later, I'd be happier.
I also think ERROR should be raised when user tries to assign different
security properties on child tables from its parent. At least, I think
it should be configurable using a guc variable.
If WARNING/NOTICE, we can easily break consistency of the permissions...
I don't really want to force people doing routine partition additions
to have to set the permissions on the child before adding it, but I
also don't want to have to figure out "are these two sets of permissions
identical", since that's not really trivial to determine. We do have
default permissions now though, so maybe requiring the permissions be
the same from the get-go is the right idea. Of course, if we change the
permissions on the child when the inheiritance relationship is created,
we'll need to update those perms every time the parents perms are
changed.
I also think it is a good idea to copy permissions from the parent when
we try to define an inheritance relationship. It obviously reduces user's
routine task on defining many of child tables. It seems to me a case when
we provide a NOTICE to users, if permissions of child table is overwritten.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
KaiGai,
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
I believed that table inheritance is a unique feature in PostgreSQL.
It's actually not..
Does the SQL spec really mention about whether a child table is an
independent table object, or not?
The SQL spec does discuss 'subtables' and inheiritance. It also does
describe some information under 'Access Rules' regarding these
sub-tables (check the 'table definition' clause). I've been looking at
them and trying to make some sense out of what I see.
Thanks,
Stephen
2010/8/17 KaiGai Kohei <kaigai@ak.jp.nec.com>:
I dunno. None of the above makes me feel very comfortable from a
security perspective because I'm concerned any of the above could too
easily be overlooked by someone upgrading. It also doesn't really
address the concern that, at some point, a child table could have
different permissions than a parent table. If we forcibly set the
permissions on the child, or ERROR'd if the permissions weren't either
the same or empty on the child, and then ERROR'd if someone tried to
change the child's permissions later, I'd be happier.I also think ERROR should be raised when user tries to assign different
security properties on child tables from its parent. At least, I think
it should be configurable using a guc variable.
If C1, C2, and C3 inherit from P, it's perfectly reasonable to grant
permissions to X on C1 and C2, Y on C3, and Z on C1, C2, C3, and P. I
don't think we should disallow that. Sure, it's possible to do things
that are less sane, but if we put ourselves in the business of
removing useful functionality because it might be misused, we'll put
ourselves out of business.
Having said that, I'm not sure that the same arguments really hold
water in the world of label based security. Suppose we have
compartmentalized security: P is a table of threats, with C1
containing data on nukes, C2 containing data on terrorists, and C3
containing data on foreign militaries. If we create a label for each
of these threat types, we can apply that label to the corresponding
table; but what label shall we assign P? Logically, the label for P
should be set up in such a fashion that the only people who can read P
are those who can read C1, C2, and C3 anyway, but who is to say that
such a label exists? Even if KaiGai's intended implementation of
SE-PostgreSQL supports construction of such a label, who is to say
that EVERY conceivable labeling system will also do so? In fact, it
seems to me that it might be far more reasonable, in a case like this,
to ignore the *parent* label and look only at each *child* label,
which to me is an argument that we should set this up so as to allow
individual users of this hook to do as they like.
It's also worth pointing out that the hook in ExecCheckRTPerms() does
not presuppose label-based security. It could be used to implement
some other policy altogether, which only strengthens the argument that
we can't know how the user of the hook wants to handle these cases.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/08/18 12:02), Robert Haas wrote:
2010/8/17 KaiGai Kohei<kaigai@ak.jp.nec.com>:
I dunno. None of the above makes me feel very comfortable from a
security perspective because I'm concerned any of the above could too
easily be overlooked by someone upgrading. It also doesn't really
address the concern that, at some point, a child table could have
different permissions than a parent table. If we forcibly set the
permissions on the child, or ERROR'd if the permissions weren't either
the same or empty on the child, and then ERROR'd if someone tried to
change the child's permissions later, I'd be happier.I also think ERROR should be raised when user tries to assign different
security properties on child tables from its parent. At least, I think
it should be configurable using a guc variable.If C1, C2, and C3 inherit from P, it's perfectly reasonable to grant
permissions to X on C1 and C2, Y on C3, and Z on C1, C2, C3, and P. I
don't think we should disallow that. Sure, it's possible to do things
that are less sane, but if we put ourselves in the business of
removing useful functionality because it might be misused, we'll put
ourselves out of business.
Hmm. If C1, C2 and C3 are defined to store information for different
categories, but shares common data structure, indeed, it is useful.
Having said that, I'm not sure that the same arguments really hold
water in the world of label based security. Suppose we have
compartmentalized security: P is a table of threats, with C1
containing data on nukes, C2 containing data on terrorists, and C3
containing data on foreign militaries. If we create a label for each
of these threat types, we can apply that label to the corresponding
table; but what label shall we assign P? Logically, the label for P
should be set up in such a fashion that the only people who can read P
are those who can read C1, C2, and C3 anyway, but who is to say that
such a label exists?
Right. If access privileges on P implicitly allow accesses on children,
the P must have a label that only allows people who can access both of
the children. However, in SELinux at least, here is no guarantee that
we can always find out such a label in the security policy installed. :(
Even if KaiGai's intended implementation of
SE-PostgreSQL supports construction of such a label, who is to say
that EVERY conceivable labeling system will also do so? In fact, it
seems to me that it might be far more reasonable, in a case like this,
to ignore the *parent* label and look only at each *child* label,
which to me is an argument that we should set this up so as to allow
individual users of this hook to do as they like.
It will be helpful. If we can check each children's label, no need
to restrict an identical security label within a certain inheritance
hierarchy. Of course, other security module may check permissions
in different basic, but it seems to me characteristics.
It's also worth pointing out that the hook in ExecCheckRTPerms() does
not presuppose label-based security. It could be used to implement
some other policy altogether, which only strengthens the argument that
we can't know how the user of the hook wants to handle these cases.
If rte->requiredPerms would not be cleared, the user of the hook will
be able to check access rights on the child tables, as they like.
How about an idea to add a new flag in RangeTblEntry which shows where
the RangeTblEntry came from, instead of clearing requiredPerms?
If the flag is true, I think ExecCheckRTEPerms() can simply skip checks
on the child tables.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/8/18 KaiGai Kohei <kaigai@ak.jp.nec.com>:
It's also worth pointing out that the hook in ExecCheckRTPerms() does
not presuppose label-based security. It could be used to implement
some other policy altogether, which only strengthens the argument that
we can't know how the user of the hook wants to handle these cases.If rte->requiredPerms would not be cleared, the user of the hook will
be able to check access rights on the child tables, as they like.
How about an idea to add a new flag in RangeTblEntry which shows where
the RangeTblEntry came from, instead of clearing requiredPerms?
If the flag is true, I think ExecCheckRTEPerms() can simply skip checks
on the child tables.
Something along those lines might work, although I haven't yet
scrutinized the code well enough to have a real clear opinion on what
the best way of dealing with this is.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Robert,
* Robert Haas (robertmhaas@gmail.com) wrote:
If C1, C2, and C3 inherit from P, it's perfectly reasonable to grant
permissions to X on C1 and C2, Y on C3, and Z on C1, C2, C3, and P. I
don't think we should disallow that. Sure, it's possible to do things
that are less sane, but if we put ourselves in the business of
removing useful functionality because it might be misused, we'll put
ourselves out of business.Having said that, I'm not sure that the same arguments really hold
water in the world of label based security. Suppose we have
compartmentalized security: P is a table of threats, with C1
containing data on nukes, C2 containing data on terrorists, and C3
containing data on foreign militaries. If we create a label for each
of these threat types, we can apply that label to the corresponding
table; but what label shall we assign P? Logically, the label for P
should be set up in such a fashion that the only people who can read P
are those who can read C1, C2, and C3 anyway, but who is to say that
such a label exists? Even if KaiGai's intended implementation of
SE-PostgreSQL supports construction of such a label, who is to say
that EVERY conceivable labeling system will also do so?
I don't see why using labels in the second case changes anything.
Consider roles. If you only had a role that could see threats, a role
that could see nukes, and a role that could see terrorists, but no role
that could see all of them, it's the same problem. Additionally, this
kind of problem *isn't* typically addressed with the semantics or the
structure of inheiritance- it's done with row-level security and is
completely orthogonal to the inheiritance issue.
Imagine a new table, C4, is added to P and the admin configures it such
that only the 'view_c4' role has access to that child table directly.
Now, Z can see what's in C4 through P, even though Z doesn't have access
to C4. In the old system, if Z's query happened to hit C4, the whole
query would fail but at least Z wouldn't see any C4 data. Other queries
on P done by Z would be fine, so long as they didn't hit C4.
In fact, it
seems to me that it might be far more reasonable, in a case like this,
to ignore the *parent* label and look only at each *child* label,
which to me is an argument that we should set this up so as to allow
individual users of this hook to do as they like.
I think it'd be more reasonable to do this for inheiritance in general,
but the problem is that people use it for partitioning, and there is a
claim out there that it's against what the SQL spec says. The folks
using inheiritance for partitioning would probably prefer to not have to
deal with setting up the permissions on the child tables. I think
that's less of an issue now, but I didn't like the previous behavior
where certain queries would work and certain queries wouldn't work
against the parent table, either.
It's also worth pointing out that the hook in ExecCheckRTPerms() does
not presuppose label-based security. It could be used to implement
some other policy altogether, which only strengthens the argument that
we can't know how the user of the hook wants to handle these cases.
This comes back around, in my view, to the distinction between really
using inheiritance for inheiritance, vs using it for partitioning. If
it's used for partitioning (which certainly seems to be the vast
majority of the cases I've seen it used) then I think it should really
be considered and viewed as a single object to the authentication
system. I don't suppose we're going to get rid of inheiritance for
inheiritance any time soon though.
In the end, I'm thinking that if the external security module wants to
enforce a check against all the children of a parent, they could quite
possibly handle that already and do it in such a way that it won't break
depending on the specific query. To wit, it could query the catalog to
determine if the current table is a parent of any children, and if so,
go check the labels/permissions/etc on those children. I'd much rather
have something where the permissions check either succeeds or fails
against the parent, depending on the permissions of the parent and its
children, than on what the query is itself and what conditionals are
applied to it.
Thanks,
Stephen
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
If rte->requiredPerms would not be cleared, the user of the hook will
be able to check access rights on the child tables, as they like.
This would only be the case for those children which are being touched
in the current query, which would depend on what conditionals are
applied, what the current setting of check_constraints is, and possibly
other factors. I do *not* like this approach.
How about an idea to add a new flag in RangeTblEntry which shows where
the RangeTblEntry came from, instead of clearing requiredPerms?
If the flag is true, I think ExecCheckRTEPerms() can simply skip checks
on the child tables.
How about the external module just checks if the current object being
queried has parents, and if so, goes and checks the
labels/permissions/etc on those children? That way the query either
always fails or never fails for a given caller, rather than sometimes
working and sometimes not depending on the query.
Thanks,
Stephen
On Wed, Aug 18, 2010 at 8:49 AM, Stephen Frost <sfrost@snowman.net> wrote:
In the end, I'm thinking that if the external security module wants to
enforce a check against all the children of a parent, they could quite
possibly handle that already and do it in such a way that it won't break
depending on the specific query. To wit, it could query the catalog to
determine if the current table is a parent of any children, and if so,
go check the labels/permissions/etc on those children. I'd much rather
have something where the permissions check either succeeds or fails
against the parent, depending on the permissions of the parent and its
children, than on what the query is itself and what conditionals are
applied to it.
Interesting idea. Again, I haven't read the code, but seems worth
further investigation, at least.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/08/18 21:52), Stephen Frost wrote:
* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
If rte->requiredPerms would not be cleared, the user of the hook will
be able to check access rights on the child tables, as they like.This would only be the case for those children which are being touched
in the current query, which would depend on what conditionals are
applied, what the current setting of check_constraints is, and possibly
other factors. I do *not* like this approach.
Indeed, the planner might omit scan on the children which are not obviously
referenced, but I'm not certain whether its RangeTblEntry would be also
removed from the PlannedStmt->rtable, or not.
How about an idea to add a new flag in RangeTblEntry which shows where
the RangeTblEntry came from, instead of clearing requiredPerms?
If the flag is true, I think ExecCheckRTEPerms() can simply skip checks
on the child tables.How about the external module just checks if the current object being
queried has parents, and if so, goes and checks the
labels/permissions/etc on those children? That way the query either
always fails or never fails for a given caller, rather than sometimes
working and sometimes not depending on the query.
Hmm, this idea may be feasible. The RangeTblEntry->inh flag of the parent
will give us a hint whether we also should check labels on its children.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
How about an idea to add a new flag in RangeTblEntry which shows where
the RangeTblEntry came from, instead of clearing requiredPerms?
If the flag is true, I think ExecCheckRTEPerms() can simply skip checks
on the child tables.How about the external module just checks if the current object being
queried has parents, and if so, goes and checks the
labels/permissions/etc on those children? That way the query either
always fails or never fails for a given caller, rather than sometimes
working and sometimes not depending on the query.Hmm, this idea may be feasible. The RangeTblEntry->inh flag of the parent
will give us a hint whether we also should check labels on its children.
http://code.google.com/p/sepgsql/source/browse/trunk/sepgsql/relation.c#293
At least, it seems to me this logic works as expected.
postgres=# CREATE TABLE tbl_p (a int, b text);
CREATE TABLE
postgres=# CREATE TABLE tbl_1 (check (a < 100)) inherits (tbl_p);
CREATE TABLE
postgres=# CREATE TABLE tbl_2 (check (a >= 100 and a < 200)) inherits (tbl_p);
CREATE TABLE
postgres=# CREATE TABLE tbl_3 (check (a >= 300)) inherits (tbl_p);
CREATE TABLE
postgres=# SECURITY LABEL on TABLE tbl_p IS 'system_u:object_r:sepgsql_table_t:s0';
SECURITY LABEL
postgres=# SECURITY LABEL on COLUMN tbl_p.a IS 'system_u:object_r:sepgsql_table_t:s0';
SECURITY LABEL
postgres=# SECURITY LABEL on COLUMN tbl_p.b IS 'system_u:object_r:sepgsql_table_t:s0';
SECURITY LABEL
postgres=# set sepgsql_debug_audit = on;
SET
postgres=# SELECT a FROM ONLY tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_table name=tbl_p
STATEMENT: SELECT a FROM ONLY tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_column name=tbl_p.a
STATEMENT: SELECT a FROM ONLY tbl_p WHERE a = 150;
a
---
(0 rows)
-> ONLY tbl_p was not expanded
postgres=# SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_table name=tbl_p
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_column name=tbl_p.a
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_table name=tbl_1
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_column name=tbl_1.a
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_table name=tbl_2
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_column name=tbl_2.a
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_table name=tbl_3
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_table_t:s0 tclass=db_column name=tbl_3.a
STATEMENT: SELECT a FROM tbl_p WHERE a = 150;
a
---
(0 rows)
-> tbl_p was expanded to tbl_1, tbl_2 and tbl_3
postgres=# set sepgsql_debug_audit = off;
SET
postgres=# EXPLAIN SELECT a FROM tbl_p WHERE a = 150;
QUERY PLAN
------------------------------------------------------------------------
Result (cost=0.00..50.75 rows=12 width=4)
-> Append (cost=0.00..50.75 rows=12 width=4)
-> Seq Scan on tbl_p (cost=0.00..25.38 rows=6 width=4)
Filter: (a = 150)
-> Seq Scan on tbl_2 tbl_p (cost=0.00..25.38 rows=6 width=4)
Filter: (a = 150)
(6 rows)
-> Actually, it does not scan tbl_1 and tbl_3 due to the a = 150.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
On tis, 2010-08-17 at 20:04 -0400, Stephen Frost wrote:
What I'm thinking of is something like a warning if the permissions on
the child don't match those of the parent when the relationship is
created, or maybe forcibly setting the permissions on the child (with
a
NOTICE), so it's at least clear what is going on. Or perhaps, set the
permissions on the child only if it doesn't have permissions (with the
NOTICE), and issue a WARNING if the child already has permissions set.
Perhaps also a WARNING if someone changes the permissions on a child
after the relationship has been created too, but let it happen in case
someone really wants it..
I think there are perfectly good reasons to have different permissions
on parent and child tables. I don't see any reason to monkey around
with that.
* Peter Eisentraut (peter_e@gmx.net) wrote:
I think there are perfectly good reasons to have different permissions
on parent and child tables. I don't see any reason to monkey around
with that.
Even though the permissions on the child table aren't invovled at all if
queried through the parent..? The parent implicitly adds to the set of
privileges which are granted on the child, but that's not clear at all
from the permissions visible on the child. That's principally what I'm
complaining about here.
Thanks,
Stephen
On sön, 2010-08-22 at 15:08 -0400, Stephen Frost wrote:
* Peter Eisentraut (peter_e@gmx.net) wrote:
I think there are perfectly good reasons to have different permissions
on parent and child tables. I don't see any reason to monkey around
with that.Even though the permissions on the child table aren't invovled at all if
queried through the parent..? The parent implicitly adds to the set of
privileges which are granted on the child, but that's not clear at all
from the permissions visible on the child. That's principally what I'm
complaining about here.
Perhaps this is a user interface issue then. Maybe the fact that a
table is inherited from another one needs to be shown closer to
whereever the permissions are listed.
* Peter Eisentraut (peter_e@gmx.net) wrote:
On sön, 2010-08-22 at 15:08 -0400, Stephen Frost wrote:
Even though the permissions on the child table aren't invovled at all if
queried through the parent..? The parent implicitly adds to the set of
privileges which are granted on the child, but that's not clear at all
from the permissions visible on the child. That's principally what I'm
complaining about here.Perhaps this is a user interface issue then. Maybe the fact that a
table is inherited from another one needs to be shown closer to
whereever the permissions are listed.
That's a nice idea, except that we've got a pretty well defined API
regarding how to determine what the privileges on a table are, and many
different UIs which use it. Fixing it in psql (if it needs to be..
iirc, \d or \d+ may already show it) doesn't really address the problem,
in my view.
Thanks,
Stephen
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.OK, I'll try to describe with appropriate granularity.
Do we need an independent section in addition to the introduction of
SECURITY LABEL syntax?I think so. I suggest a new chapter called "Enhanced Security
Providers" just after "Database Roles and Privileges".OK,
Now I'm under describing the new chapter.
http://git.postgresql.org/gitweb?p=users/kaigai/sepgsql.git;a=blob;f=doc/src/sgml/esp.sgml;hb=devel/seclabel
However, I'm wondering whether the topic about security hooks and some
others are appropriate for the "III. Server Administration" part.
Perhaps, it is a good idea a new section at the last of "Database Roles
and Privileges" which introduce a fact that PostgreSQL allows plugins
to make access control decision, and a new chapter in the "VII. Internals"
part.
How about the idea?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/8/25 KaiGai Kohei <kaigai@ak.jp.nec.com>:
7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.OK, I'll try to describe with appropriate granularity.
Do we need an independent section in addition to the introduction of
SECURITY LABEL syntax?I think so. I suggest a new chapter called "Enhanced Security
Providers" just after "Database Roles and Privileges".OK,
Now I'm under describing the new chapter.
http://git.postgresql.org/gitweb?p=users/kaigai/sepgsql.git;a=blob;f=doc/src/sgml/esp.sgml;hb=devel/seclabelHowever, I'm wondering whether the topic about security hooks and some
others are appropriate for the "III. Server Administration" part.Perhaps, it is a good idea a new section at the last of "Database Roles
and Privileges" which introduce a fact that PostgreSQL allows plugins
to make access control decision, and a new chapter in the "VII. Internals"
part.How about the idea?
Well, I prefer what I suggested, but of course I'm biased.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
The attached patch is a revised version of security label support.
summary of changes:
* The logic to translate object-name to object-id was rewritten
with the new get_object_address().
* Right now, it does not support labeling on shared database object
(ie; pg_database), so wrapper functions to XXXLocalSecLabel() were
removed.
* The restriction of same security label within whole of inheritance
tree has gone. And, the 'num_parents' argument was also removed
from the security hook.
* ExecRelationSecLabel() was also removed, although you suggested
to rename it, because it translate the supplied relation name
into relation id and handled child tables, but it get unnecessary.
* The chapter of 'External Security Provider' was added.
It introduces overview of ESP concept and MAC features.
Perhaps, other structures of chapters are more preferable,
but I also think we need a draft at the begining of discussion.
* The '--security-label' option was added to pg_dump/pg_dumpall;
it allows to include security label of objects in the archives.
The '--no-security-label' option was also added to pg_restore;
it allows to skip security labels, even if the archive contains
security labels.
Thanks,
(2010/08/10 5:02), Robert Haas wrote:
2010/7/26 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patches are revised ones, as follows.
I think this is pretty good, and I'm generally in favor of committing
it. Some concerns:1. Since nobody has violently objected to the comment.c refactoring
patch I recently proposed, I'm hopeful that can go in. And if that's
the case, then I'd prefer to see that committed first, and then rework
this to use that code. That would eliminate some code here, and it
would also make it much easier to support labels on other types of
objects.2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.3. Is it really appropriate for ExecRelationSecLabel() to have an
"Exec" prefix? I don't think so.4. Please get rid of the nkeys++ stuff in DeleteLocalSecLabel() and
just use fixed offsets as we do everywhere else.5. Why do we think that the relabel hook needs to be passed the number
of expected parents?6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.8. Generally, the English in both the comments and documentation needs
work. However, we can address that problem when we're closer to
commit.I am going to mark this Returned with Feedback because I don't believe
it's realistic to get the comment code committed in the next week,
rework this patch, and then get this patch committed also. However,
I'm feeling pretty good about this effort and I think we're making
good progress toward getting this done.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-seclabel.2.patchtext/x-patch; name=pgsql-seclabel.2.patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 209,214 ****
--- 209,219 ----
</row>
<row>
+ <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+ <entry>security labels on local database objects</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
***************
*** 4220,4225 ****
--- 4225,4315 ----
</table>
</sect1>
+ <sect1 id="catalog-pg-seclabel">
+ <title><structname>pg_seclabel</structname></title>
+
+ <indexterm zone="catalog-pg-seclabel">
+ <primary>pg_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seclabel</structname> stores the security
+ label of database objects. This information allows external security
+ providers to apply label based mandatory access controls.
+ </para>
+
+ <para>
+ 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.)
+ </para>
+ <para>
+ The security labels shall be automatically cleaned up when the database
+ object being labeled is dropped.
+ </para>
+ <para>
+ See also <link linkend="sql-security-label"><command>SECURITY LABEL</command></link>,
+ which provides a functionality to relabel a certain database object,
+ as long as user has enough privileges.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabel</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>reloid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+
+ <row>
+ <entry><structfield>subid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>For a security label on a table column, this is the column number
+ (the <structfield>objoid</structfield> and <structfield>classoid</structfield>
+ refer to the table itself). For all other object types, this column is
+ zero
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>tag</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>identifier of external security provider</entry>
+ </row>
+
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>security label in text format</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
*** /dev/null
--- b/doc/src/sgml/esp.sgml
***************
*** 0 ****
--- 1,249 ----
+ <chapter id="esp">
+ <title>External Security Provider</title>
+ <para>
+ External security provider (ESP) is a plugin which makes access control
+ decision based on its security model and policy.
+ </para>
+ <para>
+ <productname>PostgreSQL</productname> 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 <xref linkend="user-manag">.
+ </para>
+ <para>
+ 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.
+ </para>
+
+ <sect1 id="esp-overview">
+ <title>Overview</title>
+ <para>
+ The ESP concept was designed to integrate various kind of security
+ models into <productname>PostgreSQL</productname>.
+
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ The default database privilege mechanism makes its access control
+ decision based on the authenticated user identifier and permissions
+ of database objects to be referenced.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ At the implementation level, <productname>PostgreSQL</productname> 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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'.
+ </para>
+ <para>
+ These hooks shall be filled up when we load plugins.
+
+ Administrator can add a configuration of
+ <literal>shared_preload_libraries</literal> on which we specify a few
+ plugins to be loaded on starting up the server process, as follows:
+
+ <programlisting>
+ shared_preload_libraries = 'sepgsql' # (change requires restart)
+ </programlisting>
+
+ You should never use <literal>local_preload_libraries</literal> instead,
+ even if the plugin is well verified without any vulnerabilities, because
+ it can be overridden by GUC variable settings passed in startup packet.
+ </para>
+ <para>
+ 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.
+ </para>
+ </sect1>
+
+ <sect1 id="esp-mac">
+ <title>Mandatory Access Control</title>
+ <para>
+ It is a major of ESP to support mandatory access control (MAC) policy
+ in <productname>PostgreSQL</productname>.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ MAC is designed to tackle such a strict security requirement.
+ </para>
+
+ <sect2 id="esp-mac-label">
+ <title>Security Label</title>
+ <para>
+ In the default database privileges, <command>GRANT</command> and
+ <command>REVOKE</command> commands allow superusers and owner of the
+ database obejcts to set up access permissions, then it makes access
+ control decision based on these properties.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ <synopsis>
+ dbadm_u:system_r:postgresql_t:s0
+
+ system_u:object_r:sepgsql_table_t:s0:c0
+ </synopsis>
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ </sect2>
+ <sect2 id="esp-mac-label-manage">
+ <title>Management of security label</title>
+ <para>
+ As we introduced above, MAC mechanism needs both of user and object to be
+ labeled, to make its access control decision.
+ </para>
+ <para>
+ For database objects, <productname>PostgreSQL</productname> 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
+ <xref linkend="SQL-SECURITY-LABEL"> command later, as long as this
+ operation is allowed by security mechanisms.
+ </para>
+ <para>
+ The following example creates a new table <literal>tbl</literal> on the
+ system with <productname>SELinux</productname> support using ESP.
+ <screen>
+ 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)
+ </screen>
+ It shows us that <literal>system_u:object_r:sepgsql_table_t:s0</literal>
+ was assigned on the creation time of table <literal>tbl</literal>.
+ <screen>
+ 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)
+ </screen>
+ It also shows us <command>SECURITY LABEL</command> command allows to relabel
+ the table to <literal>system_u:object_r:sepgsql_ro_table_t:s0</literal>.
+ </para>
+ <para>
+ The
+ <link linkend="catalog-pg-seclabel">
+ <structname>pg_seclabel</structname>
+ </link>
+ is a system catalog to store
+ security labels, and ESP plugins shall reference the catalog to fetch
+ them on making its access control decisions.
+ </para>
+ <para>
+ 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, <productname>PostgreSQL</productname> 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.
+ </para>
+ <para>
+ When we backup and restore databases, security labels are also properties
+ of database objects to be handled correctly.
+
+ The <command>pg_dump</command> and <command>pg_dumpall</command> support
+ <literal>--security-label</literal> option that enables to include
+ security label of the database objects into archives.
+
+ And the <command>pg_restore</command> also supports
+ <literal>--no-security-label</literal> option that enables to ignore
+ security labels, even if the archives contains labels.
+ </para>
+ </sect2>
+ </sect1>
+ </chapter>
*** a/doc/src/sgml/external-projects.sgml
--- b/doc/src/sgml/external-projects.sgml
***************
*** 250,254 ****
--- 250,266 ----
<application><ulink url="http://www.pgadmin.org/">pgAdmin III</ulink></>,
and there are several commercially available ones as well.
</para>
+
+ <para>
+ The <xref linkend="esp"> introduces how <productname>PostgreSQL</>
+ integrate additional security features using external security
+ providers.
+
+ <application><ulink url="http://code.google.com/p/sepgsql/">
+ SE-PostgreSQL
+ </ulink></application> is an external security provider which
+ applies mandatory access control based on the centralized
+ security policy of operation system.
+ </para>
</sect1>
</appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 46,51 ****
--- 46,52 ----
<!entity runtime SYSTEM "runtime.sgml">
<!entity config SYSTEM "config.sgml">
<!entity user-manag SYSTEM "user-manag.sgml">
+ <!entity esp SYSTEM "esp.sgml">
<!entity wal SYSTEM "wal.sgml">
<!-- programmer's guide -->
*** 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 ----
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
+ <!entity securityLabel system "seclabel.sgml">
<!entity select system "select.sgml">
<!entity selectInto system "select_into.sgml">
<!entity set system "set.sgml">
*** a/doc/src/sgml/ref/pg_dump.sgml
--- b/doc/src/sgml/ref/pg_dump.sgml
***************
*** 778,783 **** PostgreSQL documentation
--- 778,793 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
*** a/doc/src/sgml/ref/pg_dumpall.sgml
--- b/doc/src/sgml/ref/pg_dumpall.sgml
***************
*** 493,498 **** PostgreSQL documentation
--- 493,507 ----
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
*** a/doc/src/sgml/ref/pg_restore.sgml
--- b/doc/src/sgml/ref/pg_restore.sgml
***************
*** 329,334 ****
--- 329,347 ----
</varlistentry>
<varlistentry>
+ <term><option>--no-security-label</option></term>
+ <listitem>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-P <replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<term><option>--function=<replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<listitem>
*** /dev/null
--- b/doc/src/sgml/ref/seclabel.sgml
***************
*** 0 ****
--- 1,123 ----
+ <refentry id="SQL-SECURITY-LABEL">
+ <refmeta>
+ <refentrytitle>SECURITY LABEL</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SECURITY LABEL</refname>
+ <refpurpose>relabel the security label of an object</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-security-label">
+ <primary>SECURITY LABEL</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+ <synopsis>
+ SECURITY LABEL [ FOR '<replaceable class="PARAMETER">esp_tag</replaceable>' ] ON
+ {
+ TABLE <replaceable class="PARAMETER">object_name</replaceable> |
+ COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
+ SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+ VIEW <replaceable class="PARAMETER">object_name</replaceable>
+ } IS '<replaceable class="PARAMETER">security_label</replaceable>';
+ </synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>SECURITY LABEL</command> relabels a security label of a database object.
+ </para>
+
+ <para>
+ We can assign individual security labels on a certain database object for
+ each security providers, using <command>SECURITY LABEL</command> command.
+ </para>
+ <para>
+ Security labels are automatically assigned when the object is created,
+ and also automatically dropped when the object is dropped.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">object_name</replaceable></term>
+ <term><replaceable class="parameter">table_name</replaceable></term>
+ <term><replaceable class="parameter">table_name.column_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the object to be relabeled. Names of tables, columns,
+ sequences and views can be schema-qualified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">security_label</replaceable></term>
+ <listitem>
+ <para>
+ The new security label as a string literal.
+ </para>
+ <para>
+ It shall be validated by one of the label based security features,
+ in addition to its permission checks relabeling on the specified
+ database objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">esp_tag</replaceable></term>
+ <listitem>
+ <para>
+ The identifier string of external security provider.
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+ 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.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Relabel a security label on the table to '<literal>system_u:object_r:sepgsql_table_t:s0</literal>'.
+
+ <programlisting>
+ SECURITY LABEL FOR 'selinux' ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
+ </programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+ <para>
+ There is no <command>SECURITY LABEL</command> command in the SQL standard.
+ </para>
+ </refsect1>
+ </refentry>
+
+
+
*** 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,416 ----
+ /* -------------------------------------------------------------------------
+ *
+ * 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);
+ }
+
+ /*
+ * 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);
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ /*
+ * ExecSecLabelStmt
+ *
+ * SECURITY LABEL [FOR <esp>] ON <class> <name> IS <new label>
+ */
+ void
+ ExecSecLabelStmt(SecLabelStmt *stmt)
+ {
+ esp_relabel_hook_entry *esp_entry = NULL;
+ ObjectAddress address;
+ Relation relation;
+ ListCell *l;
+
+ /*
+ * SECURITY LABEL statement needs a label based security feature is
+ * available at least.
+ */
+ if (esp_relabel_hook_list == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("no label based security is available")));
+
+ /*
+ * when just one label based security is available, we can omit
+ * FOR <esp> clause because it is obvious which ESP module handles
+ * it. But, we have to specify the tag when multiple label based
+ * securities are available.
+ */
+ if (!stmt->tag)
+ {
+ if (list_length(esp_relabel_hook_list) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("multiple label based securities are available")));
+
+ esp_entry = linitial(esp_relabel_hook_list);
+ }
+ else
+ {
+ foreach (l, esp_relabel_hook_list)
+ {
+ esp_entry = lfirst(l);
+
+ if (strcmp(stmt->tag, esp_entry->tag) == 0)
+ goto found;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("no label based security for the tag: '%s'",
+ stmt->tag)));
+ }
+
+ found:
+ /*
+ * 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);
+ }
+
+ Assert(esp_entry->hook != NULL);
+ (*esp_entry->hook)(&address, stmt->seclabel);
+
+ /*
+ * Do actual relabeling.
+ */
+ SetSecurityLabel(&address, esp_entry->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 <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+ %type <str> label_item opt_esp
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> 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/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
Robert, although you suggested that only one ESP module shall be
invoked on relabeling an object before, and I agreed this design
at that time, but I noticed that we cannot implement the following
behavior correctly.
SELinux defines these permissions corresponding to table relabeling.
- db_table:{setattr}
It is necessary to change *any* properties of the table.
Security label is one of properties of the table, so, needs to be
checked on relabeling, not only ALTER TABLE and so on.
- db_table:{relabelfrom relabelto}
It is neccesary to change label of the table.
User must have {relabelfrom} permission on the older security label,
and {relabelto} permission on the new security label.
If and when multiple ESP modules are installed, we need to consider
the way to handle SECURITY LABEL statement for other modules.
When user tries to relabel security label of a table managed by
something except for selinux, it is not relevant to {relabelfrom
relabelto} permission in selinux, but we want to check {setattr}
permission on the operation, because it is a property of the table,
although not a security label in selinux.
In the current patch, the core PG (ExecSecurityLabel()) invokes only
one hook that matches with the given "FOR <esp>" option.
However, I reconsidered this hook should be simply invoked like other
hooks. Then, the ESP module decides whether ignores or handles the
invocation, and also decides to call secondary hook when multiple
modules are loaded. If so, selinux module can check {setattr} and
also calls secondary modules.
In the previous discussion, I missed the possibility of the case
when we want to check relabeling a security label managed by other
ESP. But it might be also necessary to call all the modules which
want to get control on SECURITY LABEL statement, apart from whether
it manages the supplied security label, or not.
Thanks,
(2010/08/31 15:27), KaiGai Kohei wrote:
The attached patch is a revised version of security label support.
summary of changes:
* The logic to translate object-name to object-id was rewritten
with the new get_object_address().* Right now, it does not support labeling on shared database object
(ie; pg_database), so wrapper functions to XXXLocalSecLabel() were
removed.* The restriction of same security label within whole of inheritance
tree has gone. And, the 'num_parents' argument was also removed
from the security hook.* ExecRelationSecLabel() was also removed, although you suggested
to rename it, because it translate the supplied relation name
into relation id and handled child tables, but it get unnecessary.* The chapter of 'External Security Provider' was added.
It introduces overview of ESP concept and MAC features.
Perhaps, other structures of chapters are more preferable,
but I also think we need a draft at the begining of discussion.* The '--security-label' option was added to pg_dump/pg_dumpall;
it allows to include security label of objects in the archives.
The '--no-security-label' option was also added to pg_restore;
it allows to skip security labels, even if the archive contains
security labels.Thanks,
(2010/08/10 5:02), Robert Haas wrote:
2010/7/26 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patches are revised ones, as follows.
I think this is pretty good, and I'm generally in favor of committing
it. Some concerns:1. Since nobody has violently objected to the comment.c refactoring
patch I recently proposed, I'm hopeful that can go in. And if that's
the case, then I'd prefer to see that committed first, and then rework
this to use that code. That would eliminate some code here, and it
would also make it much easier to support labels on other types of
objects.2. Some of this code refers to "local" security labels. I'm not sure
what's "local" about them - aren't they just security labels? On a
related note, I don't like the trivial wrappers you have here, with
DeleteSecurityLabel around DeleteLocalSecLabel, SetSecurityLabel
around SetLocalSecLabel, etc. Just collapse these into a single set
of functions.3. Is it really appropriate for ExecRelationSecLabel() to have an
"Exec" prefix? I don't think so.4. Please get rid of the nkeys++ stuff in DeleteLocalSecLabel() and
just use fixed offsets as we do everywhere else.5. Why do we think that the relabel hook needs to be passed the number
of expected parents?6. What are we doing about the assignment of initial security labels?
I had initially thought that perhaps new objects would just start out
unlabeled, and the user would be responsible for labeling them as
needed. But maybe we can do better. Perhaps we should extend the
security provider hook API with a function that gets called when a
labellable object gets created, and let each loaded security provider
return any label it would like attached. Even if we don't do that
now, esp_relabel_hook_entry needs to be renamed to something more
generic; we will certainly want to add more fields to that structure
later.7. I think we need to write and include in the fine documentation some
"big picture" documentation about enhanced security providers. Of
course, we have to decide what we want to say. But the SECURITY LABEL
documentation is just kind of hanging out there in space right now; it
needs to connect to a broad introduction to the subject.8. Generally, the English in both the comments and documentation needs
work. However, we can address that problem when we're closer to
commit.I am going to mark this Returned with Feedback because I don't believe
it's realistic to get the comment code committed in the next week,
rework this patch, and then get this patch committed also. However,
I'm feeling pretty good about this effort and I think we're making
good progress toward getting this done.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/9/13 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Robert, although you suggested that only one ESP module shall be
invoked on relabeling an object before, and I agreed this design
at that time, but I noticed that we cannot implement the following
behavior correctly.SELinux defines these permissions corresponding to table relabeling.
- db_table:{setattr}
It is necessary to change *any* properties of the table.
Security label is one of properties of the table, so, needs to be
checked on relabeling, not only ALTER TABLE and so on.
- db_table:{relabelfrom relabelto}
It is neccesary to change label of the table.
User must have {relabelfrom} permission on the older security label,
and {relabelto} permission on the new security label.If and when multiple ESP modules are installed, we need to consider
the way to handle SECURITY LABEL statement for other modules.
When user tries to relabel security label of a table managed by
something except for selinux, it is not relevant to {relabelfrom
relabelto} permission in selinux, but we want to check {setattr}
permission on the operation, because it is a property of the table,
although not a security label in selinux.In the current patch, the core PG (ExecSecurityLabel()) invokes only
one hook that matches with the given "FOR <esp>" option.
However, I reconsidered this hook should be simply invoked like other
hooks. Then, the ESP module decides whether ignores or handles the
invocation, and also decides to call secondary hook when multiple
modules are loaded. If so, selinux module can check {setattr} and
also calls secondary modules.In the previous discussion, I missed the possibility of the case
when we want to check relabeling a security label managed by other
ESP. But it might be also necessary to call all the modules which
want to get control on SECURITY LABEL statement, apart from whether
it manages the supplied security label, or not.
Maybe. The whole point of MAC is that the security policy itself is
capable of enforcing all of the necessary protections. It shouldn't
be necessary for it to control what DAC or other MAC providers do,
should it? At any rate, they'll probably treat it quite a bit
differently than a change of their own label. I think it might be
cleaner to fold that in under some of the DDL permissions checking and
refactoring which we know still needs to be done, rather than cramming
it in here. Note that presumably COMMENT ON would need similar
treatment, and there may be other things.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/09/13 20:46), Robert Haas wrote:
2010/9/13 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Robert, although you suggested that only one ESP module shall be
invoked on relabeling an object before, and I agreed this design
at that time, but I noticed that we cannot implement the following
behavior correctly.SELinux defines these permissions corresponding to table relabeling.
- db_table:{setattr}
It is necessary to change *any* properties of the table.
Security label is one of properties of the table, so, needs to be
checked on relabeling, not only ALTER TABLE and so on.
- db_table:{relabelfrom relabelto}
It is neccesary to change label of the table.
User must have {relabelfrom} permission on the older security label,
and {relabelto} permission on the new security label.If and when multiple ESP modules are installed, we need to consider
the way to handle SECURITY LABEL statement for other modules.
When user tries to relabel security label of a table managed by
something except for selinux, it is not relevant to {relabelfrom
relabelto} permission in selinux, but we want to check {setattr}
permission on the operation, because it is a property of the table,
although not a security label in selinux.In the current patch, the core PG (ExecSecurityLabel()) invokes only
one hook that matches with the given "FOR<esp>" option.
However, I reconsidered this hook should be simply invoked like other
hooks. Then, the ESP module decides whether ignores or handles the
invocation, and also decides to call secondary hook when multiple
modules are loaded. If so, selinux module can check {setattr} and
also calls secondary modules.In the previous discussion, I missed the possibility of the case
when we want to check relabeling a security label managed by other
ESP. But it might be also necessary to call all the modules which
want to get control on SECURITY LABEL statement, apart from whether
it manages the supplied security label, or not.Maybe. The whole point of MAC is that the security policy itself is
capable of enforcing all of the necessary protections. It shouldn't
be necessary for it to control what DAC or other MAC providers do,
should it?
Yes, what we should do here is that DAC and any MACs make their own
decision individually, then PG eventually prevents user's request if
one of them denied at least.
At any rate, they'll probably treat it quite a bit
differently than a change of their own label. I think it might be
cleaner to fold that in under some of the DDL permissions checking and
refactoring which we know still needs to be done, rather than cramming
it in here.
Yes, if and when MAC-X and MAC-Y are installed, it is significant event
for MAC-X to change X's label, so MAC-X may need to check special
permissions. But it is a common event for MAC-Y and DAC, so they checks
an appropriate permission to change one of the properties. Hoever, it
does not mean we should not give any chance MAC-Y and DAC to check something.
I'll revise my patch within a couple of days.
Thanks,
Note that presumably COMMENT ON would need similar
treatment, and there may be other things.
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Mon, Sep 13, 2010 at 8:38 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
Yes, if and when MAC-X and MAC-Y are installed, it is significant event
for MAC-X to change X's label, so MAC-X may need to check special
permissions. But it is a common event for MAC-Y and DAC, so they checks
an appropriate permission to change one of the properties. Hoever, it
does not mean we should not give any chance MAC-Y and DAC to check
something.I'll revise my patch within a couple of days.
I have a feeling we are talking past each other.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/09/13 21:57), Robert Haas wrote:
On Mon, Sep 13, 2010 at 8:38 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
Yes, if and when MAC-X and MAC-Y are installed, it is significant event
for MAC-X to change X's label, so MAC-X may need to check special
permissions. But it is a common event for MAC-Y and DAC, so they checks
an appropriate permission to change one of the properties. Hoever, it
does not mean we should not give any chance MAC-Y and DAC to check
something.I'll revise my patch within a couple of days.
I have a feeling we are talking past each other.
Perhaps, we might discuss about this topic before, but it's unclear for me.
The attached patch is a revised version, but a bit difference from what
I introduced yesterday.
The commands/seclabel.c still keeps the list of a pair of esp tag and
its security hook on relabeling, but it was modified to invoke all the
registered hooks with/without the supplied security label.
The guest of the hook has the following prototype:
void check_object_relabel(const ObjectAddress *object,
const char *seclabel);
When user tries to change the security label owned by other ESP,
the hook shall be invoked with NULL as the 'seclabel' argument,
because it does not need to know the new label itself.
(Perhaps, a flag as 3rd argument is more preferable.)
If we would implement it as a simple hook chain, like other existing
hooks, it is not easy to put the logic that allows to omit FOR clause
when only one ESP is install, on the core PG routine, because here is
no way to count number of installed ESPs. :-(
Code example of ESP module at:
http://code.google.com/p/sepgsql/source/browse/trunk/sepgsql/label.c#214
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-seclabel.3.patchapplication/octect-stream; name=pgsql-seclabel.3.patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 209,214 ****
--- 209,219 ----
</row>
<row>
+ <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+ <entry>security labels on local database objects</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
***************
*** 4227,4232 ****
--- 4232,4322 ----
</table>
</sect1>
+ <sect1 id="catalog-pg-seclabel">
+ <title><structname>pg_seclabel</structname></title>
+
+ <indexterm zone="catalog-pg-seclabel">
+ <primary>pg_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seclabel</structname> stores the security
+ label of database objects. This information allows external security
+ providers to apply label based mandatory access controls.
+ </para>
+
+ <para>
+ 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.)
+ </para>
+ <para>
+ The security labels shall be automatically cleaned up when the database
+ object being labeled is dropped.
+ </para>
+ <para>
+ See also <link linkend="sql-security-label"><command>SECURITY LABEL</command></link>,
+ which provides a functionality to relabel a certain database object,
+ as long as user has enough privileges.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabel</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>reloid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+
+ <row>
+ <entry><structfield>subid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>For a security label on a table column, this is the column number
+ (the <structfield>objoid</structfield> and <structfield>classoid</structfield>
+ refer to the table itself). For all other object types, this column is
+ zero
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>tag</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>identifier of external security provider</entry>
+ </row>
+
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>security label in text format</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
*** /dev/null
--- b/doc/src/sgml/esp.sgml
***************
*** 0 ****
--- 1,249 ----
+ <chapter id="esp">
+ <title>External Security Provider</title>
+ <para>
+ External security provider (ESP) is a plugin which makes access control
+ decision based on its security model and policy.
+ </para>
+ <para>
+ <productname>PostgreSQL</productname> 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 <xref linkend="user-manag">.
+ </para>
+ <para>
+ 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.
+ </para>
+
+ <sect1 id="esp-overview">
+ <title>Overview</title>
+ <para>
+ The ESP concept was designed to integrate various kind of security
+ models into <productname>PostgreSQL</productname>.
+
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ The default database privilege mechanism makes its access control
+ decision based on the authenticated user identifier and permissions
+ of database objects to be referenced.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ At the implementation level, <productname>PostgreSQL</productname> 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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'.
+ </para>
+ <para>
+ These hooks shall be filled up when we load plugins.
+
+ Administrator can add a configuration of
+ <literal>shared_preload_libraries</literal> on which we specify a few
+ plugins to be loaded on starting up the server process, as follows:
+
+ <programlisting>
+ shared_preload_libraries = 'sepgsql' # (change requires restart)
+ </programlisting>
+
+ You should never use <literal>local_preload_libraries</literal> instead,
+ even if the plugin is well verified without any vulnerabilities, because
+ it can be overridden by GUC variable settings passed in startup packet.
+ </para>
+ <para>
+ 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.
+ </para>
+ </sect1>
+
+ <sect1 id="esp-mac">
+ <title>Mandatory Access Control</title>
+ <para>
+ It is a major of ESP to support mandatory access control (MAC) policy
+ in <productname>PostgreSQL</productname>.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ MAC is designed to tackle such a strict security requirement.
+ </para>
+
+ <sect2 id="esp-mac-label">
+ <title>Security Label</title>
+ <para>
+ In the default database privileges, <command>GRANT</command> and
+ <command>REVOKE</command> commands allow superusers and owner of the
+ database obejcts to set up access permissions, then it makes access
+ control decision based on these properties.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ <synopsis>
+ dbadm_u:system_r:postgresql_t:s0
+
+ system_u:object_r:sepgsql_table_t:s0:c0
+ </synopsis>
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ </sect2>
+ <sect2 id="esp-mac-label-manage">
+ <title>Management of security label</title>
+ <para>
+ As we introduced above, MAC mechanism needs both of user and object to be
+ labeled, to make its access control decision.
+ </para>
+ <para>
+ For database objects, <productname>PostgreSQL</productname> 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
+ <xref linkend="SQL-SECURITY-LABEL"> command later, as long as this
+ operation is allowed by security mechanisms.
+ </para>
+ <para>
+ The following example creates a new table <literal>tbl</literal> on the
+ system with <productname>SELinux</productname> support using ESP.
+ <screen>
+ 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)
+ </screen>
+ It shows us that <literal>system_u:object_r:sepgsql_table_t:s0</literal>
+ was assigned on the creation time of table <literal>tbl</literal>.
+ <screen>
+ 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)
+ </screen>
+ It also shows us <command>SECURITY LABEL</command> command allows to relabel
+ the table to <literal>system_u:object_r:sepgsql_ro_table_t:s0</literal>.
+ </para>
+ <para>
+ The
+ <link linkend="catalog-pg-seclabel">
+ <structname>pg_seclabel</structname>
+ </link>
+ is a system catalog to store
+ security labels, and ESP plugins shall reference the catalog to fetch
+ them on making its access control decisions.
+ </para>
+ <para>
+ 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, <productname>PostgreSQL</productname> 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.
+ </para>
+ <para>
+ When we backup and restore databases, security labels are also properties
+ of database objects to be handled correctly.
+
+ The <command>pg_dump</command> and <command>pg_dumpall</command> support
+ <literal>--security-label</literal> option that enables to include
+ security label of the database objects into archives.
+
+ And the <command>pg_restore</command> also supports
+ <literal>--no-security-label</literal> option that enables to ignore
+ security labels, even if the archives contains labels.
+ </para>
+ </sect2>
+ </sect1>
+ </chapter>
*** a/doc/src/sgml/external-projects.sgml
--- b/doc/src/sgml/external-projects.sgml
***************
*** 250,254 ****
--- 250,266 ----
<application><ulink url="http://www.pgadmin.org/">pgAdmin III</ulink></>,
and there are several commercially available ones as well.
</para>
+
+ <para>
+ The <xref linkend="esp"> introduces how <productname>PostgreSQL</>
+ integrate additional security features using external security
+ providers.
+
+ <application><ulink url="http://code.google.com/p/sepgsql/">
+ SE-PostgreSQL
+ </ulink></application> is an external security provider which
+ applies mandatory access control based on the centralized
+ security policy of operation system.
+ </para>
</sect1>
</appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 46,51 ****
--- 46,52 ----
<!entity runtime SYSTEM "runtime.sgml">
<!entity config SYSTEM "config.sgml">
<!entity user-manag SYSTEM "user-manag.sgml">
+ <!entity esp SYSTEM "esp.sgml">
<!entity wal SYSTEM "wal.sgml">
<!-- programmer's guide -->
*** 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 ----
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
+ <!entity securityLabel system "seclabel.sgml">
<!entity select system "select.sgml">
<!entity selectInto system "select_into.sgml">
<!entity set system "set.sgml">
*** a/doc/src/sgml/ref/pg_dump.sgml
--- b/doc/src/sgml/ref/pg_dump.sgml
***************
*** 778,783 **** PostgreSQL documentation
--- 778,793 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
*** a/doc/src/sgml/ref/pg_dumpall.sgml
--- b/doc/src/sgml/ref/pg_dumpall.sgml
***************
*** 493,498 **** PostgreSQL documentation
--- 493,507 ----
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
*** a/doc/src/sgml/ref/pg_restore.sgml
--- b/doc/src/sgml/ref/pg_restore.sgml
***************
*** 329,334 ****
--- 329,347 ----
</varlistentry>
<varlistentry>
+ <term><option>--no-security-label</option></term>
+ <listitem>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-P <replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<term><option>--function=<replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<listitem>
*** /dev/null
--- b/doc/src/sgml/ref/seclabel.sgml
***************
*** 0 ****
--- 1,123 ----
+ <refentry id="SQL-SECURITY-LABEL">
+ <refmeta>
+ <refentrytitle>SECURITY LABEL</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SECURITY LABEL</refname>
+ <refpurpose>relabel the security label of an object</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-security-label">
+ <primary>SECURITY LABEL</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+ <synopsis>
+ SECURITY LABEL [ FOR '<replaceable class="PARAMETER">esp_tag</replaceable>' ] ON
+ {
+ TABLE <replaceable class="PARAMETER">object_name</replaceable> |
+ COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
+ SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+ VIEW <replaceable class="PARAMETER">object_name</replaceable>
+ } IS '<replaceable class="PARAMETER">security_label</replaceable>';
+ </synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>SECURITY LABEL</command> relabels a security label of a database object.
+ </para>
+
+ <para>
+ We can assign individual security labels on a certain database object for
+ each security providers, using <command>SECURITY LABEL</command> command.
+ </para>
+ <para>
+ Security labels are automatically assigned when the object is created,
+ and also automatically dropped when the object is dropped.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">object_name</replaceable></term>
+ <term><replaceable class="parameter">table_name</replaceable></term>
+ <term><replaceable class="parameter">table_name.column_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the object to be relabeled. Names of tables, columns,
+ sequences and views can be schema-qualified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">security_label</replaceable></term>
+ <listitem>
+ <para>
+ The new security label as a string literal.
+ </para>
+ <para>
+ It shall be validated by one of the label based security features,
+ in addition to its permission checks relabeling on the specified
+ database objects.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">esp_tag</replaceable></term>
+ <listitem>
+ <para>
+ The identifier string of external security provider.
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+ <para>
+ 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.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ Relabel a security label on the table to '<literal>system_u:object_r:sepgsql_table_t:s0</literal>'.
+
+ <programlisting>
+ SECURITY LABEL FOR 'selinux' ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
+ </programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+ <para>
+ There is no <command>SECURITY LABEL</command> command in the SQL standard.
+ </para>
+ </refsect1>
+ </refentry>
+
+
+
*** 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 <esp>] ON <class> <name> IS <new label>
+ */
+ 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 <provider> 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 <provider> 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 <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+ %type <str> label_item opt_esp
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> 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
2010/9/14 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised version, but a bit difference from what
I introduced yesterday.
I am working through this patch and fixing a variety of things things
that seem to need fixing. Please hang tight and don't send any new
versions for now.
Thanks,
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On Thu, Sep 16, 2010 at 11:12 PM, Robert Haas <robertmhaas@gmail.com> wrote:
2010/9/14 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised version, but a bit difference from what
I introduced yesterday.I am working through this patch and fixing a variety of things things
that seem to need fixing. Please hang tight and don't send any new
versions for now.
There's no particularly good way to say this, so I'm just going to
spit it out: this patch was a real mess. In particular, there are a
huge number of cases where the identifier names were poorly chosen,
which I have mostly gone through and fixed now. There may yet be some
arguable and/or wrong cases remaining, and it's certainly possible
that not everyone may agree with all the choices I made, but it's
certainly a lot better than it was. I also had to rewrite pretty much
all of the documentation, comments, and error messages. I reorganized
a fair amount of the code, too; and ripped out a bunch of stuff that
looked irrelevant. In theory, this was supposed to be patterned off
the COMMENT code, but there were various changes which mostly did not
seem like improvements to me, and which in at least one case were
plain wrong.
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.
For a couple of reasons, I decided that it made sense to broaden the
set of objects to which the SECURITY LABEL command can apply. My
meeting with the NSA folks at BWPUG more or less convinced me that
we're not going to get very far with this unless we have suitable
hooks for additional permissions-checking when functions are executed
or large objects are accessed, so I added labels for those, as well as
for types, schemas, and procedural languages. It is possible that we
need more than that, but supporting all of these rather than just
relations and attributes requires only fairly trivial code changes,
and I'd like to have at least a month or two go by before I have to
look at another patch in this area. It's worth noting that labels on
schemas can be useful even if we don't have a hook for schema-related
permissions checking, once we have hooks to set labels at object
creation time: the label for a newly assigned table can be a function
of the user's label and the schema's label.
I removed the crock that let one label provider veto another label
provider's label. I understand that MAC will require a control there,
but (as I said before) that's not the right way to do it. Let's leave
that as material for a separate patch that solves the whole problem
well instead of 5% of it poorly.
I think the backend code here is now in pretty good shape, but there
are still a number of things that need to be fixed. The pg_dump
support is broken at the moment, because of the change to the set of
objects that can be labeled. I also don't think it's right to dump
security labels only when asked to do so. I think that the option
should be --no-security-label in pg_dump(all) just as it is in
pg_restore. Also, the pg_dump support for security labels should
really reuse the existing design for comments, rather than inventing a
new and less efficient method, unless there is some really compelling
reason why the method used for comments won't work. Please send a
reworked patch for just this directory (src/bin/pg_dump).
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands. So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good. Alternatively, or in addition, we could
add a \d command after all. The best way forward is debatable, but we
certainly need *something*, because interpreting the pg_seclabel
catalog by hand is not for the faint of heart. Second, there are no
regression tests. It's a bit tricky to think about how to crack that
nut because this feature is somewhat unusual in that it can't be used
without loading an appropriate loadable module. I'm wondering if we
can ship a "dummy_seclabel" contrib module that can be loaded during
the regression test run and then run various tests using that, but I'm
not quite sure what the best way to set that up is. SECURITY LABEL is
a core feature, so it would be nice to test it in the core regression
tests... but maybe that's too complicated to get working, and we
should just test it from the contrib module.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Attachments:
seclabel-v4.patchapplication/octet-stream; name=seclabel-v4.patchDownload
diff --git a/contrib/dummy_seclabel/Makefile b/contrib/dummy_seclabel/Makefile
new file mode 100644
index 0000000..316c27f
diff --git a/contrib/dummy_seclabel/dummy_seclabel.c b/contrib/dummy_seclabel/dummy_seclabel.c
new file mode 100644
index 0000000..e46fc45
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ab11b15..3758b64 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -209,6 +209,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+ <entry>security labels on database objects</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
@@ -4229,6 +4234,81 @@
</sect1>
+ <sect1 id="catalog-pg-seclabel">
+ <title><structname>pg_seclabel</structname></title>
+
+ <indexterm zone="catalog-pg-seclabel">
+ <primary>pg_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seclabel</structname> stores security
+ labels on database objects. See the
+ <xref linkend="sql-security-label"> statement.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabel</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+
+ <row>
+ <entry><structfield>classoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+
+ <row>
+ <entry><structfield>objsubid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ For a security label on a table column, this is the column number (the
+ <structfield>objoid</> and <structfield>classoid</> refer to
+ the table itself). For all other object types, this column is
+ zero.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>provider</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>
+ The label provider associated with this label.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>
+ The security label applied to this object.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7b97883..f5d67a2 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -132,6 +132,7 @@ Complete list of usable sgml source files in this directory.
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
+<!entity securityLabel system "security_label.sgml">
<!entity select system "select.sgml">
<!entity selectInto system "select_into.sgml">
<!entity set system "set.sgml">
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 1b8402e..8242b53 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -778,6 +778,16 @@ PostgreSQL documentation
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 14fa109..68dcc35 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -493,6 +493,15 @@ PostgreSQL documentation
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 9dc2511..7860696 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -329,6 +329,16 @@
</varlistentry>
<varlistentry>
+ <term><option>--no-security-label</option></term>
+ <listitem>
+ <para>
+ Do not output commands to restore security labels,
+ even if the archive contains them.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-P <replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<term><option>--function=<replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<listitem>
diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml
new file mode 100644
index 0000000..7fce58b
--- /dev/null
+++ b/doc/src/sgml/ref/security_label.sgml
@@ -0,0 +1,194 @@
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-SECURITY-LABEL">
+ <refmeta>
+ <refentrytitle>SECURITY LABEL</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SECURITY LABEL</refname>
+ <refpurpose>define or change a security label applied to an object</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-security-label">
+ <primary>SECURITY LABEL</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
+{
+ TABLE <replaceable class="PARAMETER">object_name</replaceable> |
+ COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
+ AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
+ DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
+ FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
+ LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |
+ [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">object_name</replaceable> |
+ SCHEMA <replaceable class="PARAMETER">object_name</replaceable> |
+ SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+ TYPE <replaceable class="PARAMETER">object_name</replaceable> |
+ VIEW <replaceable class="PARAMETER">object_name</replaceable>
+} IS '<replaceable class="PARAMETER">label</replaceable>'
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>SECURITY LABEL</command> applies a security label to a database
+ object. An arbitrary number of security labels, one per label provider, can
+ be associated with a given database object. Label providers are loadable
+ modules which register themselves by using the function
+ <function>register_label_provider</>.
+ </para>
+
+ <note>
+ <para>
+ <function>register_label_provider</> is not an SQL function; it can
+ only be called from C code loaded into the backend.
+ </para>
+ </note>
+
+ <para>
+ The label provider determines whether a given a label is valid and whether
+ it is permissible to assign that label to a given object. The meaning of a
+ given label is likewise at the discretion of the label provider.
+ <productname>PostgreSQL</> places no restrictions on whether or how a
+ label provider must interpret security labels; it merely provides a
+ mechanism for storing them. In practice, this facility is intended to allow
+ integration with label-based mandatory access control (MAC) systems such as
+ <productname>SE-Linux</>. Such systems make all access control decisions
+ based on object labels, rather than traditional discretionary access control
+ (DAC) concepts such as users and groups.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">object_name</replaceable></term>
+ <term><replaceable class="parameter">table_name.column_name</replaceable></term>
+ <term><replaceable class="parameter">agg_name</replaceable></term>
+ <term><replaceable class="parameter">function_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of the object to be commented. Names of tables,
+ aggregates, domains, functions, sequences, types, and views can
+ be schema-qualified.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">provider</replaceable></term>
+ <listitem>
+ <para>
+ The name of the provider with which this label is to be associated. The
+ named provider must be loaded and must consent to the proposed labeling
+ operation. If exactly one provider is loaded, the provider name may be
+ omitted for brevity.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">argmode</replaceable></term>
+
+ <listitem>
+ <para>
+ The mode of a function argument: <literal>IN</>, <literal>OUT</>,
+ <literal>INOUT</>, or <literal>VARIADIC</>.
+ If omitted, the default is <literal>IN</>.
+ Note that <command>COMMENT ON FUNCTION</command> does not actually pay
+ any attention to <literal>OUT</> arguments, since only the input
+ arguments are needed to determine the function's identity.
+ So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
+ and <literal>VARIADIC</> arguments.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">argname</replaceable></term>
+
+ <listitem>
+ <para>
+ The name of a function argument.
+ Note that <command>COMMENT ON FUNCTION</command> does not actually pay
+ any attention to argument names, since only the argument data
+ types are needed to determine the function's identity.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">argtype</replaceable></term>
+
+ <listitem>
+ <para>
+ The data type(s) of the function's arguments (optionally
+ schema-qualified), if any.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">large_object_oid</replaceable></term>
+ <listitem>
+ <para>
+ The OID of the large object.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PROCEDURAL</literal></term>
+
+ <listitem>
+ <para>
+ This is a noise word.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">label</replaceable></term>
+ <listitem>
+ <para>
+ The new security label, written as a string literal; or <literal>NULL</>
+ to drop the security label.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ The following example shows how the security label of a table might
+ be changed.
+
+<programlisting>
+SECURITY LABEL FOR selinux ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+ <para>
+ There is no <command>SECURITY LABEL</command> command in the SQL standard.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 052fe0e..463746c 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -160,6 +160,7 @@
&rollbackPrepared;
&rollbackTo;
&savepoint;
+ &securityLabel;
&select;
&selectInto;
&set;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index f4a7eb0..6a47f39 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -38,7 +38,7 @@ 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 \
+ pg_default_acl.h pg_seclabel.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 62598ee..18e07eb 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -57,6 +57,7 @@
#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"
@@ -1004,10 +1005,12 @@ deleteOneObject(const ObjectAddress *object, Relation depRel)
doDeletion(object);
/*
- * Delete any comments associated with this object. (This is a convenient
- * place to do it instead of having every object type know to do it.)
+ * Delete any comments or security labels associated with this object.
+ * (This is a convenient place to do these things, rather than having every
+ * object type know to do it.)
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
+ DeleteSecurityLabel(object);
/*
* CommandCounterIncrement here to ensure that preceding changes are all
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4e9bf43..9d2a732 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -17,7 +17,7 @@ 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 \
+ 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
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
new file mode 100644
index 0000000..417ad88
--- /dev/null
+++ b/src/backend/commands/seclabel.c
@@ -0,0 +1,387 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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/namespace.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"
+
+/*
+ * For most object types, the permissions-checking logic is simple enough
+ * that it makes sense to just include it in CommentObject(). However,
+ * attributes require a bit more checking.
+ */
+static void CheckAttributeSecLabel(Relation relation);
+
+typedef struct
+{
+ const char *provider_name;
+ check_object_relabel_type hook;
+} LabelProvider;
+
+static List *label_provider_list = NIL;
+
+/*
+ * ExecSecLabelStmt --
+ *
+ * Apply a security label to a database object.
+ */
+void
+ExecSecLabelStmt(SecLabelStmt *stmt)
+{
+ LabelProvider *provider = NULL;
+ ObjectAddress address;
+ Relation relation;
+ ListCell *lc;
+
+ /*
+ * Find the named label provider, or if none specified, check whether
+ * there's exactly one, and if so use it.
+ */
+ if (stmt->provider == NULL)
+ {
+ if (label_provider_list == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("security label providers have been loaded")));
+ if (lnext(list_head(label_provider_list)) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("must specify provider when multiple security label providers have been loaded")));
+ provider = (LabelProvider *) linitial(label_provider_list);
+ }
+ else
+ {
+ foreach (lc, label_provider_list)
+ {
+ LabelProvider *lp = lfirst(lc);
+
+ if (strcmp(stmt->provider, lp->provider_name) == 0)
+ {
+ provider = lp;
+ break;
+ }
+ }
+ if (provider == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("security label provider \"%s\" is not loaded",
+ stmt->provider)));
+ }
+
+ /*
+ * Translate the parser representation which identifies this object
+ * into an 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 modifications.
+ */
+ 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:
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
+ break;
+ case OBJECT_COLUMN:
+ CheckAttributeSecLabel(relation);
+ break;
+ case OBJECT_TYPE:
+ if (!pg_type_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(address.objectId));
+ break;
+ case OBJECT_AGGREGATE:
+ case OBJECT_FUNCTION:
+ if (!pg_proc_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameListToString(stmt->objname));
+ break;
+ case OBJECT_SCHEMA:
+ if (!pg_namespace_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+ strVal(linitial(stmt->objname)));
+ break;
+ case OBJECT_LANGUAGE:
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on procedural language")));
+ break;
+ case OBJECT_LARGEOBJECT:
+ if (!pg_largeobject_ownercheck(address.objectId, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u",
+ address.objectId)));
+ break;
+ default:
+ elog(ERROR, "unrecognized object type: %d",
+ (int) stmt->objtype);
+ }
+
+ /* Provider gets control here, may throw ERROR to veto new label. */
+ (*provider->hook)(&address, stmt->label);
+
+ /* Apply new label. */
+ SetSecurityLabel(&address, provider->provider_name, stmt->label);
+
+ /*
+ * 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);
+}
+
+/*
+ * GetSecurityLabel returns the security label for a database object for a
+ * given provider, or NULL if there is no such label.
+ */
+char *
+GetSecurityLabel(const ObjectAddress *object, const char *provider)
+{
+ 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_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&keys[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&keys[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ ScanKeyInit(&keys[3],
+ Anum_pg_seclabel_provider,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(provider));
+
+ 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 attempts to set the security label for the specified
+ * provider on the specified object to the given value. NULL means that any
+ * any existing label should be deleted.
+ */
+void
+SetSecurityLabel(const ObjectAddress *object,
+ const char *provider, const char *label)
+{
+ 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];
+
+ /* Security labels on shared objects are not supported. */
+ Assert(!IsSharedRelation(object->classId));
+
+ /* Prepare to form or update a tuple, if necessary. */
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(replaces));
+ values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
+ values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
+ values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId);
+ values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider);
+ if (label != NULL)
+ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label);
+
+ /* Use the index to search for a matching old tuple */
+ ScanKeyInit(&keys[0],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&keys[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&keys[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ ScanKeyInit(&keys[3],
+ Anum_pg_seclabel_provider,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(provider));
+
+ pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ SnapshotNow, 4, keys);
+
+ oldtup = systable_getnext(scan);
+ if (HeapTupleIsValid(oldtup))
+ {
+ if (label == NULL)
+ simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ else
+ {
+ replaces[Anum_pg_seclabel_label - 1] = true;
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
+ values, nulls, replaces);
+ simple_heap_update(pg_seclabel, &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_seclabel),
+ values, nulls);
+ simple_heap_insert(pg_seclabel, newtup);
+ }
+
+ /* Update indexes, if necessary */
+ if (newtup != NULL)
+ {
+ CatalogUpdateIndexes(pg_seclabel, newtup);
+ heap_freetuple(newtup);
+ }
+
+ heap_close(pg_seclabel, RowExclusiveLock);
+}
+
+/*
+ * DeleteSecurityLabel removes all security labels for an object (and any
+ * sub-objects, if applicable).
+ */
+void
+DeleteSecurityLabel(const ObjectAddress *object)
+{
+ Relation pg_seclabel;
+ ScanKeyData skey[3];
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ int nkeys;
+
+ /* Security labels on shared objects are not supported. */
+ if (IsSharedRelation(object->classId))
+ return;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_seclabel_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&skey[1],
+ Anum_pg_seclabel_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyInit(&skey[2],
+ Anum_pg_seclabel_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
+ pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ SnapshotNow, nkeys, skey);
+ while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+ simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ systable_endscan(scan);
+
+ heap_close(pg_seclabel, RowExclusiveLock);
+}
+
+/*
+ * Check whether the user is allowed to comment on an attribute of the
+ * specified relation.
+ */
+static void
+CheckAttributeSecLabel(Relation relation)
+{
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
+
+ /*
+ * Allow security labels only on columns of tables, views, and composite
+ * types (which are the only relkinds for which pg_dump will dump labels).
+ */
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_VIEW &&
+ relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table, view, or composite type",
+ RelationGetRelationName(relation))));
+}
+
+void
+register_label_provider(const char *provider_name, check_object_relabel_type hook)
+{
+ LabelProvider *provider;
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ provider = palloc(sizeof(LabelProvider));
+ provider->provider_name = pstrdup(provider_name);
+ provider->hook = hook;
+ label_provider_list = lappend(label_provider_list, provider);
+ MemoryContextSwitchTo(oldcxt);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index deaeb76..e07aa3e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2607,6 +2607,20 @@ _copyCommentStmt(CommentStmt *from)
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(provider);
+ COPY_STRING_FIELD(label);
+
+ return newnode;
+}
+
static FetchStmt *
_copyFetchStmt(FetchStmt *from)
{
@@ -3958,6 +3972,9 @@ copyObject(void *from)
case T_CommentStmt:
retval = _copyCommentStmt(from);
break;
+ case T_SecLabelStmt:
+ retval = _copySecLabelStmt(from);
+ break;
case T_FetchStmt:
retval = _copyFetchStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6b6cd99..8d083c8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1164,6 +1164,18 @@ _equalCommentStmt(CommentStmt *a, CommentStmt *b)
}
static bool
+_equalSecLabelStmt(SecLabelStmt *a, SecLabelStmt *b)
+{
+ COMPARE_SCALAR_FIELD(objtype);
+ COMPARE_NODE_FIELD(objname);
+ COMPARE_NODE_FIELD(objargs);
+ COMPARE_STRING_FIELD(provider);
+ COMPARE_STRING_FIELD(label);
+
+ return true;
+}
+
+static bool
_equalFetchStmt(FetchStmt *a, FetchStmt *b)
{
COMPARE_SCALAR_FIELD(direction);
@@ -2624,6 +2636,9 @@ equal(void *a, void *b)
case T_CommentStmt:
retval = _equalCommentStmt(a, b);
break;
+ case T_SecLabelStmt:
+ retval = _equalSecLabelStmt(a, b);
+ break;
case T_FetchStmt:
retval = _equalFetchStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 22ce353..51fea86 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -204,7 +204,7 @@ static TypeName *TableFuncTypeName(List *columns);
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
- SelectStmt TransactionStmt TruncateStmt
+ SecLabelStmt SelectStmt TransactionStmt TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
ViewStmt CheckPointStmt CreateConversionStmt
@@ -334,7 +334,7 @@ static TypeName *TableFuncTypeName(List *columns);
%type <boolean> copy_from
%type <ival> opt_column event cursor_options opt_hold opt_set_data
-%type <objtype> reindex_type drop_type comment_type
+%type <objtype> reindex_type drop_type comment_type security_label_type
%type <node> fetch_args limit_clause select_limit_value
offset_clause select_offset_value
@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+%type <str> opt_provider security_label
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
@@ -499,7 +501,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ 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,6 +739,7 @@ stmt :
| RevokeStmt
| RevokeRoleStmt
| RuleStmt
+ | SecLabelStmt
| SelectStmt
| TransactionStmt
| TruncateStmt
@@ -4327,6 +4330,92 @@ comment_text:
| NULL_P { $$ = NULL; }
;
+
+/*****************************************************************************
+ *
+ * SECURITY LABEL [FOR <provider>] ON <object> IS <label>
+ *
+ * As with COMMENT ON, <object> can refer to various types of database
+ * objects (e.g. TABLE, COLUMN, etc.).
+ *
+ *****************************************************************************/
+
+SecLabelStmt:
+ SECURITY LABEL opt_provider ON security_label_type any_name
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = $5;
+ n->objname = $6;
+ n->objargs = NIL;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON AGGREGATE func_name aggr_args
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_AGGREGATE;
+ n->objname = $6;
+ n->objargs = $7;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON FUNCTION func_name func_args
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_FUNCTION;
+ n->objname = $6;
+ n->objargs = extractArgTypes($7);
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON LARGE_P OBJECT_P NumericOnly
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_LARGEOBJECT;
+ n->objname = list_make1($7);
+ n->objargs = NIL;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON opt_procedural LANGUAGE any_name
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_LANGUAGE;
+ n->objname = $7;
+ n->objargs = NIL;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ ;
+
+opt_provider: FOR ColId_or_Sconst { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+security_label_type:
+ COLUMN { $$ = OBJECT_COLUMN; }
+ | SCHEMA { $$ = OBJECT_SCHEMA; }
+ | SEQUENCE { $$ = OBJECT_SEQUENCE; }
+ | TABLE { $$ = OBJECT_TABLE; }
+ | DOMAIN_P { $$ = OBJECT_TYPE; }
+ | TYPE_P { $$ = OBJECT_TYPE; }
+ | VIEW { $$ = OBJECT_VIEW; }
+ ;
+
+security_label: Sconst { $$ = $1; }
+ | NULL_P { $$ = NULL; }
+ ;
+
/*****************************************************************************
*
* QUERY:
@@ -10993,6 +11082,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 1865f84..3636173 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -37,6 +37,7 @@
#include "commands/prepare.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
+#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
@@ -218,6 +219,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
@@ -663,6 +665,10 @@ standard_ProcessUtility(Node *parsetree,
CommentObject((CommentStmt *) parsetree);
break;
+ case T_SecLabelStmt:
+ ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ break;
+
case T_CopyStmt:
{
uint64 processed;
@@ -1592,6 +1598,10 @@ CreateCommandTag(Node *parsetree)
tag = "COMMENT";
break;
+ case T_SecLabelStmt:
+ tag = "SECURITY LABEL";
+ break;
+
case T_CopyStmt:
tag = "COPY";
break;
@@ -2314,6 +2324,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_SecLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CopyStmt:
if (((CopyStmt *) parsetree)->is_from)
lev = LOGSTMT_MOD;
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 6f0277e..8fa9a57 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -103,6 +103,7 @@ typedef struct _restoreOptions
* 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;
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index a73afec..d1a9c54 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -2275,6 +2275,10 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
return 0;
+ /* If it's security labels, maybe ignore it */
+ if (ropt->skip_seclabel && strcmp(te->desc, "SECURITY LABEL") == 0)
+ return 0;
+
/* Ignore DATABASE entry unless we should create it */
if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
return 0;
@@ -2341,6 +2345,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
(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, "SECURITY LABEL") == 0 &&
strncmp(te->tag, "LARGE OBJECT ", 13) == 0))
res = res & REQ_DATA;
else
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fdf48fe..7bacfb1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -125,6 +125,7 @@ static int binary_upgrade = 0;
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,6 +184,10 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
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,6 +305,7 @@ main(int argc, char **argv)
{"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,6 +454,8 @@ main(int argc, char **argv)
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,6 +651,15 @@ main(int argc, char **argv)
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,6 +856,7 @@ help(const char *progname)
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,6 +10453,111 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
}
/*
+ * 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_provider;
+ 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.provider, s.label"
+ " FROM pg_catalog.pg_seclabel s"
+ " LEFT OUTER JOIN"
+ " pg_catalog.pg_attribute a"
+ " ON s.objoid = a.attrelid AND"
+ " s.objsubid = a.attnum"
+ " WHERE classoid = %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_provider = PQfnumber(res, "provider");
+ 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_provider),
+ 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_provider),
+ 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, "SECURITY 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,6 +10581,13 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
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
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9294e07..79708be 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -69,6 +69,7 @@ static int disable_triggers = 0;
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,6 +134,7 @@ main(int argc, char *argv[])
{"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,6 +288,8 @@ main(int argc, char *argv[])
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,6 +375,8 @@ main(int argc, char *argv[])
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,6 +573,7 @@ help(void)
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"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index b9f0e86..1ddba72 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -76,6 +76,7 @@ main(int argc, char **argv)
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,6 +117,7 @@ main(int argc, char **argv)
{"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,6 +264,8 @@ main(int argc, char **argv)
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,6 +341,7 @@ main(int argc, char **argv)
opts->noDataForFailedTables = no_data_for_failed_tables;
opts->noTablespace = outputNoTablespaces;
opts->use_setsessauth = use_setsessauth;
+ opts->skip_seclabel = skip_seclabel;
if (opts->formatName)
{
@@ -442,6 +447,7 @@ usage(const char *progname)
" 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"
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 38c48b9..9fa11c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -281,6 +281,9 @@ DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre
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(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
+#define SecLabelObjectIndexId 3038
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
new file mode 100644
index 0000000..1fd7451
--- /dev/null
+++ b/src/include/catalog/pg_seclabel.h
@@ -0,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 objoid; /* OID of the object itself */
+ Oid classoid; /* OID of table containing the object */
+ int4 objsubid; /* column number, or 0 if not used */
+ text provider; /* name of label 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_objoid 1
+#define Anum_pg_seclabel_classoid 2
+#define Anum_pg_seclabel_objsubid 3
+#define Anum_pg_seclabel_provider 4
+#define Anum_pg_seclabel_label 5
+
+#endif /* PG_SECLABEL_H */
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index 560d837..1e59cd2 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -45,6 +45,7 @@ DECLARE_TOAST(pg_constraint, 2832, 2833);
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);
diff --git a/src/include/commands/seclabel.h b/src/include/commands/seclabel.h
new file mode 100644
index 0000000..4c3854e
--- /dev/null
+++ b/src/include/commands/seclabel.h
@@ -0,0 +1,35 @@
+/*
+ * 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 *provider);
+extern void SetSecurityLabel(const ObjectAddress *object,
+ const char *provider, const char *label);
+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_label_provider(const char *provider,
+ check_object_relabel_type hook);
+
+#endif /* SECLABEL_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 35def5e..0d33a2e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -347,6 +347,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_SecLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b67ab68..5d6498e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1850,6 +1850,20 @@ typedef struct CommentStmt
} CommentStmt;
/* ----------------------
+ * SECURITY LABEL Statement
+ * ----------------------
+ */
+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 *provider; /* Label provider (or NULL) */
+ char *label; /* New security label to be assigned */
+} SecLabelStmt;
+
+/* ----------------------
* Declare Cursor Statement
*
* Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 5e3ccd5..028f5ae 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
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)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1d9e110..9596b0b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -114,6 +114,7 @@ SELECT relname, relhasindex
pg_pltemplate | t
pg_proc | t
pg_rewrite | t
+ pg_seclabel | t
pg_shdepend | t
pg_shdescription | t
pg_statistic | t
@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have
Robert,
First off, thanks alot for working on this. My apologies for not having
time to help out. A few minor comments:
* Robert Haas (robertmhaas@gmail.com) wrote:
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.
Do we have a place where we actually document hooks today..? Seems like
we should and that'd be a good place to put the few necessary comments
regarding these.
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands.
One thought would be to add it to \dp or have a \dp+.
So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good.
I think that regardless of psql and \d, we should have a sensible system
view for it.
Second, there are no
regression tests. It's a bit tricky to think about how to crack that
nut because this feature is somewhat unusual in that it can't be used
without loading an appropriate loadable module. I'm wondering if we
can ship a "dummy_seclabel" contrib module that can be loaded during
the regression test run and then run various tests using that, but I'm
not quite sure what the best way to set that up is. SECURITY LABEL is
a core feature, so it would be nice to test it in the core regression
tests... but maybe that's too complicated to get working, and we
should just test it from the contrib module.
The first set of regression tests could simply run the SECURITY LABEL
commands and then check the results in the catalog. If some kind of
psql support is included, it could test that also. That doesn't check
that the hooks are called at the right time and with the right data, so
I agree with the suggestion to have dummy contrib modules (or something)
to do that generically for all our hooks, but I don't think we've got
anything like that today..? If we do, then we should model it off
whatever's there now. Perhaps we can look at how to do it
comprehensively for all hooks..
Thanks,
Stephen
On Thu, Sep 23, 2010 at 10:21 AM, Stephen Frost <sfrost@snowman.net> wrote:
* Robert Haas (robertmhaas@gmail.com) wrote:
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.Do we have a place where we actually document hooks today..? Seems like
we should and that'd be a good place to put the few necessary comments
regarding these.
We do not. Whether or not we should, I'm not sure.
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands.One thought would be to add it to \dp or have a \dp+.
That only works for table-ish things, though.
So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good.I think that regardless of psql and \d, we should have a sensible system
view for it.
That's fine with me. The one I wrote for comments can probably be
adapted pretty easily.
Second, there are no
regression tests. It's a bit tricky to think about how to crack that
nut because this feature is somewhat unusual in that it can't be used
without loading an appropriate loadable module. I'm wondering if we
can ship a "dummy_seclabel" contrib module that can be loaded during
the regression test run and then run various tests using that, but I'm
not quite sure what the best way to set that up is. SECURITY LABEL is
a core feature, so it would be nice to test it in the core regression
tests... but maybe that's too complicated to get working, and we
should just test it from the contrib module.The first set of regression tests could simply run the SECURITY LABEL
commands and then check the results in the catalog. If some kind of
psql support is included, it could test that also. That doesn't check
that the hooks are called at the right time and with the right data, so
I agree with the suggestion to have dummy contrib modules (or something)
to do that generically for all our hooks, but I don't think we've got
anything like that today..? If we do, then we should model it off
whatever's there now. Perhaps we can look at how to do it
comprehensively for all hooks..
The point is that SECURITY LABEL, as coded, will fail utterly unless
there is a label provider loaded. So you can't actually run it and
check the results in the catalog without loading a contrib module.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
* Robert Haas (robertmhaas@gmail.com) wrote:
The point is that SECURITY LABEL, as coded, will fail utterly unless
there is a label provider loaded. So you can't actually run it and
check the results in the catalog without loading a contrib module.
Urgh, yes, point. Well, we could test that it errors out correctly. :)
Another thought might be to allow the "check if a module is loaded
before doing things" to be a postgresql.conf option that is disabled in
the regression testing.. If you can modify postgresql.conf you can
remove the module anyway.. Interesting question as to if we should
auto-fail queries against objects which have labels when no security
module is loaded. Have we discussed that yet?
Thanks,
Stephen
On Thu, Sep 23, 2010 at 2:06 PM, Stephen Frost <sfrost@snowman.net> wrote:
* Robert Haas (robertmhaas@gmail.com) wrote:
The point is that SECURITY LABEL, as coded, will fail utterly unless
there is a label provider loaded. So you can't actually run it and
check the results in the catalog without loading a contrib module.Urgh, yes, point. Well, we could test that it errors out correctly. :)
Indeed.
Another thought might be to allow the "check if a module is loaded
before doing things" to be a postgresql.conf option that is disabled in
the regression testing.. If you can modify postgresql.conf you can
remove the module anyway..
That might work, although I'm not sure whether it's any easier that
getting a contrib module to run during the regression tests. I think
we're testing LOAD in there already somewhere, so...
Interesting question as to if we should
auto-fail queries against objects which have labels when no security
module is loaded. Have we discussed that yet?
My feeling is that we should do what the existing code does, namely,
bounce the request immediately if the relevant label provider can't be
found. It isn't as if people can't modify the labels anyway in that
case, by messing with pg_seclabel by hand, but I don't really see the
need to spend extra code trying to make this work sensibly when I'm
not sure there's any real sensible behavior. I think that people who
write these modules will need to include a mechanism to disable
checking, hedged about with some appropriate protections. Isn't that
what SE-Linux permissive mode is for? (And you could possibly have a
similar concept within the module, just local to PG, driven off a GUC;
of course the assign_hook can ask SE-Linux whether it's OK to enable
PG-only permissive mode.)
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Robert, thanks for your reviewing and revising.
(2010/09/23 13:28), Robert Haas wrote:
On Thu, Sep 16, 2010 at 11:12 PM, Robert Haas<robertmhaas@gmail.com> wrote:
2010/9/14 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patch is a revised version, but a bit difference from what
I introduced yesterday.I am working through this patch and fixing a variety of things things
that seem to need fixing. Please hang tight and don't send any new
versions for now.There's no particularly good way to say this, so I'm just going to
spit it out: this patch was a real mess. In particular, there are a
huge number of cases where the identifier names were poorly chosen,
which I have mostly gone through and fixed now. There may yet be some
arguable and/or wrong cases remaining, and it's certainly possible
that not everyone may agree with all the choices I made, but it's
certainly a lot better than it was. I also had to rewrite pretty much
all of the documentation, comments, and error messages. I reorganized
a fair amount of the code, too; and ripped out a bunch of stuff that
looked irrelevant. In theory, this was supposed to be patterned off
the COMMENT code, but there were various changes which mostly did not
seem like improvements to me, and which in at least one case were
plain wrong.
Thanks for this revising that I didn't found out.
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.
One headache thing when I tried to describe the new documentation section
was what we should describe here, because whole of the chapters in Server
Administration are on the standpoint of users, not developers.
Under the previous discussion, I suggested to move the most of descriptions
about external security providers into chapters in Internals, except for
a mention about a fact we have external security provider at the tail of
Database Roles and Privileges. How about the idea?
Perhaps, the chapters in Internals are appropriate place to introduce
specification of security hooks.
I also think it is irrelevant to describe label based mandatory access
control in the PgSQL documentation. It should be moved to the package of
SE-PgSQL.
For a couple of reasons, I decided that it made sense to broaden the
set of objects to which the SECURITY LABEL command can apply. My
meeting with the NSA folks at BWPUG more or less convinced me that
we're not going to get very far with this unless we have suitable
hooks for additional permissions-checking when functions are executed
or large objects are accessed, so I added labels for those, as well as
for types, schemas, and procedural languages. It is possible that we
need more than that, but supporting all of these rather than just
relations and attributes requires only fairly trivial code changes,
and I'd like to have at least a month or two go by before I have to
look at another patch in this area. It's worth noting that labels on
schemas can be useful even if we don't have a hook for schema-related
permissions checking, once we have hooks to set labels at object
creation time: the label for a newly assigned table can be a function
of the user's label and the schema's label.
I agree this enhancement. Early or late, security labels of these objects
become eventually necessary to apply permission checks rather than relations
and attributes.
I removed the crock that let one label provider veto another label
provider's label. I understand that MAC will require a control there,
but (as I said before) that's not the right way to do it. Let's leave
that as material for a separate patch that solves the whole problem
well instead of 5% of it poorly.
OK, I'll revise this matter later.
I think the backend code here is now in pretty good shape, but there
are still a number of things that need to be fixed. The pg_dump
support is broken at the moment, because of the change to the set of
objects that can be labeled. I also don't think it's right to dump
security labels only when asked to do so. I think that the option
should be --no-security-label in pg_dump(all) just as it is in
pg_restore.
OK, I'll fix up the specification.
Also, the pg_dump support for security labels should
really reuse the existing design for comments, rather than inventing a
new and less efficient method, unless there is some really compelling
reason why the method used for comments won't work. Please send a
reworked patch for just this directory (src/bin/pg_dump).
I intended to follow on the existing design for comments.
Could you suggest me how should it be fixed up the design?
Because of the --no-security-label option, we need to dump security
labels in a separated section from comments. So, we cannot pack them
into "COMMENT" sections.
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands. So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good. Alternatively, or in addition, we could
add a \d command after all. The best way forward is debatable, but we
certainly need *something*, because interpreting the pg_seclabel
catalog by hand is not for the faint of heart.
Do you suggest the new system views should be defined for each supported
object classes, such as pg_largeobject_seclabel? It seems to me a bit
inflation of number of system views.
My preference is psql's \d commands at first.
Second, there are no
regression tests. It's a bit tricky to think about how to crack that
nut because this feature is somewhat unusual in that it can't be used
without loading an appropriate loadable module. I'm wondering if we
can ship a "dummy_seclabel" contrib module that can be loaded during
the regression test run and then run various tests using that, but I'm
not quite sure what the best way to set that up is. SECURITY LABEL is
a core feature, so it would be nice to test it in the core regression
tests... but maybe that's too complicated to get working, and we
should just test it from the contrib module.
As you suggested in the following topic, I think it is the best way to
use LOAD command at the head of regression test (or just after SECURITY
LABEL command being failed due to no modules).
I'll add a dummy module into contrib, and regression test for labels.
Right now, I don't have any complaint about the patch to the backend
you revised, so I'd like to submit the next patch as an incremental
one to the seclabel-v4.patch, except for src/bin/pg_dump.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/9/23 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.One headache thing when I tried to describe the new documentation section
was what we should describe here, because whole of the chapters in Server
Administration are on the standpoint of users, not developers.Under the previous discussion, I suggested to move the most of descriptions
about external security providers into chapters in Internals, except for
a mention about a fact we have external security provider at the tail of
Database Roles and Privileges. How about the idea?
Perhaps, the chapters in Internals are appropriate place to introduce
specification of security hooks.
Perhaps. I know that in the past we have not documented hook
functions, and I'm thinking that there may be people (in particular,
possibly Tom) who have strong feelings about keeping it that way.
Even if that's not the case, once we do start documenting the hooks,
we will presumably need to document all of them, and that may be more
of a project than I really want to get into right now, especially if I
will have to do much of the work myself. I'd be perfectly ecstatic if
a committable patch spontaneously materialized, but...
Also, the pg_dump support for security labels should
really reuse the existing design for comments, rather than inventing a
new and less efficient method, unless there is some really compelling
reason why the method used for comments won't work. Please send a
reworked patch for just this directory (src/bin/pg_dump).I intended to follow on the existing design for comments.
Could you suggest me how should it be fixed up the design?
dumpComment calls findComments calls collectComments, which dumps all
the comments in one query (not one query per object).
Because of the --no-security-label option, we need to dump security
labels in a separated section from comments. So, we cannot pack them
into "COMMENT" sections.
I'm not proposing that - I just want to avoid sending so many database
queries, if that's possible.
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands. So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good. Alternatively, or in addition, we could
add a \d command after all. The best way forward is debatable, but we
certainly need *something*, because interpreting the pg_seclabel
catalog by hand is not for the faint of heart.Do you suggest the new system views should be defined for each supported
object classes, such as pg_largeobject_seclabel? It seems to me a bit
inflation of number of system views.
My preference is psql's \d commands at first.
Please see http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.php
Right now, I don't have any complaint about the patch to the backend
you revised, so I'd like to submit the next patch as an incremental
one to the seclabel-v4.patch, except for src/bin/pg_dump.
Yes, an incremental diff would be preferable, thanks.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Robert Haas <robertmhaas@gmail.com> writes:
Perhaps. I know that in the past we have not documented hook
functions, and I'm thinking that there may be people (in particular,
possibly Tom) who have strong feelings about keeping it that way.
Even if that's not the case, once we do start documenting the hooks,
we will presumably need to document all of them, and that may be more
of a project than I really want to get into right now, especially if I
will have to do much of the work myself. I'd be perfectly ecstatic if
a committable patch spontaneously materialized, but...
I wouldn't say I have strong feelings about it; but most of the hooks
we've put in so far are things that you really had better be prepared to
read the source code if you want to exploit them. Does anyone want to
write and maintain SGML documentation specifying a complete API for
ProcessUtility, for example?
One of the powerful advantages of being an open source project is that
"use the source, Luke" is a perfectly reasonable approach to documenting
some things. I think hook functions are one.
regards, tom lane
(2010/09/24 11:53), Robert Haas wrote:
2010/9/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Most of the contents of the new documentation section on external
security providers seemed irrelevant to me, which I guess I can only
blame myself for since I was the one who asked for that section to be
created, and I didn't specify what it should contain all that well. I
took a try at rewriting it to be more on-topic, but it didn't amount
to much so I ended up just ripping that part out completely.One headache thing when I tried to describe the new documentation section
was what we should describe here, because whole of the chapters in Server
Administration are on the standpoint of users, not developers.Under the previous discussion, I suggested to move the most of descriptions
about external security providers into chapters in Internals, except for
a mention about a fact we have external security provider at the tail of
Database Roles and Privileges. How about the idea?
Perhaps, the chapters in Internals are appropriate place to introduce
specification of security hooks.Perhaps. I know that in the past we have not documented hook
functions, and I'm thinking that there may be people (in particular,
possibly Tom) who have strong feelings about keeping it that way.
Even if that's not the case, once we do start documenting the hooks,
we will presumably need to document all of them, and that may be more
of a project than I really want to get into right now, especially if I
will have to do much of the work myself. I'd be perfectly ecstatic if
a committable patch spontaneously materialized, but...
If so, maybe, we should keep the scale of documentation to a minimum,
then, rest of the detailed specifications of hooks are kept as source
code comments.
Because authors of ESP obviously reference source code, the comments
will provide them enough information.
Also, the pg_dump support for security labels should
really reuse the existing design for comments, rather than inventing a
new and less efficient method, unless there is some really compelling
reason why the method used for comments won't work. Please send a
reworked patch for just this directory (src/bin/pg_dump).I intended to follow on the existing design for comments.
Could you suggest me how should it be fixed up the design?dumpComment calls findComments calls collectComments, which dumps all
the comments in one query (not one query per object).Because of the --no-security-label option, we need to dump security
labels in a separated section from comments. So, we cannot pack them
into "COMMENT" sections.I'm not proposing that - I just want to avoid sending so many database
queries, if that's possible.
Ahh, Thanks. It makes me clear.
I'll revise dumpSecLabel to call findSecLabels and collectSecLabels on
the first call.
There are a few other problems. First, there's no psql support of any
kind. Now, this is kind of a corner-case feature: so maybe we don't
really need it. And as I mentioned on another thread, there aren't a
lot of good letters left for backslash-d commands. So I'd be just as
happy to add a system view along the lines I previously proposed for
comments and call it good. Alternatively, or in addition, we could
add a \d command after all. The best way forward is debatable, but we
certainly need *something*, because interpreting the pg_seclabel
catalog by hand is not for the faint of heart.Do you suggest the new system views should be defined for each supported
object classes, such as pg_largeobject_seclabel? It seems to me a bit
inflation of number of system views.
My preference is psql's \d commands at first.Please see http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.php
OK, I'll emulate this approach at first.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/9/23 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Please see http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.php
OK, I'll emulate this approach at first.
Don't worry about this part - I will do this myself. If you can just
fix the pg_dump stuff, I think we will be in pretty good shape.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
(2010/09/24 20:56), Robert Haas wrote:
2010/9/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Please see http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.php
OK, I'll emulate this approach at first.
Don't worry about this part - I will do this myself. If you can just
fix the pg_dump stuff, I think we will be in pretty good shape.
Ahh, I already did this part at the today's afternoon:
http://bit.ly/9kOsnx
And, the pg_dump stuff has been just implemented(, but not tested yet):
http://bit.ly/a0eVfL
If you prefer to keep the patch small, I'll revert the system_views.sql
in the next patch.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Fri, Sep 24, 2010 at 8:54 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
(2010/09/24 20:56), Robert Haas wrote:
2010/9/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Please see
http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.phpOK, I'll emulate this approach at first.
Don't worry about this part - I will do this myself. If you can just
fix the pg_dump stuff, I think we will be in pretty good shape.Ahh, I already did this part at the today's afternoon:
http://bit.ly/9kOsnxAnd, the pg_dump stuff has been just implemented(, but not tested yet):
http://bit.ly/a0eVfL
If you prefer to keep the patch small, I'll revert the system_views.sql
in the next patch.
It probably doesn't matter much - it'll likely take me about the same
amount of time to check your work as it would to do it myself, so it's
pretty much six of one, half a dozen of the other.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
The attached patch can be applied on the Robert's seclabel-v4.patch.
It contains the following stuffs.
* The "dummy_esp" module and regression test for SECURITY LABEL statement.
This module allows only four labels: "unclassified", "classified",
"secret" and "top secret". The later two labels can be set by only
superusers. The new regression test uses this "dummy_esp" module to
find out future regression in SECURITY LABEL statement.
* A minimum description about external security provider at the tail
of Database Roles and Privileges chapter.
* Add pg_seclabels system view
* Revising pg_dump/pg_dumpall
- '--security-label' was replaced by '--no-security-label'
- implemented according to the manner in comments.
findSecLabels() and collectSecLabels() are added to reduce number of
SQL queries, in addition to dumpSecLabel().
Thanks,
(2010/09/24 21:58), Robert Haas wrote:
On Fri, Sep 24, 2010 at 8:54 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
(2010/09/24 20:56), Robert Haas wrote:
2010/9/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
Please see
http://archives.postgresql.org/pgsql-hackers/2010-09/msg01080.phpOK, I'll emulate this approach at first.
Don't worry about this part - I will do this myself. If you can just
fix the pg_dump stuff, I think we will be in pretty good shape.Ahh, I already did this part at the today's afternoon:
http://bit.ly/9kOsnxAnd, the pg_dump stuff has been just implemented(, but not tested yet):
http://bit.ly/a0eVfL
If you prefer to keep the patch small, I'll revert the system_views.sql
in the next patch.It probably doesn't matter much - it'll likely take me about the same
amount of time to check your work as it would to do it myself, so it's
pretty much six of one, half a dozen of the other.
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-seclabel.5.patchapplication/octect-stream; name=pgsql-seclabel.5.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 15,20 **** SUBDIRS = \
--- 15,21 ----
dblink \
dict_int \
dict_xsyn \
+ dummy_esp \
earthdistance \
fuzzystrmatch \
hstore \
*** /dev/null
--- b/contrib/dummy_esp/Makefile
***************
*** 0 ****
--- 1,14 ----
+ # contrib/dummy_esp/Makefile
+
+ MODULES = dummy_esp
+
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/dummy_esp
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/dummy_esp/dummy_esp.c
***************
*** 0 ****
--- 1,56 ----
+ /*
+ * dummy_esp.c
+ *
+ * A dummy plugin of external security provider with security label.
+ *
+ * This module does not provide something worthful from security perspective,
+ * but being used to regression test independent from the platform specific
+ * features like SELinux.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+ #include "postgres.h"
+
+ #include "commands/seclabel.h"
+
+ PG_MODULE_MAGIC;
+
+ /* Entrypoint of the module */
+ void _PG_init(void);
+
+ /*
+ * dummy_object_relabel
+ *
+ * It shall be invoked when user tries to assign security label of
+ * the database object.
+ */
+ void
+ dummy_object_relabel(const ObjectAddress *object, const char *seclabel)
+ {
+ if (strcmp(seclabel, "unclassified") == 0 ||
+ strcmp(seclabel, "classified") == 0)
+ return;
+
+ if (strcmp(seclabel, "secret") == 0 ||
+ strcmp(seclabel, "top secret") == 0)
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("only superuser can set '%s' label", seclabel)));
+ return;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("'%s' is not a valid security label", seclabel)));
+ }
+
+ /*
+ * Entrypoint of the module
+ */
+ void
+ _PG_init(void)
+ {
+ register_label_provider("dummy", dummy_object_relabel);
+ }
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 5964,5969 ****
--- 5964,5974 ----
</row>
<row>
+ <entry><link linkend="view-pg-seclabels"><structname>pg_seclabels</structname></link></entry>
+ <entry>security labels</entry>
+ </row>
+
+ <row>
<entry><link linkend="view-pg-settings"><structname>pg_settings</structname></link></entry>
<entry>parameter settings</entry>
</row>
***************
*** 6870,6876 ****
--- 6875,6965 ----
</para>
</sect1>
+ <sect1 id="view-pg-seclabels">
+ <title><structname>pg_seclabels</structname></title>
+
+ <indexterm zone="view-pg-seclabels">
+ <primary>pg_seclabels</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_seclabels</structname> provides references to
+ security label of database objects with their text identifiers.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabels</> Columns</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+ <row>
+ <entry><structfield>classoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+ <row>
+ <entry><structfield>objsubid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ For a security label on a table column, this is the column number (the
+ <structfield>objoid</> and <structfield>classoid</> refer to
+ the table itself). For all other object types, this column is
+ zero.
+ </entry>
+ </row>
+ <row>
+ <entry><structfield>objtype</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>text identifier of the object type</entry>
+ </row>
+ <row>
+ <entry><structfield>objnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this object.
+ Maybe <literal>NULL</literal>, if object does not have its namespace.
+ </entry>
+ </row>
+ <row>
+ <entry><structfield>objname</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>The name of the object this security label assigned on</entry>
+ </row>
+ <row>
+ <entry><structfield>provider</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry><literal><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link>.provider</literal></entry>
+ <entry>The label provider associated with this label.</entry>
+ </row>
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry><literal><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link>.label</literal></entry>
+ <entry>The security label applied to this object.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
<sect1 id="view-pg-settings">
<title><structname>pg_settings</structname></title>
*** a/doc/src/sgml/user-manag.sgml
--- b/doc/src/sgml/user-manag.sgml
***************
*** 502,505 **** DROP ROLE <replaceable>name</replaceable>;
--- 502,536 ----
</para>
</sect1>
+ <sect1 id="external-security-providers">
+ <title>External Security Providers</title>
+
+ <para>
+ <productname>PostgreSQL</productname> allows third party plugins to
+ make their access control decision in addition to the default database
+ privilege mechanism. We call these plugins external security providers.
+ The version <literal>9.1.0</literal> or later provide various kind of
+ security hooks on strategic points of code paths, then the external
+ security providers shall be invoked via the hooks.
+ </para>
+ <para>
+ The major purpose to support external security providers is porting
+ mandatory access control (MAC) features;
+ such as <productname>SELinux</productname>.
+ Unlike the default database privilege mechanism, it makes access
+ control decision using a security label of database objects being
+ referenced and a centralized security policy.
+ <productname>PostgreSQL</productname> provides a set of facilities
+ to manage security labels of database objects as built-in feature.
+ The <link linkend="catalog-pg-seclabel"><structname>pg_seclabel
+ </structname></link> system catalog enables to assign security labels
+ on database objects, <xref linkend="sql-security-label"> command
+ allows us to manage security labels, and some of security hooks allows
+ to label database objects on its creation time.
+ </para>
+ <para>
+ We don't mention about detail of the individual security module here,
+ so please reference their own documentation to see any more information.
+ </para>
+ </sect1>
</chapter>
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 160,165 **** CREATE VIEW pg_prepared_xacts AS
--- 160,274 ----
CREATE VIEW pg_prepared_statements AS
SELECT * FROM pg_prepared_statement() AS P;
+ CREATE VIEW pg_seclabels AS
+ SELECT
+ l.objoid, l.classoid, l.objsubid,
+ CASE WHEN rel.relkind = 'r' THEN 'table'::text
+ WHEN rel.relkind = 'v' THEN 'view'::text
+ WHEN rel.relkind = 'S' THEN 'sequence'::text END AS objtype,
+ rel.relnamespace AS objnamespace,
+ CASE WHEN pg_table_is_visible(rel.oid)
+ THEN quote_ident(rel.relname)
+ ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname)
+ 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)
+ ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname)
+ END || '.' || att.attname 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
+ END AS objtype,
+ pro.pronamespace AS objnamespace,
+ CASE WHEN pg_function_is_visible(pro.oid)
+ THEN quote_ident(pro.proname)
+ ELSE quote_ident(nsp.nspname) || '.' || quote_ident(pro.proname)
+ END || '(' || pg_catalog.pg_get_function_arguments(pro.oid) || ')' 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' 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)
+ ELSE quote_ident(nsp.nspname) || '.' || quote_ident(typ.typname)
+ 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_catalog.pg_largeobject'::regclass 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) 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) 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
+ ;
+
CREATE VIEW pg_settings AS
SELECT * FROM pg_show_all_settings() AS A;
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 70,75 **** typedef struct
--- 70,83 ----
int objsubid; /* subobject (table column #) */
} CommentItem;
+ typedef struct
+ {
+ const char *provider; /* label provider of this security label */
+ const char *label; /* security label for an object */
+ Oid classoid; /* object class (catalog OID) */
+ Oid objoid; /* object OID */
+ int objsubid; /* subobject (table column #) */
+ } SecLabelItem;
/* global decls */
bool g_verbose; /* User wants verbose narration of our
***************
*** 125,131 **** static int binary_upgrade = 0;
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);
--- 133,139 ----
static int disable_dollar_quoting = 0;
static int dump_inserts = 0;
static int column_inserts = 0;
! static int no_security_label = 0;
static void help(const char *progname);
***************
*** 142,147 **** static void dumpComment(Archive *fout, const char *target,
--- 150,161 ----
static int findComments(Archive *fout, Oid classoid, Oid objoid,
CommentItem **items);
static int collectComments(Archive *fout, CommentItem **items);
+ static void dumpSecLabel(Archive *fout, const char *target,
+ const char *namespace, const char *owner,
+ CatalogId catalogId, int subid, DumpId dumpId);
+ static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
+ SecLabelItem **items);
+ static int collectSecLabels(Archive *fout, SecLabelItem **items);
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tyinfo);
***************
*** 184,193 **** static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
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);
--- 198,203 ----
***************
*** 305,311 **** main(int argc, char **argv)
{"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}
};
--- 315,321 ----
{"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
{"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
! {"no-security-label", no_argument, &no_security_label, 1},
{NULL, 0, NULL, 0}
};
***************
*** 454,461 **** main(int argc, char **argv)
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,
--- 464,471 ----
outputNoTablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
! else if (strcmp(optarg, "no-security-label") == 0)
! no_security_label = 1;
else
{
fprintf(stderr,
***************
*** 653,663 **** main(int argc, char **argv)
/*
* 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.
--- 663,670 ----
/*
* Disables security label support if server version < v9.1.x
*/
! if (!no_security_label && g_fout->remoteVersion < 90100)
! no_security_label = 1;
/*
* Start serializable transaction to dump consistent data.
***************
*** 856,862 **** help(const char *progname)
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"));
--- 863,869 ----
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(_(" --no-security-label do not dump security label assignments\n"));
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
***************
*** 2076,2081 **** dumpBlob(Archive *AH, BlobInfo *binfo)
--- 2083,2093 ----
NULL, binfo->rolname,
binfo->dobj.catId, 0, binfo->dobj.dumpId);
+ /* Dump security label if any */
+ dumpSecLabel(AH, cquery->data,
+ NULL, binfo->rolname,
+ binfo->dobj.catId, 0, binfo->dobj.dumpId);
+
/* Dump ACL if any */
if (binfo->blobacl)
dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
***************
*** 6587,6598 **** dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Schema Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "SCHEMA %s", qnspname);
dumpComment(fout, q->data,
NULL, nspinfo->rolname,
nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
qnspname, NULL, nspinfo->dobj.name, NULL,
--- 6599,6613 ----
nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Schema Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "SCHEMA %s", qnspname);
dumpComment(fout, q->data,
NULL, nspinfo->rolname,
nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ NULL, nspinfo->rolname,
+ nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
qnspname, NULL, nspinfo->dobj.name, NULL,
***************
*** 6717,6729 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Type Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
--- 6732,6747 ----
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Type Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
***************
*** 7093,7105 **** dumpBaseType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Type Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
--- 7111,7126 ----
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Type Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
***************
*** 7217,7229 **** dumpDomain(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Domain Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
--- 7238,7253 ----
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
NULL, NULL);
! /* Dump Domain Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
***************
*** 7323,7335 **** dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
NULL, NULL);
! /* Dump Type Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
--- 7347,7362 ----
NULL, NULL);
! /* Dump Type Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
dumpComment(fout, q->data,
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+ tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
PQclear(res);
destroyPQExpBuffer(q);
***************
*** 7647,7658 **** dumpProcLang(Archive *fout, ProcLangInfo *plang)
plang->dobj.dependencies, plang->dobj.nDeps,
NULL, NULL);
! /* Dump Proc Lang Comments */
resetPQExpBuffer(defqry);
appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
dumpComment(fout, defqry->data,
NULL, "",
plang->dobj.catId, 0, plang->dobj.dumpId);
if (plang->lanpltrusted)
dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
--- 7674,7688 ----
plang->dobj.dependencies, plang->dobj.nDeps,
NULL, NULL);
! /* Dump Proc Lang Comments and Security Labels */
resetPQExpBuffer(defqry);
appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
dumpComment(fout, defqry->data,
NULL, "",
plang->dobj.catId, 0, plang->dobj.dumpId);
+ dumpSecLabel(fout, defqry->data,
+ NULL, "",
+ plang->dobj.catId, 0, plang->dobj.dumpId);
if (plang->lanpltrusted)
dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
***************
*** 8208,8219 **** dumpFunc(Archive *fout, FuncInfo *finfo)
finfo->dobj.dependencies, finfo->dobj.nDeps,
NULL, NULL);
! /* Dump Function Comments */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "FUNCTION %s", funcsig);
dumpComment(fout, q->data,
finfo->dobj.namespace->dobj.name, finfo->rolname,
finfo->dobj.catId, 0, finfo->dobj.dumpId);
dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
funcsig, NULL, funcsig_tag,
--- 8238,8252 ----
finfo->dobj.dependencies, finfo->dobj.nDeps,
NULL, NULL);
! /* Dump Function Comments and Security Labels */
resetPQExpBuffer(q);
appendPQExpBuffer(q, "FUNCTION %s", funcsig);
dumpComment(fout, q->data,
finfo->dobj.namespace->dobj.name, finfo->rolname,
finfo->dobj.catId, 0, finfo->dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ finfo->dobj.namespace->dobj.name, finfo->rolname,
+ finfo->dobj.catId, 0, finfo->dobj.dumpId);
dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
funcsig, NULL, funcsig_tag,
***************
*** 9711,9716 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 9744,9752 ----
dumpComment(fout, q->data,
agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+ dumpSecLabel(fout, q->data,
+ agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+ agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
/*
* Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
***************
*** 10461,10566 **** dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
/*
* 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_provider;
! 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.provider, s.label"
! " FROM pg_catalog.pg_seclabel s"
! " LEFT OUTER JOIN"
! " pg_catalog.pg_attribute a"
! " ON s.objoid = a.attrelid AND"
! " s.objsubid = a.attnum"
! " WHERE classoid = %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_provider = PQfnumber(res, "provider");
! 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_provider),
- 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_provider),
! 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, "SECURITY LABEL", SECTION_NONE,
! cquery->data, "", NULL,
! &(objDumpId), 1,
NULL, NULL);
}
! destroyPQExpBuffer(lquery);
! destroyPQExpBuffer(cquery);
! destroyPQExpBuffer(tgbuf);
}
/*
--- 10497,10796 ----
/*
* dumpSecLabel
*
! * This routine is used to dump 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 catalog ID and subid which are the lookup key for pg_seclabel,
! * plus the dump ID for the object (for setting a dependency).
* If a matching pg_seclabel entry is found, it is dumped.
+ *
+ * Note: although this routine takes a dumpId for dependency purposes,
+ * that purpose is just to mark the dependency in the emitted dump file
+ * for possible future use by pg_restore. We do NOT use it for determining
+ * ordering of the label in the dump file, because this routine is called
+ * after dependency sorting occurs. This routine should be called just after
+ * calling ArchiveEntry() for the specified object.
*/
static void
! dumpSecLabel(Archive *fout, const char *target,
! const char *namespace, const char *owner,
! CatalogId catalogId, int subid, DumpId dumpId)
{
! SecLabelItem *labels;
! int nlabels;
! int i;
! PQExpBuffer query;
! /* do nothing, if --no-security-label is supplied */
! if (no_security_label)
return;
! /* Comments are schema not data ... except blob comments are data */
! if (strncmp(target, "LARGE OBJECT ", 13) != 0)
! {
! if (dataOnly)
! return;
! }
! else
! {
! if (schemaOnly)
! return;
! }
! /* Search for security labels associated with catalogId, using table */
! nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels);
! query = createPQExpBuffer();
! for (i = 0; i < nlabels; i++)
{
/*
! * Ignore label entries which does not match with the supplied subid.
! * However, only relations have objsubid except for zero, and we dump
! * security labels of relations at dumpTableSecLabel(), so it might
! * be a bug if labels[i].objsubid has any values except for zero.
*/
! if (labels[i].objsubid != subid)
continue;
! appendPQExpBuffer(query,
! "SECURITY LABEL FOR '%s' ON %s IS ",
! labels[i].provider, target);
! appendStringLiteralAH(query, labels[i].label, fout);
! appendPQExpBuffer(query, ";\n");
}
! if (query->len > 0)
{
ArchiveEntry(fout, nilCatalogId, createDumpId(),
! target, namespace, NULL, owner,
false, "SECURITY LABEL", SECTION_NONE,
! query->data, "", NULL,
! &(dumpId), 1,
NULL, NULL);
}
! destroyPQExpBuffer(query);
! }
!
! /*
! * dumpTableSecLabel
! *
! * As above, but dump security label for both the specified table (or view)
! * and its columns.
! */
! static void
! dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename)
! {
! SecLabelItem *labels;
! int nlabels;
! int i;
! PQExpBuffer query;
! PQExpBuffer target;
!
! /* do nothing, if --no-security-label is supplied */
! if (no_security_label)
! return;
!
! /* SecLabel are SCHEMA not data */
! if (dataOnly)
! return;
!
! /* Search for comments associated with relation, using table */
! nlabels = findSecLabels(fout,
! tbinfo->dobj.catId.tableoid,
! tbinfo->dobj.catId.oid,
! &labels);
!
! /* If comments exist, build SECURITY LABEL statements */
! if (nlabels <= 0)
! return;
!
! query = createPQExpBuffer();
! target = createPQExpBuffer();
!
! for (i = 0; i < nlabels; i++)
! {
! const char *colname;
! const char *provider = labels[i].provider;
! const char *label = labels[i].label;
! int objsubid = labels[i].objsubid;
!
! resetPQExpBuffer(target);
! if (objsubid == 0)
! {
! appendPQExpBuffer(target, "%s %s", reltypename,
! fmtId(tbinfo->dobj.name));
! }
! else
! {
! colname = getAttrName(objsubid, tbinfo);
! appendPQExpBuffer(target, "COLUMN %s.%s",
! fmtId(tbinfo->dobj.name),
! fmtId(colname));
! }
! appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
! provider, target->data);
! appendStringLiteralAH(query, label, fout);
! appendPQExpBuffer(query, ";\n");
! }
! if (query->len > 0)
! {
! resetPQExpBuffer(target);
! appendPQExpBuffer(target, "%s %s", reltypename,
! fmtId(tbinfo->dobj.name));
! ArchiveEntry(fout, nilCatalogId, createDumpId(),
! target->data,
! tbinfo->dobj.namespace->dobj.name,
! NULL, tbinfo->rolname,
! false, "SECURITY LABEL", SECTION_NONE,
! query->data, "", NULL,
! &(tbinfo->dobj.dumpId), 1,
! NULL, NULL);
! }
! destroyPQExpBuffer(query);
! destroyPQExpBuffer(target);
! }
!
! /*
! * findSecLabels
! *
! * Find the security label(s), if any, associated with the given object.
! * All the objsubid values associated with the given classoid/objoid are
! * found with one search.
! */
! static int
! findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
! {
! /* static storage for table of security labels */
! static SecLabelItem *labels = NULL;
! static int nlabels = -1;
!
! SecLabelItem *middle = NULL;
! SecLabelItem *low;
! SecLabelItem *high;
! int nmatch;
!
! /* Get security labels if we didn't already */
! if (nlabels < 0)
! nlabels = collectSecLabels(fout, &labels);
!
! /*
! * Do binary search to find some item matching the object.
! */
! low = &labels[0];
! high = &labels[nlabels - 1];
! while (low <= high)
! {
! middle = low + (high - low) / 2;
!
! if (classoid < middle->classoid)
! high = middle - 1;
! else if (classoid > middle->classoid)
! low = middle + 1;
! else if (objoid < middle->objoid)
! high = middle - 1;
! else if (objoid > middle->objoid)
! low = middle + 1;
! else
! break; /* found a match */
! }
!
! if (low > high) /* no matches */
! {
! *items = NULL;
! return 0;
! }
!
! /*
! * Now determine how many items match the object. The search loop
! * invariant still holds: only items between low and high inclusive could
! * match.
! */
! nmatch = 1;
! while (middle > low)
! {
! if (classoid != middle[-1].classoid ||
! objoid != middle[-1].objoid)
! break;
! middle--;
! nmatch++;
! }
!
! *items = middle;
!
! middle += nmatch;
! while (middle <= high)
! {
! if (classoid != middle->classoid ||
! objoid != middle->objoid)
! break;
! middle++;
! nmatch++;
! }
!
! return nmatch;
! }
!
! /*
! * collectSecLabels
! *
! * Construct a table of all security labels available for database objects.
! * We used to do per-object queries for the comments, but it's much faster
! * to pull them all over at once, and on most databases the memory cost
! * isn't high.
! *
! * The table is sorted by classoid/objid/objsubid for speed in lookup.
! */
! static int
! collectSecLabels(Archive *fout, SecLabelItem **items)
! {
! PGresult *res;
! PQExpBuffer query;
! int i_label;
! int i_provider;
! int i_classoid;
! int i_objoid;
! int i_objsubid;
! int ntups;
! int i;
! SecLabelItem *labels;
!
! query = createPQExpBuffer();
!
! appendPQExpBuffer(query,
! "SELECT label, provider, classoid, objoid, objsubid "
! "FROM pg_catalog.pg_seclabel "
! "ORDER BY classoid, objoid, objsubid");
!
! res = PQexec(g_conn, query->data);
! check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
!
! /* Construct lookup table containing OIDs in numeric form */
! i_label = PQfnumber(res, "label");
! i_provider = PQfnumber(res, "provider");
! i_classoid = PQfnumber(res, "classoid");
! i_objoid = PQfnumber(res, "objoid");
! i_objsubid = PQfnumber(res, "objsubid");
!
! ntups = PQntuples(res);
!
! labels = (SecLabelItem *) malloc(ntups * sizeof(SecLabelItem));
!
! for (i = 0; i < ntups; i++)
! {
! labels[i].label = PQgetvalue(res, i, i_label);
! labels[i].provider = PQgetvalue(res, i, i_provider);
! labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
! labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
! labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
! }
!
! /* Do NOT free the PGresult since we are keeping pointers into it */
! destroyPQExpBuffer(query);
!
! *items = labels;
! return ntups;
}
/*
***************
*** 10587,10599 **** dumpTable(Archive *fout, TableInfo *tbinfo)
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
--- 10817,10822 ----
***************
*** 11086,11091 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
--- 11309,11317 ----
/* Dump Table Comments */
dumpTableComment(fout, tbinfo, reltypename);
+ /* Dump Table Security Labels */
+ dumpTableSecLabel(fout, tbinfo, reltypename);
+
/* Dump comments on inlined table constraints */
for (j = 0; j < tbinfo->ncheck; j++)
{
***************
*** 11799,11810 **** dumpSequence(Archive *fout, TableInfo *tbinfo)
}
}
! /* Dump Sequence Comments */
resetPQExpBuffer(query);
appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
dumpComment(fout, query->data,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
}
if (!schemaOnly)
--- 12025,12039 ----
}
}
! /* Dump Sequence Comments and Security Labels */
resetPQExpBuffer(query);
appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
dumpComment(fout, query->data,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+ dumpSecLabel(fout, query->data,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
}
if (!schemaOnly)
*** a/src/bin/pg_dump/pg_dumpall.c
--- b/src/bin/pg_dump/pg_dumpall.c
***************
*** 69,75 **** static int disable_triggers = 0;
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;
--- 69,75 ----
static int inserts = 0;
static int no_tablespaces = 0;
static int use_setsessauth = 0;
! static int no_security_label = 0;
static int server_version;
static FILE *OPF;
***************
*** 134,140 **** main(int argc, char *argv[])
{"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}
};
--- 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},
! {"no-security-label", no_argument, &no_security_label, 1},
{NULL, 0, NULL, 0}
};
***************
*** 288,295 **** main(int argc, char *argv[])
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,
--- 288,295 ----
no_tablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
! else if (strcmp(optarg, "no-security-label") == 0)
! no_security_label = 1;
else
{
fprintf(stderr,
***************
*** 375,382 **** main(int argc, char *argv[])
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,
--- 375,382 ----
appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers");
if (use_setsessauth)
appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization");
! if (no_security_label)
! appendPQExpBuffer(pgdumpopts, " --no-security-label");
/*
* If there was a database specified on the command line, use that,
***************
*** 573,579 **** help(void)
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"));
--- 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(_(" --no-security-label do not dump security label assignments\n"));
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
*** a/src/test/regress/GNUmakefile
--- b/src/test/regress/GNUmakefile
***************
*** 109,115 **** installdirs-tests: installdirs
# Get some extra C modules from contrib/spi...
! all: refint$(DLSUFFIX) autoinc$(DLSUFFIX)
refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX)
cp $< $@
--- 109,115 ----
# Get some extra C modules from contrib/spi...
! all: refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_esp$(DLSUFFIX)
refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX)
cp $< $@
***************
*** 117,128 **** refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX)
--- 117,133 ----
autoinc$(DLSUFFIX): $(top_builddir)/contrib/spi/autoinc$(DLSUFFIX)
cp $< $@
+ dummy_esp$(DLSUFFIX): $(top_builddir)/contrib/dummy_esp/dummy_esp$(DLSUFFIX)
+ cp $< $@
+
$(top_builddir)/contrib/spi/refint$(DLSUFFIX): $(top_srcdir)/contrib/spi/refint.c
$(MAKE) -C $(top_builddir)/contrib/spi refint$(DLSUFFIX)
$(top_builddir)/contrib/spi/autoinc$(DLSUFFIX): $(top_srcdir)/contrib/spi/autoinc.c
$(MAKE) -C $(top_builddir)/contrib/spi autoinc$(DLSUFFIX)
+ $(top_builddir)/contrib/dummy_esp/dummy_esp$(DLSUFFIX): $(top_builddir)/contrib/dummy_esp/dummy_esp.c
+ $(MAKE) -C $(top_builddir)/contrib/dummy_esp dummy_esp$(DLSUFFIX)
# Tablespace setup
***************
*** 171,177 **** bigcheck: all
clean distclean maintainer-clean: clean-lib
# things built by `all' target
! rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) pg_regress_main.o pg_regress.o pg_regress$(X)
# things created by various check targets
rm -f $(output_files) $(input_files)
rm -rf testtablespace
--- 176,183 ----
clean distclean maintainer-clean: clean-lib
# things built by `all' target
! rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_esp$(DLSUFFIX)
! rm -f pg_regress_main.o pg_regress.o pg_regress$(X)
# things created by various check targets
rm -f $(output_files) $(input_files)
rm -rf testtablespace
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1276,1283 **** drop table cchild;
-- Check that ruleutils are working
--
SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
! 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_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
--- 1276,1283 ----
-- Check that ruleutils are working
--
SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
! 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_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
***************
*** 1287,1292 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
--- 1287,1293 ----
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.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 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 = (SELECT pg_class.oid FROM pg_class WHERE ((pg_class.relname = 'pg_largeobject'::name) AND (pg_class.relnamespace = (SELECT pg_namespace.oid FROM pg_namespace WHERE (pg_namespace.nspname = 'pg_catalog'::name)))))) 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_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.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_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_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
***************
*** 1333,1339 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (55 rows)
SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
--- 1334,1340 ----
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
! (56 rows)
SELECT tablename, rulename, definition FROM pg_rules
ORDER BY tablename, rulename;
*** /dev/null
--- b/src/test/regress/input/security_label.source
***************
*** 0 ****
--- 1,66 ----
+ --
+ -- Test for facilities of security label
+ --
+
+ -- initial setups
+ SET client_min_messages TO 'warning';
+
+ DROP ROLE IF EXISTS esp_user1;
+ DROP ROLE IF EXISTS esp_user2;
+
+ DROP TABLE IF EXISTS esp_tbl1;
+ DROP TABLE IF EXISTS esp_tbl2;
+ DROP TABLE IF EXISTS esp_tbl3;
+
+ CREATE USER esp_user1;
+ CREATE USER esp_user2;
+
+ CREATE TABLE esp_tbl1 (a int, b text);
+ CREATE TABLE esp_tbl2 (x int, y text);
+
+ ALTER TABLE esp_tbl1 OWNER TO esp_user1;
+ ALTER TABLE esp_tbl2 OWNER TO esp_user2;
+
+ RESET client_min_messages;
+
+ --
+ -- Test of SECURITY LABEL statement without a plugin
+ --
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- fail
+ SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'classified'; -- fail
+ SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail
+ SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail
+
+ -- Load dummy external security provider
+ LOAD '@abs_builddir@/dummy_esp';
+
+ --
+ -- Test of SECURITY LABEL statement with a plugin
+ --
+ SET SESSION AUTHORIZATION esp_user1;
+
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- OK
+ SECURITY LABEL ON COLUMN esp_tbl1.a IS 'unclassified'; -- OK
+ SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail
+ SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'unclassified'; -- OK
+ SECURITY LABEL FOR 'unknown_esp' ON TABLE esp_tbl1 IS 'classified'; -- fail
+ SECURITY LABEL ON TABLE esp_tbl2 IS 'unclassified'; -- fail (not owner)
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'secret'; -- fail (not superuser)
+ SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail (not found)
+
+ SET SESSION AUTHORIZATION esp_user2;
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'unclassified'; -- fail
+ SECURITY LABEL ON TABLE esp_tbl2 IS 'classified'; -- OK
+
+ RESET SESSION AUTHORIZATION;
+
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'top secret'; -- OK
+
+ SELECT objtype, objname, provider, label FROM pg_seclabels
+ WHERE objname in ('esp_tbl1', 'esp_tbl2');
+
+ -- clean up objects
+ DROP TABLE esp_tbl1;
+ DROP TABLE esp_tbl2;
+ DROP USER esp_user1;
+ DROP USER esp_user2;
*** /dev/null
--- b/src/test/regress/output/security_label.source
***************
*** 0 ****
--- 1,66 ----
+ --
+ -- Test for facilities of security label
+ --
+ -- initial setups
+ SET client_min_messages TO 'warning';
+ DROP ROLE IF EXISTS esp_user1;
+ DROP ROLE IF EXISTS esp_user2;
+ DROP TABLE IF EXISTS esp_tbl1;
+ DROP TABLE IF EXISTS esp_tbl2;
+ DROP TABLE IF EXISTS esp_tbl3;
+ CREATE USER esp_user1;
+ CREATE USER esp_user2;
+ CREATE TABLE esp_tbl1 (a int, b text);
+ CREATE TABLE esp_tbl2 (x int, y text);
+ ALTER TABLE esp_tbl1 OWNER TO esp_user1;
+ ALTER TABLE esp_tbl2 OWNER TO esp_user2;
+ RESET client_min_messages;
+ --
+ -- Test of SECURITY LABEL statement without a plugin
+ --
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- fail
+ ERROR: security label providers have been loaded
+ SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'classified'; -- fail
+ ERROR: security label provider "dummy" is not loaded
+ SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail
+ ERROR: security label providers have been loaded
+ SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail
+ ERROR: security label providers have been loaded
+ -- Load dummy external security provider
+ LOAD '@abs_builddir@/dummy_esp';
+ --
+ -- Test of SECURITY LABEL statement with a plugin
+ --
+ SET SESSION AUTHORIZATION esp_user1;
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- OK
+ SECURITY LABEL ON COLUMN esp_tbl1.a IS 'unclassified'; -- OK
+ SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail
+ ERROR: '...invalid label...' is not a valid security label
+ SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'unclassified'; -- OK
+ SECURITY LABEL FOR 'unknown_esp' ON TABLE esp_tbl1 IS 'classified'; -- fail
+ ERROR: security label provider "unknown_esp" is not loaded
+ SECURITY LABEL ON TABLE esp_tbl2 IS 'unclassified'; -- fail (not owner)
+ ERROR: must be owner of relation esp_tbl2
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'secret'; -- fail (not superuser)
+ ERROR: only superuser can set 'secret' label
+ SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail (not found)
+ ERROR: relation "esp_tbl3" does not exist
+ SET SESSION AUTHORIZATION esp_user2;
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'unclassified'; -- fail
+ ERROR: must be owner of relation esp_tbl1
+ SECURITY LABEL ON TABLE esp_tbl2 IS 'classified'; -- OK
+ RESET SESSION AUTHORIZATION;
+ SECURITY LABEL ON TABLE esp_tbl1 IS 'top secret'; -- OK
+ SELECT objtype, objname, provider, label FROM pg_seclabels
+ WHERE objname in ('esp_tbl1', 'esp_tbl2');
+ objtype | objname | provider | label
+ ---------+----------+----------+------------
+ table | esp_tbl1 | dummy | top secret
+ table | esp_tbl2 | dummy | classified
+ (2 rows)
+
+ -- clean up objects
+ DROP TABLE esp_tbl1;
+ DROP TABLE esp_tbl2;
+ DROP USER esp_user1;
+ DROP USER esp_user2;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 76,82 **** ignore: random
# ----------
test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete
! test: privileges
test: misc
# rules cannot run concurrently with any test that creates a view
test: rules
--- 76,82 ----
# ----------
test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete
! test: privileges security_label
test: misc
# rules cannot run concurrently with any test that creates a view
test: rules
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 88,93 **** test: delete
--- 88,94 ----
test: namespace
test: prepared_xacts
test: privileges
+ test: security_label
test: misc
test: rules
test: select_views
On Sat, Sep 25, 2010 at 7:04 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
* The "dummy_esp" module and regression test for SECURITY LABEL statement.
This module allows only four labels: "unclassified", "classified",
"secret" and "top secret". The later two labels can be set by only
superusers. The new regression test uses this "dummy_esp" module to
find out future regression in SECURITY LABEL statement.
* A minimum description about external security provider at the tail
of Database Roles and Privileges chapter.
* Add pg_seclabels system view
* Revising pg_dump/pg_dumpall
- '--security-label' was replaced by '--no-security-label'
- implemented according to the manner in comments.
findSecLabels() and collectSecLabels() are added to reduce number of
SQL queries, in addition to dumpSecLabel().
Thanks, this looks like mostly good stuff. Here's a new version of
the patch with some bug fixes, additional regression tests, and other
cleanup. I think this is about ready to commit. I didn't adopt your
documentation and I renamed your contrib module from dummy_esp to
dummy_seclabel, but the rest I took more or less as you had it. For
now, I don't want to use the term "external security provider" because
that's not really what this provides - it just provides labels. I
initially thought that an external security provider would really turn
out to be a concept that was somewhat embedded in the system, but on
further reflection I don't think that's the case. I think what we're
going to end up with is a collection of hooks that might happen to be
useful for security-related things, but not necessarily just those.
Anyway, I feel that it's a bit premature to document too much about
what this might do someday; the documentation already in the patch is
adequate to explain what it does now.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
Attachments:
(2010/09/27 11:49), Robert Haas wrote:
On Sat, Sep 25, 2010 at 7:04 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
* The "dummy_esp" module and regression test for SECURITY LABEL statement.
This module allows only four labels: "unclassified", "classified",
"secret" and "top secret". The later two labels can be set by only
superusers. The new regression test uses this "dummy_esp" module to
find out future regression in SECURITY LABEL statement.
* A minimum description about external security provider at the tail
of Database Roles and Privileges chapter.
* Add pg_seclabels system view
* Revising pg_dump/pg_dumpall
- '--security-label' was replaced by '--no-security-label'
- implemented according to the manner in comments.
findSecLabels() and collectSecLabels() are added to reduce number of
SQL queries, in addition to dumpSecLabel().Thanks, this looks like mostly good stuff. Here's a new version of
the patch with some bug fixes, additional regression tests, and other
cleanup. I think this is about ready to commit.
Thanks for your reviewing and cleaning-up.
I didn't adopt your
documentation and I renamed your contrib module from dummy_esp to
dummy_seclabel, but the rest I took more or less as you had it.
Fair enough. I intended the name of "dummy_esp" to host any other
upcoming regression tests corresponding to security hooks, but
right now it just provides dummy labels.
For
now, I don't want to use the term "external security provider" because
that's not really what this provides - it just provides labels. I
initially thought that an external security provider would really turn
out to be a concept that was somewhat embedded in the system, but on
further reflection I don't think that's the case. I think what we're
going to end up with is a collection of hooks that might happen to be
useful for security-related things, but not necessarily just those.
Right, the "security provider" plugin which uses the collection of
security hooks will provides access control decision, but we don't
force anything in the way to make decisions.
Someone may provide label based security, but other one may provide
non-label based security.
All we can say is that guest of the hook on SECURITY LABEL provides
security label, but it is unclear whether it also provides any access
control, or not.
I also think the "label provider" is a fair enough naming.
Anyway, I feel that it's a bit premature to document too much about
what this might do someday; the documentation already in the patch is
adequate to explain what it does now.
I agreed. It is quite internal stuff how security hooks should perform
on interactions with plugin modules, so it might be premature.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Mon, Sep 27, 2010 at 4:05 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
Thanks, this looks like mostly good stuff. Here's a new version of
the patch with some bug fixes, additional regression tests, and other
cleanup. I think this is about ready to commit.Thanks for your reviewing and cleaning-up.
I found and fixed a few more issues and committed this. The pg_dump
support had a few escaping bugs, and I added tab completion support
for psql. Considering the size of the patch, it seems likely that
there are some issues we both overlooked, but this is as solid as I
can make it for right now.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On Mon, 27 Sep 2010 21:07:33 -0400
Robert Haas <robertmhaas@gmail.com> wrote:
I found and fixed a few more issues and committed this. The pg_dump
support had a few escaping bugs, and I added tab completion support
for psql. Considering the size of the patch, it seems likely that
there are some issues we both overlooked, but this is as solid as I
can make it for right now.
Some OIDs used in SECURITY LABEL patch have already been used for
some functions such as pg_stat_get_xact_numscans().
The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.
Though all regression tests finish successfully, should this be
fixed ?
regards,
--
Shigeru Hanada
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
<hanada@metrosystems.co.jp> wrote:
On Mon, 27 Sep 2010 21:07:33 -0400
Robert Haas <robertmhaas@gmail.com> wrote:I found and fixed a few more issues and committed this. The pg_dump
support had a few escaping bugs, and I added tab completion support
for psql. Considering the size of the patch, it seems likely that
there are some issues we both overlooked, but this is as solid as I
can make it for right now.Some OIDs used in SECURITY LABEL patch have already been used for
some functions such as pg_stat_get_xact_numscans().The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?
Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On Tue, Sep 28, 2010 at 13:50, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
<hanada@metrosystems.co.jp> wrote:On Mon, 27 Sep 2010 21:07:33 -0400
Robert Haas <robertmhaas@gmail.com> wrote:I found and fixed a few more issues and committed this. The pg_dump
support had a few escaping bugs, and I added tab completion support
for psql. Considering the size of the patch, it seems likely that
there are some issues we both overlooked, but this is as solid as I
can make it for right now.Some OIDs used in SECURITY LABEL patch have already been used for
some functions such as pg_stat_get_xact_numscans().The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.
Could we run the script as part of the regression tests? :-)
Or if not, could we have the buildfarm run it?
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
On tis, 2010-09-28 at 13:53 +0200, Magnus Hagander wrote:
On Tue, Sep 28, 2010 at 13:50, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.Could we run the script as part of the regression tests? :-)
Or if not, could we have the buildfarm run it?
I think it should actually be run as part of the regular build. Ever
since I started using git and developing like 10 features at once, and
other people doing the same, the chances of (hidden) OID conflicts is
growing immensely.
On Tue, Sep 28, 2010 at 8:03 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On tis, 2010-09-28 at 13:53 +0200, Magnus Hagander wrote:
On Tue, Sep 28, 2010 at 13:50, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.Could we run the script as part of the regression tests? :-)
Or if not, could we have the buildfarm run it?
I think it should actually be run as part of the regular build. Ever
since I started using git and developing like 10 features at once, and
other people doing the same, the chances of (hidden) OID conflicts is
growing immensely.
Injecting nonessential checks into the build process doesn't seem like
a good idea to me. Typing 'make' should just do the build. If you
want to check for breakage, well, that's what 'make check' is for.
Another angle on this problem is that, at least AFAICT, the duplicate
OIDs are completely harmless so long as they are in different
catalogs. And if they are in the same catalog, then initdb will fail
(and shame on you if you don't notice that). Long, long ago
pg_description was indexed just by object-OID, so duplicates would be
a problem, but that hasn't been the case since 2001, and I'm not aware
of anything else that relies on OIDs being globally unique either. So
maybe we should decide that we just don't care about this any more.
It seems a little silly since we're in no danger of running out of
OIDs any time soon, but if there's no practical reason to do it...
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On tis, 2010-09-28 at 09:22 -0400, Robert Haas wrote:
I think it should actually be run as part of the regular build.
Ever
since I started using git and developing like 10 features at once,
and
other people doing the same, the chances of (hidden) OID conflicts
is
growing immensely.
Injecting nonessential checks into the build process doesn't seem like
a good idea to me. Typing 'make' should just do the build. If you
want to check for breakage, well, that's what 'make check' is for.
I don't feel strongly either way, but if we want to philosophize for a
minute, all these -W option on the compiler command line are
nonessential checks. ;-) I suppose 'make check' should test whether the
code behaves correctly rather than checking whether the code was
structured consistently to begin with.
Robert Haas <robertmhaas@gmail.com> writes:
Another angle on this problem is that, at least AFAICT, the duplicate
OIDs are completely harmless so long as they are in different
catalogs. And if they are in the same catalog, then initdb will fail
(and shame on you if you don't notice that). Long, long ago
pg_description was indexed just by object-OID, so duplicates would be
a problem, but that hasn't been the case since 2001, and I'm not aware
of anything else that relies on OIDs being globally unique either. So
maybe we should decide that we just don't care about this any more.
No, we shouldn't. The reason we still have the policy of global
uniqueness of manually-assigned OIDs is that those OIDs frequently
need to be copied in multiple places (eg, operators may need to be
entered in pg_amop). It gets a lot easier to make mistakes, and
harder to check for mistakes, if the OIDs aren't unique.
The duplicate_oids script is just something that committers are supposed
to know to run when applying a patch that messes with the catalogs.
That's a sufficiently small minority of patches that I don't see the
attraction of trying to wire it into every build, nor every regression
test. Maybe the landscape is changing to the point where we can't trust
committers to get this right, but I haven't seen evidence of that. You
certainly won't forget it again soon ;-)
regards, tom lane
On Tue, Sep 28, 2010 at 11:14 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Another angle on this problem is that, at least AFAICT, the duplicate
OIDs are completely harmless so long as they are in different
catalogs. And if they are in the same catalog, then initdb will fail
(and shame on you if you don't notice that). Long, long ago
pg_description was indexed just by object-OID, so duplicates would be
a problem, but that hasn't been the case since 2001, and I'm not aware
of anything else that relies on OIDs being globally unique either. So
maybe we should decide that we just don't care about this any more.No, we shouldn't. The reason we still have the policy of global
uniqueness of manually-assigned OIDs is that those OIDs frequently
need to be copied in multiple places (eg, operators may need to be
entered in pg_amop). It gets a lot easier to make mistakes, and
harder to check for mistakes, if the OIDs aren't unique.
Yeah, I guess that's true.
The duplicate_oids script is just something that committers are supposed
to know to run when applying a patch that messes with the catalogs.
That's a sufficiently small minority of patches that I don't see the
attraction of trying to wire it into every build, nor every regression
test. Maybe the landscape is changing to the point where we can't trust
committers to get this right, but I haven't seen evidence of that. You
certainly won't forget it again soon ;-)
Maybe so, but the more steps there are that have to be manually
remembered, the harder it gets. Run the regression tests, check for
duplicate OIDs, bump catversion, bump WAL version, bump pg_dump
archive version, bump pg_control version, check for unnecessary header
includes, audit trailing whitespace, look for leftovers that don't
need to be committed. If we can combine a step or two, it just makes
life easier.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise Postgres Company
On Tue, Sep 28, 2010 at 3:22 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 28, 2010 at 8:03 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On tis, 2010-09-28 at 13:53 +0200, Magnus Hagander wrote:
On Tue, Sep 28, 2010 at 13:50, Robert Haas <robertmhaas@gmail.com>
wrote:
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.Could we run the script as part of the regression tests? :-)
Or if not, could we have the buildfarm run it?
I think it should actually be run as part of the regular build. Ever
since I started using git and developing like 10 features at once, and
other people doing the same, the chances of (hidden) OID conflicts is
growing immensely.Injecting nonessential checks into the build process doesn't seem like
a good idea to me. Typing 'make' should just do the build. If you
want to check for breakage, well, that's what 'make check' is for.
How about having git-hooks execute the script on specific action of our
choice? pre-commit hook sounds like a good place to check for this.
Regards,
(I don't have the complete context of this thread, so please ignore my
ignorance)
--
gurjeet.singh
@ EnterpriseDB - The Enterprise Postgres Company
http://www.EnterpriseDB.com
singh.gurjeet@{ gmail | yahoo }.com
Twitter/Skype: singh_gurjeet
Mail sent from my BlackLaptop device
Robert Haas wrote:
On Tue, Sep 28, 2010 at 3:57 AM, Shigeru HANADA
<hanada@metrosystems.co.jp> wrote:On Mon, 27 Sep 2010 21:07:33 -0400
Robert Haas <robertmhaas@gmail.com> wrote:I found and fixed a few more issues and committed this. ?The pg_dump
support had a few escaping bugs, and I added tab completion support
for psql. ?Considering the size of the patch, it seems likely that
there are some issues we both overlooked, but this is as solid as I
can make it for right now.Some OIDs used in SECURITY LABEL patch have already been used for
some functions such as pg_stat_get_xact_numscans().The src/include/catalog/duplicate_oids script reports that 3037 ~
3040 are used two or more times.Though all regression tests finish successfully, should this be
fixed ?Woops. Thanks for the report, fixed. I wish we had a regression test
that would catch these mistakes. It's easy to forget to run this
script.
Attached it the script I use for checks that eventually calls
src/tools/pgtest.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ It's impossible for everything to be true. +