Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * aclchk.c
4 : * Routines to check access control permissions.
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/catalog/aclchk.c
12 : *
13 : * NOTES
14 : * See acl.h.
15 : *
16 : * The xxx_aclmask() functions in this file are wrappers around
17 : * acl.c's aclmask() function; see that for basic usage information.
18 : * The wrapper functions add object-type-specific lookup capability.
19 : * Generally, they will throw error if the object doesn't exist.
20 : *
21 : * The xxx_aclmask_ext() functions add the ability to not throw
22 : * error if the object doesn't exist. If their "is_missing" argument
23 : * isn't NULL, then when the object isn't found they will set
24 : * *is_missing = true and return zero (no privileges) instead of
25 : * throwing an error. Caller must initialize *is_missing = false.
26 : *
27 : * The xxx_aclcheck() functions are simplified wrappers around the
28 : * corresponding xxx_aclmask() functions, simply returning ACLCHECK_OK
29 : * if any of the privileges specified in "mode" are held, and otherwise
30 : * a suitable error code (in practice, always ACLCHECK_NO_PRIV).
31 : * Again, they will throw error if the object doesn't exist.
32 : *
33 : * The xxx_aclcheck_ext() functions add the ability to not throw
34 : * error if the object doesn't exist. Their "is_missing" argument
35 : * works similarly to the xxx_aclmask_ext() functions.
36 : *
37 : *-------------------------------------------------------------------------
38 : */
39 : #include "postgres.h"
40 :
41 : #include "access/genam.h"
42 : #include "access/heapam.h"
43 : #include "access/htup_details.h"
44 : #include "access/sysattr.h"
45 : #include "access/tableam.h"
46 : #include "access/xact.h"
47 : #include "catalog/binary_upgrade.h"
48 : #include "catalog/catalog.h"
49 : #include "catalog/dependency.h"
50 : #include "catalog/indexing.h"
51 : #include "catalog/objectaccess.h"
52 : #include "catalog/pg_authid.h"
53 : #include "catalog/pg_class.h"
54 : #include "catalog/pg_database.h"
55 : #include "catalog/pg_default_acl.h"
56 : #include "catalog/pg_foreign_data_wrapper.h"
57 : #include "catalog/pg_foreign_server.h"
58 : #include "catalog/pg_init_privs.h"
59 : #include "catalog/pg_language.h"
60 : #include "catalog/pg_largeobject.h"
61 : #include "catalog/pg_largeobject_metadata.h"
62 : #include "catalog/pg_namespace.h"
63 : #include "catalog/pg_parameter_acl.h"
64 : #include "catalog/pg_proc.h"
65 : #include "catalog/pg_tablespace.h"
66 : #include "catalog/pg_type.h"
67 : #include "commands/defrem.h"
68 : #include "commands/event_trigger.h"
69 : #include "commands/extension.h"
70 : #include "commands/proclang.h"
71 : #include "commands/tablespace.h"
72 : #include "foreign/foreign.h"
73 : #include "miscadmin.h"
74 : #include "nodes/makefuncs.h"
75 : #include "parser/parse_func.h"
76 : #include "parser/parse_type.h"
77 : #include "storage/lmgr.h"
78 : #include "utils/acl.h"
79 : #include "utils/aclchk_internal.h"
80 : #include "utils/builtins.h"
81 : #include "utils/fmgroids.h"
82 : #include "utils/guc.h"
83 : #include "utils/lsyscache.h"
84 : #include "utils/rel.h"
85 : #include "utils/syscache.h"
86 :
87 : /*
88 : * Internal format used by ALTER DEFAULT PRIVILEGES.
89 : */
90 : typedef struct
91 : {
92 : Oid roleid; /* owning role */
93 : Oid nspid; /* namespace, or InvalidOid if none */
94 : /* remaining fields are same as in InternalGrant: */
95 : bool is_grant;
96 : ObjectType objtype;
97 : bool all_privs;
98 : AclMode privileges;
99 : List *grantees;
100 : bool grant_option;
101 : RoleSpec *grantor;
102 : DropBehavior behavior;
103 : } InternalDefaultACL;
104 :
105 : /*
106 : * When performing a binary-upgrade, pg_dump will call a function to set
107 : * this variable to let us know that we need to populate the pg_init_privs
108 : * table for the GRANT/REVOKE commands while this variable is set to true.
109 : */
110 : bool binary_upgrade_record_init_privs = false;
111 :
112 : static void ExecGrantStmt_oids(InternalGrant *istmt);
113 : static void ExecGrant_Relation(InternalGrant *istmt);
114 : static void ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
115 : void (*object_check) (InternalGrant *istmt, HeapTuple tuple));
116 : static void ExecGrant_Language_check(InternalGrant *istmt, HeapTuple tuple);
117 : static void ExecGrant_Largeobject(InternalGrant *istmt);
118 : static void ExecGrant_Type_check(InternalGrant *istmt, HeapTuple tuple);
119 : static void ExecGrant_Parameter(InternalGrant *istmt);
120 :
121 : static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
122 : static void SetDefaultACL(InternalDefaultACL *iacls);
123 :
124 : static List *objectNamesToOids(ObjectType objtype, List *objnames,
125 : bool is_grant);
126 : static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
127 : static List *getRelationsInNamespace(Oid namespaceId, char relkind);
128 : static void expand_col_privileges(List *colnames, Oid table_oid,
129 : AclMode this_privileges,
130 : AclMode *col_privileges,
131 : int num_col_privileges);
132 : static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
133 : AclMode this_privileges,
134 : AclMode *col_privileges,
135 : int num_col_privileges);
136 : static AclMode string_to_privilege(const char *privname);
137 : static const char *privilege_to_string(AclMode privilege);
138 : static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
139 : bool all_privs, AclMode privileges,
140 : Oid objectId, Oid grantorId,
141 : ObjectType objtype, const char *objname,
142 : AttrNumber att_number, const char *colname);
143 : static AclMode pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum,
144 : Oid roleid, AclMode mask, AclMaskHow how);
145 : static AclMode object_aclmask(Oid classid, Oid objectid, Oid roleid,
146 : AclMode mask, AclMaskHow how);
147 : static AclMode object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
148 : AclMode mask, AclMaskHow how,
149 : bool *is_missing);
150 : static AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
151 : Oid roleid, AclMode mask, AclMaskHow how);
152 : static AclMode pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum,
153 : Oid roleid, AclMode mask,
154 : AclMaskHow how, bool *is_missing);
155 : static AclMode pg_class_aclmask_ext(Oid table_oid, Oid roleid,
156 : AclMode mask, AclMaskHow how,
157 : bool *is_missing);
158 : static AclMode pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid,
159 : AclMode mask, AclMaskHow how);
160 : static AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
161 : AclMode mask, AclMaskHow how, Snapshot snapshot);
162 : static AclMode pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
163 : AclMode mask, AclMaskHow how,
164 : bool *is_missing);
165 : static AclMode pg_type_aclmask_ext(Oid type_oid, Oid roleid,
166 : AclMode mask, AclMaskHow how,
167 : bool *is_missing);
168 : static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
169 : Acl *new_acl);
170 : static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
171 : Acl *new_acl);
172 :
173 :
174 : /*
175 : * If is_grant is true, adds the given privileges for the list of
176 : * grantees to the existing old_acl. If is_grant is false, the
177 : * privileges for the given grantees are removed from old_acl.
178 : *
179 : * NB: the original old_acl is pfree'd.
180 : */
181 : static Acl *
182 40697 : merge_acl_with_grant(Acl *old_acl, bool is_grant,
183 : bool grant_option, DropBehavior behavior,
184 : List *grantees, AclMode privileges,
185 : Oid grantorId, Oid ownerId)
186 : {
187 : unsigned modechg;
188 : ListCell *j;
189 : Acl *new_acl;
190 :
191 40697 : modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;
192 :
193 40697 : new_acl = old_acl;
194 :
195 81472 : foreach(j, grantees)
196 : {
197 : AclItem aclitem;
198 : Acl *newer_acl;
199 :
200 40783 : aclitem.ai_grantee = lfirst_oid(j);
201 :
202 : /*
203 : * Grant options can only be granted to individual roles, not PUBLIC.
204 : * The reason is that if a user would re-grant a privilege that he
205 : * held through PUBLIC, and later the user is removed, the situation
206 : * is impossible to clean up.
207 : */
208 40783 : if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC)
209 0 : ereport(ERROR,
210 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
211 : errmsg("grant options can only be granted to roles")));
212 :
213 40783 : aclitem.ai_grantor = grantorId;
214 :
215 : /*
216 : * The asymmetry in the conditions here comes from the spec. In
217 : * GRANT, the grant_option flag signals WITH GRANT OPTION, which means
218 : * to grant both the basic privilege and its grant option. But in
219 : * REVOKE, plain revoke revokes both the basic privilege and its grant
220 : * option, while REVOKE GRANT OPTION revokes only the option.
221 : */
222 40783 : ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
223 : (is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
224 : (!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS);
225 :
226 40783 : newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior);
227 :
228 : /* avoid memory leak when there are many grantees */
229 40775 : pfree(new_acl);
230 40775 : new_acl = newer_acl;
231 : }
232 :
233 40689 : return new_acl;
234 : }
235 :
236 : /*
237 : * Restrict the privileges to what we can actually grant, and emit
238 : * the standards-mandated warning and error messages.
239 : */
240 : static AclMode
241 40552 : restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
242 : AclMode privileges, Oid objectId, Oid grantorId,
243 : ObjectType objtype, const char *objname,
244 : AttrNumber att_number, const char *colname)
245 : {
246 : AclMode this_privileges;
247 : AclMode whole_mask;
248 :
249 40552 : switch (objtype)
250 : {
251 27994 : case OBJECT_COLUMN:
252 27994 : whole_mask = ACL_ALL_RIGHTS_COLUMN;
253 27994 : break;
254 10875 : case OBJECT_TABLE:
255 10875 : whole_mask = ACL_ALL_RIGHTS_RELATION;
256 10875 : break;
257 116 : case OBJECT_SEQUENCE:
258 116 : whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
259 116 : break;
260 220 : case OBJECT_DATABASE:
261 220 : whole_mask = ACL_ALL_RIGHTS_DATABASE;
262 220 : break;
263 582 : case OBJECT_FUNCTION:
264 582 : whole_mask = ACL_ALL_RIGHTS_FUNCTION;
265 582 : break;
266 23 : case OBJECT_LANGUAGE:
267 23 : whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
268 23 : break;
269 62 : case OBJECT_LARGEOBJECT:
270 62 : whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
271 62 : break;
272 392 : case OBJECT_SCHEMA:
273 392 : whole_mask = ACL_ALL_RIGHTS_SCHEMA;
274 392 : break;
275 3 : case OBJECT_TABLESPACE:
276 3 : whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
277 3 : break;
278 61 : case OBJECT_FDW:
279 61 : whole_mask = ACL_ALL_RIGHTS_FDW;
280 61 : break;
281 72 : case OBJECT_FOREIGN_SERVER:
282 72 : whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
283 72 : break;
284 0 : case OBJECT_EVENT_TRIGGER:
285 0 : elog(ERROR, "grantable rights not supported for event triggers");
286 : /* not reached, but keep compiler quiet */
287 : return ACL_NO_RIGHTS;
288 84 : case OBJECT_TYPE:
289 84 : whole_mask = ACL_ALL_RIGHTS_TYPE;
290 84 : break;
291 68 : case OBJECT_PARAMETER_ACL:
292 68 : whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
293 68 : break;
294 0 : case OBJECT_PROPGRAPH:
295 0 : whole_mask = ACL_ALL_RIGHTS_PROPGRAPH;
296 0 : break;
297 0 : default:
298 0 : elog(ERROR, "unrecognized object type: %d", objtype);
299 : /* not reached, but keep compiler quiet */
300 : return ACL_NO_RIGHTS;
301 : }
302 :
303 : /*
304 : * If we found no grant options, consider whether to issue a hard error.
305 : * Per spec, having any privilege at all on the object will get you by
306 : * here.
307 : */
308 40552 : if (avail_goptions == ACL_NO_RIGHTS)
309 : {
310 48 : if (pg_aclmask(objtype, objectId, att_number, grantorId,
311 48 : whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
312 : ACLMASK_ANY) == ACL_NO_RIGHTS)
313 : {
314 24 : if (objtype == OBJECT_COLUMN && colname)
315 0 : aclcheck_error_col(ACLCHECK_NO_PRIV, objtype, objname, colname);
316 : else
317 24 : aclcheck_error(ACLCHECK_NO_PRIV, objtype, objname);
318 : }
319 : }
320 :
321 : /*
322 : * Restrict the operation to what we can actually grant or revoke, and
323 : * issue a warning if appropriate. (For REVOKE this isn't quite what the
324 : * spec says to do: the spec seems to want a warning only if no privilege
325 : * bits actually change in the ACL. In practice that behavior seems much
326 : * too noisy, as well as inconsistent with the GRANT case.)
327 : */
328 40528 : this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
329 40528 : if (is_grant)
330 : {
331 9836 : if (this_privileges == 0)
332 : {
333 20 : if (objtype == OBJECT_COLUMN && colname)
334 0 : ereport(WARNING,
335 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
336 : errmsg("no privileges were granted for column \"%s\" of relation \"%s\"",
337 : colname, objname)));
338 : else
339 20 : ereport(WARNING,
340 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
341 : errmsg("no privileges were granted for \"%s\"",
342 : objname)));
343 : }
344 9816 : else if (!all_privs && this_privileges != privileges)
345 : {
346 12 : if (objtype == OBJECT_COLUMN && colname)
347 0 : ereport(WARNING,
348 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
349 : errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"",
350 : colname, objname)));
351 : else
352 12 : ereport(WARNING,
353 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
354 : errmsg("not all privileges were granted for \"%s\"",
355 : objname)));
356 : }
357 : }
358 : else
359 : {
360 30692 : if (this_privileges == 0)
361 : {
362 4 : if (objtype == OBJECT_COLUMN && colname)
363 0 : ereport(WARNING,
364 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
365 : errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"",
366 : colname, objname)));
367 : else
368 4 : ereport(WARNING,
369 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
370 : errmsg("no privileges could be revoked for \"%s\"",
371 : objname)));
372 : }
373 30688 : else if (!all_privs && this_privileges != privileges)
374 : {
375 56 : if (objtype == OBJECT_COLUMN && colname)
376 48 : ereport(WARNING,
377 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
378 : errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"",
379 : colname, objname)));
380 : else
381 8 : ereport(WARNING,
382 : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
383 : errmsg("not all privileges could be revoked for \"%s\"",
384 : objname)));
385 : }
386 : }
387 :
388 40528 : return this_privileges;
389 : }
390 :
391 : /*
392 : * Called to execute the utility commands GRANT and REVOKE
393 : */
394 : void
395 12606 : ExecuteGrantStmt(GrantStmt *stmt)
396 : {
397 : InternalGrant istmt;
398 : ListCell *cell;
399 : const char *errormsg;
400 : AclMode all_privileges;
401 :
402 : /*
403 : * Turn the regular GrantStmt into the InternalGrant form.
404 : */
405 12606 : istmt.is_grant = stmt->is_grant;
406 12606 : istmt.objtype = stmt->objtype;
407 :
408 : /* Collect the OIDs of the target objects */
409 12606 : switch (stmt->targtype)
410 : {
411 12585 : case ACL_TARGET_OBJECT:
412 25149 : istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects,
413 12585 : stmt->is_grant);
414 12564 : break;
415 21 : case ACL_TARGET_ALL_IN_SCHEMA:
416 21 : istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
417 21 : break;
418 : /* ACL_TARGET_DEFAULTS should not be seen here */
419 0 : default:
420 0 : elog(ERROR, "unrecognized GrantStmt.targtype: %d",
421 : (int) stmt->targtype);
422 : }
423 :
424 : /* all_privs to be filled below */
425 : /* privileges to be filled below */
426 12585 : istmt.col_privs = NIL; /* may get filled below */
427 12585 : istmt.grantees = NIL; /* filled below */
428 12585 : istmt.grant_option = stmt->grant_option;
429 12585 : istmt.grantor = stmt->grantor;
430 12585 : istmt.behavior = stmt->behavior;
431 :
432 : /*
433 : * Convert the RoleSpec list into an Oid list. Note that at this point we
434 : * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
435 : * there shouldn't be any additional work needed to support this case.
436 : */
437 25232 : foreach(cell, stmt->grantees)
438 : {
439 12651 : RoleSpec *grantee = (RoleSpec *) lfirst(cell);
440 : Oid grantee_uid;
441 :
442 12651 : switch (grantee->roletype)
443 : {
444 10459 : case ROLESPEC_PUBLIC:
445 10459 : grantee_uid = ACL_ID_PUBLIC;
446 10459 : break;
447 2192 : default:
448 2192 : grantee_uid = get_rolespec_oid(grantee, false);
449 2188 : break;
450 : }
451 12647 : istmt.grantees = lappend_oid(istmt.grantees, grantee_uid);
452 : }
453 :
454 : /*
455 : * Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
456 : * bitmask. Note: objtype can't be OBJECT_COLUMN.
457 : */
458 12581 : switch (stmt->objtype)
459 : {
460 11156 : case OBJECT_TABLE:
461 :
462 : /*
463 : * Because this might be a sequence, we test both relation and
464 : * sequence bits, and later do a more limited test when we know
465 : * the object type.
466 : */
467 11156 : all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
468 11156 : errormsg = gettext_noop("invalid privilege type %s for relation");
469 11156 : break;
470 14 : case OBJECT_SEQUENCE:
471 14 : all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
472 14 : errormsg = gettext_noop("invalid privilege type %s for sequence");
473 14 : break;
474 214 : case OBJECT_DATABASE:
475 214 : all_privileges = ACL_ALL_RIGHTS_DATABASE;
476 214 : errormsg = gettext_noop("invalid privilege type %s for database");
477 214 : break;
478 13 : case OBJECT_DOMAIN:
479 13 : all_privileges = ACL_ALL_RIGHTS_TYPE;
480 13 : errormsg = gettext_noop("invalid privilege type %s for domain");
481 13 : break;
482 508 : case OBJECT_FUNCTION:
483 508 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
484 508 : errormsg = gettext_noop("invalid privilege type %s for function");
485 508 : break;
486 27 : case OBJECT_LANGUAGE:
487 27 : all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
488 27 : errormsg = gettext_noop("invalid privilege type %s for language");
489 27 : break;
490 50 : case OBJECT_LARGEOBJECT:
491 50 : all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
492 50 : errormsg = gettext_noop("invalid privilege type %s for large object");
493 50 : break;
494 312 : case OBJECT_SCHEMA:
495 312 : all_privileges = ACL_ALL_RIGHTS_SCHEMA;
496 312 : errormsg = gettext_noop("invalid privilege type %s for schema");
497 312 : break;
498 32 : case OBJECT_PROCEDURE:
499 32 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
500 32 : errormsg = gettext_noop("invalid privilege type %s for procedure");
501 32 : break;
502 4 : case OBJECT_ROUTINE:
503 4 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
504 4 : errormsg = gettext_noop("invalid privilege type %s for routine");
505 4 : break;
506 3 : case OBJECT_TABLESPACE:
507 3 : all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
508 3 : errormsg = gettext_noop("invalid privilege type %s for tablespace");
509 3 : break;
510 76 : case OBJECT_TYPE:
511 76 : all_privileges = ACL_ALL_RIGHTS_TYPE;
512 76 : errormsg = gettext_noop("invalid privilege type %s for type");
513 76 : break;
514 60 : case OBJECT_FDW:
515 60 : all_privileges = ACL_ALL_RIGHTS_FDW;
516 60 : errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
517 60 : break;
518 63 : case OBJECT_FOREIGN_SERVER:
519 63 : all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
520 63 : errormsg = gettext_noop("invalid privilege type %s for foreign server");
521 63 : break;
522 37 : case OBJECT_PARAMETER_ACL:
523 37 : all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
524 37 : errormsg = gettext_noop("invalid privilege type %s for parameter");
525 37 : break;
526 12 : case OBJECT_PROPGRAPH:
527 12 : all_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
528 12 : errormsg = gettext_noop("invalid privilege type %s for property graph");
529 12 : break;
530 0 : default:
531 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
532 : (int) stmt->objtype);
533 : /* keep compiler quiet */
534 : all_privileges = ACL_NO_RIGHTS;
535 : errormsg = NULL;
536 : }
537 :
538 12581 : if (stmt->privileges == NIL)
539 : {
540 1639 : istmt.all_privs = true;
541 :
542 : /*
543 : * will be turned into ACL_ALL_RIGHTS_* by the internal routines
544 : * depending on the object type
545 : */
546 1639 : istmt.privileges = ACL_NO_RIGHTS;
547 : }
548 : else
549 : {
550 10942 : istmt.all_privs = false;
551 10942 : istmt.privileges = ACL_NO_RIGHTS;
552 :
553 22241 : foreach(cell, stmt->privileges)
554 : {
555 11319 : AccessPriv *privnode = (AccessPriv *) lfirst(cell);
556 : AclMode priv;
557 :
558 : /*
559 : * If it's a column-level specification, we just set it aside in
560 : * col_privs for the moment; but insist it's for a relation.
561 : */
562 11319 : if (privnode->cols)
563 : {
564 303 : if (stmt->objtype != OBJECT_TABLE)
565 0 : ereport(ERROR,
566 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
567 : errmsg("column privileges are only valid for relations")));
568 303 : istmt.col_privs = lappend(istmt.col_privs, privnode);
569 303 : continue;
570 : }
571 :
572 11016 : if (privnode->priv_name == NULL) /* parser mistake? */
573 0 : elog(ERROR, "AccessPriv node must specify privilege or columns");
574 11016 : priv = string_to_privilege(privnode->priv_name);
575 :
576 11016 : if (priv & ~all_privileges)
577 20 : ereport(ERROR,
578 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
579 : errmsg(errormsg, privilege_to_string(priv))));
580 :
581 10996 : istmt.privileges |= priv;
582 : }
583 : }
584 :
585 12561 : ExecGrantStmt_oids(&istmt);
586 12510 : }
587 :
588 : /*
589 : * ExecGrantStmt_oids
590 : *
591 : * Internal entry point for granting and revoking privileges.
592 : */
593 : static void
594 12697 : ExecGrantStmt_oids(InternalGrant *istmt)
595 : {
596 12697 : switch (istmt->objtype)
597 : {
598 11240 : case OBJECT_TABLE:
599 : case OBJECT_SEQUENCE:
600 : case OBJECT_PROPGRAPH:
601 11240 : ExecGrant_Relation(istmt);
602 11225 : break;
603 220 : case OBJECT_DATABASE:
604 220 : ExecGrant_common(istmt, DatabaseRelationId, ACL_ALL_RIGHTS_DATABASE, NULL);
605 220 : break;
606 92 : case OBJECT_DOMAIN:
607 : case OBJECT_TYPE:
608 92 : ExecGrant_common(istmt, TypeRelationId, ACL_ALL_RIGHTS_TYPE, ExecGrant_Type_check);
609 80 : break;
610 61 : case OBJECT_FDW:
611 61 : ExecGrant_common(istmt, ForeignDataWrapperRelationId, ACL_ALL_RIGHTS_FDW, NULL);
612 49 : break;
613 72 : case OBJECT_FOREIGN_SERVER:
614 72 : ExecGrant_common(istmt, ForeignServerRelationId, ACL_ALL_RIGHTS_FOREIGN_SERVER, NULL);
615 64 : break;
616 554 : case OBJECT_FUNCTION:
617 : case OBJECT_PROCEDURE:
618 : case OBJECT_ROUTINE:
619 554 : ExecGrant_common(istmt, ProcedureRelationId, ACL_ALL_RIGHTS_FUNCTION, NULL);
620 554 : break;
621 27 : case OBJECT_LANGUAGE:
622 27 : ExecGrant_common(istmt, LanguageRelationId, ACL_ALL_RIGHTS_LANGUAGE, ExecGrant_Language_check);
623 23 : break;
624 58 : case OBJECT_LARGEOBJECT:
625 58 : ExecGrant_Largeobject(istmt);
626 58 : break;
627 321 : case OBJECT_SCHEMA:
628 321 : ExecGrant_common(istmt, NamespaceRelationId, ACL_ALL_RIGHTS_SCHEMA, NULL);
629 321 : break;
630 3 : case OBJECT_TABLESPACE:
631 3 : ExecGrant_common(istmt, TableSpaceRelationId, ACL_ALL_RIGHTS_TABLESPACE, NULL);
632 3 : break;
633 49 : case OBJECT_PARAMETER_ACL:
634 49 : ExecGrant_Parameter(istmt);
635 49 : break;
636 0 : default:
637 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
638 : (int) istmt->objtype);
639 : }
640 :
641 : /*
642 : * Pass the info to event triggers about the just-executed GRANT. Note
643 : * that we prefer to do it after actually executing it, because that gives
644 : * the functions a chance to adjust the istmt with privileges actually
645 : * granted.
646 : */
647 12646 : if (EventTriggerSupportsObjectType(istmt->objtype))
648 12374 : EventTriggerCollectGrant(istmt);
649 12646 : }
650 :
651 : /*
652 : * objectNamesToOids
653 : *
654 : * Turn a list of object names of a given type into an Oid list.
655 : *
656 : * XXX This function intentionally takes only an AccessShareLock. In the face
657 : * of concurrent DDL, we might easily latch onto an old version of an object,
658 : * causing the GRANT or REVOKE statement to fail. But it does prevent the
659 : * object from disappearing altogether. To do better, we would need to use a
660 : * self-exclusive lock, perhaps ShareUpdateExclusiveLock, here and before
661 : * *every* CatalogTupleUpdate() of a row that GRANT/REVOKE can affect.
662 : * Besides that additional work, this could have operational costs. For
663 : * example, it would make GRANT ALL TABLES IN SCHEMA terminate every
664 : * autovacuum running in the schema and consume a shared lock table entry per
665 : * table in the schema. The user-visible benefit of that additional work is
666 : * just changing "ERROR: tuple concurrently updated" to blocking. That's not
667 : * nothing, but it might not outweigh autovacuum termination and lock table
668 : * consumption spikes.
669 : */
670 : static List *
671 12585 : objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
672 : {
673 12585 : List *objects = NIL;
674 : ListCell *cell;
675 12585 : const LOCKMODE lockmode = AccessShareLock;
676 :
677 : Assert(objnames != NIL);
678 :
679 12585 : switch (objtype)
680 : {
681 1281 : default:
682 :
683 : /*
684 : * For most object types, we use get_object_address() directly.
685 : */
686 2637 : foreach(cell, objnames)
687 : {
688 : ObjectAddress address;
689 :
690 1372 : address = get_object_address(objtype, lfirst(cell), NULL, lockmode, false);
691 1356 : objects = lappend_oid(objects, address.objectId);
692 : }
693 1265 : break;
694 :
695 11173 : case OBJECT_TABLE:
696 : case OBJECT_SEQUENCE:
697 : case OBJECT_PROPGRAPH:
698 :
699 : /*
700 : * Here, we don't use get_object_address(). It requires that the
701 : * specified object type match the actual type of the object, but
702 : * in GRANT/REVOKE, all table-like things are addressed as TABLE.
703 : */
704 22381 : foreach(cell, objnames)
705 : {
706 11208 : RangeVar *relvar = (RangeVar *) lfirst(cell);
707 : Oid relOid;
708 :
709 11208 : relOid = RangeVarGetRelid(relvar, lockmode, false);
710 11208 : objects = lappend_oid(objects, relOid);
711 : }
712 11173 : break;
713 :
714 93 : case OBJECT_DOMAIN:
715 : case OBJECT_TYPE:
716 :
717 : /*
718 : * The parse representation of types and domains in privilege
719 : * targets is different from that expected by get_object_address()
720 : * (for parse conflict reasons), so we have to do a bit of
721 : * conversion here.
722 : */
723 182 : foreach(cell, objnames)
724 : {
725 93 : List *typname = (List *) lfirst(cell);
726 93 : TypeName *tn = makeTypeNameFromNameList(typname);
727 : ObjectAddress address;
728 : Relation relation;
729 :
730 93 : address = get_object_address(objtype, (Node *) tn, &relation, lockmode, false);
731 : Assert(relation == NULL);
732 89 : objects = lappend_oid(objects, address.objectId);
733 : }
734 89 : break;
735 :
736 38 : case OBJECT_PARAMETER_ACL:
737 :
738 : /*
739 : * Parameters are handled completely differently.
740 : */
741 100 : foreach(cell, objnames)
742 : {
743 : /*
744 : * In this code we represent a GUC by the OID of its entry in
745 : * pg_parameter_acl, which we have to manufacture here if it
746 : * doesn't exist yet. (That's a hack for sure, but it avoids
747 : * messing with all the GRANT/REVOKE infrastructure that
748 : * expects to use OIDs for object identities.) However, if
749 : * this is a REVOKE, we can instead just ignore any GUCs that
750 : * don't have such an entry, as they must not have any
751 : * privileges needing removal.
752 : */
753 63 : char *parameter = strVal(lfirst(cell));
754 63 : Oid parameterId = ParameterAclLookup(parameter, true);
755 :
756 63 : if (!OidIsValid(parameterId) && is_grant)
757 : {
758 34 : parameterId = ParameterAclCreate(parameter);
759 :
760 : /*
761 : * Prevent error when processing duplicate objects, and
762 : * make this new entry visible so that ExecGrant_Parameter
763 : * can update it.
764 : */
765 33 : CommandCounterIncrement();
766 : }
767 62 : if (OidIsValid(parameterId))
768 56 : objects = lappend_oid(objects, parameterId);
769 : }
770 37 : break;
771 : }
772 :
773 12564 : return objects;
774 : }
775 :
776 : /*
777 : * objectsInSchemaToOids
778 : *
779 : * Find all objects of a given type in specified schemas, and make a list
780 : * of their Oids. We check USAGE privilege on the schemas, but there is
781 : * no privilege checking on the individual objects here.
782 : */
783 : static List *
784 21 : objectsInSchemaToOids(ObjectType objtype, List *nspnames)
785 : {
786 21 : List *objects = NIL;
787 : ListCell *cell;
788 :
789 42 : foreach(cell, nspnames)
790 : {
791 21 : char *nspname = strVal(lfirst(cell));
792 : Oid namespaceId;
793 : List *objs;
794 :
795 21 : namespaceId = LookupExplicitNamespace(nspname, false);
796 :
797 21 : switch (objtype)
798 : {
799 8 : case OBJECT_TABLE:
800 8 : objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
801 8 : objects = list_concat(objects, objs);
802 8 : objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
803 8 : objects = list_concat(objects, objs);
804 8 : objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
805 8 : objects = list_concat(objects, objs);
806 8 : objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
807 8 : objects = list_concat(objects, objs);
808 8 : objs = getRelationsInNamespace(namespaceId, RELKIND_PARTITIONED_TABLE);
809 8 : objects = list_concat(objects, objs);
810 8 : break;
811 1 : case OBJECT_SEQUENCE:
812 1 : objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
813 1 : objects = list_concat(objects, objs);
814 1 : break;
815 0 : case OBJECT_PROPGRAPH:
816 0 : objs = getRelationsInNamespace(namespaceId, RELKIND_PROPGRAPH);
817 0 : objects = list_concat(objects, objs);
818 0 : break;
819 12 : case OBJECT_FUNCTION:
820 : case OBJECT_PROCEDURE:
821 : case OBJECT_ROUTINE:
822 : {
823 : ScanKeyData key[2];
824 : int keycount;
825 : Relation rel;
826 : TableScanDesc scan;
827 : HeapTuple tuple;
828 :
829 12 : keycount = 0;
830 12 : ScanKeyInit(&key[keycount++],
831 : Anum_pg_proc_pronamespace,
832 : BTEqualStrategyNumber, F_OIDEQ,
833 : ObjectIdGetDatum(namespaceId));
834 :
835 12 : if (objtype == OBJECT_FUNCTION)
836 : /* includes aggregates and window functions */
837 4 : ScanKeyInit(&key[keycount++],
838 : Anum_pg_proc_prokind,
839 : BTEqualStrategyNumber, F_CHARNE,
840 : CharGetDatum(PROKIND_PROCEDURE));
841 8 : else if (objtype == OBJECT_PROCEDURE)
842 4 : ScanKeyInit(&key[keycount++],
843 : Anum_pg_proc_prokind,
844 : BTEqualStrategyNumber, F_CHAREQ,
845 : CharGetDatum(PROKIND_PROCEDURE));
846 :
847 12 : rel = table_open(ProcedureRelationId, AccessShareLock);
848 12 : scan = table_beginscan_catalog(rel, keycount, key);
849 :
850 36 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
851 : {
852 24 : Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid;
853 :
854 24 : objects = lappend_oid(objects, oid);
855 : }
856 :
857 12 : table_endscan(scan);
858 12 : table_close(rel, AccessShareLock);
859 : }
860 12 : break;
861 0 : default:
862 : /* should not happen */
863 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
864 : (int) objtype);
865 : }
866 : }
867 :
868 21 : return objects;
869 : }
870 :
871 : /*
872 : * getRelationsInNamespace
873 : *
874 : * Return Oid list of relations in given namespace filtered by relation kind
875 : */
876 : static List *
877 41 : getRelationsInNamespace(Oid namespaceId, char relkind)
878 : {
879 41 : List *relations = NIL;
880 : ScanKeyData key[2];
881 : Relation rel;
882 : TableScanDesc scan;
883 : HeapTuple tuple;
884 :
885 41 : ScanKeyInit(&key[0],
886 : Anum_pg_class_relnamespace,
887 : BTEqualStrategyNumber, F_OIDEQ,
888 : ObjectIdGetDatum(namespaceId));
889 41 : ScanKeyInit(&key[1],
890 : Anum_pg_class_relkind,
891 : BTEqualStrategyNumber, F_CHAREQ,
892 : CharGetDatum(relkind));
893 :
894 41 : rel = table_open(RelationRelationId, AccessShareLock);
895 41 : scan = table_beginscan_catalog(rel, 2, key);
896 :
897 62 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
898 : {
899 21 : Oid oid = ((Form_pg_class) GETSTRUCT(tuple))->oid;
900 :
901 21 : relations = lappend_oid(relations, oid);
902 : }
903 :
904 41 : table_endscan(scan);
905 41 : table_close(rel, AccessShareLock);
906 :
907 41 : return relations;
908 : }
909 :
910 :
911 : /*
912 : * ALTER DEFAULT PRIVILEGES statement
913 : */
914 : void
915 135 : ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *stmt)
916 : {
917 135 : GrantStmt *action = stmt->action;
918 : InternalDefaultACL iacls;
919 : ListCell *cell;
920 135 : List *rolespecs = NIL;
921 135 : List *nspnames = NIL;
922 135 : DefElem *drolespecs = NULL;
923 135 : DefElem *dnspnames = NULL;
924 : AclMode all_privileges;
925 : const char *errormsg;
926 :
927 : /* Deconstruct the "options" part of the statement */
928 228 : foreach(cell, stmt->options)
929 : {
930 93 : DefElem *defel = (DefElem *) lfirst(cell);
931 :
932 93 : if (strcmp(defel->defname, "schemas") == 0)
933 : {
934 39 : if (dnspnames)
935 0 : errorConflictingDefElem(defel, pstate);
936 39 : dnspnames = defel;
937 : }
938 54 : else if (strcmp(defel->defname, "roles") == 0)
939 : {
940 54 : if (drolespecs)
941 0 : errorConflictingDefElem(defel, pstate);
942 54 : drolespecs = defel;
943 : }
944 : else
945 0 : elog(ERROR, "option \"%s\" not recognized", defel->defname);
946 : }
947 :
948 135 : if (dnspnames)
949 39 : nspnames = (List *) dnspnames->arg;
950 135 : if (drolespecs)
951 54 : rolespecs = (List *) drolespecs->arg;
952 :
953 : /* Prepare the InternalDefaultACL representation of the statement */
954 : /* roleid to be filled below */
955 : /* nspid to be filled in SetDefaultACLsInSchemas */
956 135 : iacls.is_grant = action->is_grant;
957 135 : iacls.objtype = action->objtype;
958 : /* all_privs to be filled below */
959 : /* privileges to be filled below */
960 135 : iacls.grantees = NIL; /* filled below */
961 135 : iacls.grant_option = action->grant_option;
962 135 : iacls.grantor = action->grantor;
963 135 : iacls.behavior = action->behavior;
964 :
965 : /*
966 : * Convert the RoleSpec list into an Oid list. Note that at this point we
967 : * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
968 : * there shouldn't be any additional work needed to support this case.
969 : */
970 274 : foreach(cell, action->grantees)
971 : {
972 139 : RoleSpec *grantee = (RoleSpec *) lfirst(cell);
973 : Oid grantee_uid;
974 :
975 139 : switch (grantee->roletype)
976 : {
977 30 : case ROLESPEC_PUBLIC:
978 30 : grantee_uid = ACL_ID_PUBLIC;
979 30 : break;
980 109 : default:
981 109 : grantee_uid = get_rolespec_oid(grantee, false);
982 109 : break;
983 : }
984 139 : iacls.grantees = lappend_oid(iacls.grantees, grantee_uid);
985 : }
986 :
987 : /*
988 : * Convert action->privileges, a list of privilege strings, into an
989 : * AclMode bitmask.
990 : */
991 135 : switch (action->objtype)
992 : {
993 51 : case OBJECT_TABLE:
994 51 : all_privileges = ACL_ALL_RIGHTS_RELATION;
995 51 : errormsg = gettext_noop("invalid privilege type %s for relation");
996 51 : break;
997 4 : case OBJECT_SEQUENCE:
998 4 : all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
999 4 : errormsg = gettext_noop("invalid privilege type %s for sequence");
1000 4 : break;
1001 14 : case OBJECT_FUNCTION:
1002 14 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1003 14 : errormsg = gettext_noop("invalid privilege type %s for function");
1004 14 : break;
1005 0 : case OBJECT_PROCEDURE:
1006 0 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1007 0 : errormsg = gettext_noop("invalid privilege type %s for procedure");
1008 0 : break;
1009 0 : case OBJECT_ROUTINE:
1010 0 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1011 0 : errormsg = gettext_noop("invalid privilege type %s for routine");
1012 0 : break;
1013 22 : case OBJECT_TYPE:
1014 22 : all_privileges = ACL_ALL_RIGHTS_TYPE;
1015 22 : errormsg = gettext_noop("invalid privilege type %s for type");
1016 22 : break;
1017 24 : case OBJECT_SCHEMA:
1018 24 : all_privileges = ACL_ALL_RIGHTS_SCHEMA;
1019 24 : errormsg = gettext_noop("invalid privilege type %s for schema");
1020 24 : break;
1021 20 : case OBJECT_LARGEOBJECT:
1022 20 : all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
1023 20 : errormsg = gettext_noop("invalid privilege type %s for large object");
1024 20 : break;
1025 0 : case OBJECT_PROPGRAPH:
1026 0 : all_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
1027 0 : errormsg = gettext_noop("invalid privilege type %s for property graph");
1028 0 : break;
1029 0 : default:
1030 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
1031 : (int) action->objtype);
1032 : /* keep compiler quiet */
1033 : all_privileges = ACL_NO_RIGHTS;
1034 : errormsg = NULL;
1035 : }
1036 :
1037 135 : if (action->privileges == NIL)
1038 : {
1039 51 : iacls.all_privs = true;
1040 :
1041 : /*
1042 : * will be turned into ACL_ALL_RIGHTS_* by the internal routines
1043 : * depending on the object type
1044 : */
1045 51 : iacls.privileges = ACL_NO_RIGHTS;
1046 : }
1047 : else
1048 : {
1049 84 : iacls.all_privs = false;
1050 84 : iacls.privileges = ACL_NO_RIGHTS;
1051 :
1052 168 : foreach(cell, action->privileges)
1053 : {
1054 84 : AccessPriv *privnode = (AccessPriv *) lfirst(cell);
1055 : AclMode priv;
1056 :
1057 84 : if (privnode->cols)
1058 0 : ereport(ERROR,
1059 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1060 : errmsg("default privileges cannot be set for columns")));
1061 :
1062 84 : if (privnode->priv_name == NULL) /* parser mistake? */
1063 0 : elog(ERROR, "AccessPriv node must specify privilege");
1064 84 : priv = string_to_privilege(privnode->priv_name);
1065 :
1066 84 : if (priv & ~all_privileges)
1067 0 : ereport(ERROR,
1068 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1069 : errmsg(errormsg, privilege_to_string(priv))));
1070 :
1071 84 : iacls.privileges |= priv;
1072 : }
1073 : }
1074 :
1075 135 : if (rolespecs == NIL)
1076 : {
1077 : /* Set permissions for myself */
1078 81 : iacls.roleid = GetUserId();
1079 :
1080 81 : SetDefaultACLsInSchemas(&iacls, nspnames);
1081 : }
1082 : else
1083 : {
1084 : /* Look up the role OIDs and do permissions checks */
1085 : ListCell *rolecell;
1086 :
1087 108 : foreach(rolecell, rolespecs)
1088 : {
1089 54 : RoleSpec *rolespec = lfirst(rolecell);
1090 :
1091 54 : iacls.roleid = get_rolespec_oid(rolespec, false);
1092 :
1093 54 : if (!has_privs_of_role(GetUserId(), iacls.roleid))
1094 0 : ereport(ERROR,
1095 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1096 : errmsg("permission denied to change default privileges")));
1097 :
1098 54 : SetDefaultACLsInSchemas(&iacls, nspnames);
1099 : }
1100 : }
1101 127 : }
1102 :
1103 : /*
1104 : * Process ALTER DEFAULT PRIVILEGES for a list of target schemas
1105 : *
1106 : * All fields of *iacls except nspid were filled already
1107 : */
1108 : static void
1109 135 : SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
1110 : {
1111 135 : if (nspnames == NIL)
1112 : {
1113 : /* Set database-wide permissions if no schema was specified */
1114 96 : iacls->nspid = InvalidOid;
1115 :
1116 96 : SetDefaultACL(iacls);
1117 : }
1118 : else
1119 : {
1120 : /* Look up the schema OIDs and set permissions for each one */
1121 : ListCell *nspcell;
1122 :
1123 74 : foreach(nspcell, nspnames)
1124 : {
1125 43 : char *nspname = strVal(lfirst(nspcell));
1126 :
1127 43 : iacls->nspid = get_namespace_oid(nspname, false);
1128 :
1129 : /*
1130 : * We used to insist that the target role have CREATE privileges
1131 : * on the schema, since without that it wouldn't be able to create
1132 : * an object for which these default privileges would apply.
1133 : * However, this check proved to be more confusing than helpful,
1134 : * and it also caused certain database states to not be
1135 : * dumpable/restorable, since revoking CREATE doesn't cause
1136 : * default privileges for the schema to go away. So now, we just
1137 : * allow the ALTER; if the user lacks CREATE he'll find out when
1138 : * he tries to create an object.
1139 : */
1140 :
1141 43 : SetDefaultACL(iacls);
1142 : }
1143 : }
1144 127 : }
1145 :
1146 :
1147 : /*
1148 : * Create or update a pg_default_acl entry
1149 : */
1150 : static void
1151 163 : SetDefaultACL(InternalDefaultACL *iacls)
1152 : {
1153 163 : AclMode this_privileges = iacls->privileges;
1154 : char objtype;
1155 : Relation rel;
1156 : HeapTuple tuple;
1157 : bool isNew;
1158 : Acl *def_acl;
1159 : Acl *old_acl;
1160 : Acl *new_acl;
1161 : HeapTuple newtuple;
1162 : int noldmembers;
1163 : int nnewmembers;
1164 : Oid *oldmembers;
1165 : Oid *newmembers;
1166 :
1167 163 : rel = table_open(DefaultAclRelationId, RowExclusiveLock);
1168 :
1169 : /*
1170 : * The default for a global entry is the hard-wired default ACL for the
1171 : * particular object type. The default for non-global entries is an empty
1172 : * ACL. This must be so because global entries replace the hard-wired
1173 : * defaults, while others are added on.
1174 : */
1175 163 : if (!OidIsValid(iacls->nspid))
1176 120 : def_acl = acldefault(iacls->objtype, iacls->roleid);
1177 : else
1178 43 : def_acl = make_empty_acl();
1179 :
1180 : /*
1181 : * Convert ACL object type to pg_default_acl object type and handle
1182 : * all_privs option
1183 : */
1184 163 : switch (iacls->objtype)
1185 : {
1186 59 : case OBJECT_TABLE:
1187 59 : objtype = DEFACLOBJ_RELATION;
1188 59 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1189 17 : this_privileges = ACL_ALL_RIGHTS_RELATION;
1190 59 : break;
1191 :
1192 8 : case OBJECT_SEQUENCE:
1193 8 : objtype = DEFACLOBJ_SEQUENCE;
1194 8 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1195 8 : this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
1196 8 : break;
1197 :
1198 18 : case OBJECT_FUNCTION:
1199 18 : objtype = DEFACLOBJ_FUNCTION;
1200 18 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1201 8 : this_privileges = ACL_ALL_RIGHTS_FUNCTION;
1202 18 : break;
1203 :
1204 26 : case OBJECT_TYPE:
1205 26 : objtype = DEFACLOBJ_TYPE;
1206 26 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1207 10 : this_privileges = ACL_ALL_RIGHTS_TYPE;
1208 26 : break;
1209 :
1210 28 : case OBJECT_SCHEMA:
1211 28 : if (OidIsValid(iacls->nspid))
1212 4 : ereport(ERROR,
1213 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1214 : errmsg("cannot use IN SCHEMA clause when using %s",
1215 : "GRANT/REVOKE ON SCHEMAS")));
1216 24 : objtype = DEFACLOBJ_NAMESPACE;
1217 24 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1218 16 : this_privileges = ACL_ALL_RIGHTS_SCHEMA;
1219 24 : break;
1220 :
1221 24 : case OBJECT_LARGEOBJECT:
1222 24 : if (OidIsValid(iacls->nspid))
1223 4 : ereport(ERROR,
1224 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1225 : errmsg("cannot use IN SCHEMA clause when using %s",
1226 : "GRANT/REVOKE ON LARGE OBJECTS")));
1227 20 : objtype = DEFACLOBJ_LARGEOBJECT;
1228 20 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1229 12 : this_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
1230 20 : break;
1231 :
1232 0 : default:
1233 0 : elog(ERROR, "unrecognized object type: %d",
1234 : (int) iacls->objtype);
1235 : objtype = 0; /* keep compiler quiet */
1236 : break;
1237 : }
1238 :
1239 : /* Search for existing row for this object type in catalog */
1240 155 : tuple = SearchSysCache3(DEFACLROLENSPOBJ,
1241 : ObjectIdGetDatum(iacls->roleid),
1242 : ObjectIdGetDatum(iacls->nspid),
1243 : CharGetDatum(objtype));
1244 :
1245 155 : if (HeapTupleIsValid(tuple))
1246 : {
1247 : Datum aclDatum;
1248 : bool isNull;
1249 :
1250 60 : aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
1251 : Anum_pg_default_acl_defaclacl,
1252 : &isNull);
1253 60 : if (!isNull)
1254 60 : old_acl = DatumGetAclPCopy(aclDatum);
1255 : else
1256 0 : old_acl = NULL; /* this case shouldn't happen, probably */
1257 60 : isNew = false;
1258 : }
1259 : else
1260 : {
1261 95 : old_acl = NULL;
1262 95 : isNew = true;
1263 : }
1264 :
1265 155 : if (old_acl != NULL)
1266 : {
1267 : /*
1268 : * We need the members of both old and new ACLs so we can correct the
1269 : * shared dependency information. Collect data before
1270 : * merge_acl_with_grant throws away old_acl.
1271 : */
1272 60 : noldmembers = aclmembers(old_acl, &oldmembers);
1273 : }
1274 : else
1275 : {
1276 : /* If no or null entry, start with the default ACL value */
1277 95 : old_acl = aclcopy(def_acl);
1278 : /* There are no old member roles according to the catalogs */
1279 95 : noldmembers = 0;
1280 95 : oldmembers = NULL;
1281 : }
1282 :
1283 : /*
1284 : * Generate new ACL. Grantor of rights is always the same as the target
1285 : * role.
1286 : */
1287 155 : new_acl = merge_acl_with_grant(old_acl,
1288 155 : iacls->is_grant,
1289 155 : iacls->grant_option,
1290 : iacls->behavior,
1291 : iacls->grantees,
1292 : this_privileges,
1293 : iacls->roleid,
1294 : iacls->roleid);
1295 :
1296 : /*
1297 : * If the result is the same as the default value, we do not need an
1298 : * explicit pg_default_acl entry, and should in fact remove the entry if
1299 : * it exists. Must sort both arrays to compare properly.
1300 : */
1301 155 : aclitemsort(new_acl);
1302 155 : aclitemsort(def_acl);
1303 155 : if (aclequal(new_acl, def_acl))
1304 : {
1305 : /* delete old entry, if indeed there is one */
1306 41 : if (!isNew)
1307 : {
1308 : ObjectAddress myself;
1309 :
1310 : /*
1311 : * The dependency machinery will take care of removing all
1312 : * associated dependency entries. We use DROP_RESTRICT since
1313 : * there shouldn't be anything depending on this entry.
1314 : */
1315 40 : myself.classId = DefaultAclRelationId;
1316 40 : myself.objectId = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
1317 40 : myself.objectSubId = 0;
1318 :
1319 40 : performDeletion(&myself, DROP_RESTRICT, 0);
1320 : }
1321 : }
1322 : else
1323 : {
1324 114 : Datum values[Natts_pg_default_acl] = {0};
1325 114 : bool nulls[Natts_pg_default_acl] = {0};
1326 114 : bool replaces[Natts_pg_default_acl] = {0};
1327 : Oid defAclOid;
1328 :
1329 114 : if (isNew)
1330 : {
1331 : /* insert new entry */
1332 94 : defAclOid = GetNewOidWithIndex(rel, DefaultAclOidIndexId,
1333 : Anum_pg_default_acl_oid);
1334 94 : values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(defAclOid);
1335 94 : values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
1336 94 : values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
1337 94 : values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
1338 94 : values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
1339 :
1340 94 : newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
1341 94 : CatalogTupleInsert(rel, newtuple);
1342 : }
1343 : else
1344 : {
1345 20 : defAclOid = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
1346 :
1347 : /* update existing entry */
1348 20 : values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
1349 20 : replaces[Anum_pg_default_acl_defaclacl - 1] = true;
1350 :
1351 20 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
1352 : values, nulls, replaces);
1353 20 : CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
1354 : }
1355 :
1356 : /* these dependencies don't change in an update */
1357 114 : if (isNew)
1358 : {
1359 : /* dependency on role */
1360 94 : recordDependencyOnOwner(DefaultAclRelationId, defAclOid,
1361 : iacls->roleid);
1362 :
1363 : /* dependency on namespace */
1364 94 : if (OidIsValid(iacls->nspid))
1365 : {
1366 : ObjectAddress myself,
1367 : referenced;
1368 :
1369 22 : myself.classId = DefaultAclRelationId;
1370 22 : myself.objectId = defAclOid;
1371 22 : myself.objectSubId = 0;
1372 :
1373 22 : referenced.classId = NamespaceRelationId;
1374 22 : referenced.objectId = iacls->nspid;
1375 22 : referenced.objectSubId = 0;
1376 :
1377 22 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1378 : }
1379 : }
1380 :
1381 : /*
1382 : * Update the shared dependency ACL info
1383 : */
1384 114 : nnewmembers = aclmembers(new_acl, &newmembers);
1385 :
1386 114 : updateAclDependencies(DefaultAclRelationId,
1387 : defAclOid, 0,
1388 : iacls->roleid,
1389 : noldmembers, oldmembers,
1390 : nnewmembers, newmembers);
1391 :
1392 114 : if (isNew)
1393 94 : InvokeObjectPostCreateHook(DefaultAclRelationId, defAclOid, 0);
1394 : else
1395 20 : InvokeObjectPostAlterHook(DefaultAclRelationId, defAclOid, 0);
1396 : }
1397 :
1398 155 : if (HeapTupleIsValid(tuple))
1399 60 : ReleaseSysCache(tuple);
1400 :
1401 155 : table_close(rel, RowExclusiveLock);
1402 :
1403 : /* prevent error when processing duplicate objects */
1404 155 : CommandCounterIncrement();
1405 155 : }
1406 :
1407 :
1408 : /*
1409 : * RemoveRoleFromObjectACL
1410 : *
1411 : * Used by shdepDropOwned to remove mentions of a role in ACLs.
1412 : *
1413 : * Notice that this doesn't accept an objsubid parameter, which is a bit bogus
1414 : * since the pg_shdepend record that caused us to call it certainly had one.
1415 : * If, for example, pg_shdepend records the existence of a permission on
1416 : * mytable.mycol, this function will effectively issue a REVOKE ALL ON TABLE
1417 : * mytable. That gets the job done because (per SQL spec) such a REVOKE also
1418 : * revokes per-column permissions. We could not recreate a situation where
1419 : * the role has table-level but not column-level permissions; but it's okay
1420 : * (for now anyway) because this is only used when we're dropping the role
1421 : * and so all its permissions everywhere must go away. At worst it's a bit
1422 : * inefficient if the role has column permissions on several columns of the
1423 : * same table.
1424 : */
1425 : void
1426 160 : RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
1427 : {
1428 160 : if (classid == DefaultAclRelationId)
1429 : {
1430 : InternalDefaultACL iacls;
1431 : Form_pg_default_acl pg_default_acl_tuple;
1432 : Relation rel;
1433 : ScanKeyData skey[1];
1434 : SysScanDesc scan;
1435 : HeapTuple tuple;
1436 :
1437 : /* first fetch info needed by SetDefaultACL */
1438 24 : rel = table_open(DefaultAclRelationId, AccessShareLock);
1439 :
1440 24 : ScanKeyInit(&skey[0],
1441 : Anum_pg_default_acl_oid,
1442 : BTEqualStrategyNumber, F_OIDEQ,
1443 : ObjectIdGetDatum(objid));
1444 :
1445 24 : scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
1446 : NULL, 1, skey);
1447 :
1448 24 : tuple = systable_getnext(scan);
1449 :
1450 24 : if (!HeapTupleIsValid(tuple))
1451 0 : elog(ERROR, "could not find tuple for default ACL %u", objid);
1452 :
1453 24 : pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
1454 :
1455 24 : iacls.roleid = pg_default_acl_tuple->defaclrole;
1456 24 : iacls.nspid = pg_default_acl_tuple->defaclnamespace;
1457 :
1458 24 : switch (pg_default_acl_tuple->defaclobjtype)
1459 : {
1460 4 : case DEFACLOBJ_RELATION:
1461 4 : iacls.objtype = OBJECT_TABLE;
1462 4 : break;
1463 4 : case DEFACLOBJ_SEQUENCE:
1464 4 : iacls.objtype = OBJECT_SEQUENCE;
1465 4 : break;
1466 4 : case DEFACLOBJ_FUNCTION:
1467 4 : iacls.objtype = OBJECT_FUNCTION;
1468 4 : break;
1469 4 : case DEFACLOBJ_TYPE:
1470 4 : iacls.objtype = OBJECT_TYPE;
1471 4 : break;
1472 4 : case DEFACLOBJ_NAMESPACE:
1473 4 : iacls.objtype = OBJECT_SCHEMA;
1474 4 : break;
1475 4 : case DEFACLOBJ_LARGEOBJECT:
1476 4 : iacls.objtype = OBJECT_LARGEOBJECT;
1477 4 : break;
1478 0 : default:
1479 : /* Shouldn't get here */
1480 0 : elog(ERROR, "unexpected default ACL type: %d",
1481 : (int) pg_default_acl_tuple->defaclobjtype);
1482 : break;
1483 : }
1484 :
1485 24 : systable_endscan(scan);
1486 24 : table_close(rel, AccessShareLock);
1487 :
1488 24 : iacls.is_grant = false;
1489 24 : iacls.all_privs = true;
1490 24 : iacls.privileges = ACL_NO_RIGHTS;
1491 24 : iacls.grantees = list_make1_oid(roleid);
1492 24 : iacls.grant_option = false;
1493 24 : iacls.grantor = NULL;
1494 24 : iacls.behavior = DROP_CASCADE;
1495 :
1496 : /* Do it */
1497 24 : SetDefaultACL(&iacls);
1498 : }
1499 : else
1500 : {
1501 : InternalGrant istmt;
1502 :
1503 136 : switch (classid)
1504 : {
1505 62 : case RelationRelationId:
1506 : /* it's OK to use TABLE for a sequence */
1507 62 : istmt.objtype = OBJECT_TABLE;
1508 62 : break;
1509 6 : case DatabaseRelationId:
1510 6 : istmt.objtype = OBJECT_DATABASE;
1511 6 : break;
1512 3 : case TypeRelationId:
1513 3 : istmt.objtype = OBJECT_TYPE;
1514 3 : break;
1515 22 : case ProcedureRelationId:
1516 22 : istmt.objtype = OBJECT_ROUTINE;
1517 22 : break;
1518 0 : case LanguageRelationId:
1519 0 : istmt.objtype = OBJECT_LANGUAGE;
1520 0 : break;
1521 12 : case LargeObjectRelationId:
1522 12 : istmt.objtype = OBJECT_LARGEOBJECT;
1523 12 : break;
1524 9 : case NamespaceRelationId:
1525 9 : istmt.objtype = OBJECT_SCHEMA;
1526 9 : break;
1527 0 : case TableSpaceRelationId:
1528 0 : istmt.objtype = OBJECT_TABLESPACE;
1529 0 : break;
1530 9 : case ForeignServerRelationId:
1531 9 : istmt.objtype = OBJECT_FOREIGN_SERVER;
1532 9 : break;
1533 1 : case ForeignDataWrapperRelationId:
1534 1 : istmt.objtype = OBJECT_FDW;
1535 1 : break;
1536 12 : case ParameterAclRelationId:
1537 12 : istmt.objtype = OBJECT_PARAMETER_ACL;
1538 12 : break;
1539 0 : default:
1540 0 : elog(ERROR, "unexpected object class %u", classid);
1541 : break;
1542 : }
1543 136 : istmt.is_grant = false;
1544 136 : istmt.objects = list_make1_oid(objid);
1545 136 : istmt.all_privs = true;
1546 136 : istmt.privileges = ACL_NO_RIGHTS;
1547 136 : istmt.col_privs = NIL;
1548 136 : istmt.grantees = list_make1_oid(roleid);
1549 136 : istmt.grant_option = false;
1550 136 : istmt.grantor = NULL;
1551 136 : istmt.behavior = DROP_CASCADE;
1552 :
1553 136 : ExecGrantStmt_oids(&istmt);
1554 : }
1555 160 : }
1556 :
1557 :
1558 : /*
1559 : * expand_col_privileges
1560 : *
1561 : * OR the specified privilege(s) into per-column array entries for each
1562 : * specified attribute. The per-column array is indexed starting at
1563 : * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
1564 : */
1565 : static void
1566 303 : expand_col_privileges(List *colnames, Oid table_oid,
1567 : AclMode this_privileges,
1568 : AclMode *col_privileges,
1569 : int num_col_privileges)
1570 : {
1571 : ListCell *cell;
1572 :
1573 1887 : foreach(cell, colnames)
1574 : {
1575 1584 : char *colname = strVal(lfirst(cell));
1576 : AttrNumber attnum;
1577 :
1578 1584 : attnum = get_attnum(table_oid, colname);
1579 1584 : if (attnum == InvalidAttrNumber)
1580 0 : ereport(ERROR,
1581 : (errcode(ERRCODE_UNDEFINED_COLUMN),
1582 : errmsg("column \"%s\" of relation \"%s\" does not exist",
1583 : colname, get_rel_name(table_oid))));
1584 1584 : attnum -= FirstLowInvalidHeapAttributeNumber;
1585 1584 : if (attnum <= 0 || attnum >= num_col_privileges)
1586 0 : elog(ERROR, "column number out of range"); /* safety check */
1587 1584 : col_privileges[attnum] |= this_privileges;
1588 : }
1589 303 : }
1590 :
1591 : /*
1592 : * expand_all_col_privileges
1593 : *
1594 : * OR the specified privilege(s) into per-column array entries for each valid
1595 : * attribute of a relation. The per-column array is indexed starting at
1596 : * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
1597 : */
1598 : static void
1599 3505 : expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
1600 : AclMode this_privileges,
1601 : AclMode *col_privileges,
1602 : int num_col_privileges)
1603 : {
1604 : AttrNumber curr_att;
1605 :
1606 : Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges);
1607 3505 : for (curr_att = FirstLowInvalidHeapAttributeNumber + 1;
1608 37208 : curr_att <= classForm->relnatts;
1609 33703 : curr_att++)
1610 : {
1611 : HeapTuple attTuple;
1612 : bool isdropped;
1613 :
1614 33703 : if (curr_att == InvalidAttrNumber)
1615 3505 : continue;
1616 :
1617 : /* Views don't have any system columns at all */
1618 30198 : if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
1619 3756 : continue;
1620 :
1621 26442 : attTuple = SearchSysCache2(ATTNUM,
1622 : ObjectIdGetDatum(table_oid),
1623 : Int16GetDatum(curr_att));
1624 26442 : if (!HeapTupleIsValid(attTuple))
1625 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1626 : curr_att, table_oid);
1627 :
1628 26442 : isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
1629 :
1630 26442 : ReleaseSysCache(attTuple);
1631 :
1632 : /* ignore dropped columns */
1633 26442 : if (isdropped)
1634 4 : continue;
1635 :
1636 26438 : col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
1637 : }
1638 3505 : }
1639 :
1640 : /*
1641 : * This processes attributes, but expects to be called from
1642 : * ExecGrant_Relation, not directly from ExecuteGrantStmt.
1643 : */
1644 : static void
1645 27994 : ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
1646 : AttrNumber attnum, Oid ownerId, AclMode col_privileges,
1647 : Relation attRelation, const Acl *old_rel_acl)
1648 : {
1649 : HeapTuple attr_tuple;
1650 : Form_pg_attribute pg_attribute_tuple;
1651 : Acl *old_acl;
1652 : Acl *new_acl;
1653 : Acl *merged_acl;
1654 : Datum aclDatum;
1655 : bool isNull;
1656 : Oid grantorId;
1657 : AclMode avail_goptions;
1658 : bool need_update;
1659 : HeapTuple newtuple;
1660 27994 : Datum values[Natts_pg_attribute] = {0};
1661 27994 : bool nulls[Natts_pg_attribute] = {0};
1662 27994 : bool replaces[Natts_pg_attribute] = {0};
1663 : int noldmembers;
1664 : int nnewmembers;
1665 : Oid *oldmembers;
1666 : Oid *newmembers;
1667 :
1668 27994 : attr_tuple = SearchSysCache2(ATTNUM,
1669 : ObjectIdGetDatum(relOid),
1670 : Int16GetDatum(attnum));
1671 27994 : if (!HeapTupleIsValid(attr_tuple))
1672 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1673 : attnum, relOid);
1674 27994 : pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple);
1675 :
1676 : /*
1677 : * Get working copy of existing ACL. If there's no ACL, substitute the
1678 : * proper default.
1679 : */
1680 27994 : aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
1681 : &isNull);
1682 27994 : if (isNull)
1683 : {
1684 27774 : old_acl = acldefault(OBJECT_COLUMN, ownerId);
1685 : /* There are no old member roles according to the catalogs */
1686 27774 : noldmembers = 0;
1687 27774 : oldmembers = NULL;
1688 : }
1689 : else
1690 : {
1691 220 : old_acl = DatumGetAclPCopy(aclDatum);
1692 : /* Get the roles mentioned in the existing ACL */
1693 220 : noldmembers = aclmembers(old_acl, &oldmembers);
1694 : }
1695 :
1696 : /*
1697 : * In select_best_grantor we should consider existing table-level ACL bits
1698 : * as well as the per-column ACL. Build a new ACL that is their
1699 : * concatenation. (This is a bit cheap and dirty compared to merging them
1700 : * properly with no duplications, but it's all we need here.)
1701 : */
1702 27994 : merged_acl = aclconcat(old_rel_acl, old_acl);
1703 :
1704 : /* Determine ID to do the grant as, and available grant options */
1705 27994 : select_best_grantor(istmt->grantor, col_privileges,
1706 : merged_acl, ownerId,
1707 : &grantorId, &avail_goptions);
1708 :
1709 27994 : pfree(merged_acl);
1710 :
1711 : /*
1712 : * Restrict the privileges to what we can actually grant, and emit the
1713 : * standards-mandated warning and error messages. Note: we don't track
1714 : * whether the user actually used the ALL PRIVILEGES(columns) syntax for
1715 : * each column; we just approximate it by whether all the possible
1716 : * privileges are specified now. Since the all_privs flag only determines
1717 : * whether a warning is issued, this seems close enough.
1718 : */
1719 : col_privileges =
1720 27994 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
1721 : (col_privileges == ACL_ALL_RIGHTS_COLUMN),
1722 : col_privileges,
1723 : relOid, grantorId, OBJECT_COLUMN,
1724 : relname, attnum,
1725 27994 : NameStr(pg_attribute_tuple->attname));
1726 :
1727 : /*
1728 : * Generate new ACL.
1729 : */
1730 27994 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1731 27994 : istmt->grant_option,
1732 : istmt->behavior, istmt->grantees,
1733 : col_privileges, grantorId,
1734 : ownerId);
1735 :
1736 : /*
1737 : * We need the members of both old and new ACLs so we can correct the
1738 : * shared dependency information.
1739 : */
1740 27994 : nnewmembers = aclmembers(new_acl, &newmembers);
1741 :
1742 : /* finished building new ACL value, now insert it */
1743 :
1744 : /*
1745 : * If the updated ACL is empty, we can set attacl to null, and maybe even
1746 : * avoid an update of the pg_attribute row. This is worth testing because
1747 : * we'll come through here multiple times for any relation-level REVOKE,
1748 : * even if there were never any column GRANTs. Note we are assuming that
1749 : * the "default" ACL state for columns is empty.
1750 : */
1751 27994 : if (ACL_NUM(new_acl) > 0)
1752 : {
1753 1597 : values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
1754 1597 : need_update = true;
1755 : }
1756 : else
1757 : {
1758 26397 : nulls[Anum_pg_attribute_attacl - 1] = true;
1759 26397 : need_update = !isNull;
1760 : }
1761 27994 : replaces[Anum_pg_attribute_attacl - 1] = true;
1762 :
1763 27994 : if (need_update)
1764 : {
1765 1663 : newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
1766 : values, nulls, replaces);
1767 :
1768 1663 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
1769 :
1770 : /* Update initial privileges for extensions */
1771 1663 : recordExtensionInitPriv(relOid, RelationRelationId, attnum,
1772 1663 : ACL_NUM(new_acl) > 0 ? new_acl : NULL);
1773 :
1774 : /* Update the shared dependency ACL info */
1775 1663 : updateAclDependencies(RelationRelationId, relOid, attnum,
1776 : ownerId,
1777 : noldmembers, oldmembers,
1778 : nnewmembers, newmembers);
1779 : }
1780 :
1781 27994 : pfree(new_acl);
1782 :
1783 27994 : ReleaseSysCache(attr_tuple);
1784 27994 : }
1785 :
1786 : /*
1787 : * This processes both sequences and non-sequences.
1788 : */
1789 : static void
1790 11240 : ExecGrant_Relation(InternalGrant *istmt)
1791 : {
1792 : Relation relation;
1793 : Relation attRelation;
1794 : ListCell *cell;
1795 :
1796 11240 : relation = table_open(RelationRelationId, RowExclusiveLock);
1797 11240 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
1798 :
1799 22512 : foreach(cell, istmt->objects)
1800 : {
1801 11287 : Oid relOid = lfirst_oid(cell);
1802 : Datum aclDatum;
1803 : Form_pg_class pg_class_tuple;
1804 : bool isNull;
1805 : AclMode this_privileges;
1806 : AclMode *col_privileges;
1807 : int num_col_privileges;
1808 : bool have_col_privileges;
1809 : Acl *old_acl;
1810 : Acl *old_rel_acl;
1811 : int noldmembers;
1812 : Oid *oldmembers;
1813 : Oid ownerId;
1814 : HeapTuple tuple;
1815 : ListCell *cell_colprivs;
1816 :
1817 11287 : tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relOid));
1818 11287 : if (!HeapTupleIsValid(tuple))
1819 1 : elog(ERROR, "cache lookup failed for relation %u", relOid);
1820 11286 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
1821 :
1822 : /* Not sensible to grant on an index */
1823 11286 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
1824 11286 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
1825 0 : ereport(ERROR,
1826 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1827 : errmsg("\"%s\" is an index",
1828 : NameStr(pg_class_tuple->relname))));
1829 :
1830 : /* Composite types aren't tables either */
1831 11286 : if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
1832 0 : ereport(ERROR,
1833 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1834 : errmsg("\"%s\" is a composite type",
1835 : NameStr(pg_class_tuple->relname))));
1836 :
1837 : /* Used GRANT SEQUENCE on a non-sequence? */
1838 11286 : if (istmt->objtype == OBJECT_SEQUENCE &&
1839 18 : pg_class_tuple->relkind != RELKIND_SEQUENCE)
1840 0 : ereport(ERROR,
1841 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1842 : errmsg("\"%s\" is not a sequence",
1843 : NameStr(pg_class_tuple->relname))));
1844 :
1845 11286 : if (istmt->objtype == OBJECT_PROPGRAPH &&
1846 8 : pg_class_tuple->relkind != RELKIND_PROPGRAPH)
1847 0 : ereport(ERROR,
1848 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1849 : errmsg("\"%s\" is not a property graph",
1850 : NameStr(pg_class_tuple->relname))));
1851 :
1852 : /* Adjust the default permissions based on object type */
1853 11286 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1854 : {
1855 1279 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1856 52 : this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
1857 1227 : else if (pg_class_tuple->relkind == RELKIND_PROPGRAPH)
1858 3 : this_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
1859 : else
1860 1224 : this_privileges = ACL_ALL_RIGHTS_RELATION;
1861 : }
1862 : else
1863 10007 : this_privileges = istmt->privileges;
1864 :
1865 : /*
1866 : * The GRANT TABLE syntax can be used for sequences and non-sequences,
1867 : * so we have to look at the relkind to determine the supported
1868 : * permissions. The OR of table and sequence permissions were already
1869 : * checked.
1870 : */
1871 11286 : if (istmt->objtype == OBJECT_TABLE)
1872 : {
1873 11260 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1874 : {
1875 : /*
1876 : * For backward compatibility, just throw a warning for
1877 : * invalid sequence permissions when using the non-sequence
1878 : * GRANT syntax.
1879 : */
1880 98 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
1881 : {
1882 : /*
1883 : * Mention the object name because the user needs to know
1884 : * which operations succeeded. This is required because
1885 : * WARNING allows the command to continue.
1886 : */
1887 0 : ereport(WARNING,
1888 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1889 : errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
1890 : NameStr(pg_class_tuple->relname))));
1891 0 : this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
1892 : }
1893 : }
1894 : else
1895 : {
1896 11162 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
1897 : {
1898 : /*
1899 : * USAGE is the only permission supported by sequences but
1900 : * not by non-sequences. Don't mention the object name
1901 : * because we didn't in the combined TABLE | SEQUENCE
1902 : * check.
1903 : */
1904 0 : ereport(ERROR,
1905 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1906 : errmsg("invalid privilege type %s for table",
1907 : "USAGE")));
1908 : }
1909 : }
1910 : }
1911 :
1912 : /*
1913 : * Set up array in which we'll accumulate any column privilege bits
1914 : * that need modification. The array is indexed such that entry [0]
1915 : * corresponds to FirstLowInvalidHeapAttributeNumber.
1916 : */
1917 11286 : num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
1918 11286 : col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode));
1919 11286 : have_col_privileges = false;
1920 :
1921 : /*
1922 : * If we are revoking relation privileges that are also column
1923 : * privileges, we must implicitly revoke them from each column too,
1924 : * per SQL spec. (We don't need to implicitly add column privileges
1925 : * during GRANT because the permissions-checking code always checks
1926 : * both relation and per-column privileges.)
1927 : */
1928 11286 : if (!istmt->is_grant &&
1929 3554 : (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
1930 : {
1931 3505 : expand_all_col_privileges(relOid, pg_class_tuple,
1932 : this_privileges & ACL_ALL_RIGHTS_COLUMN,
1933 : col_privileges,
1934 : num_col_privileges);
1935 3505 : have_col_privileges = true;
1936 : }
1937 :
1938 : /*
1939 : * Get owner ID and working copy of existing ACL. If there's no ACL,
1940 : * substitute the proper default.
1941 : */
1942 11286 : ownerId = pg_class_tuple->relowner;
1943 11286 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
1944 : &isNull);
1945 11286 : if (isNull)
1946 : {
1947 5821 : switch (pg_class_tuple->relkind)
1948 : {
1949 63 : case RELKIND_SEQUENCE:
1950 63 : old_acl = acldefault(OBJECT_SEQUENCE, ownerId);
1951 63 : break;
1952 22 : case RELKIND_PROPGRAPH:
1953 22 : old_acl = acldefault(OBJECT_PROPGRAPH, ownerId);
1954 22 : break;
1955 5736 : default:
1956 5736 : old_acl = acldefault(OBJECT_TABLE, ownerId);
1957 5736 : break;
1958 : }
1959 : /* There are no old member roles according to the catalogs */
1960 5821 : noldmembers = 0;
1961 5821 : oldmembers = NULL;
1962 : }
1963 : else
1964 : {
1965 5465 : old_acl = DatumGetAclPCopy(aclDatum);
1966 : /* Get the roles mentioned in the existing ACL */
1967 5465 : noldmembers = aclmembers(old_acl, &oldmembers);
1968 : }
1969 :
1970 : /* Need an extra copy of original rel ACL for column handling */
1971 11286 : old_rel_acl = aclcopy(old_acl);
1972 :
1973 : /*
1974 : * Handle relation-level privileges, if any were specified
1975 : */
1976 11286 : if (this_privileges != ACL_NO_RIGHTS)
1977 : {
1978 : AclMode avail_goptions;
1979 : Acl *new_acl;
1980 : Oid grantorId;
1981 : HeapTuple newtuple;
1982 10995 : Datum values[Natts_pg_class] = {0};
1983 10995 : bool nulls[Natts_pg_class] = {0};
1984 10995 : bool replaces[Natts_pg_class] = {0};
1985 : int nnewmembers;
1986 : Oid *newmembers;
1987 : ObjectType objtype;
1988 :
1989 : /* Determine ID to do the grant as, and available grant options */
1990 10995 : select_best_grantor(istmt->grantor, this_privileges,
1991 : old_acl, ownerId,
1992 : &grantorId, &avail_goptions);
1993 :
1994 10991 : switch (pg_class_tuple->relkind)
1995 : {
1996 116 : case RELKIND_SEQUENCE:
1997 116 : objtype = OBJECT_SEQUENCE;
1998 116 : break;
1999 10875 : default:
2000 10875 : objtype = OBJECT_TABLE;
2001 10875 : break;
2002 : }
2003 :
2004 : /*
2005 : * Restrict the privileges to what we can actually grant, and emit
2006 : * the standards-mandated warning and error messages.
2007 : */
2008 : this_privileges =
2009 10991 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2010 10991 : istmt->all_privs, this_privileges,
2011 : relOid, grantorId, objtype,
2012 10991 : NameStr(pg_class_tuple->relname),
2013 : 0, NULL);
2014 :
2015 : /*
2016 : * Generate new ACL.
2017 : */
2018 10987 : new_acl = merge_acl_with_grant(old_acl,
2019 10987 : istmt->is_grant,
2020 10987 : istmt->grant_option,
2021 : istmt->behavior,
2022 : istmt->grantees,
2023 : this_privileges,
2024 : grantorId,
2025 : ownerId);
2026 :
2027 : /*
2028 : * We need the members of both old and new ACLs so we can correct
2029 : * the shared dependency information.
2030 : */
2031 10983 : nnewmembers = aclmembers(new_acl, &newmembers);
2032 :
2033 : /* finished building new ACL value, now insert it */
2034 10983 : replaces[Anum_pg_class_relacl - 1] = true;
2035 10983 : values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
2036 :
2037 10983 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2038 : values, nulls, replaces);
2039 :
2040 10983 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2041 10981 : UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
2042 :
2043 : /* Update initial privileges for extensions */
2044 10981 : recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
2045 :
2046 : /* Update the shared dependency ACL info */
2047 10981 : updateAclDependencies(RelationRelationId, relOid, 0,
2048 : ownerId,
2049 : noldmembers, oldmembers,
2050 : nnewmembers, newmembers);
2051 :
2052 10981 : pfree(new_acl);
2053 : }
2054 : else
2055 291 : UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
2056 :
2057 : /*
2058 : * Handle column-level privileges, if any were specified or implied.
2059 : * We first expand the user-specified column privileges into the
2060 : * array, and then iterate over all nonempty array entries.
2061 : */
2062 11575 : foreach(cell_colprivs, istmt->col_privs)
2063 : {
2064 303 : AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
2065 :
2066 303 : if (col_privs->priv_name == NULL)
2067 12 : this_privileges = ACL_ALL_RIGHTS_COLUMN;
2068 : else
2069 291 : this_privileges = string_to_privilege(col_privs->priv_name);
2070 :
2071 303 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
2072 0 : ereport(ERROR,
2073 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2074 : errmsg("invalid privilege type %s for column",
2075 : privilege_to_string(this_privileges))));
2076 :
2077 303 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
2078 0 : this_privileges & ~((AclMode) ACL_SELECT))
2079 : {
2080 : /*
2081 : * The only column privilege allowed on sequences is SELECT.
2082 : * This is a warning not error because we do it that way for
2083 : * relation-level privileges.
2084 : */
2085 0 : ereport(WARNING,
2086 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2087 : errmsg("sequence \"%s\" only supports SELECT column privileges",
2088 : NameStr(pg_class_tuple->relname))));
2089 :
2090 0 : this_privileges &= (AclMode) ACL_SELECT;
2091 : }
2092 :
2093 303 : expand_col_privileges(col_privs->cols, relOid,
2094 : this_privileges,
2095 : col_privileges,
2096 : num_col_privileges);
2097 303 : have_col_privileges = true;
2098 : }
2099 :
2100 11272 : if (have_col_privileges)
2101 : {
2102 : AttrNumber i;
2103 :
2104 45552 : for (i = 0; i < num_col_privileges; i++)
2105 : {
2106 41756 : if (col_privileges[i] == ACL_NO_RIGHTS)
2107 13762 : continue;
2108 27994 : ExecGrant_Attribute(istmt,
2109 : relOid,
2110 27994 : NameStr(pg_class_tuple->relname),
2111 27994 : i + FirstLowInvalidHeapAttributeNumber,
2112 : ownerId,
2113 27994 : col_privileges[i],
2114 : attRelation,
2115 : old_rel_acl);
2116 : }
2117 : }
2118 :
2119 11272 : pfree(old_rel_acl);
2120 11272 : pfree(col_privileges);
2121 :
2122 11272 : ReleaseSysCache(tuple);
2123 :
2124 : /* prevent error when processing duplicate objects */
2125 11272 : CommandCounterIncrement();
2126 : }
2127 :
2128 11225 : table_close(attRelation, RowExclusiveLock);
2129 11225 : table_close(relation, RowExclusiveLock);
2130 11225 : }
2131 :
2132 : static void
2133 1350 : ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
2134 : void (*object_check) (InternalGrant *istmt, HeapTuple tuple))
2135 : {
2136 : SysCacheIdentifier cacheid;
2137 : Relation relation;
2138 : ListCell *cell;
2139 :
2140 1350 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
2141 474 : istmt->privileges = default_privs;
2142 :
2143 1350 : cacheid = get_object_catcache_oid(classid);
2144 :
2145 1350 : relation = table_open(classid, RowExclusiveLock);
2146 :
2147 2763 : foreach(cell, istmt->objects)
2148 : {
2149 1449 : Oid objectid = lfirst_oid(cell);
2150 : Datum aclDatum;
2151 : Datum nameDatum;
2152 : bool isNull;
2153 : AclMode avail_goptions;
2154 : AclMode this_privileges;
2155 : Acl *old_acl;
2156 : Acl *new_acl;
2157 : Oid grantorId;
2158 : Oid ownerId;
2159 : HeapTuple tuple;
2160 : HeapTuple newtuple;
2161 1449 : Datum *values = palloc0_array(Datum, RelationGetDescr(relation)->natts);
2162 1449 : bool *nulls = palloc0_array(bool, RelationGetDescr(relation)->natts);
2163 1449 : bool *replaces = palloc0_array(bool, RelationGetDescr(relation)->natts);
2164 : int noldmembers;
2165 : int nnewmembers;
2166 : Oid *oldmembers;
2167 : Oid *newmembers;
2168 :
2169 1449 : tuple = SearchSysCacheLocked1(cacheid, ObjectIdGetDatum(objectid));
2170 1449 : if (!HeapTupleIsValid(tuple))
2171 0 : elog(ERROR, "cache lookup failed for %s %u", get_object_class_descr(classid), objectid);
2172 :
2173 : /*
2174 : * Additional object-type-specific checks
2175 : */
2176 1449 : if (object_check)
2177 119 : object_check(istmt, tuple);
2178 :
2179 : /*
2180 : * Get owner ID and working copy of existing ACL. If there's no ACL,
2181 : * substitute the proper default.
2182 : */
2183 1437 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
2184 : tuple,
2185 1437 : get_object_attnum_owner(classid)));
2186 1437 : aclDatum = SysCacheGetAttr(cacheid,
2187 : tuple,
2188 1437 : get_object_attnum_acl(classid),
2189 : &isNull);
2190 1437 : if (isNull)
2191 : {
2192 854 : old_acl = acldefault(get_object_type(classid, objectid), ownerId);
2193 : /* There are no old member roles according to the catalogs */
2194 854 : noldmembers = 0;
2195 854 : oldmembers = NULL;
2196 : }
2197 : else
2198 : {
2199 583 : old_acl = DatumGetAclPCopy(aclDatum);
2200 : /* Get the roles mentioned in the existing ACL */
2201 583 : noldmembers = aclmembers(old_acl, &oldmembers);
2202 : }
2203 :
2204 : /* Determine ID to do the grant as, and available grant options */
2205 1437 : select_best_grantor(istmt->grantor, istmt->privileges,
2206 : old_acl, ownerId,
2207 : &grantorId, &avail_goptions);
2208 :
2209 1437 : nameDatum = SysCacheGetAttrNotNull(cacheid, tuple,
2210 1437 : get_object_attnum_name(classid));
2211 :
2212 : /*
2213 : * Restrict the privileges to what we can actually grant, and emit the
2214 : * standards-mandated warning and error messages.
2215 : */
2216 : this_privileges =
2217 2874 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2218 1437 : istmt->all_privs, istmt->privileges,
2219 : objectid, grantorId, get_object_type(classid, objectid),
2220 1437 : NameStr(*DatumGetName(nameDatum)),
2221 : 0, NULL);
2222 :
2223 : /*
2224 : * Generate new ACL.
2225 : */
2226 1417 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2227 1417 : istmt->grant_option, istmt->behavior,
2228 : istmt->grantees, this_privileges,
2229 : grantorId, ownerId);
2230 :
2231 : /*
2232 : * We need the members of both old and new ACLs so we can correct the
2233 : * shared dependency information.
2234 : */
2235 1413 : nnewmembers = aclmembers(new_acl, &newmembers);
2236 :
2237 : /* finished building new ACL value, now insert it */
2238 1413 : replaces[get_object_attnum_acl(classid) - 1] = true;
2239 1413 : values[get_object_attnum_acl(classid) - 1] = PointerGetDatum(new_acl);
2240 :
2241 1413 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
2242 : nulls, replaces);
2243 :
2244 1413 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2245 1413 : UnlockTuple(relation, &tuple->t_self, InplaceUpdateTupleLock);
2246 :
2247 : /* Update initial privileges for extensions */
2248 1413 : recordExtensionInitPriv(objectid, classid, 0, new_acl);
2249 :
2250 : /* Update the shared dependency ACL info */
2251 1413 : updateAclDependencies(classid,
2252 : objectid, 0,
2253 : ownerId,
2254 : noldmembers, oldmembers,
2255 : nnewmembers, newmembers);
2256 :
2257 1413 : ReleaseSysCache(tuple);
2258 :
2259 1413 : pfree(new_acl);
2260 :
2261 : /* prevent error when processing duplicate objects */
2262 1413 : CommandCounterIncrement();
2263 : }
2264 :
2265 1314 : table_close(relation, RowExclusiveLock);
2266 1314 : }
2267 :
2268 : static void
2269 27 : ExecGrant_Language_check(InternalGrant *istmt, HeapTuple tuple)
2270 : {
2271 : Form_pg_language pg_language_tuple;
2272 :
2273 27 : pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
2274 :
2275 27 : if (!pg_language_tuple->lanpltrusted)
2276 4 : ereport(ERROR,
2277 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2278 : errmsg("language \"%s\" is not trusted",
2279 : NameStr(pg_language_tuple->lanname)),
2280 : errdetail("GRANT and REVOKE are not allowed on untrusted languages, "
2281 : "because only superusers can use untrusted languages.")));
2282 23 : }
2283 :
2284 : static void
2285 58 : ExecGrant_Largeobject(InternalGrant *istmt)
2286 : {
2287 : Relation relation;
2288 : ListCell *cell;
2289 :
2290 58 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
2291 29 : istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
2292 :
2293 58 : relation = table_open(LargeObjectMetadataRelationId,
2294 : RowExclusiveLock);
2295 :
2296 120 : foreach(cell, istmt->objects)
2297 : {
2298 62 : Oid loid = lfirst_oid(cell);
2299 : Form_pg_largeobject_metadata form_lo_meta;
2300 : char loname[NAMEDATALEN];
2301 : Datum aclDatum;
2302 : bool isNull;
2303 : AclMode avail_goptions;
2304 : AclMode this_privileges;
2305 : Acl *old_acl;
2306 : Acl *new_acl;
2307 : Oid grantorId;
2308 : Oid ownerId;
2309 : HeapTuple newtuple;
2310 62 : Datum values[Natts_pg_largeobject_metadata] = {0};
2311 62 : bool nulls[Natts_pg_largeobject_metadata] = {0};
2312 62 : bool replaces[Natts_pg_largeobject_metadata] = {0};
2313 : int noldmembers;
2314 : int nnewmembers;
2315 : Oid *oldmembers;
2316 : Oid *newmembers;
2317 : ScanKeyData entry[1];
2318 : SysScanDesc scan;
2319 : HeapTuple tuple;
2320 :
2321 : /* There's no syscache for pg_largeobject_metadata */
2322 62 : ScanKeyInit(&entry[0],
2323 : Anum_pg_largeobject_metadata_oid,
2324 : BTEqualStrategyNumber, F_OIDEQ,
2325 : ObjectIdGetDatum(loid));
2326 :
2327 62 : scan = systable_beginscan(relation,
2328 : LargeObjectMetadataOidIndexId, true,
2329 : NULL, 1, entry);
2330 :
2331 62 : tuple = systable_getnext(scan);
2332 62 : if (!HeapTupleIsValid(tuple))
2333 0 : elog(ERROR, "could not find tuple for large object %u", loid);
2334 :
2335 62 : form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
2336 :
2337 : /*
2338 : * Get owner ID and working copy of existing ACL. If there's no ACL,
2339 : * substitute the proper default.
2340 : */
2341 62 : ownerId = form_lo_meta->lomowner;
2342 62 : aclDatum = heap_getattr(tuple,
2343 : Anum_pg_largeobject_metadata_lomacl,
2344 : RelationGetDescr(relation), &isNull);
2345 62 : if (isNull)
2346 : {
2347 38 : old_acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
2348 : /* There are no old member roles according to the catalogs */
2349 38 : noldmembers = 0;
2350 38 : oldmembers = NULL;
2351 : }
2352 : else
2353 : {
2354 24 : old_acl = DatumGetAclPCopy(aclDatum);
2355 : /* Get the roles mentioned in the existing ACL */
2356 24 : noldmembers = aclmembers(old_acl, &oldmembers);
2357 : }
2358 :
2359 : /* Determine ID to do the grant as, and available grant options */
2360 62 : select_best_grantor(istmt->grantor, istmt->privileges,
2361 : old_acl, ownerId,
2362 : &grantorId, &avail_goptions);
2363 :
2364 : /*
2365 : * Restrict the privileges to what we can actually grant, and emit the
2366 : * standards-mandated warning and error messages.
2367 : */
2368 62 : snprintf(loname, sizeof(loname), "large object %u", loid);
2369 : this_privileges =
2370 62 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2371 62 : istmt->all_privs, istmt->privileges,
2372 : loid, grantorId, OBJECT_LARGEOBJECT,
2373 : loname, 0, NULL);
2374 :
2375 : /*
2376 : * Generate new ACL.
2377 : */
2378 62 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2379 62 : istmt->grant_option, istmt->behavior,
2380 : istmt->grantees, this_privileges,
2381 : grantorId, ownerId);
2382 :
2383 : /*
2384 : * We need the members of both old and new ACLs so we can correct the
2385 : * shared dependency information.
2386 : */
2387 62 : nnewmembers = aclmembers(new_acl, &newmembers);
2388 :
2389 : /* finished building new ACL value, now insert it */
2390 62 : replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
2391 : values[Anum_pg_largeobject_metadata_lomacl - 1]
2392 62 : = PointerGetDatum(new_acl);
2393 :
2394 62 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2395 : values, nulls, replaces);
2396 :
2397 62 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2398 :
2399 : /* Update initial privileges for extensions */
2400 62 : recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
2401 :
2402 : /* Update the shared dependency ACL info */
2403 62 : updateAclDependencies(LargeObjectRelationId,
2404 : form_lo_meta->oid, 0,
2405 : ownerId,
2406 : noldmembers, oldmembers,
2407 : nnewmembers, newmembers);
2408 :
2409 62 : systable_endscan(scan);
2410 :
2411 62 : pfree(new_acl);
2412 :
2413 : /* prevent error when processing duplicate objects */
2414 62 : CommandCounterIncrement();
2415 : }
2416 :
2417 58 : table_close(relation, RowExclusiveLock);
2418 58 : }
2419 :
2420 : static void
2421 92 : ExecGrant_Type_check(InternalGrant *istmt, HeapTuple tuple)
2422 : {
2423 : Form_pg_type pg_type_tuple;
2424 :
2425 92 : pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple);
2426 :
2427 : /* Disallow GRANT on dependent types */
2428 92 : if (IsTrueArrayType(pg_type_tuple))
2429 4 : ereport(ERROR,
2430 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2431 : errmsg("cannot set privileges of array types"),
2432 : errhint("Set the privileges of the element type instead.")));
2433 88 : if (pg_type_tuple->typtype == TYPTYPE_MULTIRANGE)
2434 4 : ereport(ERROR,
2435 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2436 : errmsg("cannot set privileges of multirange types"),
2437 : errhint("Set the privileges of the range type instead.")));
2438 84 : }
2439 :
2440 : static void
2441 49 : ExecGrant_Parameter(InternalGrant *istmt)
2442 : {
2443 : Relation relation;
2444 : ListCell *cell;
2445 :
2446 49 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
2447 21 : istmt->privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
2448 :
2449 49 : relation = table_open(ParameterAclRelationId, RowExclusiveLock);
2450 :
2451 117 : foreach(cell, istmt->objects)
2452 : {
2453 68 : Oid parameterId = lfirst_oid(cell);
2454 : Datum nameDatum;
2455 : const char *parname;
2456 : Datum aclDatum;
2457 : bool isNull;
2458 : AclMode avail_goptions;
2459 : AclMode this_privileges;
2460 : Acl *old_acl;
2461 : Acl *new_acl;
2462 : Oid grantorId;
2463 : Oid ownerId;
2464 : HeapTuple tuple;
2465 : int noldmembers;
2466 : int nnewmembers;
2467 : Oid *oldmembers;
2468 : Oid *newmembers;
2469 :
2470 68 : tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(parameterId));
2471 68 : if (!HeapTupleIsValid(tuple))
2472 0 : elog(ERROR, "cache lookup failed for parameter ACL %u",
2473 : parameterId);
2474 :
2475 : /* We'll need the GUC's name */
2476 68 : nameDatum = SysCacheGetAttrNotNull(PARAMETERACLOID, tuple,
2477 : Anum_pg_parameter_acl_parname);
2478 68 : parname = TextDatumGetCString(nameDatum);
2479 :
2480 : /* Treat all parameters as belonging to the bootstrap superuser. */
2481 68 : ownerId = BOOTSTRAP_SUPERUSERID;
2482 :
2483 : /*
2484 : * Get working copy of existing ACL. If there's no ACL, substitute the
2485 : * proper default.
2486 : */
2487 68 : aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
2488 : Anum_pg_parameter_acl_paracl,
2489 : &isNull);
2490 :
2491 68 : if (isNull)
2492 : {
2493 33 : old_acl = acldefault(istmt->objtype, ownerId);
2494 : /* There are no old member roles according to the catalogs */
2495 33 : noldmembers = 0;
2496 33 : oldmembers = NULL;
2497 : }
2498 : else
2499 : {
2500 35 : old_acl = DatumGetAclPCopy(aclDatum);
2501 : /* Get the roles mentioned in the existing ACL */
2502 35 : noldmembers = aclmembers(old_acl, &oldmembers);
2503 : }
2504 :
2505 : /* Determine ID to do the grant as, and available grant options */
2506 68 : select_best_grantor(istmt->grantor, istmt->privileges,
2507 : old_acl, ownerId,
2508 : &grantorId, &avail_goptions);
2509 :
2510 : /*
2511 : * Restrict the privileges to what we can actually grant, and emit the
2512 : * standards-mandated warning and error messages.
2513 : */
2514 : this_privileges =
2515 68 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2516 68 : istmt->all_privs, istmt->privileges,
2517 : parameterId, grantorId,
2518 : OBJECT_PARAMETER_ACL,
2519 : parname,
2520 : 0, NULL);
2521 :
2522 : /*
2523 : * Generate new ACL.
2524 : */
2525 68 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2526 68 : istmt->grant_option, istmt->behavior,
2527 : istmt->grantees, this_privileges,
2528 : grantorId, ownerId);
2529 :
2530 : /*
2531 : * We need the members of both old and new ACLs so we can correct the
2532 : * shared dependency information.
2533 : */
2534 68 : nnewmembers = aclmembers(new_acl, &newmembers);
2535 :
2536 : /*
2537 : * If the new ACL is equal to the default, we don't need the catalog
2538 : * entry any longer. Delete it rather than updating it, to avoid
2539 : * leaving a degenerate entry.
2540 : */
2541 68 : if (aclequal(new_acl, acldefault(istmt->objtype, ownerId)))
2542 : {
2543 30 : CatalogTupleDelete(relation, &tuple->t_self);
2544 : }
2545 : else
2546 : {
2547 : /* finished building new ACL value, now insert it */
2548 : HeapTuple newtuple;
2549 38 : Datum values[Natts_pg_parameter_acl] = {0};
2550 38 : bool nulls[Natts_pg_parameter_acl] = {0};
2551 38 : bool replaces[Natts_pg_parameter_acl] = {0};
2552 :
2553 38 : replaces[Anum_pg_parameter_acl_paracl - 1] = true;
2554 38 : values[Anum_pg_parameter_acl_paracl - 1] = PointerGetDatum(new_acl);
2555 :
2556 38 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2557 : values, nulls, replaces);
2558 :
2559 38 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2560 : }
2561 :
2562 : /* Update initial privileges for extensions */
2563 68 : recordExtensionInitPriv(parameterId, ParameterAclRelationId, 0,
2564 : new_acl);
2565 :
2566 : /* Update the shared dependency ACL info */
2567 68 : updateAclDependencies(ParameterAclRelationId, parameterId, 0,
2568 : ownerId,
2569 : noldmembers, oldmembers,
2570 : nnewmembers, newmembers);
2571 :
2572 68 : ReleaseSysCache(tuple);
2573 68 : pfree(new_acl);
2574 :
2575 : /* prevent error when processing duplicate objects */
2576 68 : CommandCounterIncrement();
2577 : }
2578 :
2579 49 : table_close(relation, RowExclusiveLock);
2580 49 : }
2581 :
2582 :
2583 : static AclMode
2584 11391 : string_to_privilege(const char *privname)
2585 : {
2586 11391 : if (strcmp(privname, "insert") == 0)
2587 161 : return ACL_INSERT;
2588 11230 : if (strcmp(privname, "select") == 0)
2589 9648 : return ACL_SELECT;
2590 1582 : if (strcmp(privname, "update") == 0)
2591 298 : return ACL_UPDATE;
2592 1284 : if (strcmp(privname, "delete") == 0)
2593 91 : return ACL_DELETE;
2594 1193 : if (strcmp(privname, "truncate") == 0)
2595 29 : return ACL_TRUNCATE;
2596 1164 : if (strcmp(privname, "references") == 0)
2597 9 : return ACL_REFERENCES;
2598 1155 : if (strcmp(privname, "trigger") == 0)
2599 5 : return ACL_TRIGGER;
2600 1150 : if (strcmp(privname, "execute") == 0)
2601 266 : return ACL_EXECUTE;
2602 884 : if (strcmp(privname, "usage") == 0)
2603 459 : return ACL_USAGE;
2604 425 : if (strcmp(privname, "create") == 0)
2605 206 : return ACL_CREATE;
2606 219 : if (strcmp(privname, "temporary") == 0)
2607 120 : return ACL_CREATE_TEMP;
2608 99 : if (strcmp(privname, "temp") == 0)
2609 1 : return ACL_CREATE_TEMP;
2610 98 : if (strcmp(privname, "connect") == 0)
2611 24 : return ACL_CONNECT;
2612 74 : if (strcmp(privname, "set") == 0)
2613 25 : return ACL_SET;
2614 49 : if (strcmp(privname, "alter system") == 0)
2615 12 : return ACL_ALTER_SYSTEM;
2616 37 : if (strcmp(privname, "maintain") == 0)
2617 37 : return ACL_MAINTAIN;
2618 0 : ereport(ERROR,
2619 : (errcode(ERRCODE_SYNTAX_ERROR),
2620 : errmsg("unrecognized privilege type \"%s\"", privname)));
2621 : return 0; /* appease compiler */
2622 : }
2623 :
2624 : static const char *
2625 20 : privilege_to_string(AclMode privilege)
2626 : {
2627 20 : switch (privilege)
2628 : {
2629 4 : case ACL_INSERT:
2630 4 : return "INSERT";
2631 0 : case ACL_SELECT:
2632 0 : return "SELECT";
2633 4 : case ACL_UPDATE:
2634 4 : return "UPDATE";
2635 0 : case ACL_DELETE:
2636 0 : return "DELETE";
2637 0 : case ACL_TRUNCATE:
2638 0 : return "TRUNCATE";
2639 0 : case ACL_REFERENCES:
2640 0 : return "REFERENCES";
2641 0 : case ACL_TRIGGER:
2642 0 : return "TRIGGER";
2643 0 : case ACL_EXECUTE:
2644 0 : return "EXECUTE";
2645 12 : case ACL_USAGE:
2646 12 : return "USAGE";
2647 0 : case ACL_CREATE:
2648 0 : return "CREATE";
2649 0 : case ACL_CREATE_TEMP:
2650 0 : return "TEMP";
2651 0 : case ACL_CONNECT:
2652 0 : return "CONNECT";
2653 0 : case ACL_SET:
2654 0 : return "SET";
2655 0 : case ACL_ALTER_SYSTEM:
2656 0 : return "ALTER SYSTEM";
2657 0 : case ACL_MAINTAIN:
2658 0 : return "MAINTAIN";
2659 0 : default:
2660 0 : elog(ERROR, "unrecognized privilege: %d", (int) privilege);
2661 : }
2662 : return NULL; /* appease compiler */
2663 : }
2664 :
2665 : /*
2666 : * Standardized reporting of aclcheck permissions failures.
2667 : *
2668 : * Note: we do not double-quote the %s's below, because many callers
2669 : * supply strings that might be already quoted.
2670 : */
2671 : void
2672 1920 : aclcheck_error(AclResult aclerr, ObjectType objtype,
2673 : const char *objectname)
2674 : {
2675 1920 : switch (aclerr)
2676 : {
2677 0 : case ACLCHECK_OK:
2678 : /* no error, so return to caller */
2679 0 : break;
2680 1546 : case ACLCHECK_NO_PRIV:
2681 : {
2682 1546 : const char *msg = "???";
2683 :
2684 1546 : switch (objtype)
2685 : {
2686 4 : case OBJECT_AGGREGATE:
2687 4 : msg = gettext_noop("permission denied for aggregate %s");
2688 4 : break;
2689 0 : case OBJECT_COLLATION:
2690 0 : msg = gettext_noop("permission denied for collation %s");
2691 0 : break;
2692 0 : case OBJECT_COLUMN:
2693 0 : msg = gettext_noop("permission denied for column %s");
2694 0 : break;
2695 0 : case OBJECT_CONVERSION:
2696 0 : msg = gettext_noop("permission denied for conversion %s");
2697 0 : break;
2698 13 : case OBJECT_DATABASE:
2699 13 : msg = gettext_noop("permission denied for database %s");
2700 13 : break;
2701 0 : case OBJECT_DOMAIN:
2702 0 : msg = gettext_noop("permission denied for domain %s");
2703 0 : break;
2704 0 : case OBJECT_EVENT_TRIGGER:
2705 0 : msg = gettext_noop("permission denied for event trigger %s");
2706 0 : break;
2707 0 : case OBJECT_EXTENSION:
2708 0 : msg = gettext_noop("permission denied for extension %s");
2709 0 : break;
2710 29 : case OBJECT_FDW:
2711 29 : msg = gettext_noop("permission denied for foreign-data wrapper %s");
2712 29 : break;
2713 17 : case OBJECT_FOREIGN_SERVER:
2714 17 : msg = gettext_noop("permission denied for foreign server %s");
2715 17 : break;
2716 1 : case OBJECT_FOREIGN_TABLE:
2717 1 : msg = gettext_noop("permission denied for foreign table %s");
2718 1 : break;
2719 61 : case OBJECT_FUNCTION:
2720 61 : msg = gettext_noop("permission denied for function %s");
2721 61 : break;
2722 9 : case OBJECT_INDEX:
2723 9 : msg = gettext_noop("permission denied for index %s");
2724 9 : break;
2725 5 : case OBJECT_LANGUAGE:
2726 5 : msg = gettext_noop("permission denied for language %s");
2727 5 : break;
2728 0 : case OBJECT_LARGEOBJECT:
2729 0 : msg = gettext_noop("permission denied for large object %s");
2730 0 : break;
2731 4 : case OBJECT_MATVIEW:
2732 4 : msg = gettext_noop("permission denied for materialized view %s");
2733 4 : break;
2734 0 : case OBJECT_OPCLASS:
2735 0 : msg = gettext_noop("permission denied for operator class %s");
2736 0 : break;
2737 0 : case OBJECT_OPERATOR:
2738 0 : msg = gettext_noop("permission denied for operator %s");
2739 0 : break;
2740 0 : case OBJECT_OPFAMILY:
2741 0 : msg = gettext_noop("permission denied for operator family %s");
2742 0 : break;
2743 0 : case OBJECT_PARAMETER_ACL:
2744 0 : msg = gettext_noop("permission denied for parameter %s");
2745 0 : break;
2746 0 : case OBJECT_POLICY:
2747 0 : msg = gettext_noop("permission denied for policy %s");
2748 0 : break;
2749 8 : case OBJECT_PROCEDURE:
2750 8 : msg = gettext_noop("permission denied for procedure %s");
2751 8 : break;
2752 8 : case OBJECT_PROPGRAPH:
2753 8 : msg = gettext_noop("permission denied for property graph %s");
2754 8 : break;
2755 0 : case OBJECT_PUBLICATION:
2756 0 : msg = gettext_noop("permission denied for publication %s");
2757 0 : break;
2758 0 : case OBJECT_ROUTINE:
2759 0 : msg = gettext_noop("permission denied for routine %s");
2760 0 : break;
2761 29 : case OBJECT_SCHEMA:
2762 29 : msg = gettext_noop("permission denied for schema %s");
2763 29 : break;
2764 0 : case OBJECT_SEQUENCE:
2765 0 : msg = gettext_noop("permission denied for sequence %s");
2766 0 : break;
2767 0 : case OBJECT_STATISTIC_EXT:
2768 0 : msg = gettext_noop("permission denied for statistics object %s");
2769 0 : break;
2770 0 : case OBJECT_SUBSCRIPTION:
2771 0 : msg = gettext_noop("permission denied for subscription %s");
2772 0 : break;
2773 990 : case OBJECT_TABLE:
2774 990 : msg = gettext_noop("permission denied for table %s");
2775 990 : break;
2776 10 : case OBJECT_TABLESPACE:
2777 10 : msg = gettext_noop("permission denied for tablespace %s");
2778 10 : break;
2779 0 : case OBJECT_TSCONFIGURATION:
2780 0 : msg = gettext_noop("permission denied for text search configuration %s");
2781 0 : break;
2782 0 : case OBJECT_TSDICTIONARY:
2783 0 : msg = gettext_noop("permission denied for text search dictionary %s");
2784 0 : break;
2785 80 : case OBJECT_TYPE:
2786 80 : msg = gettext_noop("permission denied for type %s");
2787 80 : break;
2788 278 : case OBJECT_VIEW:
2789 278 : msg = gettext_noop("permission denied for view %s");
2790 278 : break;
2791 : /* these currently aren't used */
2792 0 : case OBJECT_ACCESS_METHOD:
2793 : case OBJECT_AMOP:
2794 : case OBJECT_AMPROC:
2795 : case OBJECT_ATTRIBUTE:
2796 : case OBJECT_CAST:
2797 : case OBJECT_DEFAULT:
2798 : case OBJECT_DEFACL:
2799 : case OBJECT_DOMCONSTRAINT:
2800 : case OBJECT_PUBLICATION_NAMESPACE:
2801 : case OBJECT_PUBLICATION_REL:
2802 : case OBJECT_ROLE:
2803 : case OBJECT_RULE:
2804 : case OBJECT_TABCONSTRAINT:
2805 : case OBJECT_TRANSFORM:
2806 : case OBJECT_TRIGGER:
2807 : case OBJECT_TSPARSER:
2808 : case OBJECT_TSTEMPLATE:
2809 : case OBJECT_USER_MAPPING:
2810 0 : elog(ERROR, "unsupported object type: %d", objtype);
2811 : }
2812 :
2813 1546 : ereport(ERROR,
2814 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2815 : errmsg(msg, objectname)));
2816 : break;
2817 : }
2818 374 : case ACLCHECK_NOT_OWNER:
2819 : {
2820 374 : const char *msg = "???";
2821 :
2822 374 : switch (objtype)
2823 : {
2824 4 : case OBJECT_AGGREGATE:
2825 4 : msg = gettext_noop("must be owner of aggregate %s");
2826 4 : break;
2827 0 : case OBJECT_COLLATION:
2828 0 : msg = gettext_noop("must be owner of collation %s");
2829 0 : break;
2830 12 : case OBJECT_CONVERSION:
2831 12 : msg = gettext_noop("must be owner of conversion %s");
2832 12 : break;
2833 0 : case OBJECT_DATABASE:
2834 0 : msg = gettext_noop("must be owner of database %s");
2835 0 : break;
2836 0 : case OBJECT_DOMAIN:
2837 0 : msg = gettext_noop("must be owner of domain %s");
2838 0 : break;
2839 0 : case OBJECT_EVENT_TRIGGER:
2840 0 : msg = gettext_noop("must be owner of event trigger %s");
2841 0 : break;
2842 0 : case OBJECT_EXTENSION:
2843 0 : msg = gettext_noop("must be owner of extension %s");
2844 0 : break;
2845 12 : case OBJECT_FDW:
2846 12 : msg = gettext_noop("must be owner of foreign-data wrapper %s");
2847 12 : break;
2848 76 : case OBJECT_FOREIGN_SERVER:
2849 76 : msg = gettext_noop("must be owner of foreign server %s");
2850 76 : break;
2851 0 : case OBJECT_FOREIGN_TABLE:
2852 0 : msg = gettext_noop("must be owner of foreign table %s");
2853 0 : break;
2854 28 : case OBJECT_FUNCTION:
2855 28 : msg = gettext_noop("must be owner of function %s");
2856 28 : break;
2857 16 : case OBJECT_INDEX:
2858 16 : msg = gettext_noop("must be owner of index %s");
2859 16 : break;
2860 8 : case OBJECT_LANGUAGE:
2861 8 : msg = gettext_noop("must be owner of language %s");
2862 8 : break;
2863 0 : case OBJECT_LARGEOBJECT:
2864 0 : msg = gettext_noop("must be owner of large object %s");
2865 0 : break;
2866 0 : case OBJECT_MATVIEW:
2867 0 : msg = gettext_noop("must be owner of materialized view %s");
2868 0 : break;
2869 12 : case OBJECT_OPCLASS:
2870 12 : msg = gettext_noop("must be owner of operator class %s");
2871 12 : break;
2872 12 : case OBJECT_OPERATOR:
2873 12 : msg = gettext_noop("must be owner of operator %s");
2874 12 : break;
2875 12 : case OBJECT_OPFAMILY:
2876 12 : msg = gettext_noop("must be owner of operator family %s");
2877 12 : break;
2878 4 : case OBJECT_PROCEDURE:
2879 4 : msg = gettext_noop("must be owner of procedure %s");
2880 4 : break;
2881 12 : case OBJECT_PROPGRAPH:
2882 12 : msg = gettext_noop("must be owner of property graph %s");
2883 12 : break;
2884 4 : case OBJECT_PUBLICATION:
2885 4 : msg = gettext_noop("must be owner of publication %s");
2886 4 : break;
2887 0 : case OBJECT_ROUTINE:
2888 0 : msg = gettext_noop("must be owner of routine %s");
2889 0 : break;
2890 4 : case OBJECT_SEQUENCE:
2891 4 : msg = gettext_noop("must be owner of sequence %s");
2892 4 : break;
2893 4 : case OBJECT_SUBSCRIPTION:
2894 4 : msg = gettext_noop("must be owner of subscription %s");
2895 4 : break;
2896 66 : case OBJECT_TABLE:
2897 66 : msg = gettext_noop("must be owner of table %s");
2898 66 : break;
2899 4 : case OBJECT_TYPE:
2900 4 : msg = gettext_noop("must be owner of type %s");
2901 4 : break;
2902 12 : case OBJECT_VIEW:
2903 12 : msg = gettext_noop("must be owner of view %s");
2904 12 : break;
2905 12 : case OBJECT_SCHEMA:
2906 12 : msg = gettext_noop("must be owner of schema %s");
2907 12 : break;
2908 24 : case OBJECT_STATISTIC_EXT:
2909 24 : msg = gettext_noop("must be owner of statistics object %s");
2910 24 : break;
2911 0 : case OBJECT_TABLESPACE:
2912 0 : msg = gettext_noop("must be owner of tablespace %s");
2913 0 : break;
2914 12 : case OBJECT_TSCONFIGURATION:
2915 12 : msg = gettext_noop("must be owner of text search configuration %s");
2916 12 : break;
2917 12 : case OBJECT_TSDICTIONARY:
2918 12 : msg = gettext_noop("must be owner of text search dictionary %s");
2919 12 : break;
2920 :
2921 : /*
2922 : * Special cases: For these, the error message talks
2923 : * about "relation", because that's where the
2924 : * ownership is attached. See also
2925 : * check_object_ownership().
2926 : */
2927 12 : case OBJECT_COLUMN:
2928 : case OBJECT_POLICY:
2929 : case OBJECT_RULE:
2930 : case OBJECT_TABCONSTRAINT:
2931 : case OBJECT_TRIGGER:
2932 12 : msg = gettext_noop("must be owner of relation %s");
2933 12 : break;
2934 : /* these currently aren't used */
2935 0 : case OBJECT_ACCESS_METHOD:
2936 : case OBJECT_AMOP:
2937 : case OBJECT_AMPROC:
2938 : case OBJECT_ATTRIBUTE:
2939 : case OBJECT_CAST:
2940 : case OBJECT_DEFAULT:
2941 : case OBJECT_DEFACL:
2942 : case OBJECT_DOMCONSTRAINT:
2943 : case OBJECT_PARAMETER_ACL:
2944 : case OBJECT_PUBLICATION_NAMESPACE:
2945 : case OBJECT_PUBLICATION_REL:
2946 : case OBJECT_ROLE:
2947 : case OBJECT_TRANSFORM:
2948 : case OBJECT_TSPARSER:
2949 : case OBJECT_TSTEMPLATE:
2950 : case OBJECT_USER_MAPPING:
2951 0 : elog(ERROR, "unsupported object type: %d", objtype);
2952 : }
2953 :
2954 374 : ereport(ERROR,
2955 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2956 : errmsg(msg, objectname)));
2957 : break;
2958 : }
2959 0 : default:
2960 0 : elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2961 : break;
2962 : }
2963 0 : }
2964 :
2965 :
2966 : void
2967 0 : aclcheck_error_col(AclResult aclerr, ObjectType objtype,
2968 : const char *objectname, const char *colname)
2969 : {
2970 0 : switch (aclerr)
2971 : {
2972 0 : case ACLCHECK_OK:
2973 : /* no error, so return to caller */
2974 0 : break;
2975 0 : case ACLCHECK_NO_PRIV:
2976 0 : ereport(ERROR,
2977 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2978 : errmsg("permission denied for column \"%s\" of relation \"%s\"",
2979 : colname, objectname)));
2980 : break;
2981 0 : case ACLCHECK_NOT_OWNER:
2982 : /* relation msg is OK since columns don't have separate owners */
2983 0 : aclcheck_error(aclerr, objtype, objectname);
2984 0 : break;
2985 0 : default:
2986 0 : elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2987 : break;
2988 : }
2989 0 : }
2990 :
2991 :
2992 : /*
2993 : * Special common handling for types: use element type instead of array type,
2994 : * and format nicely
2995 : */
2996 : void
2997 80 : aclcheck_error_type(AclResult aclerr, Oid typeOid)
2998 : {
2999 80 : Oid element_type = get_element_type(typeOid);
3000 :
3001 80 : aclcheck_error(aclerr, OBJECT_TYPE, format_type_be(element_type ? element_type : typeOid));
3002 0 : }
3003 :
3004 :
3005 : /*
3006 : * Relay for the various pg_*_mask routines depending on object kind
3007 : */
3008 : static AclMode
3009 48 : pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum, Oid roleid,
3010 : AclMode mask, AclMaskHow how)
3011 : {
3012 48 : switch (objtype)
3013 : {
3014 0 : case OBJECT_COLUMN:
3015 : return
3016 0 : pg_class_aclmask(object_oid, roleid, mask, how) |
3017 0 : pg_attribute_aclmask(object_oid, attnum, roleid, mask, how);
3018 12 : case OBJECT_TABLE:
3019 : case OBJECT_SEQUENCE:
3020 : case OBJECT_PROPGRAPH:
3021 12 : return pg_class_aclmask(object_oid, roleid, mask, how);
3022 0 : case OBJECT_DATABASE:
3023 0 : return object_aclmask(DatabaseRelationId, object_oid, roleid, mask, how);
3024 0 : case OBJECT_FUNCTION:
3025 0 : return object_aclmask(ProcedureRelationId, object_oid, roleid, mask, how);
3026 4 : case OBJECT_LANGUAGE:
3027 4 : return object_aclmask(LanguageRelationId, object_oid, roleid, mask, how);
3028 0 : case OBJECT_LARGEOBJECT:
3029 0 : return pg_largeobject_aclmask_snapshot(object_oid, roleid,
3030 : mask, how, NULL);
3031 0 : case OBJECT_PARAMETER_ACL:
3032 0 : return pg_parameter_acl_aclmask(object_oid, roleid, mask, how);
3033 0 : case OBJECT_SCHEMA:
3034 0 : return object_aclmask(NamespaceRelationId, object_oid, roleid, mask, how);
3035 0 : case OBJECT_STATISTIC_EXT:
3036 0 : elog(ERROR, "grantable rights not supported for statistics objects");
3037 : /* not reached, but keep compiler quiet */
3038 : return ACL_NO_RIGHTS;
3039 0 : case OBJECT_TABLESPACE:
3040 0 : return object_aclmask(TableSpaceRelationId, object_oid, roleid, mask, how);
3041 12 : case OBJECT_FDW:
3042 12 : return object_aclmask(ForeignDataWrapperRelationId, object_oid, roleid, mask, how);
3043 12 : case OBJECT_FOREIGN_SERVER:
3044 12 : return object_aclmask(ForeignServerRelationId, object_oid, roleid, mask, how);
3045 0 : case OBJECT_EVENT_TRIGGER:
3046 0 : elog(ERROR, "grantable rights not supported for event triggers");
3047 : /* not reached, but keep compiler quiet */
3048 : return ACL_NO_RIGHTS;
3049 8 : case OBJECT_TYPE:
3050 8 : return object_aclmask(TypeRelationId, object_oid, roleid, mask, how);
3051 0 : default:
3052 0 : elog(ERROR, "unrecognized object type: %d",
3053 : (int) objtype);
3054 : /* not reached, but keep compiler quiet */
3055 : return ACL_NO_RIGHTS;
3056 : }
3057 : }
3058 :
3059 :
3060 : /*
3061 : * ****************************************************************
3062 : * Exported routines for examining a user's privileges for various objects
3063 : *
3064 : * See aclmask() for a description of the common API for these functions.
3065 : * ****************************************************************
3066 : */
3067 :
3068 : /*
3069 : * Generic routine for examining a user's privileges for an object
3070 : */
3071 : static AclMode
3072 36 : object_aclmask(Oid classid, Oid objectid, Oid roleid,
3073 : AclMode mask, AclMaskHow how)
3074 : {
3075 36 : return object_aclmask_ext(classid, objectid, roleid, mask, how, NULL);
3076 : }
3077 :
3078 : /*
3079 : * Generic routine for examining a user's privileges for an object,
3080 : * with is_missing
3081 : */
3082 : static AclMode
3083 2093289 : object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
3084 : AclMode mask, AclMaskHow how,
3085 : bool *is_missing)
3086 : {
3087 : SysCacheIdentifier cacheid;
3088 : AclMode result;
3089 : HeapTuple tuple;
3090 : Datum aclDatum;
3091 : bool isNull;
3092 : Acl *acl;
3093 : Oid ownerId;
3094 :
3095 : /* Special cases */
3096 2093289 : switch (classid)
3097 : {
3098 635510 : case NamespaceRelationId:
3099 635510 : return pg_namespace_aclmask_ext(objectid, roleid, mask, how,
3100 : is_missing);
3101 213729 : case TypeRelationId:
3102 213729 : return pg_type_aclmask_ext(objectid, roleid, mask, how,
3103 : is_missing);
3104 : }
3105 :
3106 : /* Even more special cases */
3107 : Assert(classid != RelationRelationId); /* should use pg_class_acl* */
3108 : Assert(classid != LargeObjectMetadataRelationId); /* should use
3109 : * pg_largeobject_acl* */
3110 :
3111 : /* Superusers bypass all permission checking. */
3112 1244050 : if (superuser_arg(roleid))
3113 1212993 : return mask;
3114 :
3115 : /*
3116 : * Get the object's ACL from its catalog
3117 : */
3118 :
3119 31057 : cacheid = get_object_catcache_oid(classid);
3120 :
3121 31057 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
3122 31057 : if (!HeapTupleIsValid(tuple))
3123 : {
3124 0 : if (is_missing != NULL)
3125 : {
3126 : /* return "no privileges" instead of throwing an error */
3127 0 : *is_missing = true;
3128 0 : return 0;
3129 : }
3130 : else
3131 0 : elog(ERROR, "cache lookup failed for %s %u",
3132 : get_object_class_descr(classid), objectid);
3133 : }
3134 :
3135 31057 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
3136 : tuple,
3137 31057 : get_object_attnum_owner(classid)));
3138 :
3139 31057 : aclDatum = SysCacheGetAttr(cacheid, tuple, get_object_attnum_acl(classid),
3140 : &isNull);
3141 31057 : if (isNull)
3142 : {
3143 : /* No ACL, so build default ACL */
3144 29315 : acl = acldefault(get_object_type(classid, objectid), ownerId);
3145 29315 : aclDatum = (Datum) 0;
3146 : }
3147 : else
3148 : {
3149 : /* detoast ACL if necessary */
3150 1742 : acl = DatumGetAclP(aclDatum);
3151 : }
3152 :
3153 31057 : result = aclmask(acl, roleid, ownerId, mask, how);
3154 :
3155 : /* if we have a detoasted copy, free it */
3156 31057 : if (acl && acl != DatumGetPointer(aclDatum))
3157 31057 : pfree(acl);
3158 :
3159 31057 : ReleaseSysCache(tuple);
3160 :
3161 31057 : return result;
3162 : }
3163 :
3164 : /*
3165 : * Routine for examining a user's privileges for a column
3166 : *
3167 : * Note: this considers only privileges granted specifically on the column.
3168 : * It is caller's responsibility to take relation-level privileges into account
3169 : * as appropriate. (For the same reason, we have no special case for
3170 : * superuser-ness here.)
3171 : */
3172 : static AclMode
3173 0 : pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
3174 : AclMode mask, AclMaskHow how)
3175 : {
3176 0 : return pg_attribute_aclmask_ext(table_oid, attnum, roleid,
3177 : mask, how, NULL);
3178 : }
3179 :
3180 : /*
3181 : * Routine for examining a user's privileges for a column, with is_missing
3182 : */
3183 : static AclMode
3184 366411 : pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
3185 : AclMode mask, AclMaskHow how, bool *is_missing)
3186 : {
3187 : AclMode result;
3188 : HeapTuple classTuple;
3189 : HeapTuple attTuple;
3190 : Form_pg_class classForm;
3191 : Form_pg_attribute attributeForm;
3192 : Datum aclDatum;
3193 : bool isNull;
3194 : Acl *acl;
3195 : Oid ownerId;
3196 :
3197 : /*
3198 : * First, get the column's ACL from its pg_attribute entry
3199 : */
3200 366411 : attTuple = SearchSysCache2(ATTNUM,
3201 : ObjectIdGetDatum(table_oid),
3202 : Int16GetDatum(attnum));
3203 366411 : if (!HeapTupleIsValid(attTuple))
3204 : {
3205 20 : if (is_missing != NULL)
3206 : {
3207 : /* return "no privileges" instead of throwing an error */
3208 20 : *is_missing = true;
3209 20 : return 0;
3210 : }
3211 : else
3212 0 : ereport(ERROR,
3213 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3214 : errmsg("attribute %d of relation with OID %u does not exist",
3215 : attnum, table_oid)));
3216 : }
3217 :
3218 366391 : attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
3219 :
3220 : /* Check dropped columns, too */
3221 366391 : if (attributeForm->attisdropped)
3222 : {
3223 8 : if (is_missing != NULL)
3224 : {
3225 : /* return "no privileges" instead of throwing an error */
3226 8 : *is_missing = true;
3227 8 : ReleaseSysCache(attTuple);
3228 8 : return 0;
3229 : }
3230 : else
3231 0 : ereport(ERROR,
3232 : (errcode(ERRCODE_UNDEFINED_COLUMN),
3233 : errmsg("attribute %d of relation with OID %u does not exist",
3234 : attnum, table_oid)));
3235 : }
3236 :
3237 366383 : aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
3238 : &isNull);
3239 :
3240 : /*
3241 : * Here we hard-wire knowledge that the default ACL for a column grants no
3242 : * privileges, so that we can fall out quickly in the very common case
3243 : * where attacl is null.
3244 : */
3245 366383 : if (isNull)
3246 : {
3247 360376 : ReleaseSysCache(attTuple);
3248 360376 : return 0;
3249 : }
3250 :
3251 : /*
3252 : * Must get the relation's ownerId from pg_class. Since we already found
3253 : * a pg_attribute entry, the only likely reason for this to fail is that a
3254 : * concurrent DROP of the relation committed since then (which could only
3255 : * happen if we don't have lock on the relation). Treat that similarly to
3256 : * not finding the attribute entry.
3257 : */
3258 6007 : classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
3259 6007 : if (!HeapTupleIsValid(classTuple))
3260 : {
3261 0 : ReleaseSysCache(attTuple);
3262 0 : if (is_missing != NULL)
3263 : {
3264 : /* return "no privileges" instead of throwing an error */
3265 0 : *is_missing = true;
3266 0 : return 0;
3267 : }
3268 : else
3269 0 : ereport(ERROR,
3270 : (errcode(ERRCODE_UNDEFINED_TABLE),
3271 : errmsg("relation with OID %u does not exist",
3272 : table_oid)));
3273 : }
3274 6007 : classForm = (Form_pg_class) GETSTRUCT(classTuple);
3275 :
3276 6007 : ownerId = classForm->relowner;
3277 :
3278 6007 : ReleaseSysCache(classTuple);
3279 :
3280 : /* detoast column's ACL if necessary */
3281 6007 : acl = DatumGetAclP(aclDatum);
3282 :
3283 6007 : result = aclmask(acl, roleid, ownerId, mask, how);
3284 :
3285 : /* if we have a detoasted copy, free it */
3286 6007 : if (acl && acl != DatumGetPointer(aclDatum))
3287 6007 : pfree(acl);
3288 :
3289 6007 : ReleaseSysCache(attTuple);
3290 :
3291 6007 : return result;
3292 : }
3293 :
3294 : /*
3295 : * Exported routine for examining a user's privileges for a table
3296 : */
3297 : AclMode
3298 399437 : pg_class_aclmask(Oid table_oid, Oid roleid,
3299 : AclMode mask, AclMaskHow how)
3300 : {
3301 399437 : return pg_class_aclmask_ext(table_oid, roleid, mask, how, NULL);
3302 : }
3303 :
3304 : /*
3305 : * Routine for examining a user's privileges for a table, with is_missing
3306 : */
3307 : static AclMode
3308 2416609 : pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
3309 : AclMaskHow how, bool *is_missing)
3310 : {
3311 : AclMode result;
3312 : HeapTuple tuple;
3313 : Form_pg_class classForm;
3314 : Datum aclDatum;
3315 : bool isNull;
3316 : Acl *acl;
3317 : Oid ownerId;
3318 :
3319 : /*
3320 : * Must get the relation's tuple from pg_class
3321 : */
3322 2416609 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
3323 2416609 : if (!HeapTupleIsValid(tuple))
3324 : {
3325 5 : if (is_missing != NULL)
3326 : {
3327 : /* return "no privileges" instead of throwing an error */
3328 5 : *is_missing = true;
3329 5 : return 0;
3330 : }
3331 : else
3332 0 : ereport(ERROR,
3333 : (errcode(ERRCODE_UNDEFINED_TABLE),
3334 : errmsg("relation with OID %u does not exist",
3335 : table_oid)));
3336 : }
3337 :
3338 2416604 : classForm = (Form_pg_class) GETSTRUCT(tuple);
3339 :
3340 : /*
3341 : * Deny anyone permission to update a system catalog unless
3342 : * pg_authid.rolsuper is set.
3343 : *
3344 : * As of 7.4 we have some updatable system views; those shouldn't be
3345 : * protected in this way. Assume the view rules can take care of
3346 : * themselves. ACL_USAGE is if we ever have system sequences.
3347 : */
3348 2903065 : if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
3349 486461 : IsSystemClass(table_oid, classForm) &&
3350 2910 : classForm->relkind != RELKIND_VIEW &&
3351 2910 : !superuser_arg(roleid))
3352 46 : mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
3353 :
3354 : /*
3355 : * Otherwise, superusers bypass all permission-checking.
3356 : */
3357 2416604 : if (superuser_arg(roleid))
3358 : {
3359 2376881 : ReleaseSysCache(tuple);
3360 2376881 : return mask;
3361 : }
3362 :
3363 : /*
3364 : * Normal case: get the relation's ACL from pg_class
3365 : */
3366 39723 : ownerId = classForm->relowner;
3367 :
3368 39723 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
3369 : &isNull);
3370 39723 : if (isNull)
3371 : {
3372 : /* No ACL, so build default ACL */
3373 11656 : switch (classForm->relkind)
3374 : {
3375 84 : case RELKIND_SEQUENCE:
3376 84 : acl = acldefault(OBJECT_SEQUENCE, ownerId);
3377 84 : break;
3378 11572 : default:
3379 11572 : acl = acldefault(OBJECT_TABLE, ownerId);
3380 11572 : break;
3381 : }
3382 11656 : aclDatum = (Datum) 0;
3383 : }
3384 : else
3385 : {
3386 : /* detoast rel's ACL if necessary */
3387 28067 : acl = DatumGetAclP(aclDatum);
3388 : }
3389 :
3390 39723 : result = aclmask(acl, roleid, ownerId, mask, how);
3391 :
3392 : /* if we have a detoasted copy, free it */
3393 39723 : if (acl && acl != DatumGetPointer(aclDatum))
3394 39723 : pfree(acl);
3395 :
3396 39723 : ReleaseSysCache(tuple);
3397 :
3398 : /*
3399 : * Check if ACL_SELECT is being checked and, if so, and not set already as
3400 : * part of the result, then check if the user is a member of the
3401 : * pg_read_all_data role, which allows read access to all relations.
3402 : */
3403 44166 : if (mask & ACL_SELECT && !(result & ACL_SELECT) &&
3404 4443 : has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA))
3405 8 : result |= ACL_SELECT;
3406 :
3407 : /*
3408 : * Check if ACL_INSERT, ACL_UPDATE, or ACL_DELETE is being checked and, if
3409 : * so, and not set already as part of the result, then check if the user
3410 : * is a member of the pg_write_all_data role, which allows
3411 : * INSERT/UPDATE/DELETE access to all relations (except system catalogs,
3412 : * which requires superuser, see above).
3413 : */
3414 39723 : if (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE) &&
3415 6046 : !(result & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
3416 1353 : has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
3417 12 : result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));
3418 :
3419 : /*
3420 : * Check if ACL_MAINTAIN is being checked and, if so, and not already set
3421 : * as part of the result, then check if the user is a member of the
3422 : * pg_maintain role, which allows VACUUM, ANALYZE, CLUSTER, REFRESH
3423 : * MATERIALIZED VIEW, REINDEX, and LOCK TABLE on all relations.
3424 : */
3425 39723 : if (mask & ACL_MAINTAIN &&
3426 11444 : !(result & ACL_MAINTAIN) &&
3427 5519 : has_privs_of_role(roleid, ROLE_PG_MAINTAIN))
3428 44 : result |= ACL_MAINTAIN;
3429 :
3430 39723 : return result;
3431 : }
3432 :
3433 : /*
3434 : * Routine for examining a user's privileges for a configuration
3435 : * parameter (GUC), identified by GUC name.
3436 : */
3437 : static AclMode
3438 80 : pg_parameter_aclmask(const char *name, Oid roleid, AclMode mask, AclMaskHow how)
3439 : {
3440 : AclMode result;
3441 : char *parname;
3442 : text *partext;
3443 : HeapTuple tuple;
3444 :
3445 : /* Superusers bypass all permission checking. */
3446 80 : if (superuser_arg(roleid))
3447 1 : return mask;
3448 :
3449 : /* Convert name to the form it should have in pg_parameter_acl... */
3450 79 : parname = convert_GUC_name_for_parameter_acl(name);
3451 79 : partext = cstring_to_text(parname);
3452 :
3453 : /* ... and look it up */
3454 79 : tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(partext));
3455 :
3456 79 : if (!HeapTupleIsValid(tuple))
3457 : {
3458 : /* If no entry, GUC has no permissions for non-superusers */
3459 35 : result = ACL_NO_RIGHTS;
3460 : }
3461 : else
3462 : {
3463 : Datum aclDatum;
3464 : bool isNull;
3465 : Acl *acl;
3466 :
3467 44 : aclDatum = SysCacheGetAttr(PARAMETERACLNAME, tuple,
3468 : Anum_pg_parameter_acl_paracl,
3469 : &isNull);
3470 44 : if (isNull)
3471 : {
3472 : /* No ACL, so build default ACL */
3473 0 : acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
3474 0 : aclDatum = (Datum) 0;
3475 : }
3476 : else
3477 : {
3478 : /* detoast ACL if necessary */
3479 44 : acl = DatumGetAclP(aclDatum);
3480 : }
3481 :
3482 44 : result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
3483 :
3484 : /* if we have a detoasted copy, free it */
3485 44 : if (acl && acl != DatumGetPointer(aclDatum))
3486 44 : pfree(acl);
3487 :
3488 44 : ReleaseSysCache(tuple);
3489 : }
3490 :
3491 79 : pfree(parname);
3492 79 : pfree(partext);
3493 :
3494 79 : return result;
3495 : }
3496 :
3497 : /*
3498 : * Routine for examining a user's privileges for a configuration
3499 : * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
3500 : */
3501 : static AclMode
3502 0 : pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid, AclMode mask, AclMaskHow how)
3503 : {
3504 : AclMode result;
3505 : HeapTuple tuple;
3506 : Datum aclDatum;
3507 : bool isNull;
3508 : Acl *acl;
3509 :
3510 : /* Superusers bypass all permission checking. */
3511 0 : if (superuser_arg(roleid))
3512 0 : return mask;
3513 :
3514 : /* Get the ACL from pg_parameter_acl */
3515 0 : tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(acl_oid));
3516 0 : if (!HeapTupleIsValid(tuple))
3517 0 : ereport(ERROR,
3518 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3519 : errmsg("parameter ACL with OID %u does not exist",
3520 : acl_oid)));
3521 :
3522 0 : aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
3523 : Anum_pg_parameter_acl_paracl,
3524 : &isNull);
3525 0 : if (isNull)
3526 : {
3527 : /* No ACL, so build default ACL */
3528 0 : acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
3529 0 : aclDatum = (Datum) 0;
3530 : }
3531 : else
3532 : {
3533 : /* detoast ACL if necessary */
3534 0 : acl = DatumGetAclP(aclDatum);
3535 : }
3536 :
3537 0 : result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
3538 :
3539 : /* if we have a detoasted copy, free it */
3540 0 : if (acl && acl != DatumGetPointer(aclDatum))
3541 0 : pfree(acl);
3542 :
3543 0 : ReleaseSysCache(tuple);
3544 :
3545 0 : return result;
3546 : }
3547 :
3548 : /*
3549 : * Routine for examining a user's privileges for a largeobject
3550 : *
3551 : * When a large object is opened for reading, it is opened relative to the
3552 : * caller's snapshot, but when it is opened for writing, a current
3553 : * MVCC snapshot will be used. See doc/src/sgml/lobj.sgml. This function
3554 : * takes a snapshot argument so that the permissions check can be made
3555 : * relative to the same snapshot that will be used to read the underlying
3556 : * data. The caller will actually pass NULL for an instantaneous MVCC
3557 : * snapshot, since all we do with the snapshot argument is pass it through
3558 : * to systable_beginscan().
3559 : */
3560 : static AclMode
3561 586 : pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
3562 : AclMode mask, AclMaskHow how,
3563 : Snapshot snapshot)
3564 : {
3565 : AclMode result;
3566 : Relation pg_lo_meta;
3567 : ScanKeyData entry[1];
3568 : SysScanDesc scan;
3569 : HeapTuple tuple;
3570 : Datum aclDatum;
3571 : bool isNull;
3572 : Acl *acl;
3573 : Oid ownerId;
3574 :
3575 : /* Superusers bypass all permission checking. */
3576 586 : if (superuser_arg(roleid))
3577 334 : return mask;
3578 :
3579 : /*
3580 : * Get the largeobject's ACL from pg_largeobject_metadata
3581 : */
3582 252 : pg_lo_meta = table_open(LargeObjectMetadataRelationId,
3583 : AccessShareLock);
3584 :
3585 252 : ScanKeyInit(&entry[0],
3586 : Anum_pg_largeobject_metadata_oid,
3587 : BTEqualStrategyNumber, F_OIDEQ,
3588 : ObjectIdGetDatum(lobj_oid));
3589 :
3590 252 : scan = systable_beginscan(pg_lo_meta,
3591 : LargeObjectMetadataOidIndexId, true,
3592 : snapshot, 1, entry);
3593 :
3594 252 : tuple = systable_getnext(scan);
3595 252 : if (!HeapTupleIsValid(tuple))
3596 0 : ereport(ERROR,
3597 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3598 : errmsg("large object %u does not exist", lobj_oid)));
3599 :
3600 252 : ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
3601 :
3602 252 : aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
3603 : RelationGetDescr(pg_lo_meta), &isNull);
3604 :
3605 252 : if (isNull)
3606 : {
3607 : /* No ACL, so build default ACL */
3608 48 : acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
3609 48 : aclDatum = (Datum) 0;
3610 : }
3611 : else
3612 : {
3613 : /* detoast ACL if necessary */
3614 204 : acl = DatumGetAclP(aclDatum);
3615 : }
3616 :
3617 252 : result = aclmask(acl, roleid, ownerId, mask, how);
3618 :
3619 : /* if we have a detoasted copy, free it */
3620 252 : if (acl && acl != DatumGetPointer(aclDatum))
3621 252 : pfree(acl);
3622 :
3623 252 : systable_endscan(scan);
3624 :
3625 252 : table_close(pg_lo_meta, AccessShareLock);
3626 :
3627 : /*
3628 : * Check if ACL_SELECT is being checked and, if so, and not set already as
3629 : * part of the result, then check if the user has privileges of the
3630 : * pg_read_all_data role, which allows read access to all large objects.
3631 : */
3632 320 : if (mask & ACL_SELECT && !(result & ACL_SELECT) &&
3633 68 : has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA))
3634 20 : result |= ACL_SELECT;
3635 :
3636 : /*
3637 : * Check if ACL_UPDATE is being checked and, if so, and not set already as
3638 : * part of the result, then check if the user has privileges of the
3639 : * pg_write_all_data role, which allows write access to all large objects.
3640 : */
3641 312 : if (mask & ACL_UPDATE && !(result & ACL_UPDATE) &&
3642 60 : has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
3643 12 : result |= ACL_UPDATE;
3644 :
3645 252 : return result;
3646 : }
3647 :
3648 : /*
3649 : * Routine for examining a user's privileges for a namespace, with is_missing
3650 : */
3651 : static AclMode
3652 635510 : pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
3653 : AclMode mask, AclMaskHow how,
3654 : bool *is_missing)
3655 : {
3656 : AclMode result;
3657 : HeapTuple tuple;
3658 : Datum aclDatum;
3659 : bool isNull;
3660 : Acl *acl;
3661 : Oid ownerId;
3662 :
3663 : /* Superusers bypass all permission checking. */
3664 635510 : if (superuser_arg(roleid))
3665 623114 : return mask;
3666 :
3667 : /*
3668 : * If we have been assigned this namespace as a temp namespace, check to
3669 : * make sure we have CREATE TEMP permission on the database, and if so act
3670 : * as though we have all standard (but not GRANT OPTION) permissions on
3671 : * the namespace. If we don't have CREATE TEMP, act as though we have
3672 : * only USAGE (and not CREATE) rights.
3673 : *
3674 : * This may seem redundant given the check in InitTempTableNamespace, but
3675 : * it really isn't since current user ID may have changed since then. The
3676 : * upshot of this behavior is that a SECURITY DEFINER function can create
3677 : * temp tables that can then be accessed (if permission is granted) by
3678 : * code in the same session that doesn't have permissions to create temp
3679 : * tables.
3680 : *
3681 : * XXX Would it be safe to ereport a special error message as
3682 : * InitTempTableNamespace does? Returning zero here means we'll get a
3683 : * generic "permission denied for schema pg_temp_N" message, which is not
3684 : * remarkably user-friendly.
3685 : */
3686 12396 : if (isTempNamespace(nsp_oid))
3687 : {
3688 208 : if (object_aclcheck_ext(DatabaseRelationId, MyDatabaseId, roleid,
3689 : ACL_CREATE_TEMP, is_missing) == ACLCHECK_OK)
3690 208 : return mask & ACL_ALL_RIGHTS_SCHEMA;
3691 : else
3692 0 : return mask & ACL_USAGE;
3693 : }
3694 :
3695 : /*
3696 : * Get the schema's ACL from pg_namespace
3697 : */
3698 12188 : tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
3699 12188 : if (!HeapTupleIsValid(tuple))
3700 : {
3701 0 : if (is_missing != NULL)
3702 : {
3703 : /* return "no privileges" instead of throwing an error */
3704 0 : *is_missing = true;
3705 0 : return 0;
3706 : }
3707 : else
3708 0 : ereport(ERROR,
3709 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
3710 : errmsg("schema with OID %u does not exist", nsp_oid)));
3711 : }
3712 :
3713 12188 : ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
3714 :
3715 12188 : aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
3716 : &isNull);
3717 12188 : if (isNull)
3718 : {
3719 : /* No ACL, so build default ACL */
3720 207 : acl = acldefault(OBJECT_SCHEMA, ownerId);
3721 207 : aclDatum = (Datum) 0;
3722 : }
3723 : else
3724 : {
3725 : /* detoast ACL if necessary */
3726 11981 : acl = DatumGetAclP(aclDatum);
3727 : }
3728 :
3729 12188 : result = aclmask(acl, roleid, ownerId, mask, how);
3730 :
3731 : /* if we have a detoasted copy, free it */
3732 12188 : if (acl && acl != DatumGetPointer(aclDatum))
3733 12188 : pfree(acl);
3734 :
3735 12188 : ReleaseSysCache(tuple);
3736 :
3737 : /*
3738 : * Check if ACL_USAGE is being checked and, if so, and not set already as
3739 : * part of the result, then check if the user is a member of the
3740 : * pg_read_all_data or pg_write_all_data roles, which allow usage access
3741 : * to all schemas.
3742 : */
3743 12225 : if (mask & ACL_USAGE && !(result & ACL_USAGE) &&
3744 70 : (has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA) ||
3745 33 : has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA)))
3746 8 : result |= ACL_USAGE;
3747 12188 : return result;
3748 : }
3749 :
3750 : /*
3751 : * Routine for examining a user's privileges for a type, with is_missing
3752 : */
3753 : static AclMode
3754 213729 : pg_type_aclmask_ext(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how,
3755 : bool *is_missing)
3756 : {
3757 : AclMode result;
3758 : HeapTuple tuple;
3759 : Form_pg_type typeForm;
3760 : Datum aclDatum;
3761 : bool isNull;
3762 : Acl *acl;
3763 : Oid ownerId;
3764 :
3765 : /* Bypass permission checks for superusers */
3766 213729 : if (superuser_arg(roleid))
3767 210620 : return mask;
3768 :
3769 : /*
3770 : * Must get the type's tuple from pg_type
3771 : */
3772 3109 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
3773 3109 : if (!HeapTupleIsValid(tuple))
3774 : {
3775 0 : if (is_missing != NULL)
3776 : {
3777 : /* return "no privileges" instead of throwing an error */
3778 0 : *is_missing = true;
3779 0 : return 0;
3780 : }
3781 : else
3782 0 : ereport(ERROR,
3783 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3784 : errmsg("type with OID %u does not exist",
3785 : type_oid)));
3786 : }
3787 3109 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3788 :
3789 : /*
3790 : * "True" array types don't manage permissions of their own; consult the
3791 : * element type instead.
3792 : */
3793 3109 : if (IsTrueArrayType(typeForm))
3794 : {
3795 32 : Oid elttype_oid = typeForm->typelem;
3796 :
3797 32 : ReleaseSysCache(tuple);
3798 :
3799 32 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
3800 32 : if (!HeapTupleIsValid(tuple))
3801 : {
3802 0 : if (is_missing != NULL)
3803 : {
3804 : /* return "no privileges" instead of throwing an error */
3805 0 : *is_missing = true;
3806 0 : return 0;
3807 : }
3808 : else
3809 0 : ereport(ERROR,
3810 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3811 : errmsg("type with OID %u does not exist",
3812 : elttype_oid)));
3813 : }
3814 32 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3815 : }
3816 :
3817 : /*
3818 : * Likewise, multirange types don't manage their own permissions; consult
3819 : * the associated range type. (Note we must do this after the array step
3820 : * to get the right answer for arrays of multiranges.)
3821 : */
3822 3109 : if (typeForm->typtype == TYPTYPE_MULTIRANGE)
3823 : {
3824 8 : Oid rangetype = get_multirange_range(typeForm->oid);
3825 :
3826 8 : ReleaseSysCache(tuple);
3827 :
3828 8 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rangetype));
3829 8 : if (!HeapTupleIsValid(tuple))
3830 : {
3831 0 : if (is_missing != NULL)
3832 : {
3833 : /* return "no privileges" instead of throwing an error */
3834 0 : *is_missing = true;
3835 0 : return 0;
3836 : }
3837 : else
3838 0 : ereport(ERROR,
3839 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3840 : errmsg("type with OID %u does not exist",
3841 : rangetype)));
3842 : }
3843 8 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3844 : }
3845 :
3846 : /*
3847 : * Now get the type's owner and ACL from the tuple
3848 : */
3849 3109 : ownerId = typeForm->typowner;
3850 :
3851 3109 : aclDatum = SysCacheGetAttr(TYPEOID, tuple,
3852 : Anum_pg_type_typacl, &isNull);
3853 3109 : if (isNull)
3854 : {
3855 : /* No ACL, so build default ACL */
3856 2953 : acl = acldefault(OBJECT_TYPE, ownerId);
3857 2953 : aclDatum = (Datum) 0;
3858 : }
3859 : else
3860 : {
3861 : /* detoast rel's ACL if necessary */
3862 156 : acl = DatumGetAclP(aclDatum);
3863 : }
3864 :
3865 3109 : result = aclmask(acl, roleid, ownerId, mask, how);
3866 :
3867 : /* if we have a detoasted copy, free it */
3868 3109 : if (acl && acl != DatumGetPointer(aclDatum))
3869 3109 : pfree(acl);
3870 :
3871 3109 : ReleaseSysCache(tuple);
3872 :
3873 3109 : return result;
3874 : }
3875 :
3876 : /*
3877 : * Exported generic routine for checking a user's access privileges to an object
3878 : */
3879 : AclResult
3880 2092973 : object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
3881 : {
3882 2092973 : return object_aclcheck_ext(classid, objectid, roleid, mode, NULL);
3883 : }
3884 :
3885 : /*
3886 : * Exported generic routine for checking a user's access privileges to an
3887 : * object, with is_missing
3888 : */
3889 : AclResult
3890 2093253 : object_aclcheck_ext(Oid classid, Oid objectid,
3891 : Oid roleid, AclMode mode,
3892 : bool *is_missing)
3893 : {
3894 2093253 : if (object_aclmask_ext(classid, objectid, roleid, mode, ACLMASK_ANY,
3895 : is_missing) != 0)
3896 2092844 : return ACLCHECK_OK;
3897 : else
3898 409 : return ACLCHECK_NO_PRIV;
3899 : }
3900 :
3901 : /*
3902 : * Exported routine for checking a user's access privileges to a column
3903 : *
3904 : * Returns ACLCHECK_OK if the user has any of the privileges identified by
3905 : * 'mode'; otherwise returns a suitable error code (in practice, always
3906 : * ACLCHECK_NO_PRIV).
3907 : *
3908 : * As with pg_attribute_aclmask, only privileges granted directly on the
3909 : * column are considered here.
3910 : */
3911 : AclResult
3912 2929 : pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
3913 : Oid roleid, AclMode mode)
3914 : {
3915 2929 : return pg_attribute_aclcheck_ext(table_oid, attnum, roleid, mode, NULL);
3916 : }
3917 :
3918 :
3919 : /*
3920 : * Exported routine for checking a user's access privileges to a column,
3921 : * with is_missing
3922 : */
3923 : AclResult
3924 366411 : pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
3925 : Oid roleid, AclMode mode, bool *is_missing)
3926 : {
3927 366411 : if (pg_attribute_aclmask_ext(table_oid, attnum, roleid, mode,
3928 : ACLMASK_ANY, is_missing) != 0)
3929 5569 : return ACLCHECK_OK;
3930 : else
3931 360842 : return ACLCHECK_NO_PRIV;
3932 : }
3933 :
3934 : /*
3935 : * Exported routine for checking a user's access privileges to any/all columns
3936 : *
3937 : * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
3938 : * privileges identified by 'mode' on any non-dropped column in the relation;
3939 : * otherwise returns a suitable error code (in practice, always
3940 : * ACLCHECK_NO_PRIV).
3941 : *
3942 : * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
3943 : * privileges identified by 'mode' on each non-dropped column in the relation
3944 : * (and there must be at least one such column); otherwise returns a suitable
3945 : * error code (in practice, always ACLCHECK_NO_PRIV).
3946 : *
3947 : * As with pg_attribute_aclmask, only privileges granted directly on the
3948 : * column(s) are considered here.
3949 : *
3950 : * Note: system columns are not considered here; there are cases where that
3951 : * might be appropriate but there are also cases where it wouldn't.
3952 : */
3953 : AclResult
3954 162 : pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
3955 : AclMaskHow how)
3956 : {
3957 162 : return pg_attribute_aclcheck_all_ext(table_oid, roleid, mode, how, NULL);
3958 : }
3959 :
3960 : /*
3961 : * Exported routine for checking a user's access privileges to any/all columns,
3962 : * with is_missing
3963 : */
3964 : AclResult
3965 162 : pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
3966 : AclMode mode, AclMaskHow how,
3967 : bool *is_missing)
3968 : {
3969 : AclResult result;
3970 : HeapTuple classTuple;
3971 : Form_pg_class classForm;
3972 : Oid ownerId;
3973 : AttrNumber nattrs;
3974 : AttrNumber curr_att;
3975 :
3976 : /*
3977 : * Must fetch pg_class row to get owner ID and number of attributes.
3978 : */
3979 162 : classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
3980 162 : if (!HeapTupleIsValid(classTuple))
3981 : {
3982 0 : if (is_missing != NULL)
3983 : {
3984 : /* return "no privileges" instead of throwing an error */
3985 0 : *is_missing = true;
3986 0 : return ACLCHECK_NO_PRIV;
3987 : }
3988 : else
3989 0 : ereport(ERROR,
3990 : (errcode(ERRCODE_UNDEFINED_TABLE),
3991 : errmsg("relation with OID %u does not exist",
3992 : table_oid)));
3993 : }
3994 162 : classForm = (Form_pg_class) GETSTRUCT(classTuple);
3995 :
3996 162 : ownerId = classForm->relowner;
3997 162 : nattrs = classForm->relnatts;
3998 :
3999 162 : ReleaseSysCache(classTuple);
4000 :
4001 : /*
4002 : * Initialize result in case there are no non-dropped columns. We want to
4003 : * report failure in such cases for either value of 'how'.
4004 : */
4005 162 : result = ACLCHECK_NO_PRIV;
4006 :
4007 386 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4008 : {
4009 : HeapTuple attTuple;
4010 : Datum aclDatum;
4011 : bool isNull;
4012 : Acl *acl;
4013 : AclMode attmask;
4014 :
4015 310 : attTuple = SearchSysCache2(ATTNUM,
4016 : ObjectIdGetDatum(table_oid),
4017 : Int16GetDatum(curr_att));
4018 :
4019 : /*
4020 : * Lookup failure probably indicates that the table was just dropped,
4021 : * but we'll treat it the same as a dropped column rather than
4022 : * throwing error.
4023 : */
4024 310 : if (!HeapTupleIsValid(attTuple))
4025 12 : continue;
4026 :
4027 : /* ignore dropped columns */
4028 310 : if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
4029 : {
4030 12 : ReleaseSysCache(attTuple);
4031 12 : continue;
4032 : }
4033 :
4034 298 : aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
4035 : &isNull);
4036 :
4037 : /*
4038 : * Here we hard-wire knowledge that the default ACL for a column
4039 : * grants no privileges, so that we can fall out quickly in the very
4040 : * common case where attacl is null.
4041 : */
4042 298 : if (isNull)
4043 121 : attmask = 0;
4044 : else
4045 : {
4046 : /* detoast column's ACL if necessary */
4047 177 : acl = DatumGetAclP(aclDatum);
4048 :
4049 177 : attmask = aclmask(acl, roleid, ownerId, mode, ACLMASK_ANY);
4050 :
4051 : /* if we have a detoasted copy, free it */
4052 177 : if (acl != DatumGetPointer(aclDatum))
4053 177 : pfree(acl);
4054 : }
4055 :
4056 298 : ReleaseSysCache(attTuple);
4057 :
4058 298 : if (attmask != 0)
4059 : {
4060 117 : result = ACLCHECK_OK;
4061 117 : if (how == ACLMASK_ANY)
4062 86 : break; /* succeed on any success */
4063 : }
4064 : else
4065 : {
4066 181 : result = ACLCHECK_NO_PRIV;
4067 181 : if (how == ACLMASK_ALL)
4068 33 : break; /* fail on any failure */
4069 : }
4070 : }
4071 :
4072 162 : return result;
4073 : }
4074 :
4075 : /*
4076 : * Exported routine for checking a user's access privileges to a table
4077 : *
4078 : * Returns ACLCHECK_OK if the user has any of the privileges identified by
4079 : * 'mode'; otherwise returns a suitable error code (in practice, always
4080 : * ACLCHECK_NO_PRIV).
4081 : */
4082 : AclResult
4083 1657518 : pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
4084 : {
4085 1657518 : return pg_class_aclcheck_ext(table_oid, roleid, mode, NULL);
4086 : }
4087 :
4088 : /*
4089 : * Exported routine for checking a user's access privileges to a table,
4090 : * with is_missing
4091 : */
4092 : AclResult
4093 2017172 : pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
4094 : AclMode mode, bool *is_missing)
4095 : {
4096 2017172 : if (pg_class_aclmask_ext(table_oid, roleid, mode,
4097 : ACLMASK_ANY, is_missing) != 0)
4098 2008376 : return ACLCHECK_OK;
4099 : else
4100 8796 : return ACLCHECK_NO_PRIV;
4101 : }
4102 :
4103 : /*
4104 : * Exported routine for checking a user's access privileges to a configuration
4105 : * parameter (GUC), identified by GUC name.
4106 : */
4107 : AclResult
4108 80 : pg_parameter_aclcheck(const char *name, Oid roleid, AclMode mode)
4109 : {
4110 80 : if (pg_parameter_aclmask(name, roleid, mode, ACLMASK_ANY) != 0)
4111 34 : return ACLCHECK_OK;
4112 : else
4113 46 : return ACLCHECK_NO_PRIV;
4114 : }
4115 :
4116 : /*
4117 : * Exported routine for checking a user's access privileges to a largeobject
4118 : */
4119 : AclResult
4120 586 : pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
4121 : Snapshot snapshot)
4122 : {
4123 586 : if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
4124 : ACLMASK_ANY, snapshot) != 0)
4125 490 : return ACLCHECK_OK;
4126 : else
4127 96 : return ACLCHECK_NO_PRIV;
4128 : }
4129 :
4130 : /*
4131 : * Generic ownership check for an object
4132 : */
4133 : bool
4134 281748 : object_ownercheck(Oid classid, Oid objectid, Oid roleid)
4135 : {
4136 : SysCacheIdentifier cacheid;
4137 : Oid ownerId;
4138 :
4139 : /* Superusers bypass all permission checking. */
4140 281748 : if (superuser_arg(roleid))
4141 273358 : return true;
4142 :
4143 : /* For large objects, the catalog to consult is pg_largeobject_metadata */
4144 8390 : if (classid == LargeObjectRelationId)
4145 24 : classid = LargeObjectMetadataRelationId;
4146 :
4147 8390 : cacheid = get_object_catcache_oid(classid);
4148 8390 : if (cacheid != SYSCACHEID_INVALID)
4149 : {
4150 : /* we can get the object's tuple from the syscache */
4151 : HeapTuple tuple;
4152 :
4153 8364 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
4154 8364 : if (!HeapTupleIsValid(tuple))
4155 0 : elog(ERROR, "cache lookup failed for %s %u",
4156 : get_object_class_descr(classid), objectid);
4157 :
4158 8364 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
4159 : tuple,
4160 8364 : get_object_attnum_owner(classid)));
4161 8364 : ReleaseSysCache(tuple);
4162 : }
4163 : else
4164 : {
4165 : /* for catalogs without an appropriate syscache */
4166 : Relation rel;
4167 : ScanKeyData entry[1];
4168 : SysScanDesc scan;
4169 : HeapTuple tuple;
4170 : bool isnull;
4171 :
4172 26 : rel = table_open(classid, AccessShareLock);
4173 :
4174 52 : ScanKeyInit(&entry[0],
4175 26 : get_object_attnum_oid(classid),
4176 : BTEqualStrategyNumber, F_OIDEQ,
4177 : ObjectIdGetDatum(objectid));
4178 :
4179 26 : scan = systable_beginscan(rel,
4180 : get_object_oid_index(classid), true,
4181 : NULL, 1, entry);
4182 :
4183 26 : tuple = systable_getnext(scan);
4184 26 : if (!HeapTupleIsValid(tuple))
4185 0 : elog(ERROR, "could not find tuple for %s %u",
4186 : get_object_class_descr(classid), objectid);
4187 :
4188 26 : ownerId = DatumGetObjectId(heap_getattr(tuple,
4189 26 : get_object_attnum_owner(classid),
4190 : RelationGetDescr(rel),
4191 : &isnull));
4192 : Assert(!isnull);
4193 :
4194 26 : systable_endscan(scan);
4195 26 : table_close(rel, AccessShareLock);
4196 : }
4197 :
4198 8390 : return has_privs_of_role(roleid, ownerId);
4199 : }
4200 :
4201 : /*
4202 : * Check whether specified role has CREATEROLE privilege (or is a superuser)
4203 : *
4204 : * Note: roles do not have owners per se; instead we use this test in
4205 : * places where an ownership-like permissions test is needed for a role.
4206 : * Be sure to apply it to the role trying to do the operation, not the
4207 : * role being operated on! Also note that this generally should not be
4208 : * considered enough privilege if the target role is a superuser.
4209 : * (We don't handle that consideration here because we want to give a
4210 : * separate error message for such cases, so the caller has to deal with it.)
4211 : */
4212 : bool
4213 1707 : has_createrole_privilege(Oid roleid)
4214 : {
4215 1707 : bool result = false;
4216 : HeapTuple utup;
4217 :
4218 : /* Superusers bypass all permission checking. */
4219 1707 : if (superuser_arg(roleid))
4220 1345 : return true;
4221 :
4222 362 : utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
4223 362 : if (HeapTupleIsValid(utup))
4224 : {
4225 362 : result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
4226 362 : ReleaseSysCache(utup);
4227 : }
4228 362 : return result;
4229 : }
4230 :
4231 : bool
4232 3790 : has_bypassrls_privilege(Oid roleid)
4233 : {
4234 3790 : bool result = false;
4235 : HeapTuple utup;
4236 :
4237 : /* Superusers bypass all permission checking. */
4238 3790 : if (superuser_arg(roleid))
4239 1158 : return true;
4240 :
4241 2632 : utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
4242 2632 : if (HeapTupleIsValid(utup))
4243 : {
4244 2632 : result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
4245 2632 : ReleaseSysCache(utup);
4246 : }
4247 2632 : return result;
4248 : }
4249 :
4250 : /*
4251 : * Fetch pg_default_acl entry for given role, namespace and object type
4252 : * (object type must be given in pg_default_acl's encoding).
4253 : * Returns NULL if no such entry.
4254 : */
4255 : static Acl *
4256 109286 : get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
4257 : {
4258 109286 : Acl *result = NULL;
4259 : HeapTuple tuple;
4260 :
4261 109286 : tuple = SearchSysCache3(DEFACLROLENSPOBJ,
4262 : ObjectIdGetDatum(roleId),
4263 : ObjectIdGetDatum(nsp_oid),
4264 : CharGetDatum(objtype));
4265 :
4266 109286 : if (HeapTupleIsValid(tuple))
4267 : {
4268 : Datum aclDatum;
4269 : bool isNull;
4270 :
4271 176 : aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
4272 : Anum_pg_default_acl_defaclacl,
4273 : &isNull);
4274 176 : if (!isNull)
4275 176 : result = DatumGetAclPCopy(aclDatum);
4276 176 : ReleaseSysCache(tuple);
4277 : }
4278 :
4279 109286 : return result;
4280 : }
4281 :
4282 : /*
4283 : * Get default permissions for newly created object within given schema
4284 : *
4285 : * Returns NULL if built-in system defaults should be used.
4286 : *
4287 : * If the result is not NULL, caller must call recordDependencyOnNewAcl
4288 : * once the OID of the new object is known.
4289 : */
4290 : Acl *
4291 54643 : get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
4292 : {
4293 : Acl *result;
4294 : Acl *glob_acl;
4295 : Acl *schema_acl;
4296 : Acl *def_acl;
4297 : char defaclobjtype;
4298 :
4299 : /*
4300 : * Use NULL during bootstrap, since pg_default_acl probably isn't there
4301 : * yet.
4302 : */
4303 54643 : if (IsBootstrapProcessingMode())
4304 0 : return NULL;
4305 :
4306 : /* Check if object type is supported in pg_default_acl */
4307 54643 : switch (objtype)
4308 : {
4309 38201 : case OBJECT_TABLE:
4310 38201 : defaclobjtype = DEFACLOBJ_RELATION;
4311 38201 : break;
4312 :
4313 1211 : case OBJECT_SEQUENCE:
4314 1211 : defaclobjtype = DEFACLOBJ_SEQUENCE;
4315 1211 : break;
4316 :
4317 10617 : case OBJECT_FUNCTION:
4318 10617 : defaclobjtype = DEFACLOBJ_FUNCTION;
4319 10617 : break;
4320 :
4321 3795 : case OBJECT_TYPE:
4322 3795 : defaclobjtype = DEFACLOBJ_TYPE;
4323 3795 : break;
4324 :
4325 715 : case OBJECT_SCHEMA:
4326 715 : defaclobjtype = DEFACLOBJ_NAMESPACE;
4327 715 : break;
4328 :
4329 104 : case OBJECT_LARGEOBJECT:
4330 104 : defaclobjtype = DEFACLOBJ_LARGEOBJECT;
4331 104 : break;
4332 :
4333 0 : default:
4334 0 : return NULL;
4335 : }
4336 :
4337 : /* Look up the relevant pg_default_acl entries */
4338 54643 : glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
4339 54643 : schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
4340 :
4341 : /* Quick out if neither entry exists */
4342 54643 : if (glob_acl == NULL && schema_acl == NULL)
4343 54503 : return NULL;
4344 :
4345 : /* We need to know the hard-wired default value, too */
4346 140 : def_acl = acldefault(objtype, ownerId);
4347 :
4348 : /* If there's no global entry, substitute the hard-wired default */
4349 140 : if (glob_acl == NULL)
4350 12 : glob_acl = def_acl;
4351 :
4352 : /* Merge in any per-schema privileges */
4353 140 : result = aclmerge(glob_acl, schema_acl, ownerId);
4354 :
4355 : /*
4356 : * For efficiency, we want to return NULL if the result equals default.
4357 : * This requires sorting both arrays to get an accurate comparison.
4358 : */
4359 140 : aclitemsort(result);
4360 140 : aclitemsort(def_acl);
4361 140 : if (aclequal(result, def_acl))
4362 16 : result = NULL;
4363 :
4364 140 : return result;
4365 : }
4366 :
4367 : /*
4368 : * Record dependencies on roles mentioned in a new object's ACL.
4369 : */
4370 : void
4371 57207 : recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId,
4372 : Oid ownerId, Acl *acl)
4373 : {
4374 : int nmembers;
4375 : Oid *members;
4376 :
4377 : /* Nothing to do if ACL is defaulted */
4378 57207 : if (acl == NULL)
4379 57083 : return;
4380 :
4381 : /* Extract roles mentioned in ACL */
4382 124 : nmembers = aclmembers(acl, &members);
4383 :
4384 : /* Update the shared dependency ACL info */
4385 124 : updateAclDependencies(classId, objectId, objsubId,
4386 : ownerId,
4387 : 0, NULL,
4388 : nmembers, members);
4389 : }
4390 :
4391 : /*
4392 : * Record initial privileges for the top-level object passed in.
4393 : *
4394 : * For the object passed in, this will record its ACL (if any) and the ACLs of
4395 : * any sub-objects (eg: columns) into pg_init_privs.
4396 : */
4397 : void
4398 53 : recordExtObjInitPriv(Oid objoid, Oid classoid)
4399 : {
4400 : /*
4401 : * pg_class / pg_attribute
4402 : *
4403 : * If this is a relation then we need to see if there are any sub-objects
4404 : * (eg: columns) for it and, if so, be sure to call
4405 : * recordExtensionInitPrivWorker() for each one.
4406 : */
4407 53 : if (classoid == RelationRelationId)
4408 : {
4409 : Form_pg_class pg_class_tuple;
4410 : Datum aclDatum;
4411 : bool isNull;
4412 : HeapTuple tuple;
4413 :
4414 8 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
4415 8 : if (!HeapTupleIsValid(tuple))
4416 0 : elog(ERROR, "cache lookup failed for relation %u", objoid);
4417 8 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
4418 :
4419 : /*
4420 : * Indexes don't have permissions, neither do the pg_class rows for
4421 : * composite types. (These cases are unreachable given the
4422 : * restrictions in ALTER EXTENSION ADD, but let's check anyway.)
4423 : */
4424 8 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
4425 8 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
4426 8 : pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
4427 : {
4428 0 : ReleaseSysCache(tuple);
4429 0 : return;
4430 : }
4431 :
4432 : /*
4433 : * If this isn't a sequence then it's possibly going to have
4434 : * column-level ACLs associated with it.
4435 : */
4436 8 : if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
4437 : {
4438 : AttrNumber curr_att;
4439 7 : AttrNumber nattrs = pg_class_tuple->relnatts;
4440 :
4441 19 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4442 : {
4443 : HeapTuple attTuple;
4444 : Datum attaclDatum;
4445 :
4446 12 : attTuple = SearchSysCache2(ATTNUM,
4447 : ObjectIdGetDatum(objoid),
4448 : Int16GetDatum(curr_att));
4449 :
4450 12 : if (!HeapTupleIsValid(attTuple))
4451 0 : continue;
4452 :
4453 : /* ignore dropped columns */
4454 12 : if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
4455 : {
4456 1 : ReleaseSysCache(attTuple);
4457 1 : continue;
4458 : }
4459 :
4460 11 : attaclDatum = SysCacheGetAttr(ATTNUM, attTuple,
4461 : Anum_pg_attribute_attacl,
4462 : &isNull);
4463 :
4464 : /* no need to do anything for a NULL ACL */
4465 11 : if (isNull)
4466 : {
4467 9 : ReleaseSysCache(attTuple);
4468 9 : continue;
4469 : }
4470 :
4471 2 : recordExtensionInitPrivWorker(objoid, classoid, curr_att,
4472 2 : DatumGetAclP(attaclDatum));
4473 :
4474 2 : ReleaseSysCache(attTuple);
4475 : }
4476 : }
4477 :
4478 8 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
4479 : &isNull);
4480 :
4481 : /* Add the record, if any, for the top-level object */
4482 8 : if (!isNull)
4483 4 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4484 4 : DatumGetAclP(aclDatum));
4485 :
4486 8 : ReleaseSysCache(tuple);
4487 : }
4488 45 : else if (classoid == LargeObjectRelationId)
4489 : {
4490 : /* For large objects, we must consult pg_largeobject_metadata */
4491 : Datum aclDatum;
4492 : bool isNull;
4493 : HeapTuple tuple;
4494 : ScanKeyData entry[1];
4495 : SysScanDesc scan;
4496 : Relation relation;
4497 :
4498 : /*
4499 : * Note: this is dead code, given that we don't allow large objects to
4500 : * be made extension members. But it seems worth carrying in case
4501 : * some future caller of this function has need for it.
4502 : */
4503 0 : relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock);
4504 :
4505 : /* There's no syscache for pg_largeobject_metadata */
4506 0 : ScanKeyInit(&entry[0],
4507 : Anum_pg_largeobject_metadata_oid,
4508 : BTEqualStrategyNumber, F_OIDEQ,
4509 : ObjectIdGetDatum(objoid));
4510 :
4511 0 : scan = systable_beginscan(relation,
4512 : LargeObjectMetadataOidIndexId, true,
4513 : NULL, 1, entry);
4514 :
4515 0 : tuple = systable_getnext(scan);
4516 0 : if (!HeapTupleIsValid(tuple))
4517 0 : elog(ERROR, "could not find tuple for large object %u", objoid);
4518 :
4519 0 : aclDatum = heap_getattr(tuple,
4520 : Anum_pg_largeobject_metadata_lomacl,
4521 : RelationGetDescr(relation), &isNull);
4522 :
4523 : /* Add the record, if any, for the top-level object */
4524 0 : if (!isNull)
4525 0 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4526 0 : DatumGetAclP(aclDatum));
4527 :
4528 0 : systable_endscan(scan);
4529 : }
4530 : /* This will error on unsupported classoid. */
4531 45 : else if (get_object_attnum_acl(classoid) != InvalidAttrNumber)
4532 : {
4533 : SysCacheIdentifier cacheid;
4534 : Datum aclDatum;
4535 : bool isNull;
4536 : HeapTuple tuple;
4537 :
4538 34 : cacheid = get_object_catcache_oid(classoid);
4539 34 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objoid));
4540 34 : if (!HeapTupleIsValid(tuple))
4541 0 : elog(ERROR, "cache lookup failed for %s %u",
4542 : get_object_class_descr(classoid), objoid);
4543 :
4544 34 : aclDatum = SysCacheGetAttr(cacheid, tuple,
4545 34 : get_object_attnum_acl(classoid),
4546 : &isNull);
4547 :
4548 : /* Add the record, if any, for the top-level object */
4549 34 : if (!isNull)
4550 5 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4551 5 : DatumGetAclP(aclDatum));
4552 :
4553 34 : ReleaseSysCache(tuple);
4554 : }
4555 : }
4556 :
4557 : /*
4558 : * For the object passed in, remove its ACL and the ACLs of any object subIds
4559 : * from pg_init_privs (via recordExtensionInitPrivWorker()).
4560 : */
4561 : void
4562 178 : removeExtObjInitPriv(Oid objoid, Oid classoid)
4563 : {
4564 : /*
4565 : * If this is a relation then we need to see if there are any sub-objects
4566 : * (eg: columns) for it and, if so, be sure to call
4567 : * recordExtensionInitPrivWorker() for each one.
4568 : */
4569 178 : if (classoid == RelationRelationId)
4570 : {
4571 : Form_pg_class pg_class_tuple;
4572 : HeapTuple tuple;
4573 :
4574 35 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
4575 35 : if (!HeapTupleIsValid(tuple))
4576 0 : elog(ERROR, "cache lookup failed for relation %u", objoid);
4577 35 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
4578 :
4579 : /*
4580 : * Indexes don't have permissions, neither do the pg_class rows for
4581 : * composite types. (These cases are unreachable given the
4582 : * restrictions in ALTER EXTENSION DROP, but let's check anyway.)
4583 : */
4584 35 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
4585 35 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
4586 35 : pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
4587 : {
4588 0 : ReleaseSysCache(tuple);
4589 0 : return;
4590 : }
4591 :
4592 : /*
4593 : * If this isn't a sequence then it's possibly going to have
4594 : * column-level ACLs associated with it.
4595 : */
4596 35 : if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
4597 : {
4598 : AttrNumber curr_att;
4599 35 : AttrNumber nattrs = pg_class_tuple->relnatts;
4600 :
4601 1178 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4602 : {
4603 : HeapTuple attTuple;
4604 :
4605 1143 : attTuple = SearchSysCache2(ATTNUM,
4606 : ObjectIdGetDatum(objoid),
4607 : Int16GetDatum(curr_att));
4608 :
4609 1143 : if (!HeapTupleIsValid(attTuple))
4610 0 : continue;
4611 :
4612 : /* when removing, remove all entries, even dropped columns */
4613 :
4614 1143 : recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL);
4615 :
4616 1143 : ReleaseSysCache(attTuple);
4617 : }
4618 : }
4619 :
4620 35 : ReleaseSysCache(tuple);
4621 : }
4622 :
4623 : /* Remove the record, if any, for the top-level object */
4624 178 : recordExtensionInitPrivWorker(objoid, classoid, 0, NULL);
4625 : }
4626 :
4627 : /*
4628 : * Record initial ACL for an extension object
4629 : *
4630 : * Can be called at any time, we check if 'creating_extension' is set and, if
4631 : * not, exit immediately.
4632 : *
4633 : * Pass in the object OID, the OID of the class (the OID of the table which
4634 : * the object is defined in) and the 'sub' id of the object (objsubid), if
4635 : * any. If there is no 'sub' id (they are currently only used for columns of
4636 : * tables) then pass in '0'. Finally, pass in the complete ACL to store.
4637 : *
4638 : * If an ACL already exists for this object/sub-object then we will replace
4639 : * it with what is passed in.
4640 : *
4641 : * Passing in NULL for 'new_acl' will result in the entry for the object being
4642 : * removed, if one is found.
4643 : */
4644 : static void
4645 14187 : recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
4646 : {
4647 : /*
4648 : * Generally, we only record the initial privileges when an extension is
4649 : * being created, but because we don't actually use CREATE EXTENSION
4650 : * during binary upgrades with pg_upgrade, there is a variable to let us
4651 : * know that the GRANT and REVOKE statements being issued, while this
4652 : * variable is true, are for the initial privileges of the extension
4653 : * object and therefore we need to record them.
4654 : */
4655 14187 : if (!creating_extension && !binary_upgrade_record_init_privs)
4656 13768 : return;
4657 :
4658 419 : recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl);
4659 : }
4660 :
4661 : /*
4662 : * Record initial ACL for an extension object, worker.
4663 : *
4664 : * This will perform a wholesale replacement of the entire ACL for the object
4665 : * passed in, therefore be sure to pass in the complete new ACL to use.
4666 : *
4667 : * Generally speaking, do *not* use this function directly but instead use
4668 : * recordExtensionInitPriv(), which checks if 'creating_extension' is set.
4669 : * This function does *not* check if 'creating_extension' is set as it is also
4670 : * used when an object is added to or removed from an extension via ALTER
4671 : * EXTENSION ... ADD/DROP.
4672 : */
4673 : static void
4674 1751 : recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
4675 : Acl *new_acl)
4676 : {
4677 : Relation relation;
4678 : ScanKeyData key[3];
4679 : SysScanDesc scan;
4680 : HeapTuple tuple;
4681 : HeapTuple oldtuple;
4682 : int noldmembers;
4683 : int nnewmembers;
4684 : Oid *oldmembers;
4685 : Oid *newmembers;
4686 :
4687 : /* We'll need the role membership of the new ACL. */
4688 1751 : nnewmembers = aclmembers(new_acl, &newmembers);
4689 :
4690 : /* Search pg_init_privs for an existing entry. */
4691 1751 : relation = table_open(InitPrivsRelationId, RowExclusiveLock);
4692 :
4693 1751 : ScanKeyInit(&key[0],
4694 : Anum_pg_init_privs_objoid,
4695 : BTEqualStrategyNumber, F_OIDEQ,
4696 : ObjectIdGetDatum(objoid));
4697 1751 : ScanKeyInit(&key[1],
4698 : Anum_pg_init_privs_classoid,
4699 : BTEqualStrategyNumber, F_OIDEQ,
4700 : ObjectIdGetDatum(classoid));
4701 1751 : ScanKeyInit(&key[2],
4702 : Anum_pg_init_privs_objsubid,
4703 : BTEqualStrategyNumber, F_INT4EQ,
4704 : Int32GetDatum(objsubid));
4705 :
4706 1751 : scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
4707 : NULL, 3, key);
4708 :
4709 : /* There should exist only one entry or none. */
4710 1751 : oldtuple = systable_getnext(scan);
4711 :
4712 : /* If we find an entry, update it with the latest ACL. */
4713 1751 : if (HeapTupleIsValid(oldtuple))
4714 : {
4715 140 : Datum values[Natts_pg_init_privs] = {0};
4716 140 : bool nulls[Natts_pg_init_privs] = {0};
4717 140 : bool replace[Natts_pg_init_privs] = {0};
4718 : Datum oldAclDatum;
4719 : bool isNull;
4720 : Acl *old_acl;
4721 :
4722 : /* Update pg_shdepend for roles mentioned in the old/new ACLs. */
4723 140 : oldAclDatum = heap_getattr(oldtuple, Anum_pg_init_privs_initprivs,
4724 : RelationGetDescr(relation), &isNull);
4725 : Assert(!isNull);
4726 140 : old_acl = DatumGetAclP(oldAclDatum);
4727 140 : noldmembers = aclmembers(old_acl, &oldmembers);
4728 :
4729 140 : updateInitAclDependencies(classoid, objoid, objsubid,
4730 : noldmembers, oldmembers,
4731 : nnewmembers, newmembers);
4732 :
4733 : /* If we have a new ACL to set, then update the row with it. */
4734 140 : if (new_acl && ACL_NUM(new_acl) != 0)
4735 : {
4736 91 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
4737 91 : replace[Anum_pg_init_privs_initprivs - 1] = true;
4738 :
4739 91 : oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
4740 : values, nulls, replace);
4741 :
4742 91 : CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple);
4743 : }
4744 : else
4745 : {
4746 : /* new_acl is NULL/empty, so delete the entry we found. */
4747 49 : CatalogTupleDelete(relation, &oldtuple->t_self);
4748 : }
4749 : }
4750 : else
4751 : {
4752 1611 : Datum values[Natts_pg_init_privs] = {0};
4753 1611 : bool nulls[Natts_pg_init_privs] = {0};
4754 :
4755 : /*
4756 : * Only add a new entry if the new ACL is non-NULL.
4757 : *
4758 : * If we are passed in a NULL ACL and no entry exists, we can just
4759 : * fall through and do nothing.
4760 : */
4761 1611 : if (new_acl && ACL_NUM(new_acl) != 0)
4762 : {
4763 : /* No entry found, so add it. */
4764 336 : values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
4765 336 : values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
4766 336 : values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
4767 :
4768 : /* This function only handles initial privileges of extensions */
4769 336 : values[Anum_pg_init_privs_privtype - 1] =
4770 336 : CharGetDatum(INITPRIVS_EXTENSION);
4771 :
4772 336 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
4773 :
4774 336 : tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
4775 :
4776 336 : CatalogTupleInsert(relation, tuple);
4777 :
4778 : /* Update pg_shdepend, too. */
4779 336 : noldmembers = 0;
4780 336 : oldmembers = NULL;
4781 :
4782 336 : updateInitAclDependencies(classoid, objoid, objsubid,
4783 : noldmembers, oldmembers,
4784 : nnewmembers, newmembers);
4785 : }
4786 : }
4787 :
4788 1751 : systable_endscan(scan);
4789 :
4790 : /* prevent error when processing objects multiple times */
4791 1751 : CommandCounterIncrement();
4792 :
4793 1751 : table_close(relation, RowExclusiveLock);
4794 1751 : }
4795 :
4796 : /*
4797 : * ReplaceRoleInInitPriv
4798 : *
4799 : * Used by shdepReassignOwned to replace mentions of a role in pg_init_privs.
4800 : */
4801 : void
4802 12 : ReplaceRoleInInitPriv(Oid oldroleid, Oid newroleid,
4803 : Oid classid, Oid objid, int32 objsubid)
4804 : {
4805 : Relation rel;
4806 : ScanKeyData key[3];
4807 : SysScanDesc scan;
4808 : HeapTuple oldtuple;
4809 : Datum oldAclDatum;
4810 : bool isNull;
4811 : Acl *old_acl;
4812 : Acl *new_acl;
4813 : HeapTuple newtuple;
4814 : int noldmembers;
4815 : int nnewmembers;
4816 : Oid *oldmembers;
4817 : Oid *newmembers;
4818 :
4819 : /* Search for existing pg_init_privs entry for the target object. */
4820 12 : rel = table_open(InitPrivsRelationId, RowExclusiveLock);
4821 :
4822 12 : ScanKeyInit(&key[0],
4823 : Anum_pg_init_privs_objoid,
4824 : BTEqualStrategyNumber, F_OIDEQ,
4825 : ObjectIdGetDatum(objid));
4826 12 : ScanKeyInit(&key[1],
4827 : Anum_pg_init_privs_classoid,
4828 : BTEqualStrategyNumber, F_OIDEQ,
4829 : ObjectIdGetDatum(classid));
4830 12 : ScanKeyInit(&key[2],
4831 : Anum_pg_init_privs_objsubid,
4832 : BTEqualStrategyNumber, F_INT4EQ,
4833 : Int32GetDatum(objsubid));
4834 :
4835 12 : scan = systable_beginscan(rel, InitPrivsObjIndexId, true,
4836 : NULL, 3, key);
4837 :
4838 : /* There should exist only one entry or none. */
4839 12 : oldtuple = systable_getnext(scan);
4840 :
4841 12 : if (!HeapTupleIsValid(oldtuple))
4842 : {
4843 : /*
4844 : * Hmm, why are we here if there's no entry? But pack up and go away
4845 : * quietly.
4846 : */
4847 0 : systable_endscan(scan);
4848 0 : table_close(rel, RowExclusiveLock);
4849 0 : return;
4850 : }
4851 :
4852 : /* Get a writable copy of the existing ACL. */
4853 12 : oldAclDatum = heap_getattr(oldtuple, Anum_pg_init_privs_initprivs,
4854 : RelationGetDescr(rel), &isNull);
4855 : Assert(!isNull);
4856 12 : old_acl = DatumGetAclPCopy(oldAclDatum);
4857 :
4858 : /*
4859 : * Generate new ACL. This usage of aclnewowner is a bit off-label when
4860 : * oldroleid isn't the owner; but it does the job fine.
4861 : */
4862 12 : new_acl = aclnewowner(old_acl, oldroleid, newroleid);
4863 :
4864 : /*
4865 : * If we end with an empty ACL, delete the pg_init_privs entry. (That
4866 : * probably can't happen here, but we may as well cover the case.)
4867 : */
4868 12 : if (new_acl == NULL || ACL_NUM(new_acl) == 0)
4869 : {
4870 0 : CatalogTupleDelete(rel, &oldtuple->t_self);
4871 : }
4872 : else
4873 : {
4874 12 : Datum values[Natts_pg_init_privs] = {0};
4875 12 : bool nulls[Natts_pg_init_privs] = {0};
4876 12 : bool replaces[Natts_pg_init_privs] = {0};
4877 :
4878 : /* Update existing entry. */
4879 12 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
4880 12 : replaces[Anum_pg_init_privs_initprivs - 1] = true;
4881 :
4882 12 : newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
4883 : values, nulls, replaces);
4884 12 : CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
4885 : }
4886 :
4887 : /*
4888 : * Update the shared dependency ACL info.
4889 : */
4890 12 : noldmembers = aclmembers(old_acl, &oldmembers);
4891 12 : nnewmembers = aclmembers(new_acl, &newmembers);
4892 :
4893 12 : updateInitAclDependencies(classid, objid, objsubid,
4894 : noldmembers, oldmembers,
4895 : nnewmembers, newmembers);
4896 :
4897 12 : systable_endscan(scan);
4898 :
4899 : /* prevent error when processing objects multiple times */
4900 12 : CommandCounterIncrement();
4901 :
4902 12 : table_close(rel, RowExclusiveLock);
4903 : }
4904 :
4905 : /*
4906 : * RemoveRoleFromInitPriv
4907 : *
4908 : * Used by shdepDropOwned to remove mentions of a role in pg_init_privs.
4909 : */
4910 : void
4911 14 : RemoveRoleFromInitPriv(Oid roleid, Oid classid, Oid objid, int32 objsubid)
4912 : {
4913 : Relation rel;
4914 : ScanKeyData key[3];
4915 : SysScanDesc scan;
4916 : HeapTuple oldtuple;
4917 : SysCacheIdentifier cacheid;
4918 : HeapTuple objtuple;
4919 : Oid ownerId;
4920 : Datum oldAclDatum;
4921 : bool isNull;
4922 : Acl *old_acl;
4923 : Acl *new_acl;
4924 : HeapTuple newtuple;
4925 : int noldmembers;
4926 : int nnewmembers;
4927 : Oid *oldmembers;
4928 : Oid *newmembers;
4929 :
4930 : /* Search for existing pg_init_privs entry for the target object. */
4931 14 : rel = table_open(InitPrivsRelationId, RowExclusiveLock);
4932 :
4933 14 : ScanKeyInit(&key[0],
4934 : Anum_pg_init_privs_objoid,
4935 : BTEqualStrategyNumber, F_OIDEQ,
4936 : ObjectIdGetDatum(objid));
4937 14 : ScanKeyInit(&key[1],
4938 : Anum_pg_init_privs_classoid,
4939 : BTEqualStrategyNumber, F_OIDEQ,
4940 : ObjectIdGetDatum(classid));
4941 14 : ScanKeyInit(&key[2],
4942 : Anum_pg_init_privs_objsubid,
4943 : BTEqualStrategyNumber, F_INT4EQ,
4944 : Int32GetDatum(objsubid));
4945 :
4946 14 : scan = systable_beginscan(rel, InitPrivsObjIndexId, true,
4947 : NULL, 3, key);
4948 :
4949 : /* There should exist only one entry or none. */
4950 14 : oldtuple = systable_getnext(scan);
4951 :
4952 14 : if (!HeapTupleIsValid(oldtuple))
4953 : {
4954 : /*
4955 : * Hmm, why are we here if there's no entry? But pack up and go away
4956 : * quietly.
4957 : */
4958 0 : systable_endscan(scan);
4959 0 : table_close(rel, RowExclusiveLock);
4960 0 : return;
4961 : }
4962 :
4963 : /* Get a writable copy of the existing ACL. */
4964 14 : oldAclDatum = heap_getattr(oldtuple, Anum_pg_init_privs_initprivs,
4965 : RelationGetDescr(rel), &isNull);
4966 : Assert(!isNull);
4967 14 : old_acl = DatumGetAclPCopy(oldAclDatum);
4968 :
4969 : /*
4970 : * We need the members of both old and new ACLs so we can correct the
4971 : * shared dependency information. Collect data before
4972 : * merge_acl_with_grant throws away old_acl.
4973 : */
4974 14 : noldmembers = aclmembers(old_acl, &oldmembers);
4975 :
4976 : /* Must find out the owner's OID the hard way. */
4977 14 : cacheid = get_object_catcache_oid(classid);
4978 14 : objtuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objid));
4979 14 : if (!HeapTupleIsValid(objtuple))
4980 0 : elog(ERROR, "cache lookup failed for %s %u",
4981 : get_object_class_descr(classid), objid);
4982 :
4983 14 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
4984 : objtuple,
4985 14 : get_object_attnum_owner(classid)));
4986 14 : ReleaseSysCache(objtuple);
4987 :
4988 : /*
4989 : * Generate new ACL. Grantor of rights is always the same as the owner.
4990 : */
4991 14 : if (old_acl != NULL)
4992 14 : new_acl = merge_acl_with_grant(old_acl,
4993 : false, /* is_grant */
4994 : false, /* grant_option */
4995 : DROP_RESTRICT,
4996 : list_make1_oid(roleid),
4997 : ACLITEM_ALL_PRIV_BITS,
4998 : ownerId,
4999 : ownerId);
5000 : else
5001 0 : new_acl = NULL; /* this case shouldn't happen, probably */
5002 :
5003 : /* If we end with an empty ACL, delete the pg_init_privs entry. */
5004 14 : if (new_acl == NULL || ACL_NUM(new_acl) == 0)
5005 : {
5006 0 : CatalogTupleDelete(rel, &oldtuple->t_self);
5007 : }
5008 : else
5009 : {
5010 14 : Datum values[Natts_pg_init_privs] = {0};
5011 14 : bool nulls[Natts_pg_init_privs] = {0};
5012 14 : bool replaces[Natts_pg_init_privs] = {0};
5013 :
5014 : /* Update existing entry. */
5015 14 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
5016 14 : replaces[Anum_pg_init_privs_initprivs - 1] = true;
5017 :
5018 14 : newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(rel),
5019 : values, nulls, replaces);
5020 14 : CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
5021 : }
5022 :
5023 : /*
5024 : * Update the shared dependency ACL info.
5025 : */
5026 14 : nnewmembers = aclmembers(new_acl, &newmembers);
5027 :
5028 14 : updateInitAclDependencies(classid, objid, objsubid,
5029 : noldmembers, oldmembers,
5030 : nnewmembers, newmembers);
5031 :
5032 14 : systable_endscan(scan);
5033 :
5034 : /* prevent error when processing objects multiple times */
5035 14 : CommandCounterIncrement();
5036 :
5037 14 : table_close(rel, RowExclusiveLock);
5038 : }
|