diff options
author | Stephen Frost <sfrost@snowman.net> | 2014-09-24 16:32:22 -0400 |
---|---|---|
committer | Stephen Frost <sfrost@snowman.net> | 2014-09-24 16:32:22 -0400 |
commit | 6550b901fe7c47c03775400e0c790c6c1234a017 (patch) | |
tree | f67c2cabd58ef765f0bcaf4307d73d7eac51e5fc /src/backend/utils/cache/relcache.c | |
parent | 3f6f9260e308a331e6809d5309b17d1613ff900f (diff) | |
download | postgresql-6550b901fe7c47c03775400e0c790c6c1234a017.tar.gz postgresql-6550b901fe7c47c03775400e0c790c6c1234a017.zip |
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.
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 91 |
1 files changed, 88 insertions, 3 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index e7f7129bd9d..c98e3132883 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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); |