The attached patch is a revised one for DML permission checks. List of updates: - Source code comments in the patched functions were revised. - ExecCheckRTPerms() and ExecCheckRTEPerms() were moved to aclchk.c, and renamed to chkpriv_relation_perms() and chkpriv_rte_perms(). - It took the 2nd argument (bool abort) that is a hint of behavior on access violations. - It also returns AclResult, instead of bool. - I assumed RI_Initial_Check() is not broken, right now. So, this patch just reworks DML permission checks without any bugfixes. - The ESP hook were moved to ExecCheckRTPerms() from ExecCheckRTEPerms(). - At DoCopy() and RI_Initial_Check() call the checker function with list_make1(&rte), instead of &rte. - In DoCopy(), required_access is used to store either ACL_SELECT or ACL_INSERT; initialized at head of the function. - In DoCopy(), it initialize selectedCols or modifiedCol of RTE depending on if (is_from), instead of columnsSet.
ToDo: - makeRangeTblEntry() stuff to allocate a RTE node with given parameter is not yet. Thanks, (2010/05/26 12:04), KaiGai Kohei wrote: > (2010/05/26 11:12), Stephen Frost wrote: >> * KaiGai Kohei (kai...@ak.jp.nec.com) wrote: >>>> #2: REALLY BIG ISSUE- You've added ExecutorCheckPerms_hook as part of >>>> this patch- don't, we're in feature-freeze right now and should not be >>>> adding hooks at this time. >>> >>> The patch is intended to submit for the v9.1 development, not v9.0, isn't >>> it? >> >> That really depends on if this is actually fixing a bug in the existing >> code or not. I'm on the fence about that at the moment, to be honest. >> I was trying to find if we expliitly say that SELECT rights are needed >> to reference a column but wasn't able to. If every code path is >> expecting that, then perhaps we should just document it that way and >> move on. In that case, all these changes would be for 9.1. If we >> decide the current behavior is a bug, it might be something which could >> be fixed in 9.0 and maybe back-patched. > > Ahh, because I found out an independent problem during the discussion, > it made us confused. Please make clear this patch does not intend to > fix the bug. > > If we decide it is an actual bug to be fixed/informed, I also agree > it should be worked in a separated patch. > > Well, rest of discussion should be haven in different thread. > >> In *either* case, given that one is a 'clean-up' patch and the other is >> 'new functionality', they should be independent *anyway*. Small >> incremental changes that don't break things when applied is what we're >> shooting for here. > > Agreed. > >>>> #3: You didn't move ExecCheckRTPerms() and ExecCheckRTEPerms() to >>>> utils/acl and instead added executor/executor.h to rt_triggers.c. >>>> I don't particularly like that. I admit that DoCopy() already knew >>>> about the executor, and if that were the only case outside of the >>>> executor where ExecCheckRTPerms() was getting called it'd probably be >>>> alright, but we already have another place that wants to use it, so >>>> let's move it to a more appropriate place. >>> >>> Sorry, I'm a bit confused. >>> It seemed to me you suggested to utilize ExecCheckRTPerms() rather than >>> moving its logic anywhere, so I kept it here. (Was it misunderstand?) >> >> I'm talking about moving the whole function (all 3 lines of it) to >> somewhere else and then reworking the function to be more appropriate >> based on it's new location (including renaming and changing arguments >> and return values, as appropriate). > > OK, I agreed. > >>> If so, but, I doubt utils/acl is the best placeholder of the moved >>> ExecCheckRTPerms(), because the checker function calls both of the >>> default acl functions and a optional external security function. >> >> Can you explain why you think that having a function in utils/acl (eg: >> include/utils/acl.h and backend/utils/aclchk.c) which calls default acl >> functions and an allows for an external hook would be a bad idea? >> >>> It means the ExecCheckRTPerms() is caller of acl functions, not acl >>> function itself, isn't it? >> >> It's providing a higher-level service, sure, but there's nothing >> particularly interesting or special about what it's doing in this case, >> and, we need it in multiple places. Why duplicate it? > > If number of the checker functions is only a reason why we move > ExecCheckRTPerms() into the backend/utils/aclchk.c right now, I > don't have any opposition. > When it reaches to a dozen, we can consider new location. Right? > > Sorry, the name of pg_rangetbl_aclcheck() was misleading for me. > >>> I agreed the checker function is not a part of executor, but it is >>> also not a part of acl functions in my opinion. >>> >>> If it is disinclined to create a new directory to deploy the checker >>> function, my preference is src/backend/utils/adt/security.c and >>> src/include/utils/security.h . >> >> We don't need a new directory or file for one function, as Robert >> already pointed out. > > OK, let's consider when aclchk.c holds a dozen of checker functions. > >>>> #6: I havn't checked yet, but if there are other things in an RTE which >>>> would make sense in the DoCopy case, beyond just what's needed for the >>>> permissions checking, and which wouldn't be 'correct' with a NULL'd >>>> value, I would set those. Yes, we're building the RTE to check >>>> permissions, but we don't want someone downstream to be suprised when >>>> they make a change to something in the permissions checking and discover >>>> that a value in RTE they expected to be there wasn't valid. Even more >>>> so, if there are function helpers which can be used to build an RTE, we >>>> should be using them. The same goes for RI_Initial_Check(). >>> >>> Are you saying something like makeFuncExpr()? >>> I basically agree. However, should it be done in this patch? >> >> Actually, I mean looking for, and using, things like >> markRTEForSelectPriv() and addRangeTableEntry() or >> addRangeTableEntryForRelation(). > > OK, I'll make it in separated patch. > >>>> #8: When moving ExecCheckRTPerms(), you should rename it to be more like >>>> the other function calls in acl.h Perhaps pg_rangetbl_aclcheck()? >>>> Also, it should return an actual AclResult instead of just true/false. >>> >>> See the comments in #3. >>> And, if the caller has to handle aclcheck_error(), user cannot distinguish >>> access violation errors between the default PG permission and any other >>> external security stuff, isn't it? >> >> I'm not suggesting that the caller handle aclcheck_error().. >> ExecCheckRTPerms() could just as easily have a flag which indicates if >> it will call aclcheck_error() or not, and if not, to return an >> AclResult to the caller. That flag could then be passed to >> ExecCheckRTEPerms() as you have it now. > > Sorry, the name of pg_rangetbl_aclcheck() is also misleading for me. > It makes me an impression that it always returns AclResult and caller handles > it appropriately, like pg_class_aclcheck(). > > What you explained seems to me same as what I plan now. > > Thanks, -- KaiGai Kohei <kai...@ak.jp.nec.com>
*** a/src/backend/catalog/aclchk.c --- b/src/backend/catalog/aclchk.c *************** *** 4722,4724 **** get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid) --- 4722,4937 ---- return result; } + + /* + * External security provider hooks + */ + chkpriv_relation_perms_hook_type chkpriv_relation_perms_hook = NULL; + + /* + * chkpriv_rte_perms + * A helper function of chkpriv_relation_perms. + * It checks access permissions for a single RTE. If violated, it raises + * an error or returned false depending on the 'abort' argument. + */ + static AclResult + chkpriv_rte_perms(RangeTblEntry *rte, bool abort) + { + AclMode requiredPerms; + AclMode relPerms; + AclMode remainingPerms; + Oid relOid; + Oid userid; + Bitmapset *tmpset; + int col; + + /* + * Only plain-relation RTEs need to be checked here. Function RTEs are + * checked by init_fcache when the function is prepared for execution. + * Join, subquery, and special RTEs need no checks. + */ + if (rte->rtekind != RTE_RELATION) + return ACLCHECK_OK; + + /* + * No work if requiredPerms is empty. + */ + requiredPerms = rte->requiredPerms; + if (requiredPerms == 0) + return ACLCHECK_OK; + + relOid = rte->relid; + + /* + * userid to check as: current user unless we have a setuid indication. + * + * Note: GetUserId() is presently fast enough that there's no harm in + * calling it separately for each RTE. If that stops being true, we could + * call it once in chkpriv_relation_perms and pass the userid down from + * there. But for now, no need for the extra clutter. + */ + userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + + /* + * We must have *all* the requiredPerms bits, but some of the bits can be + * satisfied from column-level rather than relation-level permissions. + * First, remove any bits that are satisfied by relation permissions. + */ + relPerms = pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL); + remainingPerms = requiredPerms & ~relPerms; + if (remainingPerms != 0) + { + /* + * If we lack any permissions that exist only as relation permissions, + * we can fail straight away. + */ + if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE)) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + + /* + * Check to see if we have the needed privileges at column level. + * + * Note: failures just report a table-level error; it would be nicer + * to report a column-level error if we have some but not all of the + * column privileges. + */ + if (remainingPerms & ACL_SELECT) + { + /* + * When the query doesn't explicitly reference any columns (for + * example, SELECT COUNT(*) FROM table), allow the query if we + * have SELECT on any column of the rel, as per SQL spec. + */ + if (bms_is_empty(rte->selectedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ANY) != ACLCHECK_OK) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + } + + tmpset = bms_copy(rte->selectedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* Whole-row reference, must have priv on all cols */ + if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, + ACLMASK_ALL) != ACLCHECK_OK) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, + ACL_SELECT) != ACLCHECK_OK) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + } + } + bms_free(tmpset); + } + + /* + * Basically the same for the mod columns, with either INSERT or + * UPDATE privilege as specified by remainingPerms. + */ + remainingPerms &= ~ACL_SELECT; + if (remainingPerms != 0) + { + /* + * When the query doesn't explicitly change any columns, allow the + * query if we have permission on any column of the rel. This is + * to handle SELECT FOR UPDATE as well as possible corner cases in + * INSERT and UPDATE. + */ + if (bms_is_empty(rte->modifiedCols)) + { + if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms, + ACLMASK_ANY) != ACLCHECK_OK) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + } + + tmpset = bms_copy(rte->modifiedCols); + while ((col = bms_first_member(tmpset)) >= 0) + { + /* remove the column number offset */ + col += FirstLowInvalidHeapAttributeNumber; + if (col == InvalidAttrNumber) + { + /* whole-row reference can't happen here */ + elog(ERROR, "whole-row update is not implemented"); + } + else + { + if (pg_attribute_aclcheck(relOid, col, userid, + remainingPerms) != ACLCHECK_OK) + { + if (abort) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, + get_rel_name(relOid)); + return ACLCHECK_NO_PRIV; + } + } + } + bms_free(tmpset); + } + } + return ACLCHECK_OK; + } + + /* + * chkpriv_relation_perms + * It checks access permissions for all relations listed in a range table. + * If violated, it raises an error or returns false depending on the 'abort' + * argument. + * It also invokes an external security provide to check the permissions. + * If it is available, both of the default PG checks and external checks + * have to allow the required accesses for the relations. + */ + AclResult + chkpriv_relation_perms(List *rangeTable, bool abort) + { + ListCell *l; + RangeTblEntry *rte; + AclResult retval = ACLCHECK_OK; + + foreach(l, rangeTable) + { + rte = (RangeTblEntry *) lfirst(l); + + retval = chkpriv_rte_perms(rte, abort); + if (retval != ACLCHECK_OK) + return retval; + } + + /* External security provider invocation */ + if (chkpriv_relation_perms_hook) + retval = (*chkpriv_relation_perms_hook)(rangeTable, abort); + + return retval; + } *** a/src/backend/commands/copy.c --- b/src/backend/commands/copy.c *************** *** 21,26 **** --- 21,27 ---- #include <arpa/inet.h> #include "access/heapam.h" + #include "access/sysattr.h" #include "access/xact.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" *************** *** 726,733 **** DoCopy(const CopyStmt *stmt, const char *queryString) bool force_quote_all = false; bool format_specified = false; AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); - AclMode relPerms; - AclMode remainingPerms; ListCell *option; TupleDesc tupDesc; int num_phys_attrs; --- 727,732 ---- *************** *** 988,993 **** DoCopy(const CopyStmt *stmt, const char *queryString) --- 987,996 ---- if (stmt->relation) { + RangeTblEntry rte; + List *attnums; + ListCell *cur; + Assert(!stmt->query); cstate->queryDesc = NULL; *************** *** 998,1025 **** DoCopy(const CopyStmt *stmt, const char *queryString) tupDesc = RelationGetDescr(cstate->rel); /* Check relation permissions. */ ! relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(), ! required_access, ACLMASK_ALL); ! remainingPerms = required_access & ~relPerms; ! if (remainingPerms != 0) { ! /* We don't have table permissions, check per-column permissions */ ! List *attnums; ! ListCell *cur; ! attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); ! foreach(cur, attnums) ! { ! int attnum = lfirst_int(cur); ! ! if (pg_attribute_aclcheck(RelationGetRelid(cstate->rel), ! attnum, ! GetUserId(), ! remainingPerms) != ACLCHECK_OK) ! aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, ! RelationGetRelationName(cstate->rel)); ! } } /* check read-only transaction */ if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) --- 1001,1023 ---- tupDesc = RelationGetDescr(cstate->rel); /* Check relation permissions. */ ! memset(&rte, 0, sizeof(rte)); ! rte.type = T_RangeTblEntry; ! rte.rtekind = RTE_RELATION; ! rte.relid = RelationGetRelid(cstate->rel); ! rte.requiredPerms = required_access; ! ! attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); ! foreach (cur, attnums) { ! int attnum = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber; ! if (is_from) ! rte.modifiedCols = bms_add_member(rte.modifiedCols, attnum); ! else ! rte.selectedCols = bms_add_member(rte.selectedCols, attnum); } + chkpriv_relation_perms(list_make1(&rte), true); /* check read-only transaction */ if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** *** 72,79 **** static void ExecutePlan(EState *estate, PlanState *planstate, long numberTuples, ScanDirection direction, DestReceiver *dest); - static void ExecCheckRTPerms(List *rangeTable); - static void ExecCheckRTEPerms(RangeTblEntry *rte); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree); --- 72,77 ---- *************** *** 402,572 **** ExecutorRewind(QueryDesc *queryDesc) MemoryContextSwitchTo(oldcontext); } - - /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ - static void - ExecCheckRTPerms(List *rangeTable) - { - ListCell *l; - - foreach(l, rangeTable) - { - ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); - } - } - - /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ - static void - ExecCheckRTEPerms(RangeTblEntry *rte) - { - AclMode requiredPerms; - AclMode relPerms; - AclMode remainingPerms; - Oid relOid; - Oid userid; - Bitmapset *tmpset; - int col; - - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked by init_fcache when the function is prepared for execution. - * Join, subquery, and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - - /* - * We must have *all* the requiredPerms bits, but some of the bits can be - * satisfied from column-level rather than relation-level permissions. - * First, remove any bits that are satisfied by relation permissions. - */ - relPerms = pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL); - remainingPerms = requiredPerms & ~relPerms; - if (remainingPerms != 0) - { - /* - * If we lack any permissions that exist only as relation permissions, - * we can fail straight away. - */ - if (remainingPerms & ~(ACL_SELECT | ACL_INSERT | ACL_UPDATE)) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - - /* - * Check to see if we have the needed privileges at column level. - * - * Note: failures just report a table-level error; it would be nicer - * to report a column-level error if we have some but not all of the - * column privileges. - */ - if (remainingPerms & ACL_SELECT) - { - /* - * When the query doesn't explicitly reference any columns (for - * example, SELECT COUNT(*) FROM table), allow the query if we - * have SELECT on any column of the rel, as per SQL spec. - */ - if (bms_is_empty(rte->selectedCols)) - { - if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, - ACLMASK_ANY) != ACLCHECK_OK) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - } - - tmpset = bms_copy(rte->selectedCols); - while ((col = bms_first_member(tmpset)) >= 0) - { - /* remove the column number offset */ - col += FirstLowInvalidHeapAttributeNumber; - if (col == InvalidAttrNumber) - { - /* Whole-row reference, must have priv on all cols */ - if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, - ACLMASK_ALL) != ACLCHECK_OK) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - } - else - { - if (pg_attribute_aclcheck(relOid, col, userid, ACL_SELECT) - != ACLCHECK_OK) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - } - } - bms_free(tmpset); - } - - /* - * Basically the same for the mod columns, with either INSERT or - * UPDATE privilege as specified by remainingPerms. - */ - remainingPerms &= ~ACL_SELECT; - if (remainingPerms != 0) - { - /* - * When the query doesn't explicitly change any columns, allow the - * query if we have permission on any column of the rel. This is - * to handle SELECT FOR UPDATE as well as possible corner cases in - * INSERT and UPDATE. - */ - if (bms_is_empty(rte->modifiedCols)) - { - if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms, - ACLMASK_ANY) != ACLCHECK_OK) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - } - - tmpset = bms_copy(rte->modifiedCols); - while ((col = bms_first_member(tmpset)) >= 0) - { - /* remove the column number offset */ - col += FirstLowInvalidHeapAttributeNumber; - if (col == InvalidAttrNumber) - { - /* whole-row reference can't happen here */ - elog(ERROR, "whole-row update is not implemented"); - } - else - { - if (pg_attribute_aclcheck(relOid, col, userid, remainingPerms) - != ACLCHECK_OK) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); - } - } - bms_free(tmpset); - } - } - } - /* * Check that the query does not imply any writes to non-temp tables. * --- 400,405 ---- *************** *** 630,636 **** InitPlan(QueryDesc *queryDesc, int eflags) /* * Do permissions checks */ ! ExecCheckRTPerms(rangeTable); /* * initialize the node's execution state --- 463,469 ---- /* * Do permissions checks */ ! chkpriv_relation_perms(rangeTable, true); /* * initialize the node's execution state *** a/src/backend/utils/adt/ri_triggers.c --- b/src/backend/utils/adt/ri_triggers.c *************** *** 30,35 **** --- 30,36 ---- #include "postgres.h" + #include "access/sysattr.h" #include "access/xact.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" *************** *** 2624,2629 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) --- 2625,2633 ---- char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; + Bitmapset *pkColumns = NULL; + Bitmapset *fkColumns = NULL; + RangeTblEntry rte; const char *sep; int i; int old_work_mem; *************** *** 2632,2649 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) SPIPlanPtr qplan; /* * Check to make sure current user has enough permissions to do the test * query. (If not, caller can fall back to the trigger method, which * works because it changes user IDs on the fly.) * * XXX are there any other show-stopper conditions to check? */ ! if (pg_class_aclcheck(RelationGetRelid(fk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK) ! return false; ! if (pg_class_aclcheck(RelationGetRelid(pk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK) return false; ! ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); /*---------- * The query string built is: --- 2636,2674 ---- SPIPlanPtr qplan; /* + * Set up RI_ConstraintInfo + */ + ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); + + /* * Check to make sure current user has enough permissions to do the test * query. (If not, caller can fall back to the trigger method, which * works because it changes user IDs on the fly.) * * XXX are there any other show-stopper conditions to check? */ ! for (i = 0; i < riinfo.nkeys; i++) ! { ! fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i] ! - FirstLowInvalidHeapAttributeNumber); ! pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i] ! - FirstLowInvalidHeapAttributeNumber); ! } ! ! memset(&rte, 0, sizeof(rte)); ! rte.type = T_RangeTblEntry; ! rte.rtekind = RTE_RELATION; ! rte.requiredPerms = ACL_SELECT; ! ! rte.relid = RelationGetRelid(fk_rel); ! rte.selectedCols = fkColumns; ! if (chkpriv_relation_perms(list_make1(&rte), false) != ACLCHECK_OK) return false; ! rte.relid = RelationGetRelid(pk_rel); ! rte.selectedCols = pkColumns; ! if (chkpriv_relation_perms(list_make1(&rte), false) != ACLCHECK_OK) ! return false; /*---------- * The query string built is: *** a/src/include/utils/acl.h --- b/src/include/utils/acl.h *************** *** 311,314 **** extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid); --- 311,321 ---- extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); + /* + * security checker functions and hooks + */ + typedef AclResult (*chkpriv_relation_perms_hook_type)(List *, bool); + extern PGDLLIMPORT chkpriv_relation_perms_hook_type chkpriv_relation_perms_hook; + extern AclResult chkpriv_relation_perms(List *rangeTable, bool abort); + #endif /* ACL_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers