1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Code review for row security.

Buildfarm member tick identified an issue where the policies in the
relcache for a relation were were being replaced underneath a running
query, leading to segfaults while processing the policies to be added
to a query.  Similar to how TupleDesc RuleLocks are handled, add in a
equalRSDesc() function to check if the policies have actually changed
and, if not, swap back the rsdesc field (using the original instead of
the temporairly built one; the whole structure is swapped and then
specific fields swapped back).  This now passes a CLOBBER_CACHE_ALWAYS
for me and should resolve the buildfarm error.

In addition to addressing this, add a new chapter in Data Definition
under Privileges which explains row security and provides examples of
its usage, change \d to always list policies (even if row security is
disabled- but note that it is disabled, or enabled with no policies),
rework check_role_for_policy (it really didn't need the entire policy,
but it did need to be using has_privs_of_role()), and change the field
in pg_class to relrowsecurity from relhasrowsecurity, based on
Heikki's suggestion.  Also from Heikki, only issue SET ROW_SECURITY in
pg_restore when talking to a 9.5+ server, list Bypass RLS in \du, and
document --enable-row-security options for pg_dump and pg_restore.

Lastly, fix a number of minor whitespace and typo issues from Heikki,
Dimitri, add a missing #include, per Peter E, fix a few minor
variable-assigned-but-not-used and resource leak issues from Coverity
and add tab completion for role attribute bypassrls as well.
This commit is contained in:
Stephen Frost
2014-09-24 16:32:22 -04:00
parent 3f6f9260e3
commit 6550b901fe
24 changed files with 442 additions and 125 deletions

View File

@ -847,6 +847,87 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
return true;
}
/*
* equalPolicy
*
* Determine whether two policies are equivalent
*/
static bool
equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
{
int i;
Oid *r1,
*r2;
if (policy1 != NULL)
{
if (policy2 == NULL)
return false;
if (policy1->rsecid != policy2->rsecid)
return false;
if (policy1->cmd != policy2->cmd)
return false;
if (policy1->hassublinks != policy2->hassublinks);
return false;
if (strcmp(policy1->policy_name,policy2->policy_name) != 0)
return false;
if (ARR_DIMS(policy1->roles)[0] != ARR_DIMS(policy2->roles)[0])
return false;
r1 = (Oid *) ARR_DATA_PTR(policy1->roles);
r2 = (Oid *) ARR_DATA_PTR(policy2->roles);
for (i = 0; i < ARR_DIMS(policy1->roles)[0]; i++)
{
if (r1[i] != r2[i])
return false;
}
if (!equal(policy1->qual, policy1->qual))
return false;
if (!equal(policy1->with_check_qual, policy2->with_check_qual))
return false;
}
else if (policy2 != NULL)
return false;
return true;
}
/*
* equalRSDesc
*
* Determine whether two RowSecurityDesc's are equivalent
*/
static bool
equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
{
ListCell *lc,
*rc;
if (rsdesc1 == NULL && rsdesc2 == NULL)
return true;
if ((rsdesc1 != NULL && rsdesc2 == NULL) ||
(rsdesc1 == NULL && rsdesc2 != NULL))
return false;
if (list_length(rsdesc1->policies) != list_length(rsdesc2->policies))
return false;
/* RelationBuildRowSecurity should build policies in order */
forboth(lc, rsdesc1->policies, rc, rsdesc2->policies)
{
RowSecurityPolicy *l = (RowSecurityPolicy *) lfirst(lc);
RowSecurityPolicy *r = (RowSecurityPolicy *) lfirst(rc);
if (!equalPolicy(l,r))
return false;
}
return false;
}
/*
* RelationBuildDesc
@ -967,7 +1048,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
else
relation->trigdesc = NULL;
if (relation->rd_rel->relhasrowsecurity)
if (relation->rd_rel->relrowsecurity)
RelationBuildRowSecurity(relation);
else
relation->rsdesc = NULL;
@ -2104,6 +2185,7 @@ RelationClearRelation(Relation relation, bool rebuild)
Oid save_relid = RelationGetRelid(relation);
bool keep_tupdesc;
bool keep_rules;
bool keep_policies;
/* Build temporary entry, but don't link it into hashtable */
newrel = RelationBuildDesc(save_relid, false);
@ -2117,6 +2199,7 @@ RelationClearRelation(Relation relation, bool rebuild)
keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
keep_policies = equalRSDesc(relation->rsdesc, newrel->rsdesc);
/*
* Perform swapping of the relcache entry contents. Within this
@ -2165,6 +2248,8 @@ RelationClearRelation(Relation relation, bool rebuild)
SWAPFIELD(RuleLock *, rd_rules);
SWAPFIELD(MemoryContext, rd_rulescxt);
}
if (keep_policies)
SWAPFIELD(RowSecurityDesc *, rsdesc);
/* toast OID override must be preserved */
SWAPFIELD(Oid, rd_toastoid);
/* pgstat_info must be preserved */
@ -3345,11 +3430,11 @@ RelationCacheInitializePhase3(void)
/*
* Re-load the row security policies if the relation has them, since
* they are not preserved in the cache. Note that we can never NOT
* have a policy while relhasrowsecurity is true-
* have a policy while relrowsecurity is true,
* RelationBuildRowSecurity will create a single default-deny policy
* if there is no policy defined in pg_rowsecurity.
*/
if (relation->rd_rel->relhasrowsecurity && relation->rsdesc == NULL)
if (relation->rd_rel->relrowsecurity && relation->rsdesc == NULL)
{
RelationBuildRowSecurity(relation);