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:
91
src/backend/utils/cache/relcache.c
vendored
91
src/backend/utils/cache/relcache.c
vendored
@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user