diff options
author | Joe Conway <mail@joeconway.com> | 2021-03-31 13:55:25 -0400 |
---|---|---|
committer | Joe Conway <mail@joeconway.com> | 2021-03-31 13:55:25 -0400 |
commit | b12bd4869b5e64b742a69ca07915e2f77f85a9ae (patch) | |
tree | 44c2f80d86ccabaf46d7489b515a258eaece4f1c /src/backend/utils/adt/acl.c | |
parent | 86dc90056dfdbd9d1b891718d2e5614e3e432f35 (diff) | |
download | postgresql-b12bd4869b5e64b742a69ca07915e2f77f85a9ae.tar.gz postgresql-b12bd4869b5e64b742a69ca07915e2f77f85a9ae.zip |
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
Diffstat (limited to 'src/backend/utils/adt/acl.c')
-rw-r--r-- | src/backend/utils/adt/acl.c | 48 |
1 files changed, 15 insertions, 33 deletions
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 9955c7c5c06..6a8c6a20eea 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -2444,8 +2444,7 @@ column_privilege_check(Oid tableoid, AttrNumber attnum, Oid roleid, AclMode mode) { AclResult aclresult; - HeapTuple attTuple; - Form_pg_attribute attributeForm; + bool is_missing = false; /* * If convert_column_name failed, we can just return -1 immediately. @@ -2454,42 +2453,25 @@ column_privilege_check(Oid tableoid, AttrNumber attnum, return -1; /* - * First check if we have the privilege at the table level. We check - * existence of the pg_class row before risking calling pg_class_aclcheck. - * Note: it might seem there's a race condition against concurrent DROP, - * but really it's safe because there will be no syscache flush between - * here and there. So if we see the row in the syscache, so will - * pg_class_aclcheck. + * Check for column-level privileges first. This serves in + * part as a check on whether the column even exists, so we + * need to do it before checking table-level privilege. */ - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) + aclresult = pg_attribute_aclcheck_ext(tableoid, attnum, roleid, + mode, &is_missing); + if (aclresult == ACLCHECK_OK) + return 1; + else if (is_missing) return -1; - aclresult = pg_class_aclcheck(tableoid, roleid, mode); - + /* Next check if we have the privilege at the table level */ + aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing); if (aclresult == ACLCHECK_OK) - return true; - - /* - * No table privilege, so try per-column privileges. Again, we have to - * check for dropped attribute first, and we rely on the syscache not to - * notice a concurrent drop before pg_attribute_aclcheck fetches the row. - */ - attTuple = SearchSysCache2(ATTNUM, - ObjectIdGetDatum(tableoid), - Int16GetDatum(attnum)); - if (!HeapTupleIsValid(attTuple)) - return -1; - attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple); - if (attributeForm->attisdropped) - { - ReleaseSysCache(attTuple); + return 1; + else if (is_missing) return -1; - } - ReleaseSysCache(attTuple); - - aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode); - - return (aclresult == ACLCHECK_OK); + else + return 0; } /* |