diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/common/tupdesc.c | 9 | ||||
-rw-r--r-- | src/backend/catalog/aclchk.c | 271 | ||||
-rw-r--r-- | src/backend/catalog/information_schema.sql | 84 | ||||
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 23 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 9 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 24 | ||||
-rw-r--r-- | src/backend/commands/operatorcmds.c | 24 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 22 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 5 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 15 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 19 | ||||
-rw-r--r-- | src/backend/utils/adt/acl.c | 207 |
12 files changed, 690 insertions, 22 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 9e931df4b14..ee404867fe3 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -20,7 +20,9 @@ #include "postgres.h" #include "catalog/pg_type.h" +#include "miscadmin.h" #include "parser/parse_type.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/resowner.h" #include "utils/syscache.h" @@ -557,6 +559,7 @@ BuildDescForRelation(List *schema) foreach(l, schema) { ColumnDef *entry = lfirst(l); + AclResult aclresult; /* * for each entry in the list, get the name and type information from @@ -567,6 +570,12 @@ BuildDescForRelation(List *schema) attname = entry->colname; typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod); + + aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(atttypid)); + attcollation = GetColumnDefCollation(NULL, entry, atttypid); attdim = list_length(entry->typeName->arrayBounds); diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 60c0b8a5b23..505a611a7dd 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -49,7 +49,9 @@ #include "commands/tablespace.h" #include "foreign/foreign.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "parser/parse_func.h" +#include "parser/parse_type.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -113,6 +115,7 @@ static void ExecGrant_Language(InternalGrant *grantStmt); static void ExecGrant_Largeobject(InternalGrant *grantStmt); static void ExecGrant_Namespace(InternalGrant *grantStmt); static void ExecGrant_Tablespace(InternalGrant *grantStmt); +static void ExecGrant_Type(InternalGrant *grantStmt); static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames); static void SetDefaultACL(InternalDefaultACL *iacls); @@ -274,6 +277,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, case ACL_KIND_FOREIGN_SERVER: whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER; break; + case ACL_KIND_TYPE: + whole_mask = ACL_ALL_RIGHTS_TYPE; + break; default: elog(ERROR, "unrecognized object kind: %d", objkind); /* not reached, but keep compiler quiet */ @@ -449,6 +455,10 @@ ExecuteGrantStmt(GrantStmt *stmt) all_privileges = ACL_ALL_RIGHTS_DATABASE; errormsg = gettext_noop("invalid privilege type %s for database"); break; + case ACL_OBJECT_DOMAIN: + all_privileges = ACL_ALL_RIGHTS_TYPE; + errormsg = gettext_noop("invalid privilege type %s for domain"); + break; case ACL_OBJECT_FUNCTION: all_privileges = ACL_ALL_RIGHTS_FUNCTION; errormsg = gettext_noop("invalid privilege type %s for function"); @@ -469,6 +479,10 @@ ExecuteGrantStmt(GrantStmt *stmt) all_privileges = ACL_ALL_RIGHTS_TABLESPACE; errormsg = gettext_noop("invalid privilege type %s for tablespace"); break; + case ACL_OBJECT_TYPE: + all_privileges = ACL_ALL_RIGHTS_TYPE; + errormsg = gettext_noop("invalid privilege type %s for type"); + break; case ACL_OBJECT_FDW: all_privileges = ACL_ALL_RIGHTS_FDW; errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper"); @@ -552,6 +566,10 @@ ExecGrantStmt_oids(InternalGrant *istmt) case ACL_OBJECT_DATABASE: ExecGrant_Database(istmt); break; + case ACL_OBJECT_DOMAIN: + case ACL_OBJECT_TYPE: + ExecGrant_Type(istmt); + break; case ACL_OBJECT_FDW: ExecGrant_Fdw(istmt); break; @@ -620,6 +638,17 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) objects = lappend_oid(objects, dbid); } break; + case ACL_OBJECT_DOMAIN: + case ACL_OBJECT_TYPE: + foreach(cell, objnames) + { + List *typname = (List *) lfirst(cell); + Oid oid; + + oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname)); + objects = lappend_oid(objects, oid); + } + break; case ACL_OBJECT_FUNCTION: foreach(cell, objnames) { @@ -903,6 +932,10 @@ ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt) all_privileges = ACL_ALL_RIGHTS_FUNCTION; errormsg = gettext_noop("invalid privilege type %s for function"); break; + case ACL_OBJECT_TYPE: + all_privileges = ACL_ALL_RIGHTS_TYPE; + errormsg = gettext_noop("invalid privilege type %s for type"); + break; default: elog(ERROR, "unrecognized GrantStmt.objtype: %d", (int) action->objtype); @@ -1085,6 +1118,12 @@ SetDefaultACL(InternalDefaultACL *iacls) this_privileges = ACL_ALL_RIGHTS_FUNCTION; break; + case ACL_OBJECT_TYPE: + objtype = DEFACLOBJ_TYPE; + if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) + this_privileges = ACL_ALL_RIGHTS_TYPE; + break; + default: elog(ERROR, "unrecognized objtype: %d", (int) iacls->objtype); @@ -1334,6 +1373,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid) case DatabaseRelationId: istmt.objtype = ACL_OBJECT_DATABASE; break; + case TypeRelationId: + istmt.objtype = ACL_OBJECT_TYPE; + break; case ProcedureRelationId: istmt.objtype = ACL_OBJECT_FUNCTION; break; @@ -2987,6 +3029,143 @@ ExecGrant_Tablespace(InternalGrant *istmt) heap_close(relation, RowExclusiveLock); } +static void +ExecGrant_Type(InternalGrant *istmt) +{ + Relation relation; + ListCell *cell; + + if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) + istmt->privileges = ACL_ALL_RIGHTS_TYPE; + + relation = heap_open(TypeRelationId, RowExclusiveLock); + + foreach(cell, istmt->objects) + { + Oid typId = lfirst_oid(cell); + Form_pg_type pg_type_tuple; + Datum aclDatum; + bool isNull; + AclMode avail_goptions; + AclMode this_privileges; + Acl *old_acl; + Acl *new_acl; + Oid grantorId; + Oid ownerId; + HeapTuple newtuple; + Datum values[Natts_pg_type]; + bool nulls[Natts_pg_type]; + bool replaces[Natts_pg_type]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; + HeapTuple tuple; + + /* Search syscache for pg_type */ + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type %u", typId); + + pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple); + + if (pg_type_tuple->typelem != 0 && pg_type_tuple->typlen == -1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_GRANT_OPERATION), + errmsg("cannot set privileges of array types"), + errhint("Set the privileges of the element type instead."))); + + /* Used GRANT DOMAIN on a non-domain? */ + if (istmt->objtype == ACL_OBJECT_DOMAIN && + pg_type_tuple->typtype != TYPTYPE_DOMAIN) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a domain", + NameStr(pg_type_tuple->typname)))); + + /* + * Get owner ID and working copy of existing ACL. If there's no ACL, + * substitute the proper default. + */ + ownerId = pg_type_tuple->typowner; + aclDatum = heap_getattr(tuple, Anum_pg_type_typacl, + RelationGetDescr(relation), &isNull); + if (isNull) + { + old_acl = acldefault(istmt->objtype, ownerId); + /* There are no old member roles according to the catalogs */ + noldmembers = 0; + oldmembers = NULL; + } + else + { + old_acl = DatumGetAclPCopy(aclDatum); + /* Get the roles mentioned in the existing ACL */ + noldmembers = aclmembers(old_acl, &oldmembers); + } + + /* Determine ID to do the grant as, and available grant options */ + select_best_grantor(GetUserId(), istmt->privileges, + old_acl, ownerId, + &grantorId, &avail_goptions); + + /* + * Restrict the privileges to what we can actually grant, and emit the + * standards-mandated warning and error messages. + */ + this_privileges = + restrict_and_check_grant(istmt->is_grant, avail_goptions, + istmt->all_privs, istmt->privileges, + typId, grantorId, ACL_KIND_TYPE, + NameStr(pg_type_tuple->typname), + 0, NULL); + + /* + * Generate new ACL. + */ + new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, + istmt->grant_option, istmt->behavior, + istmt->grantees, this_privileges, + grantorId, ownerId); + + /* + * We need the members of both old and new ACLs so we can correct the + * shared dependency information. + */ + nnewmembers = aclmembers(new_acl, &newmembers); + + /* finished building new ACL value, now insert it */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, false, sizeof(nulls)); + MemSet(replaces, false, sizeof(replaces)); + + replaces[Anum_pg_type_typacl - 1] = true; + values[Anum_pg_type_typacl - 1] = PointerGetDatum(new_acl); + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); + + simple_heap_update(relation, &newtuple->t_self, newtuple); + + /* keep the catalog indexes up to date */ + CatalogUpdateIndexes(relation, newtuple); + + /* Update the shared dependency ACL info */ + updateAclDependencies(TypeRelationId, typId, 0, + ownerId, + noldmembers, oldmembers, + nnewmembers, newmembers); + + ReleaseSysCache(tuple); + pfree(new_acl); + + /* prevent error when processing duplicate objects */ + CommandCounterIncrement(); + } + + heap_close(relation, RowExclusiveLock); +} + static AclMode string_to_privilege(const char *privname) @@ -3263,6 +3442,8 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how); case ACL_KIND_FOREIGN_SERVER: return pg_foreign_server_aclmask(table_oid, roleid, mask, how); + case ACL_KIND_TYPE: + return pg_type_aclmask(table_oid, roleid, mask, how); default: elog(ERROR, "unrecognized objkind: %d", (int) objkind); @@ -3972,6 +4153,80 @@ pg_foreign_server_aclmask(Oid srv_oid, Oid roleid, } /* + * Exported routine for examining a user's privileges for a type. + */ +AclMode +pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how) +{ + AclMode result; + HeapTuple tuple; + Datum aclDatum; + bool isNull; + Acl *acl; + Oid ownerId; + + Form_pg_type typeForm; + + /* Bypass permission checks for superusers */ + if (superuser_arg(roleid)) + return mask; + + /* + * Must get the type's tuple from pg_type + */ + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errmsg("type with OID %u does not exist", + type_oid))); + typeForm = (Form_pg_type) GETSTRUCT(tuple); + + /* "True" array types don't manage permissions of their own */ + if (typeForm->typelem != 0 && typeForm->typlen == -1) + { + Oid elttype_oid = typeForm->typelem; + + ReleaseSysCache(tuple); + + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errmsg("type with OID %u does not exist", + type_oid))); + typeForm = (Form_pg_type) GETSTRUCT(tuple); + } + + /* + * Normal case: get the type's ACL from pg_type + */ + ownerId = typeForm->typowner; + + aclDatum = SysCacheGetAttr(TYPEOID, tuple, + Anum_pg_type_typacl, &isNull); + if (isNull) + { + /* No ACL, so build default ACL */ + acl = acldefault(ACL_OBJECT_TYPE, ownerId); + aclDatum = (Datum) 0; + } + else + { + /* detoast rel's ACL if necessary */ + acl = DatumGetAclP(aclDatum); + } + + result = aclmask(acl, roleid, ownerId, mask, how); + + /* if we have a detoasted copy, free it */ + if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) + pfree(acl); + + ReleaseSysCache(tuple); + + return result; +} + +/* * Exported routine for checking a user's access privileges to a column * * Returns ACLCHECK_OK if the user has any of the privileges identified by @@ -4205,6 +4460,18 @@ pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode) } /* + * Exported routine for checking a user's access privileges to a type + */ +AclResult +pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode) +{ + if (pg_type_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0) + return ACLCHECK_OK; + else + return ACLCHECK_NO_PRIV; +} + +/* * Ownership check for a relation (specified by OID). */ bool @@ -4813,6 +5080,10 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid) defaclobjtype = DEFACLOBJ_FUNCTION; break; + case ACL_OBJECT_TYPE: + defaclobjtype = DEFACLOBJ_TYPE; + break; + default: return NULL; } diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 5ce7924b24e..b60409f3ca2 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -357,7 +357,9 @@ CREATE VIEW attributes AS ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default') WHERE a.attnum > 0 AND NOT a.attisdropped - AND c.relkind in ('c'); + AND c.relkind in ('c') + AND (pg_has_role(c.relowner, 'USAGE') + OR has_type_privilege(c.reltype, 'USAGE')); GRANT SELECT ON attributes TO PUBLIC; @@ -868,7 +870,9 @@ CREATE VIEW domain_constraints AS FROM pg_namespace rs, pg_namespace n, pg_constraint con, pg_type t WHERE rs.oid = con.connamespace AND n.oid = t.typnamespace - AND t.oid = con.contypid; + AND t.oid = con.contypid + AND (pg_has_role(t.typowner, 'USAGE') + OR has_type_privilege(t.oid, 'USAGE')); GRANT SELECT ON domain_constraints TO PUBLIC; @@ -978,7 +982,8 @@ CREATE VIEW domains AS LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid)) ON t.typcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default') - ; + WHERE (pg_has_role(t.typowner, 'USAGE') + OR has_type_privilege(t.oid, 'USAGE')); GRANT SELECT ON domains TO PUBLIC; @@ -2024,20 +2029,38 @@ GRANT SELECT ON triggers TO PUBLIC; */ CREATE VIEW udt_privileges AS - SELECT CAST(null AS sql_identifier) AS grantor, - CAST('PUBLIC' AS sql_identifier) AS grantee, + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(grantee.rolname AS sql_identifier) AS grantee, CAST(current_database() AS sql_identifier) AS udt_catalog, CAST(n.nspname AS sql_identifier) AS udt_schema, CAST(t.typname AS sql_identifier) AS udt_name, CAST('TYPE USAGE' AS character_data) AS privilege_type, -- sic - CAST('NO' AS yes_or_no) AS is_grantable + CAST( + CASE WHEN + -- object owner always has grant options + pg_has_role(grantee.oid, t.typowner, 'USAGE') + OR t.grantable + THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable - FROM pg_authid u, pg_namespace n, pg_type t + FROM ( + SELECT oid, typname, typnamespace, typtype, typowner, (aclexplode(typacl)).* FROM pg_type + ) AS t (oid, typname, typnamespace, typtype, typowner, grantor, grantee, prtype, grantable), + pg_namespace n, + pg_authid u_grantor, + ( + SELECT oid, rolname FROM pg_authid + UNION ALL + SELECT 0::oid, 'PUBLIC' + ) AS grantee (oid, rolname) - WHERE u.oid = t.typowner - AND n.oid = t.typnamespace - AND t.typtype <> 'd' - AND NOT (t.typelem <> 0 AND t.typlen = -1); + WHERE t.typnamespace = n.oid + AND t.typtype = 'c' + AND t.grantee = grantee.oid + AND t.grantor = u_grantor.oid + AND t.prtype IN ('USAGE') + AND (pg_has_role(u_grantor.oid, 'USAGE') + OR pg_has_role(grantee.oid, 'USAGE') + OR grantee.rolname = 'PUBLIC'); GRANT SELECT ON udt_privileges TO PUBLIC; @@ -2091,23 +2114,39 @@ CREATE VIEW usage_privileges AS UNION ALL /* domains */ - -- Domains have no real privileges, so we represent all domains with implicit usage privilege here. - SELECT CAST(u.rolname AS sql_identifier) AS grantor, - CAST('PUBLIC' AS sql_identifier) AS grantee, + SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor, + CAST(grantee.rolname AS sql_identifier) AS grantee, CAST(current_database() AS sql_identifier) AS object_catalog, CAST(n.nspname AS sql_identifier) AS object_schema, CAST(t.typname AS sql_identifier) AS object_name, CAST('DOMAIN' AS character_data) AS object_type, CAST('USAGE' AS character_data) AS privilege_type, - CAST('NO' AS yes_or_no) AS is_grantable + CAST( + CASE WHEN + -- object owner always has grant options + pg_has_role(grantee.oid, t.typowner, 'USAGE') + OR t.grantable + THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable - FROM pg_authid u, + FROM ( + SELECT oid, typname, typnamespace, typtype, typowner, (aclexplode(typacl)).* FROM pg_type + ) AS t (oid, typname, typnamespace, typtype, typowner, grantor, grantee, prtype, grantable), pg_namespace n, - pg_type t + pg_authid u_grantor, + ( + SELECT oid, rolname FROM pg_authid + UNION ALL + SELECT 0::oid, 'PUBLIC' + ) AS grantee (oid, rolname) - WHERE u.oid = t.typowner - AND t.typnamespace = n.oid + WHERE t.typnamespace = n.oid AND t.typtype = 'd' + AND t.grantee = grantee.oid + AND t.grantor = u_grantor.oid + AND t.prtype IN ('USAGE') + AND (pg_has_role(u_grantor.oid, 'USAGE') + OR pg_has_role(grantee.oid, 'USAGE') + OR grantee.rolname = 'PUBLIC') UNION ALL @@ -2237,10 +2276,13 @@ CREATE VIEW user_defined_types AS CAST(null AS sql_identifier) AS source_dtd_identifier, CAST(null AS sql_identifier) AS ref_dtd_identifier - FROM pg_namespace n, pg_class c + FROM pg_namespace n, pg_class c, pg_type t WHERE n.oid = c.relnamespace - AND c.relkind = 'c'; + AND t.typrelid = c.oid + AND c.relkind = 'c' + AND (pg_has_role(t.typowner, 'USAGE') + OR has_type_privilege(t.oid, 'USAGE')); GRANT SELECT ON user_defined_types TO PUBLIC; diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 86e8c6bddf2..b106c92983c 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -71,6 +71,7 @@ AggregateCreate(const char *aggName, int i; ObjectAddress myself, referenced; + AclResult aclresult; /* sanity checks (caller should have caught these) */ if (!aggName) @@ -201,6 +202,28 @@ AggregateCreate(const char *aggName, } /* + * permission checks on used types + */ + for (i = 0; i < numArgs; i++) + { + aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(aggArgTypes[i])); + } + + aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(aggTransType)); + + aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(finaltype)); + + + /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 21d1ef303d7..50f384816b4 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid); nulls[Anum_pg_type_typdefaultbin - 1] = true; nulls[Anum_pg_type_typdefault - 1] = true; + nulls[Anum_pg_type_typacl - 1] = true; /* * create a new type tuple @@ -224,6 +225,7 @@ TypeCreate(Oid newTypeOid, Datum values[Natts_pg_type]; NameData name; int i; + Acl *typacl = NULL; /* * We assume that the caller validated the arguments individually, but did @@ -369,6 +371,13 @@ TypeCreate(Oid newTypeOid, else nulls[Anum_pg_type_typdefault - 1] = true; + typacl = get_user_default_acl(ACL_OBJECT_TYPE, ownerId, + typeNamespace); + if (typacl != NULL) + values[Anum_pg_type_typacl - 1] = PointerGetDatum(typacl); + else + nulls[Anum_pg_type_typacl - 1] = true; + /* * open pg_type and prepare to insert or update a row. * diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 45fdfee2175..cc4ddc6006c 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -87,9 +87,11 @@ compute_return_type(TypeName *returnType, Oid languageOid, { Oid rettype; Type typtup; + AclResult aclresult; typtup = LookupTypeName(NULL, returnType, NULL); + if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) @@ -150,6 +152,11 @@ compute_return_type(TypeName *returnType, Oid languageOid, Assert(OidIsValid(rettype)); } + aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(rettype)); + *prorettype_p = rettype; *returnsSet_p = returnType->setof; } @@ -207,6 +214,7 @@ examine_parameter_list(List *parameters, Oid languageOid, bool isinput = false; Oid toid; Type typtup; + AclResult aclresult; typtup = LookupTypeName(NULL, t, NULL); if (typtup) @@ -237,6 +245,11 @@ examine_parameter_list(List *parameters, Oid languageOid, toid = InvalidOid; /* keep compiler quiet */ } + aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(toid)); + if (t->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), @@ -1429,6 +1442,7 @@ CreateCast(CreateCastStmt *stmt) bool nulls[Natts_pg_cast]; ObjectAddress myself, referenced; + AclResult aclresult; sourcetypeid = typenameTypeId(NULL, stmt->sourcetype); targettypeid = typenameTypeId(NULL, stmt->targettype); @@ -1457,6 +1471,16 @@ CreateCast(CreateCastStmt *stmt) format_type_be(sourcetypeid), format_type_be(targettypeid)))); + aclresult = pg_type_aclcheck(sourcetypeid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(sourcetypeid)); + + aclresult = pg_type_aclcheck(targettypeid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(targettypeid)); + /* Detemine the cast method */ if (stmt->func != NULL) castmethod = COERCION_METHOD_FUNCTION; diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 1e6c5ce2ad7..0fdf0803134 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -45,6 +45,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" +#include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -73,6 +74,7 @@ DefineOperator(List *names, List *parameters) TypeName *typeName2 = NULL; /* second type name */ Oid typeId1 = InvalidOid; /* types converted to OID */ Oid typeId2 = InvalidOid; + Oid rettype; List *commutatorName = NIL; /* optional commutator operator name */ List *negatorName = NIL; /* optional negator operator name */ List *restrictionName = NIL; /* optional restrict. sel. procedure */ @@ -175,6 +177,22 @@ DefineOperator(List *names, List *parameters) (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("at least one of leftarg or rightarg must be specified"))); + if (typeName1) + { + aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(typeId1)); + } + + if (typeName2) + { + aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(typeId2)); + } + /* * Look up the operator's underlying function. */ @@ -206,6 +224,12 @@ DefineOperator(List *names, List *parameters) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(functionName)); + rettype = get_func_rettype(functionOid); + aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(rettype)); + /* * Look up restriction estimator if specified */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 00b6cb9d50d..61689b13386 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -506,7 +506,16 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) (void) heap_reloptions(relkind, reloptions, true); if (stmt->ofTypename) + { + AclResult aclresult; + ofTypeId = typenameTypeId(NULL, stmt->ofTypename); + + aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(ofTypeId)); + } else ofTypeId = InvalidOid; @@ -4326,6 +4335,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, Expr *defval; List *children; ListCell *child; + AclResult aclresult; /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -4429,6 +4439,12 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, typeTuple = typenameType(NULL, colDef->typeName, &typmod); tform = (Form_pg_type) GETSTRUCT(typeTuple); typeOid = HeapTupleGetOid(typeTuple); + + aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(typeOid)); + collOid = GetColumnDefCollation(NULL, colDef, typeOid); /* make sure datatype is legal for a column */ @@ -6973,6 +6989,7 @@ ATPrepAlterColumnType(List **wqueue, Oid targetcollid; NewColumnValue *newval; ParseState *pstate = make_parsestate(NULL); + AclResult aclresult; if (rel->rd_rel->reloftype && !recursing) ereport(ERROR, @@ -7006,6 +7023,11 @@ ATPrepAlterColumnType(List **wqueue, /* Look up the target type */ typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod); + aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(targettype)); + /* And the collation */ targetcollid = GetColumnDefCollation(NULL, def, targettype); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index eda43d826fa..ea8f7f099a2 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -756,6 +756,11 @@ DefineDomain(CreateDomainStmt *stmt) errmsg("\"%s\" is not a valid base type for a domain", TypeNameToString(stmt->typeName)))); + aclresult = pg_type_aclcheck(basetypeoid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(basetypeoid)); + /* * Identify the collation if any */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 3dbd80d53bc..0cbe93c5c91 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2490,6 +2490,21 @@ OpenIntoRel(QueryDesc *queryDesc) (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); + { + AclResult aclresult; + int i; + + for (i = 0; i < intoTupDesc->natts; i++) + { + Oid atttypid = intoTupDesc->attrs[i]->atttypid; + + aclresult = pg_type_aclcheck(atttypid, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TYPE, + format_type_be(atttypid)); + } + } + /* * If a column name list was specified in CREATE TABLE AS, override the * column names derived from the query. (Too few column names are OK, too diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c3e0ee1877d..e0ff49f048a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -558,7 +558,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P - TRUNCATE TRUSTED TYPE_P + TRUNCATE TRUSTED TYPE_P TYPES_P UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED UNTIL UPDATE USER USING @@ -5434,6 +5434,14 @@ privilege_target: n->objs = $2; $$ = n; } + | DOMAIN_P any_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = ACL_OBJECT_DOMAIN; + n->objs = $2; + $$ = n; + } | LANGUAGE name_list { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); @@ -5466,6 +5474,14 @@ privilege_target: n->objs = $2; $$ = n; } + | TYPE_P any_name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = ACL_OBJECT_TYPE; + n->objs = $2; + $$ = n; + } | ALL TABLES IN_P SCHEMA name_list { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); @@ -5680,6 +5696,7 @@ defacl_privilege_target: TABLES { $$ = ACL_OBJECT_RELATION; } | FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; } | SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; } + | TYPES_P { $$ = ACL_OBJECT_TYPE; } ; diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 48fb673df25..59587c68cae 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -109,6 +109,8 @@ static Oid convert_server_name(text *servername); static AclMode convert_server_priv_string(text *priv_type_text); static Oid convert_tablespace_name(text *tablespacename); static AclMode convert_tablespace_priv_string(text *priv_type_text); +static Oid convert_type_name(text *typename); +static AclMode convert_type_priv_string(text *priv_type_text); static AclMode convert_role_priv_string(text *priv_type_text); static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode); @@ -782,6 +784,11 @@ acldefault(GrantObjectType objtype, Oid ownerId) world_default = ACL_NO_RIGHTS; owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER; break; + case ACL_OBJECT_DOMAIN: + case ACL_OBJECT_TYPE: + world_default = ACL_USAGE; + owner_default = ACL_ALL_RIGHTS_TYPE; + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); world_default = ACL_NO_RIGHTS; /* keep compiler quiet */ @@ -4127,6 +4134,206 @@ convert_tablespace_priv_string(text *priv_type_text) } /* + * has_type_privilege variants + * These are all named "has_type_privilege" at the SQL level. + * They take various combinations of type name, type OID, + * user name, user OID, or implicit user = current_user. + * + * The result is a boolean value: true if user has the indicated + * privilege, false if not, or NULL if object doesn't exist. + */ + +/* + * has_type_privilege_name_name + * Check user privileges on a type given + * name username, text typename, and text priv name. + */ +Datum +has_type_privilege_name_name(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + text *typename = PG_GETARG_TEXT_P(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid roleid; + Oid typeoid; + AclMode mode; + AclResult aclresult; + + roleid = get_role_oid_or_public(NameStr(*username)); + typeoid = convert_type_name(typename); + mode = convert_type_priv_string(priv_type_text); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_type_privilege_name + * Check user privileges on a type given + * text typename and text priv name. + * current_user is assumed + */ +Datum +has_type_privilege_name(PG_FUNCTION_ARGS) +{ + text *typename = PG_GETARG_TEXT_P(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + Oid typeoid; + AclMode mode; + AclResult aclresult; + + roleid = GetUserId(); + typeoid = convert_type_name(typename); + mode = convert_type_priv_string(priv_type_text); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_type_privilege_name_id + * Check user privileges on a type given + * name usename, type oid, and text priv name. + */ +Datum +has_type_privilege_name_id(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + Oid typeoid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid roleid; + AclMode mode; + AclResult aclresult; + + roleid = get_role_oid_or_public(NameStr(*username)); + mode = convert_type_priv_string(priv_type_text); + + if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) + PG_RETURN_NULL(); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_type_privilege_id + * Check user privileges on a type given + * type oid, and text priv name. + * current_user is assumed + */ +Datum +has_type_privilege_id(PG_FUNCTION_ARGS) +{ + Oid typeoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult; + + roleid = GetUserId(); + mode = convert_type_priv_string(priv_type_text); + + if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) + PG_RETURN_NULL(); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_type_privilege_id_name + * Check user privileges on a type given + * roleid, text typename, and text priv name. + */ +Datum +has_type_privilege_id_name(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + text *typename = PG_GETARG_TEXT_P(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid typeoid; + AclMode mode; + AclResult aclresult; + + typeoid = convert_type_name(typename); + mode = convert_type_priv_string(priv_type_text); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_type_privilege_id_id + * Check user privileges on a type given + * roleid, type oid, and text priv name. + */ +Datum +has_type_privilege_id_id(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + Oid typeoid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + AclMode mode; + AclResult aclresult; + + mode = convert_type_priv_string(priv_type_text); + + if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) + PG_RETURN_NULL(); + + aclresult = pg_type_aclcheck(typeoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * Support routines for has_type_privilege family. + */ + +/* + * Given a type name expressed as a string, look it up and return Oid + */ +static Oid +convert_type_name(text *typename) +{ + char *typname = text_to_cstring(typename); + Oid oid; + + oid = DatumGetObjectId(DirectFunctionCall1(regtypein, + CStringGetDatum(typname))); + + if (!OidIsValid(oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", typname))); + + return oid; +} + +/* + * convert_type_priv_string + * Convert text string to AclMode value. + */ +static AclMode +convert_type_priv_string(text *priv_type_text) +{ + static const priv_map type_priv_map[] = { + {"USAGE", ACL_USAGE}, + {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)}, + {NULL, 0} + }; + + return convert_any_priv_string(priv_type_text, type_priv_map); +} + + +/* * pg_has_role variants * These are all named "pg_has_role" at the SQL level. * They take various combinations of role name, role OID, |