1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * aclchk.c |
4 | * Routines to check access control permissions. |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 | *------------------------------------------------------------------------- |
17 | */ |
18 | #include "postgres.h" |
19 | |
20 | #include "access/genam.h" |
21 | #include "access/heapam.h" |
22 | #include "access/htup_details.h" |
23 | #include "access/sysattr.h" |
24 | #include "access/tableam.h" |
25 | #include "access/xact.h" |
26 | #include "catalog/binary_upgrade.h" |
27 | #include "catalog/catalog.h" |
28 | #include "catalog/dependency.h" |
29 | #include "catalog/indexing.h" |
30 | #include "catalog/objectaccess.h" |
31 | #include "catalog/pg_aggregate.h" |
32 | #include "catalog/pg_am.h" |
33 | #include "catalog/pg_authid.h" |
34 | #include "catalog/pg_cast.h" |
35 | #include "catalog/pg_collation.h" |
36 | #include "catalog/pg_conversion.h" |
37 | #include "catalog/pg_database.h" |
38 | #include "catalog/pg_default_acl.h" |
39 | #include "catalog/pg_event_trigger.h" |
40 | #include "catalog/pg_extension.h" |
41 | #include "catalog/pg_foreign_data_wrapper.h" |
42 | #include "catalog/pg_foreign_server.h" |
43 | #include "catalog/pg_init_privs.h" |
44 | #include "catalog/pg_language.h" |
45 | #include "catalog/pg_largeobject.h" |
46 | #include "catalog/pg_largeobject_metadata.h" |
47 | #include "catalog/pg_namespace.h" |
48 | #include "catalog/pg_opclass.h" |
49 | #include "catalog/pg_operator.h" |
50 | #include "catalog/pg_opfamily.h" |
51 | #include "catalog/pg_proc.h" |
52 | #include "catalog/pg_statistic_ext.h" |
53 | #include "catalog/pg_subscription.h" |
54 | #include "catalog/pg_tablespace.h" |
55 | #include "catalog/pg_type.h" |
56 | #include "catalog/pg_ts_config.h" |
57 | #include "catalog/pg_ts_dict.h" |
58 | #include "catalog/pg_ts_parser.h" |
59 | #include "catalog/pg_ts_template.h" |
60 | #include "catalog/pg_transform.h" |
61 | #include "commands/dbcommands.h" |
62 | #include "commands/event_trigger.h" |
63 | #include "commands/extension.h" |
64 | #include "commands/proclang.h" |
65 | #include "commands/tablespace.h" |
66 | #include "foreign/foreign.h" |
67 | #include "miscadmin.h" |
68 | #include "nodes/makefuncs.h" |
69 | #include "parser/parse_func.h" |
70 | #include "parser/parse_type.h" |
71 | #include "utils/acl.h" |
72 | #include "utils/aclchk_internal.h" |
73 | #include "utils/builtins.h" |
74 | #include "utils/fmgroids.h" |
75 | #include "utils/lsyscache.h" |
76 | #include "utils/rel.h" |
77 | #include "utils/syscache.h" |
78 | |
79 | |
80 | /* |
81 | * Internal format used by ALTER DEFAULT PRIVILEGES. |
82 | */ |
83 | typedef struct |
84 | { |
85 | Oid roleid; /* owning role */ |
86 | Oid nspid; /* namespace, or InvalidOid if none */ |
87 | /* remaining fields are same as in InternalGrant: */ |
88 | bool is_grant; |
89 | ObjectType objtype; |
90 | bool all_privs; |
91 | AclMode privileges; |
92 | List *grantees; |
93 | bool grant_option; |
94 | DropBehavior behavior; |
95 | } InternalDefaultACL; |
96 | |
97 | /* |
98 | * When performing a binary-upgrade, pg_dump will call a function to set |
99 | * this variable to let us know that we need to populate the pg_init_privs |
100 | * table for the GRANT/REVOKE commands while this variable is set to true. |
101 | */ |
102 | bool binary_upgrade_record_init_privs = false; |
103 | |
104 | static void ExecGrantStmt_oids(InternalGrant *istmt); |
105 | static void ExecGrant_Relation(InternalGrant *grantStmt); |
106 | static void ExecGrant_Database(InternalGrant *grantStmt); |
107 | static void ExecGrant_Fdw(InternalGrant *grantStmt); |
108 | static void ExecGrant_ForeignServer(InternalGrant *grantStmt); |
109 | static void ExecGrant_Function(InternalGrant *grantStmt); |
110 | static void ExecGrant_Language(InternalGrant *grantStmt); |
111 | static void ExecGrant_Largeobject(InternalGrant *grantStmt); |
112 | static void ExecGrant_Namespace(InternalGrant *grantStmt); |
113 | static void ExecGrant_Tablespace(InternalGrant *grantStmt); |
114 | static void ExecGrant_Type(InternalGrant *grantStmt); |
115 | |
116 | static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames); |
117 | static void SetDefaultACL(InternalDefaultACL *iacls); |
118 | |
119 | static List *objectNamesToOids(ObjectType objtype, List *objnames); |
120 | static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames); |
121 | static List *getRelationsInNamespace(Oid namespaceId, char relkind); |
122 | static void expand_col_privileges(List *colnames, Oid table_oid, |
123 | AclMode this_privileges, |
124 | AclMode *col_privileges, |
125 | int num_col_privileges); |
126 | static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, |
127 | AclMode this_privileges, |
128 | AclMode *col_privileges, |
129 | int num_col_privileges); |
130 | static AclMode string_to_privilege(const char *privname); |
131 | static const char *privilege_to_string(AclMode privilege); |
132 | static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions, |
133 | bool all_privs, AclMode privileges, |
134 | Oid objectId, Oid grantorId, |
135 | ObjectType objtype, const char *objname, |
136 | AttrNumber att_number, const char *colname); |
137 | static AclMode pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, |
138 | Oid roleid, AclMode mask, AclMaskHow how); |
139 | static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, |
140 | Acl *new_acl); |
141 | static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, |
142 | Acl *new_acl); |
143 | |
144 | |
145 | #ifdef ACLDEBUG |
146 | static void |
147 | dumpacl(Acl *acl) |
148 | { |
149 | int i; |
150 | AclItem *aip; |
151 | |
152 | elog(DEBUG2, "acl size = %d, # acls = %d" , |
153 | ACL_SIZE(acl), ACL_NUM(acl)); |
154 | aip = ACL_DAT(acl); |
155 | for (i = 0; i < ACL_NUM(acl); ++i) |
156 | elog(DEBUG2, " acl[%d]: %s" , i, |
157 | DatumGetCString(DirectFunctionCall1(aclitemout, |
158 | PointerGetDatum(aip + i)))); |
159 | } |
160 | #endif /* ACLDEBUG */ |
161 | |
162 | |
163 | /* |
164 | * If is_grant is true, adds the given privileges for the list of |
165 | * grantees to the existing old_acl. If is_grant is false, the |
166 | * privileges for the given grantees are removed from old_acl. |
167 | * |
168 | * NB: the original old_acl is pfree'd. |
169 | */ |
170 | static Acl * |
171 | merge_acl_with_grant(Acl *old_acl, bool is_grant, |
172 | bool grant_option, DropBehavior behavior, |
173 | List *grantees, AclMode privileges, |
174 | Oid grantorId, Oid ownerId) |
175 | { |
176 | unsigned modechg; |
177 | ListCell *j; |
178 | Acl *new_acl; |
179 | |
180 | modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL; |
181 | |
182 | #ifdef ACLDEBUG |
183 | dumpacl(old_acl); |
184 | #endif |
185 | new_acl = old_acl; |
186 | |
187 | foreach(j, grantees) |
188 | { |
189 | AclItem aclitem; |
190 | Acl *newer_acl; |
191 | |
192 | aclitem.ai_grantee = lfirst_oid(j); |
193 | |
194 | /* |
195 | * Grant options can only be granted to individual roles, not PUBLIC. |
196 | * The reason is that if a user would re-grant a privilege that he |
197 | * held through PUBLIC, and later the user is removed, the situation |
198 | * is impossible to clean up. |
199 | */ |
200 | if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC) |
201 | ereport(ERROR, |
202 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
203 | errmsg("grant options can only be granted to roles" ))); |
204 | |
205 | aclitem.ai_grantor = grantorId; |
206 | |
207 | /* |
208 | * The asymmetry in the conditions here comes from the spec. In |
209 | * GRANT, the grant_option flag signals WITH GRANT OPTION, which means |
210 | * to grant both the basic privilege and its grant option. But in |
211 | * REVOKE, plain revoke revokes both the basic privilege and its grant |
212 | * option, while REVOKE GRANT OPTION revokes only the option. |
213 | */ |
214 | ACLITEM_SET_PRIVS_GOPTIONS(aclitem, |
215 | (is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS, |
216 | (!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS); |
217 | |
218 | newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior); |
219 | |
220 | /* avoid memory leak when there are many grantees */ |
221 | pfree(new_acl); |
222 | new_acl = newer_acl; |
223 | |
224 | #ifdef ACLDEBUG |
225 | dumpacl(new_acl); |
226 | #endif |
227 | } |
228 | |
229 | return new_acl; |
230 | } |
231 | |
232 | /* |
233 | * Restrict the privileges to what we can actually grant, and emit |
234 | * the standards-mandated warning and error messages. |
235 | */ |
236 | static AclMode |
237 | restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, |
238 | AclMode privileges, Oid objectId, Oid grantorId, |
239 | ObjectType objtype, const char *objname, |
240 | AttrNumber att_number, const char *colname) |
241 | { |
242 | AclMode this_privileges; |
243 | AclMode whole_mask; |
244 | |
245 | switch (objtype) |
246 | { |
247 | case OBJECT_COLUMN: |
248 | whole_mask = ACL_ALL_RIGHTS_COLUMN; |
249 | break; |
250 | case OBJECT_TABLE: |
251 | whole_mask = ACL_ALL_RIGHTS_RELATION; |
252 | break; |
253 | case OBJECT_SEQUENCE: |
254 | whole_mask = ACL_ALL_RIGHTS_SEQUENCE; |
255 | break; |
256 | case OBJECT_DATABASE: |
257 | whole_mask = ACL_ALL_RIGHTS_DATABASE; |
258 | break; |
259 | case OBJECT_FUNCTION: |
260 | whole_mask = ACL_ALL_RIGHTS_FUNCTION; |
261 | break; |
262 | case OBJECT_LANGUAGE: |
263 | whole_mask = ACL_ALL_RIGHTS_LANGUAGE; |
264 | break; |
265 | case OBJECT_LARGEOBJECT: |
266 | whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT; |
267 | break; |
268 | case OBJECT_SCHEMA: |
269 | whole_mask = ACL_ALL_RIGHTS_SCHEMA; |
270 | break; |
271 | case OBJECT_TABLESPACE: |
272 | whole_mask = ACL_ALL_RIGHTS_TABLESPACE; |
273 | break; |
274 | case OBJECT_FDW: |
275 | whole_mask = ACL_ALL_RIGHTS_FDW; |
276 | break; |
277 | case OBJECT_FOREIGN_SERVER: |
278 | whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER; |
279 | break; |
280 | case OBJECT_EVENT_TRIGGER: |
281 | elog(ERROR, "grantable rights not supported for event triggers" ); |
282 | /* not reached, but keep compiler quiet */ |
283 | return ACL_NO_RIGHTS; |
284 | case OBJECT_TYPE: |
285 | whole_mask = ACL_ALL_RIGHTS_TYPE; |
286 | break; |
287 | default: |
288 | elog(ERROR, "unrecognized object type: %d" , objtype); |
289 | /* not reached, but keep compiler quiet */ |
290 | return ACL_NO_RIGHTS; |
291 | } |
292 | |
293 | /* |
294 | * If we found no grant options, consider whether to issue a hard error. |
295 | * Per spec, having any privilege at all on the object will get you by |
296 | * here. |
297 | */ |
298 | if (avail_goptions == ACL_NO_RIGHTS) |
299 | { |
300 | if (pg_aclmask(objtype, objectId, att_number, grantorId, |
301 | whole_mask | ACL_GRANT_OPTION_FOR(whole_mask), |
302 | ACLMASK_ANY) == ACL_NO_RIGHTS) |
303 | { |
304 | if (objtype == OBJECT_COLUMN && colname) |
305 | aclcheck_error_col(ACLCHECK_NO_PRIV, objtype, objname, colname); |
306 | else |
307 | aclcheck_error(ACLCHECK_NO_PRIV, objtype, objname); |
308 | } |
309 | } |
310 | |
311 | /* |
312 | * Restrict the operation to what we can actually grant or revoke, and |
313 | * issue a warning if appropriate. (For REVOKE this isn't quite what the |
314 | * spec says to do: the spec seems to want a warning only if no privilege |
315 | * bits actually change in the ACL. In practice that behavior seems much |
316 | * too noisy, as well as inconsistent with the GRANT case.) |
317 | */ |
318 | this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); |
319 | if (is_grant) |
320 | { |
321 | if (this_privileges == 0) |
322 | { |
323 | if (objtype == OBJECT_COLUMN && colname) |
324 | ereport(WARNING, |
325 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED), |
326 | errmsg("no privileges were granted for column \"%s\" of relation \"%s\"" , |
327 | colname, objname))); |
328 | else |
329 | ereport(WARNING, |
330 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED), |
331 | errmsg("no privileges were granted for \"%s\"" , |
332 | objname))); |
333 | } |
334 | else if (!all_privs && this_privileges != privileges) |
335 | { |
336 | if (objtype == OBJECT_COLUMN && colname) |
337 | ereport(WARNING, |
338 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED), |
339 | errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"" , |
340 | colname, objname))); |
341 | else |
342 | ereport(WARNING, |
343 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED), |
344 | errmsg("not all privileges were granted for \"%s\"" , |
345 | objname))); |
346 | } |
347 | } |
348 | else |
349 | { |
350 | if (this_privileges == 0) |
351 | { |
352 | if (objtype == OBJECT_COLUMN && colname) |
353 | ereport(WARNING, |
354 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED), |
355 | errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"" , |
356 | colname, objname))); |
357 | else |
358 | ereport(WARNING, |
359 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED), |
360 | errmsg("no privileges could be revoked for \"%s\"" , |
361 | objname))); |
362 | } |
363 | else if (!all_privs && this_privileges != privileges) |
364 | { |
365 | if (objtype == OBJECT_COLUMN && colname) |
366 | ereport(WARNING, |
367 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED), |
368 | errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"" , |
369 | colname, objname))); |
370 | else |
371 | ereport(WARNING, |
372 | (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED), |
373 | errmsg("not all privileges could be revoked for \"%s\"" , |
374 | objname))); |
375 | } |
376 | } |
377 | |
378 | return this_privileges; |
379 | } |
380 | |
381 | /* |
382 | * Called to execute the utility commands GRANT and REVOKE |
383 | */ |
384 | void |
385 | ExecuteGrantStmt(GrantStmt *stmt) |
386 | { |
387 | InternalGrant istmt; |
388 | ListCell *cell; |
389 | const char *errormsg; |
390 | AclMode all_privileges; |
391 | |
392 | /* |
393 | * Turn the regular GrantStmt into the InternalGrant form. |
394 | */ |
395 | istmt.is_grant = stmt->is_grant; |
396 | istmt.objtype = stmt->objtype; |
397 | |
398 | /* Collect the OIDs of the target objects */ |
399 | switch (stmt->targtype) |
400 | { |
401 | case ACL_TARGET_OBJECT: |
402 | istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects); |
403 | break; |
404 | case ACL_TARGET_ALL_IN_SCHEMA: |
405 | istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects); |
406 | break; |
407 | /* ACL_TARGET_DEFAULTS should not be seen here */ |
408 | default: |
409 | elog(ERROR, "unrecognized GrantStmt.targtype: %d" , |
410 | (int) stmt->targtype); |
411 | } |
412 | |
413 | /* all_privs to be filled below */ |
414 | /* privileges to be filled below */ |
415 | istmt.col_privs = NIL; /* may get filled below */ |
416 | istmt.grantees = NIL; /* filled below */ |
417 | istmt.grant_option = stmt->grant_option; |
418 | istmt.behavior = stmt->behavior; |
419 | |
420 | /* |
421 | * Convert the RoleSpec list into an Oid list. Note that at this point we |
422 | * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream |
423 | * there shouldn't be any additional work needed to support this case. |
424 | */ |
425 | foreach(cell, stmt->grantees) |
426 | { |
427 | RoleSpec *grantee = (RoleSpec *) lfirst(cell); |
428 | Oid grantee_uid; |
429 | |
430 | switch (grantee->roletype) |
431 | { |
432 | case ROLESPEC_PUBLIC: |
433 | grantee_uid = ACL_ID_PUBLIC; |
434 | break; |
435 | default: |
436 | grantee_uid = get_rolespec_oid(grantee, false); |
437 | break; |
438 | } |
439 | istmt.grantees = lappend_oid(istmt.grantees, grantee_uid); |
440 | } |
441 | |
442 | /* |
443 | * Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode |
444 | * bitmask. Note: objtype can't be OBJECT_COLUMN. |
445 | */ |
446 | switch (stmt->objtype) |
447 | { |
448 | case OBJECT_TABLE: |
449 | |
450 | /* |
451 | * Because this might be a sequence, we test both relation and |
452 | * sequence bits, and later do a more limited test when we know |
453 | * the object type. |
454 | */ |
455 | all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE; |
456 | errormsg = gettext_noop("invalid privilege type %s for relation" ); |
457 | break; |
458 | case OBJECT_SEQUENCE: |
459 | all_privileges = ACL_ALL_RIGHTS_SEQUENCE; |
460 | errormsg = gettext_noop("invalid privilege type %s for sequence" ); |
461 | break; |
462 | case OBJECT_DATABASE: |
463 | all_privileges = ACL_ALL_RIGHTS_DATABASE; |
464 | errormsg = gettext_noop("invalid privilege type %s for database" ); |
465 | break; |
466 | case OBJECT_DOMAIN: |
467 | all_privileges = ACL_ALL_RIGHTS_TYPE; |
468 | errormsg = gettext_noop("invalid privilege type %s for domain" ); |
469 | break; |
470 | case OBJECT_FUNCTION: |
471 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
472 | errormsg = gettext_noop("invalid privilege type %s for function" ); |
473 | break; |
474 | case OBJECT_LANGUAGE: |
475 | all_privileges = ACL_ALL_RIGHTS_LANGUAGE; |
476 | errormsg = gettext_noop("invalid privilege type %s for language" ); |
477 | break; |
478 | case OBJECT_LARGEOBJECT: |
479 | all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT; |
480 | errormsg = gettext_noop("invalid privilege type %s for large object" ); |
481 | break; |
482 | case OBJECT_SCHEMA: |
483 | all_privileges = ACL_ALL_RIGHTS_SCHEMA; |
484 | errormsg = gettext_noop("invalid privilege type %s for schema" ); |
485 | break; |
486 | case OBJECT_PROCEDURE: |
487 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
488 | errormsg = gettext_noop("invalid privilege type %s for procedure" ); |
489 | break; |
490 | case OBJECT_ROUTINE: |
491 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
492 | errormsg = gettext_noop("invalid privilege type %s for routine" ); |
493 | break; |
494 | case OBJECT_TABLESPACE: |
495 | all_privileges = ACL_ALL_RIGHTS_TABLESPACE; |
496 | errormsg = gettext_noop("invalid privilege type %s for tablespace" ); |
497 | break; |
498 | case OBJECT_TYPE: |
499 | all_privileges = ACL_ALL_RIGHTS_TYPE; |
500 | errormsg = gettext_noop("invalid privilege type %s for type" ); |
501 | break; |
502 | case OBJECT_FDW: |
503 | all_privileges = ACL_ALL_RIGHTS_FDW; |
504 | errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper" ); |
505 | break; |
506 | case OBJECT_FOREIGN_SERVER: |
507 | all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER; |
508 | errormsg = gettext_noop("invalid privilege type %s for foreign server" ); |
509 | break; |
510 | default: |
511 | elog(ERROR, "unrecognized GrantStmt.objtype: %d" , |
512 | (int) stmt->objtype); |
513 | /* keep compiler quiet */ |
514 | all_privileges = ACL_NO_RIGHTS; |
515 | errormsg = NULL; |
516 | } |
517 | |
518 | if (stmt->privileges == NIL) |
519 | { |
520 | istmt.all_privs = true; |
521 | |
522 | /* |
523 | * will be turned into ACL_ALL_RIGHTS_* by the internal routines |
524 | * depending on the object type |
525 | */ |
526 | istmt.privileges = ACL_NO_RIGHTS; |
527 | } |
528 | else |
529 | { |
530 | istmt.all_privs = false; |
531 | istmt.privileges = ACL_NO_RIGHTS; |
532 | |
533 | foreach(cell, stmt->privileges) |
534 | { |
535 | AccessPriv *privnode = (AccessPriv *) lfirst(cell); |
536 | AclMode priv; |
537 | |
538 | /* |
539 | * If it's a column-level specification, we just set it aside in |
540 | * col_privs for the moment; but insist it's for a relation. |
541 | */ |
542 | if (privnode->cols) |
543 | { |
544 | if (stmt->objtype != OBJECT_TABLE) |
545 | ereport(ERROR, |
546 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
547 | errmsg("column privileges are only valid for relations" ))); |
548 | istmt.col_privs = lappend(istmt.col_privs, privnode); |
549 | continue; |
550 | } |
551 | |
552 | if (privnode->priv_name == NULL) /* parser mistake? */ |
553 | elog(ERROR, "AccessPriv node must specify privilege or columns" ); |
554 | priv = string_to_privilege(privnode->priv_name); |
555 | |
556 | if (priv & ~((AclMode) all_privileges)) |
557 | ereport(ERROR, |
558 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
559 | errmsg(errormsg, privilege_to_string(priv)))); |
560 | |
561 | istmt.privileges |= priv; |
562 | } |
563 | } |
564 | |
565 | ExecGrantStmt_oids(&istmt); |
566 | } |
567 | |
568 | /* |
569 | * ExecGrantStmt_oids |
570 | * |
571 | * Internal entry point for granting and revoking privileges. |
572 | */ |
573 | static void |
574 | ExecGrantStmt_oids(InternalGrant *istmt) |
575 | { |
576 | switch (istmt->objtype) |
577 | { |
578 | case OBJECT_TABLE: |
579 | case OBJECT_SEQUENCE: |
580 | ExecGrant_Relation(istmt); |
581 | break; |
582 | case OBJECT_DATABASE: |
583 | ExecGrant_Database(istmt); |
584 | break; |
585 | case OBJECT_DOMAIN: |
586 | case OBJECT_TYPE: |
587 | ExecGrant_Type(istmt); |
588 | break; |
589 | case OBJECT_FDW: |
590 | ExecGrant_Fdw(istmt); |
591 | break; |
592 | case OBJECT_FOREIGN_SERVER: |
593 | ExecGrant_ForeignServer(istmt); |
594 | break; |
595 | case OBJECT_FUNCTION: |
596 | case OBJECT_PROCEDURE: |
597 | case OBJECT_ROUTINE: |
598 | ExecGrant_Function(istmt); |
599 | break; |
600 | case OBJECT_LANGUAGE: |
601 | ExecGrant_Language(istmt); |
602 | break; |
603 | case OBJECT_LARGEOBJECT: |
604 | ExecGrant_Largeobject(istmt); |
605 | break; |
606 | case OBJECT_SCHEMA: |
607 | ExecGrant_Namespace(istmt); |
608 | break; |
609 | case OBJECT_TABLESPACE: |
610 | ExecGrant_Tablespace(istmt); |
611 | break; |
612 | default: |
613 | elog(ERROR, "unrecognized GrantStmt.objtype: %d" , |
614 | (int) istmt->objtype); |
615 | } |
616 | |
617 | /* |
618 | * Pass the info to event triggers about the just-executed GRANT. Note |
619 | * that we prefer to do it after actually executing it, because that gives |
620 | * the functions a chance to adjust the istmt with privileges actually |
621 | * granted. |
622 | */ |
623 | if (EventTriggerSupportsObjectType(istmt->objtype)) |
624 | EventTriggerCollectGrant(istmt); |
625 | } |
626 | |
627 | /* |
628 | * objectNamesToOids |
629 | * |
630 | * Turn a list of object names of a given type into an Oid list. |
631 | * |
632 | * XXX: This function doesn't take any sort of locks on the objects whose |
633 | * names it looks up. In the face of concurrent DDL, we might easily latch |
634 | * onto an old version of an object, causing the GRANT or REVOKE statement |
635 | * to fail. |
636 | */ |
637 | static List * |
638 | objectNamesToOids(ObjectType objtype, List *objnames) |
639 | { |
640 | List *objects = NIL; |
641 | ListCell *cell; |
642 | |
643 | Assert(objnames != NIL); |
644 | |
645 | switch (objtype) |
646 | { |
647 | case OBJECT_TABLE: |
648 | case OBJECT_SEQUENCE: |
649 | foreach(cell, objnames) |
650 | { |
651 | RangeVar *relvar = (RangeVar *) lfirst(cell); |
652 | Oid relOid; |
653 | |
654 | relOid = RangeVarGetRelid(relvar, NoLock, false); |
655 | objects = lappend_oid(objects, relOid); |
656 | } |
657 | break; |
658 | case OBJECT_DATABASE: |
659 | foreach(cell, objnames) |
660 | { |
661 | char *dbname = strVal(lfirst(cell)); |
662 | Oid dbid; |
663 | |
664 | dbid = get_database_oid(dbname, false); |
665 | objects = lappend_oid(objects, dbid); |
666 | } |
667 | break; |
668 | case OBJECT_DOMAIN: |
669 | case OBJECT_TYPE: |
670 | foreach(cell, objnames) |
671 | { |
672 | List *typname = (List *) lfirst(cell); |
673 | Oid oid; |
674 | |
675 | oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname)); |
676 | objects = lappend_oid(objects, oid); |
677 | } |
678 | break; |
679 | case OBJECT_FUNCTION: |
680 | foreach(cell, objnames) |
681 | { |
682 | ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell); |
683 | Oid funcid; |
684 | |
685 | funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false); |
686 | objects = lappend_oid(objects, funcid); |
687 | } |
688 | break; |
689 | case OBJECT_LANGUAGE: |
690 | foreach(cell, objnames) |
691 | { |
692 | char *langname = strVal(lfirst(cell)); |
693 | Oid oid; |
694 | |
695 | oid = get_language_oid(langname, false); |
696 | objects = lappend_oid(objects, oid); |
697 | } |
698 | break; |
699 | case OBJECT_LARGEOBJECT: |
700 | foreach(cell, objnames) |
701 | { |
702 | Oid lobjOid = oidparse(lfirst(cell)); |
703 | |
704 | if (!LargeObjectExists(lobjOid)) |
705 | ereport(ERROR, |
706 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
707 | errmsg("large object %u does not exist" , |
708 | lobjOid))); |
709 | |
710 | objects = lappend_oid(objects, lobjOid); |
711 | } |
712 | break; |
713 | case OBJECT_SCHEMA: |
714 | foreach(cell, objnames) |
715 | { |
716 | char *nspname = strVal(lfirst(cell)); |
717 | Oid oid; |
718 | |
719 | oid = get_namespace_oid(nspname, false); |
720 | objects = lappend_oid(objects, oid); |
721 | } |
722 | break; |
723 | case OBJECT_PROCEDURE: |
724 | foreach(cell, objnames) |
725 | { |
726 | ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell); |
727 | Oid procid; |
728 | |
729 | procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false); |
730 | objects = lappend_oid(objects, procid); |
731 | } |
732 | break; |
733 | case OBJECT_ROUTINE: |
734 | foreach(cell, objnames) |
735 | { |
736 | ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell); |
737 | Oid routid; |
738 | |
739 | routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false); |
740 | objects = lappend_oid(objects, routid); |
741 | } |
742 | break; |
743 | case OBJECT_TABLESPACE: |
744 | foreach(cell, objnames) |
745 | { |
746 | char *spcname = strVal(lfirst(cell)); |
747 | Oid spcoid; |
748 | |
749 | spcoid = get_tablespace_oid(spcname, false); |
750 | objects = lappend_oid(objects, spcoid); |
751 | } |
752 | break; |
753 | case OBJECT_FDW: |
754 | foreach(cell, objnames) |
755 | { |
756 | char *fdwname = strVal(lfirst(cell)); |
757 | Oid fdwid = get_foreign_data_wrapper_oid(fdwname, false); |
758 | |
759 | objects = lappend_oid(objects, fdwid); |
760 | } |
761 | break; |
762 | case OBJECT_FOREIGN_SERVER: |
763 | foreach(cell, objnames) |
764 | { |
765 | char *srvname = strVal(lfirst(cell)); |
766 | Oid srvid = get_foreign_server_oid(srvname, false); |
767 | |
768 | objects = lappend_oid(objects, srvid); |
769 | } |
770 | break; |
771 | default: |
772 | elog(ERROR, "unrecognized GrantStmt.objtype: %d" , |
773 | (int) objtype); |
774 | } |
775 | |
776 | return objects; |
777 | } |
778 | |
779 | /* |
780 | * objectsInSchemaToOids |
781 | * |
782 | * Find all objects of a given type in specified schemas, and make a list |
783 | * of their Oids. We check USAGE privilege on the schemas, but there is |
784 | * no privilege checking on the individual objects here. |
785 | */ |
786 | static List * |
787 | objectsInSchemaToOids(ObjectType objtype, List *nspnames) |
788 | { |
789 | List *objects = NIL; |
790 | ListCell *cell; |
791 | |
792 | foreach(cell, nspnames) |
793 | { |
794 | char *nspname = strVal(lfirst(cell)); |
795 | Oid namespaceId; |
796 | List *objs; |
797 | |
798 | namespaceId = LookupExplicitNamespace(nspname, false); |
799 | |
800 | switch (objtype) |
801 | { |
802 | case OBJECT_TABLE: |
803 | objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION); |
804 | objects = list_concat(objects, objs); |
805 | objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW); |
806 | objects = list_concat(objects, objs); |
807 | objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW); |
808 | objects = list_concat(objects, objs); |
809 | objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE); |
810 | objects = list_concat(objects, objs); |
811 | objs = getRelationsInNamespace(namespaceId, RELKIND_PARTITIONED_TABLE); |
812 | objects = list_concat(objects, objs); |
813 | break; |
814 | case OBJECT_SEQUENCE: |
815 | objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE); |
816 | objects = list_concat(objects, objs); |
817 | break; |
818 | case OBJECT_FUNCTION: |
819 | case OBJECT_PROCEDURE: |
820 | case OBJECT_ROUTINE: |
821 | { |
822 | ScanKeyData key[2]; |
823 | int keycount; |
824 | Relation rel; |
825 | TableScanDesc scan; |
826 | HeapTuple tuple; |
827 | |
828 | keycount = 0; |
829 | ScanKeyInit(&key[keycount++], |
830 | Anum_pg_proc_pronamespace, |
831 | BTEqualStrategyNumber, F_OIDEQ, |
832 | ObjectIdGetDatum(namespaceId)); |
833 | |
834 | if (objtype == OBJECT_FUNCTION) |
835 | /* includes aggregates and window functions */ |
836 | ScanKeyInit(&key[keycount++], |
837 | Anum_pg_proc_prokind, |
838 | BTEqualStrategyNumber, F_CHARNE, |
839 | CharGetDatum(PROKIND_PROCEDURE)); |
840 | else if (objtype == OBJECT_PROCEDURE) |
841 | ScanKeyInit(&key[keycount++], |
842 | Anum_pg_proc_prokind, |
843 | BTEqualStrategyNumber, F_CHAREQ, |
844 | CharGetDatum(PROKIND_PROCEDURE)); |
845 | |
846 | rel = table_open(ProcedureRelationId, AccessShareLock); |
847 | scan = table_beginscan_catalog(rel, keycount, key); |
848 | |
849 | while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) |
850 | { |
851 | Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid; |
852 | |
853 | objects = lappend_oid(objects, oid); |
854 | } |
855 | |
856 | table_endscan(scan); |
857 | table_close(rel, AccessShareLock); |
858 | } |
859 | break; |
860 | default: |
861 | /* should not happen */ |
862 | elog(ERROR, "unrecognized GrantStmt.objtype: %d" , |
863 | (int) objtype); |
864 | } |
865 | } |
866 | |
867 | return objects; |
868 | } |
869 | |
870 | /* |
871 | * getRelationsInNamespace |
872 | * |
873 | * Return Oid list of relations in given namespace filtered by relation kind |
874 | */ |
875 | static List * |
876 | getRelationsInNamespace(Oid namespaceId, char relkind) |
877 | { |
878 | List *relations = NIL; |
879 | ScanKeyData key[2]; |
880 | Relation rel; |
881 | TableScanDesc scan; |
882 | HeapTuple tuple; |
883 | |
884 | ScanKeyInit(&key[0], |
885 | Anum_pg_class_relnamespace, |
886 | BTEqualStrategyNumber, F_OIDEQ, |
887 | ObjectIdGetDatum(namespaceId)); |
888 | ScanKeyInit(&key[1], |
889 | Anum_pg_class_relkind, |
890 | BTEqualStrategyNumber, F_CHAREQ, |
891 | CharGetDatum(relkind)); |
892 | |
893 | rel = table_open(RelationRelationId, AccessShareLock); |
894 | scan = table_beginscan_catalog(rel, 2, key); |
895 | |
896 | while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) |
897 | { |
898 | Oid oid = ((Form_pg_class) GETSTRUCT(tuple))->oid; |
899 | |
900 | relations = lappend_oid(relations, oid); |
901 | } |
902 | |
903 | table_endscan(scan); |
904 | table_close(rel, AccessShareLock); |
905 | |
906 | return relations; |
907 | } |
908 | |
909 | |
910 | /* |
911 | * ALTER DEFAULT PRIVILEGES statement |
912 | */ |
913 | void |
914 | ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *stmt) |
915 | { |
916 | GrantStmt *action = stmt->action; |
917 | InternalDefaultACL iacls; |
918 | ListCell *cell; |
919 | List *rolespecs = NIL; |
920 | List *nspnames = NIL; |
921 | DefElem *drolespecs = NULL; |
922 | DefElem *dnspnames = NULL; |
923 | AclMode all_privileges; |
924 | const char *errormsg; |
925 | |
926 | /* Deconstruct the "options" part of the statement */ |
927 | foreach(cell, stmt->options) |
928 | { |
929 | DefElem *defel = (DefElem *) lfirst(cell); |
930 | |
931 | if (strcmp(defel->defname, "schemas" ) == 0) |
932 | { |
933 | if (dnspnames) |
934 | ereport(ERROR, |
935 | (errcode(ERRCODE_SYNTAX_ERROR), |
936 | errmsg("conflicting or redundant options" ), |
937 | parser_errposition(pstate, defel->location))); |
938 | dnspnames = defel; |
939 | } |
940 | else if (strcmp(defel->defname, "roles" ) == 0) |
941 | { |
942 | if (drolespecs) |
943 | ereport(ERROR, |
944 | (errcode(ERRCODE_SYNTAX_ERROR), |
945 | errmsg("conflicting or redundant options" ), |
946 | parser_errposition(pstate, defel->location))); |
947 | drolespecs = defel; |
948 | } |
949 | else |
950 | elog(ERROR, "option \"%s\" not recognized" , defel->defname); |
951 | } |
952 | |
953 | if (dnspnames) |
954 | nspnames = (List *) dnspnames->arg; |
955 | if (drolespecs) |
956 | rolespecs = (List *) drolespecs->arg; |
957 | |
958 | /* Prepare the InternalDefaultACL representation of the statement */ |
959 | /* roleid to be filled below */ |
960 | /* nspid to be filled in SetDefaultACLsInSchemas */ |
961 | iacls.is_grant = action->is_grant; |
962 | iacls.objtype = action->objtype; |
963 | /* all_privs to be filled below */ |
964 | /* privileges to be filled below */ |
965 | iacls.grantees = NIL; /* filled below */ |
966 | iacls.grant_option = action->grant_option; |
967 | iacls.behavior = action->behavior; |
968 | |
969 | /* |
970 | * Convert the RoleSpec list into an Oid list. Note that at this point we |
971 | * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream |
972 | * there shouldn't be any additional work needed to support this case. |
973 | */ |
974 | foreach(cell, action->grantees) |
975 | { |
976 | RoleSpec *grantee = (RoleSpec *) lfirst(cell); |
977 | Oid grantee_uid; |
978 | |
979 | switch (grantee->roletype) |
980 | { |
981 | case ROLESPEC_PUBLIC: |
982 | grantee_uid = ACL_ID_PUBLIC; |
983 | break; |
984 | default: |
985 | grantee_uid = get_rolespec_oid(grantee, false); |
986 | break; |
987 | } |
988 | iacls.grantees = lappend_oid(iacls.grantees, grantee_uid); |
989 | } |
990 | |
991 | /* |
992 | * Convert action->privileges, a list of privilege strings, into an |
993 | * AclMode bitmask. |
994 | */ |
995 | switch (action->objtype) |
996 | { |
997 | case OBJECT_TABLE: |
998 | all_privileges = ACL_ALL_RIGHTS_RELATION; |
999 | errormsg = gettext_noop("invalid privilege type %s for relation" ); |
1000 | break; |
1001 | case OBJECT_SEQUENCE: |
1002 | all_privileges = ACL_ALL_RIGHTS_SEQUENCE; |
1003 | errormsg = gettext_noop("invalid privilege type %s for sequence" ); |
1004 | break; |
1005 | case OBJECT_FUNCTION: |
1006 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
1007 | errormsg = gettext_noop("invalid privilege type %s for function" ); |
1008 | break; |
1009 | case OBJECT_PROCEDURE: |
1010 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
1011 | errormsg = gettext_noop("invalid privilege type %s for procedure" ); |
1012 | break; |
1013 | case OBJECT_ROUTINE: |
1014 | all_privileges = ACL_ALL_RIGHTS_FUNCTION; |
1015 | errormsg = gettext_noop("invalid privilege type %s for routine" ); |
1016 | break; |
1017 | case OBJECT_TYPE: |
1018 | all_privileges = ACL_ALL_RIGHTS_TYPE; |
1019 | errormsg = gettext_noop("invalid privilege type %s for type" ); |
1020 | break; |
1021 | case OBJECT_SCHEMA: |
1022 | all_privileges = ACL_ALL_RIGHTS_SCHEMA; |
1023 | errormsg = gettext_noop("invalid privilege type %s for schema" ); |
1024 | break; |
1025 | default: |
1026 | elog(ERROR, "unrecognized GrantStmt.objtype: %d" , |
1027 | (int) action->objtype); |
1028 | /* keep compiler quiet */ |
1029 | all_privileges = ACL_NO_RIGHTS; |
1030 | errormsg = NULL; |
1031 | } |
1032 | |
1033 | if (action->privileges == NIL) |
1034 | { |
1035 | iacls.all_privs = true; |
1036 | |
1037 | /* |
1038 | * will be turned into ACL_ALL_RIGHTS_* by the internal routines |
1039 | * depending on the object type |
1040 | */ |
1041 | iacls.privileges = ACL_NO_RIGHTS; |
1042 | } |
1043 | else |
1044 | { |
1045 | iacls.all_privs = false; |
1046 | iacls.privileges = ACL_NO_RIGHTS; |
1047 | |
1048 | foreach(cell, action->privileges) |
1049 | { |
1050 | AccessPriv *privnode = (AccessPriv *) lfirst(cell); |
1051 | AclMode priv; |
1052 | |
1053 | if (privnode->cols) |
1054 | ereport(ERROR, |
1055 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1056 | errmsg("default privileges cannot be set for columns" ))); |
1057 | |
1058 | if (privnode->priv_name == NULL) /* parser mistake? */ |
1059 | elog(ERROR, "AccessPriv node must specify privilege" ); |
1060 | priv = string_to_privilege(privnode->priv_name); |
1061 | |
1062 | if (priv & ~((AclMode) all_privileges)) |
1063 | ereport(ERROR, |
1064 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1065 | errmsg(errormsg, privilege_to_string(priv)))); |
1066 | |
1067 | iacls.privileges |= priv; |
1068 | } |
1069 | } |
1070 | |
1071 | if (rolespecs == NIL) |
1072 | { |
1073 | /* Set permissions for myself */ |
1074 | iacls.roleid = GetUserId(); |
1075 | |
1076 | SetDefaultACLsInSchemas(&iacls, nspnames); |
1077 | } |
1078 | else |
1079 | { |
1080 | /* Look up the role OIDs and do permissions checks */ |
1081 | ListCell *rolecell; |
1082 | |
1083 | foreach(rolecell, rolespecs) |
1084 | { |
1085 | RoleSpec *rolespec = lfirst(rolecell); |
1086 | |
1087 | iacls.roleid = get_rolespec_oid(rolespec, false); |
1088 | |
1089 | /* |
1090 | * We insist that calling user be a member of each target role. If |
1091 | * he has that, he could become that role anyway via SET ROLE, so |
1092 | * FOR ROLE is just a syntactic convenience and doesn't give any |
1093 | * special privileges. |
1094 | */ |
1095 | check_is_member_of_role(GetUserId(), iacls.roleid); |
1096 | |
1097 | SetDefaultACLsInSchemas(&iacls, nspnames); |
1098 | } |
1099 | } |
1100 | } |
1101 | |
1102 | /* |
1103 | * Process ALTER DEFAULT PRIVILEGES for a list of target schemas |
1104 | * |
1105 | * All fields of *iacls except nspid were filled already |
1106 | */ |
1107 | static void |
1108 | SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames) |
1109 | { |
1110 | if (nspnames == NIL) |
1111 | { |
1112 | /* Set database-wide permissions if no schema was specified */ |
1113 | iacls->nspid = InvalidOid; |
1114 | |
1115 | SetDefaultACL(iacls); |
1116 | } |
1117 | else |
1118 | { |
1119 | /* Look up the schema OIDs and set permissions for each one */ |
1120 | ListCell *nspcell; |
1121 | |
1122 | foreach(nspcell, nspnames) |
1123 | { |
1124 | char *nspname = strVal(lfirst(nspcell)); |
1125 | |
1126 | iacls->nspid = get_namespace_oid(nspname, false); |
1127 | |
1128 | /* |
1129 | * We used to insist that the target role have CREATE privileges |
1130 | * on the schema, since without that it wouldn't be able to create |
1131 | * an object for which these default privileges would apply. |
1132 | * However, this check proved to be more confusing than helpful, |
1133 | * and it also caused certain database states to not be |
1134 | * dumpable/restorable, since revoking CREATE doesn't cause |
1135 | * default privileges for the schema to go away. So now, we just |
1136 | * allow the ALTER; if the user lacks CREATE he'll find out when |
1137 | * he tries to create an object. |
1138 | */ |
1139 | |
1140 | SetDefaultACL(iacls); |
1141 | } |
1142 | } |
1143 | } |
1144 | |
1145 | |
1146 | /* |
1147 | * Create or update a pg_default_acl entry |
1148 | */ |
1149 | static void |
1150 | SetDefaultACL(InternalDefaultACL *iacls) |
1151 | { |
1152 | AclMode this_privileges = iacls->privileges; |
1153 | char objtype; |
1154 | Relation rel; |
1155 | HeapTuple tuple; |
1156 | bool isNew; |
1157 | Acl *def_acl; |
1158 | Acl *old_acl; |
1159 | Acl *new_acl; |
1160 | HeapTuple newtuple; |
1161 | Datum values[Natts_pg_default_acl]; |
1162 | bool nulls[Natts_pg_default_acl]; |
1163 | bool replaces[Natts_pg_default_acl]; |
1164 | int noldmembers; |
1165 | int nnewmembers; |
1166 | Oid *oldmembers; |
1167 | Oid *newmembers; |
1168 | |
1169 | rel = table_open(DefaultAclRelationId, RowExclusiveLock); |
1170 | |
1171 | /* |
1172 | * The default for a global entry is the hard-wired default ACL for the |
1173 | * particular object type. The default for non-global entries is an empty |
1174 | * ACL. This must be so because global entries replace the hard-wired |
1175 | * defaults, while others are added on. |
1176 | */ |
1177 | if (!OidIsValid(iacls->nspid)) |
1178 | def_acl = acldefault(iacls->objtype, iacls->roleid); |
1179 | else |
1180 | def_acl = make_empty_acl(); |
1181 | |
1182 | /* |
1183 | * Convert ACL object type to pg_default_acl object type and handle |
1184 | * all_privs option |
1185 | */ |
1186 | switch (iacls->objtype) |
1187 | { |
1188 | case OBJECT_TABLE: |
1189 | objtype = DEFACLOBJ_RELATION; |
1190 | if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) |
1191 | this_privileges = ACL_ALL_RIGHTS_RELATION; |
1192 | break; |
1193 | |
1194 | case OBJECT_SEQUENCE: |
1195 | objtype = DEFACLOBJ_SEQUENCE; |
1196 | if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) |
1197 | this_privileges = ACL_ALL_RIGHTS_SEQUENCE; |
1198 | break; |
1199 | |
1200 | case OBJECT_FUNCTION: |
1201 | objtype = DEFACLOBJ_FUNCTION; |
1202 | if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) |
1203 | this_privileges = ACL_ALL_RIGHTS_FUNCTION; |
1204 | break; |
1205 | |
1206 | case OBJECT_TYPE: |
1207 | objtype = DEFACLOBJ_TYPE; |
1208 | if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) |
1209 | this_privileges = ACL_ALL_RIGHTS_TYPE; |
1210 | break; |
1211 | |
1212 | case OBJECT_SCHEMA: |
1213 | if (OidIsValid(iacls->nspid)) |
1214 | ereport(ERROR, |
1215 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1216 | errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS" ))); |
1217 | objtype = DEFACLOBJ_NAMESPACE; |
1218 | if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS) |
1219 | this_privileges = ACL_ALL_RIGHTS_SCHEMA; |
1220 | break; |
1221 | |
1222 | default: |
1223 | elog(ERROR, "unrecognized objtype: %d" , |
1224 | (int) iacls->objtype); |
1225 | objtype = 0; /* keep compiler quiet */ |
1226 | break; |
1227 | } |
1228 | |
1229 | /* Search for existing row for this object type in catalog */ |
1230 | tuple = SearchSysCache3(DEFACLROLENSPOBJ, |
1231 | ObjectIdGetDatum(iacls->roleid), |
1232 | ObjectIdGetDatum(iacls->nspid), |
1233 | CharGetDatum(objtype)); |
1234 | |
1235 | if (HeapTupleIsValid(tuple)) |
1236 | { |
1237 | Datum aclDatum; |
1238 | bool isNull; |
1239 | |
1240 | aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple, |
1241 | Anum_pg_default_acl_defaclacl, |
1242 | &isNull); |
1243 | if (!isNull) |
1244 | old_acl = DatumGetAclPCopy(aclDatum); |
1245 | else |
1246 | old_acl = NULL; /* this case shouldn't happen, probably */ |
1247 | isNew = false; |
1248 | } |
1249 | else |
1250 | { |
1251 | old_acl = NULL; |
1252 | isNew = true; |
1253 | } |
1254 | |
1255 | if (old_acl != NULL) |
1256 | { |
1257 | /* |
1258 | * We need the members of both old and new ACLs so we can correct the |
1259 | * shared dependency information. Collect data before |
1260 | * merge_acl_with_grant throws away old_acl. |
1261 | */ |
1262 | noldmembers = aclmembers(old_acl, &oldmembers); |
1263 | } |
1264 | else |
1265 | { |
1266 | /* If no or null entry, start with the default ACL value */ |
1267 | old_acl = aclcopy(def_acl); |
1268 | /* There are no old member roles according to the catalogs */ |
1269 | noldmembers = 0; |
1270 | oldmembers = NULL; |
1271 | } |
1272 | |
1273 | /* |
1274 | * Generate new ACL. Grantor of rights is always the same as the target |
1275 | * role. |
1276 | */ |
1277 | new_acl = merge_acl_with_grant(old_acl, |
1278 | iacls->is_grant, |
1279 | iacls->grant_option, |
1280 | iacls->behavior, |
1281 | iacls->grantees, |
1282 | this_privileges, |
1283 | iacls->roleid, |
1284 | iacls->roleid); |
1285 | |
1286 | /* |
1287 | * If the result is the same as the default value, we do not need an |
1288 | * explicit pg_default_acl entry, and should in fact remove the entry if |
1289 | * it exists. Must sort both arrays to compare properly. |
1290 | */ |
1291 | aclitemsort(new_acl); |
1292 | aclitemsort(def_acl); |
1293 | if (aclequal(new_acl, def_acl)) |
1294 | { |
1295 | /* delete old entry, if indeed there is one */ |
1296 | if (!isNew) |
1297 | { |
1298 | ObjectAddress myself; |
1299 | |
1300 | /* |
1301 | * The dependency machinery will take care of removing all |
1302 | * associated dependency entries. We use DROP_RESTRICT since |
1303 | * there shouldn't be anything depending on this entry. |
1304 | */ |
1305 | myself.classId = DefaultAclRelationId; |
1306 | myself.objectId = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid; |
1307 | myself.objectSubId = 0; |
1308 | |
1309 | performDeletion(&myself, DROP_RESTRICT, 0); |
1310 | } |
1311 | } |
1312 | else |
1313 | { |
1314 | Oid defAclOid; |
1315 | |
1316 | /* Prepare to insert or update pg_default_acl entry */ |
1317 | MemSet(values, 0, sizeof(values)); |
1318 | MemSet(nulls, false, sizeof(nulls)); |
1319 | MemSet(replaces, false, sizeof(replaces)); |
1320 | |
1321 | if (isNew) |
1322 | { |
1323 | /* insert new entry */ |
1324 | defAclOid = GetNewOidWithIndex(rel, DefaultAclOidIndexId, |
1325 | Anum_pg_default_acl_oid); |
1326 | values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(defAclOid); |
1327 | values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid); |
1328 | values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid); |
1329 | values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype); |
1330 | values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl); |
1331 | |
1332 | newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); |
1333 | CatalogTupleInsert(rel, newtuple); |
1334 | } |
1335 | else |
1336 | { |
1337 | defAclOid = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid; |
1338 | |
1339 | /* update existing entry */ |
1340 | values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl); |
1341 | replaces[Anum_pg_default_acl_defaclacl - 1] = true; |
1342 | |
1343 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), |
1344 | values, nulls, replaces); |
1345 | CatalogTupleUpdate(rel, &newtuple->t_self, newtuple); |
1346 | } |
1347 | |
1348 | /* these dependencies don't change in an update */ |
1349 | if (isNew) |
1350 | { |
1351 | /* dependency on role */ |
1352 | recordDependencyOnOwner(DefaultAclRelationId, defAclOid, |
1353 | iacls->roleid); |
1354 | |
1355 | /* dependency on namespace */ |
1356 | if (OidIsValid(iacls->nspid)) |
1357 | { |
1358 | ObjectAddress myself, |
1359 | referenced; |
1360 | |
1361 | myself.classId = DefaultAclRelationId; |
1362 | myself.objectId = defAclOid; |
1363 | myself.objectSubId = 0; |
1364 | |
1365 | referenced.classId = NamespaceRelationId; |
1366 | referenced.objectId = iacls->nspid; |
1367 | referenced.objectSubId = 0; |
1368 | |
1369 | recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); |
1370 | } |
1371 | } |
1372 | |
1373 | /* |
1374 | * Update the shared dependency ACL info |
1375 | */ |
1376 | nnewmembers = aclmembers(new_acl, &newmembers); |
1377 | |
1378 | updateAclDependencies(DefaultAclRelationId, |
1379 | defAclOid, 0, |
1380 | iacls->roleid, |
1381 | noldmembers, oldmembers, |
1382 | nnewmembers, newmembers); |
1383 | |
1384 | if (isNew) |
1385 | InvokeObjectPostCreateHook(DefaultAclRelationId, defAclOid, 0); |
1386 | else |
1387 | InvokeObjectPostAlterHook(DefaultAclRelationId, defAclOid, 0); |
1388 | } |
1389 | |
1390 | if (HeapTupleIsValid(tuple)) |
1391 | ReleaseSysCache(tuple); |
1392 | |
1393 | table_close(rel, RowExclusiveLock); |
1394 | } |
1395 | |
1396 | |
1397 | /* |
1398 | * RemoveRoleFromObjectACL |
1399 | * |
1400 | * Used by shdepDropOwned to remove mentions of a role in ACLs |
1401 | */ |
1402 | void |
1403 | RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid) |
1404 | { |
1405 | if (classid == DefaultAclRelationId) |
1406 | { |
1407 | InternalDefaultACL iacls; |
1408 | Form_pg_default_acl pg_default_acl_tuple; |
1409 | Relation rel; |
1410 | ScanKeyData skey[1]; |
1411 | SysScanDesc scan; |
1412 | HeapTuple tuple; |
1413 | |
1414 | /* first fetch info needed by SetDefaultACL */ |
1415 | rel = table_open(DefaultAclRelationId, AccessShareLock); |
1416 | |
1417 | ScanKeyInit(&skey[0], |
1418 | Anum_pg_default_acl_oid, |
1419 | BTEqualStrategyNumber, F_OIDEQ, |
1420 | ObjectIdGetDatum(objid)); |
1421 | |
1422 | scan = systable_beginscan(rel, DefaultAclOidIndexId, true, |
1423 | NULL, 1, skey); |
1424 | |
1425 | tuple = systable_getnext(scan); |
1426 | |
1427 | if (!HeapTupleIsValid(tuple)) |
1428 | elog(ERROR, "could not find tuple for default ACL %u" , objid); |
1429 | |
1430 | pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple); |
1431 | |
1432 | iacls.roleid = pg_default_acl_tuple->defaclrole; |
1433 | iacls.nspid = pg_default_acl_tuple->defaclnamespace; |
1434 | |
1435 | switch (pg_default_acl_tuple->defaclobjtype) |
1436 | { |
1437 | case DEFACLOBJ_RELATION: |
1438 | iacls.objtype = OBJECT_TABLE; |
1439 | break; |
1440 | case DEFACLOBJ_SEQUENCE: |
1441 | iacls.objtype = OBJECT_SEQUENCE; |
1442 | break; |
1443 | case DEFACLOBJ_FUNCTION: |
1444 | iacls.objtype = OBJECT_FUNCTION; |
1445 | break; |
1446 | case DEFACLOBJ_TYPE: |
1447 | iacls.objtype = OBJECT_TYPE; |
1448 | break; |
1449 | case DEFACLOBJ_NAMESPACE: |
1450 | iacls.objtype = OBJECT_SCHEMA; |
1451 | break; |
1452 | default: |
1453 | /* Shouldn't get here */ |
1454 | elog(ERROR, "unexpected default ACL type: %d" , |
1455 | (int) pg_default_acl_tuple->defaclobjtype); |
1456 | break; |
1457 | } |
1458 | |
1459 | systable_endscan(scan); |
1460 | table_close(rel, AccessShareLock); |
1461 | |
1462 | iacls.is_grant = false; |
1463 | iacls.all_privs = true; |
1464 | iacls.privileges = ACL_NO_RIGHTS; |
1465 | iacls.grantees = list_make1_oid(roleid); |
1466 | iacls.grant_option = false; |
1467 | iacls.behavior = DROP_CASCADE; |
1468 | |
1469 | /* Do it */ |
1470 | SetDefaultACL(&iacls); |
1471 | } |
1472 | else |
1473 | { |
1474 | InternalGrant istmt; |
1475 | |
1476 | switch (classid) |
1477 | { |
1478 | case RelationRelationId: |
1479 | /* it's OK to use TABLE for a sequence */ |
1480 | istmt.objtype = OBJECT_TABLE; |
1481 | break; |
1482 | case DatabaseRelationId: |
1483 | istmt.objtype = OBJECT_DATABASE; |
1484 | break; |
1485 | case TypeRelationId: |
1486 | istmt.objtype = OBJECT_TYPE; |
1487 | break; |
1488 | case ProcedureRelationId: |
1489 | istmt.objtype = OBJECT_ROUTINE; |
1490 | break; |
1491 | case LanguageRelationId: |
1492 | istmt.objtype = OBJECT_LANGUAGE; |
1493 | break; |
1494 | case LargeObjectRelationId: |
1495 | istmt.objtype = OBJECT_LARGEOBJECT; |
1496 | break; |
1497 | case NamespaceRelationId: |
1498 | istmt.objtype = OBJECT_SCHEMA; |
1499 | break; |
1500 | case TableSpaceRelationId: |
1501 | istmt.objtype = OBJECT_TABLESPACE; |
1502 | break; |
1503 | case ForeignServerRelationId: |
1504 | istmt.objtype = OBJECT_FOREIGN_SERVER; |
1505 | break; |
1506 | case ForeignDataWrapperRelationId: |
1507 | istmt.objtype = OBJECT_FDW; |
1508 | break; |
1509 | default: |
1510 | elog(ERROR, "unexpected object class %u" , classid); |
1511 | break; |
1512 | } |
1513 | istmt.is_grant = false; |
1514 | istmt.objects = list_make1_oid(objid); |
1515 | istmt.all_privs = true; |
1516 | istmt.privileges = ACL_NO_RIGHTS; |
1517 | istmt.col_privs = NIL; |
1518 | istmt.grantees = list_make1_oid(roleid); |
1519 | istmt.grant_option = false; |
1520 | istmt.behavior = DROP_CASCADE; |
1521 | |
1522 | ExecGrantStmt_oids(&istmt); |
1523 | } |
1524 | } |
1525 | |
1526 | |
1527 | /* |
1528 | * Remove a pg_default_acl entry |
1529 | */ |
1530 | void |
1531 | RemoveDefaultACLById(Oid defaclOid) |
1532 | { |
1533 | Relation rel; |
1534 | ScanKeyData skey[1]; |
1535 | SysScanDesc scan; |
1536 | HeapTuple tuple; |
1537 | |
1538 | rel = table_open(DefaultAclRelationId, RowExclusiveLock); |
1539 | |
1540 | ScanKeyInit(&skey[0], |
1541 | Anum_pg_default_acl_oid, |
1542 | BTEqualStrategyNumber, F_OIDEQ, |
1543 | ObjectIdGetDatum(defaclOid)); |
1544 | |
1545 | scan = systable_beginscan(rel, DefaultAclOidIndexId, true, |
1546 | NULL, 1, skey); |
1547 | |
1548 | tuple = systable_getnext(scan); |
1549 | |
1550 | if (!HeapTupleIsValid(tuple)) |
1551 | elog(ERROR, "could not find tuple for default ACL %u" , defaclOid); |
1552 | |
1553 | CatalogTupleDelete(rel, &tuple->t_self); |
1554 | |
1555 | systable_endscan(scan); |
1556 | table_close(rel, RowExclusiveLock); |
1557 | } |
1558 | |
1559 | |
1560 | /* |
1561 | * expand_col_privileges |
1562 | * |
1563 | * OR the specified privilege(s) into per-column array entries for each |
1564 | * specified attribute. The per-column array is indexed starting at |
1565 | * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute. |
1566 | */ |
1567 | static void |
1568 | expand_col_privileges(List *colnames, Oid table_oid, |
1569 | AclMode this_privileges, |
1570 | AclMode *col_privileges, |
1571 | int num_col_privileges) |
1572 | { |
1573 | ListCell *cell; |
1574 | |
1575 | foreach(cell, colnames) |
1576 | { |
1577 | char *colname = strVal(lfirst(cell)); |
1578 | AttrNumber attnum; |
1579 | |
1580 | attnum = get_attnum(table_oid, colname); |
1581 | if (attnum == InvalidAttrNumber) |
1582 | ereport(ERROR, |
1583 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
1584 | errmsg("column \"%s\" of relation \"%s\" does not exist" , |
1585 | colname, get_rel_name(table_oid)))); |
1586 | attnum -= FirstLowInvalidHeapAttributeNumber; |
1587 | if (attnum <= 0 || attnum >= num_col_privileges) |
1588 | elog(ERROR, "column number out of range" ); /* safety check */ |
1589 | col_privileges[attnum] |= this_privileges; |
1590 | } |
1591 | } |
1592 | |
1593 | /* |
1594 | * expand_all_col_privileges |
1595 | * |
1596 | * OR the specified privilege(s) into per-column array entries for each valid |
1597 | * attribute of a relation. The per-column array is indexed starting at |
1598 | * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute. |
1599 | */ |
1600 | static void |
1601 | expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, |
1602 | AclMode this_privileges, |
1603 | AclMode *col_privileges, |
1604 | int num_col_privileges) |
1605 | { |
1606 | AttrNumber curr_att; |
1607 | |
1608 | Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges); |
1609 | for (curr_att = FirstLowInvalidHeapAttributeNumber + 1; |
1610 | curr_att <= classForm->relnatts; |
1611 | curr_att++) |
1612 | { |
1613 | HeapTuple attTuple; |
1614 | bool isdropped; |
1615 | |
1616 | if (curr_att == InvalidAttrNumber) |
1617 | continue; |
1618 | |
1619 | /* Views don't have any system columns at all */ |
1620 | if (classForm->relkind == RELKIND_VIEW && curr_att < 0) |
1621 | continue; |
1622 | |
1623 | attTuple = SearchSysCache2(ATTNUM, |
1624 | ObjectIdGetDatum(table_oid), |
1625 | Int16GetDatum(curr_att)); |
1626 | if (!HeapTupleIsValid(attTuple)) |
1627 | elog(ERROR, "cache lookup failed for attribute %d of relation %u" , |
1628 | curr_att, table_oid); |
1629 | |
1630 | isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped; |
1631 | |
1632 | ReleaseSysCache(attTuple); |
1633 | |
1634 | /* ignore dropped columns */ |
1635 | if (isdropped) |
1636 | continue; |
1637 | |
1638 | col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges; |
1639 | } |
1640 | } |
1641 | |
1642 | /* |
1643 | * This processes attributes, but expects to be called from |
1644 | * ExecGrant_Relation, not directly from ExecGrantStmt. |
1645 | */ |
1646 | static void |
1647 | ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, |
1648 | AttrNumber attnum, Oid ownerId, AclMode col_privileges, |
1649 | Relation attRelation, const Acl *old_rel_acl) |
1650 | { |
1651 | HeapTuple attr_tuple; |
1652 | Form_pg_attribute pg_attribute_tuple; |
1653 | Acl *old_acl; |
1654 | Acl *new_acl; |
1655 | Acl *merged_acl; |
1656 | Datum aclDatum; |
1657 | bool isNull; |
1658 | Oid grantorId; |
1659 | AclMode avail_goptions; |
1660 | bool need_update; |
1661 | HeapTuple newtuple; |
1662 | Datum values[Natts_pg_attribute]; |
1663 | bool nulls[Natts_pg_attribute]; |
1664 | bool replaces[Natts_pg_attribute]; |
1665 | int noldmembers; |
1666 | int nnewmembers; |
1667 | Oid *oldmembers; |
1668 | Oid *newmembers; |
1669 | |
1670 | attr_tuple = SearchSysCache2(ATTNUM, |
1671 | ObjectIdGetDatum(relOid), |
1672 | Int16GetDatum(attnum)); |
1673 | if (!HeapTupleIsValid(attr_tuple)) |
1674 | elog(ERROR, "cache lookup failed for attribute %d of relation %u" , |
1675 | attnum, relOid); |
1676 | pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple); |
1677 | |
1678 | /* |
1679 | * Get working copy of existing ACL. If there's no ACL, substitute the |
1680 | * proper default. |
1681 | */ |
1682 | aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl, |
1683 | &isNull); |
1684 | if (isNull) |
1685 | { |
1686 | old_acl = acldefault(OBJECT_COLUMN, ownerId); |
1687 | /* There are no old member roles according to the catalogs */ |
1688 | noldmembers = 0; |
1689 | oldmembers = NULL; |
1690 | } |
1691 | else |
1692 | { |
1693 | old_acl = DatumGetAclPCopy(aclDatum); |
1694 | /* Get the roles mentioned in the existing ACL */ |
1695 | noldmembers = aclmembers(old_acl, &oldmembers); |
1696 | } |
1697 | |
1698 | /* |
1699 | * In select_best_grantor we should consider existing table-level ACL bits |
1700 | * as well as the per-column ACL. Build a new ACL that is their |
1701 | * concatenation. (This is a bit cheap and dirty compared to merging them |
1702 | * properly with no duplications, but it's all we need here.) |
1703 | */ |
1704 | merged_acl = aclconcat(old_rel_acl, old_acl); |
1705 | |
1706 | /* Determine ID to do the grant as, and available grant options */ |
1707 | select_best_grantor(GetUserId(), col_privileges, |
1708 | merged_acl, ownerId, |
1709 | &grantorId, &avail_goptions); |
1710 | |
1711 | pfree(merged_acl); |
1712 | |
1713 | /* |
1714 | * Restrict the privileges to what we can actually grant, and emit the |
1715 | * standards-mandated warning and error messages. Note: we don't track |
1716 | * whether the user actually used the ALL PRIVILEGES(columns) syntax for |
1717 | * each column; we just approximate it by whether all the possible |
1718 | * privileges are specified now. Since the all_privs flag only determines |
1719 | * whether a warning is issued, this seems close enough. |
1720 | */ |
1721 | col_privileges = |
1722 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
1723 | (col_privileges == ACL_ALL_RIGHTS_COLUMN), |
1724 | col_privileges, |
1725 | relOid, grantorId, OBJECT_COLUMN, |
1726 | relname, attnum, |
1727 | NameStr(pg_attribute_tuple->attname)); |
1728 | |
1729 | /* |
1730 | * Generate new ACL. |
1731 | */ |
1732 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
1733 | istmt->grant_option, |
1734 | istmt->behavior, istmt->grantees, |
1735 | col_privileges, grantorId, |
1736 | ownerId); |
1737 | |
1738 | /* |
1739 | * We need the members of both old and new ACLs so we can correct the |
1740 | * shared dependency information. |
1741 | */ |
1742 | nnewmembers = aclmembers(new_acl, &newmembers); |
1743 | |
1744 | /* finished building new ACL value, now insert it */ |
1745 | MemSet(values, 0, sizeof(values)); |
1746 | MemSet(nulls, false, sizeof(nulls)); |
1747 | MemSet(replaces, false, sizeof(replaces)); |
1748 | |
1749 | /* |
1750 | * If the updated ACL is empty, we can set attacl to null, and maybe even |
1751 | * avoid an update of the pg_attribute row. This is worth testing because |
1752 | * we'll come through here multiple times for any relation-level REVOKE, |
1753 | * even if there were never any column GRANTs. Note we are assuming that |
1754 | * the "default" ACL state for columns is empty. |
1755 | */ |
1756 | if (ACL_NUM(new_acl) > 0) |
1757 | { |
1758 | values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl); |
1759 | need_update = true; |
1760 | } |
1761 | else |
1762 | { |
1763 | nulls[Anum_pg_attribute_attacl - 1] = true; |
1764 | need_update = !isNull; |
1765 | } |
1766 | replaces[Anum_pg_attribute_attacl - 1] = true; |
1767 | |
1768 | if (need_update) |
1769 | { |
1770 | newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation), |
1771 | values, nulls, replaces); |
1772 | |
1773 | CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple); |
1774 | |
1775 | /* Update initial privileges for extensions */ |
1776 | recordExtensionInitPriv(relOid, RelationRelationId, attnum, |
1777 | ACL_NUM(new_acl) > 0 ? new_acl : NULL); |
1778 | |
1779 | /* Update the shared dependency ACL info */ |
1780 | updateAclDependencies(RelationRelationId, relOid, attnum, |
1781 | ownerId, |
1782 | noldmembers, oldmembers, |
1783 | nnewmembers, newmembers); |
1784 | } |
1785 | |
1786 | pfree(new_acl); |
1787 | |
1788 | ReleaseSysCache(attr_tuple); |
1789 | } |
1790 | |
1791 | /* |
1792 | * This processes both sequences and non-sequences. |
1793 | */ |
1794 | static void |
1795 | ExecGrant_Relation(InternalGrant *istmt) |
1796 | { |
1797 | Relation relation; |
1798 | Relation attRelation; |
1799 | ListCell *cell; |
1800 | |
1801 | relation = table_open(RelationRelationId, RowExclusiveLock); |
1802 | attRelation = table_open(AttributeRelationId, RowExclusiveLock); |
1803 | |
1804 | foreach(cell, istmt->objects) |
1805 | { |
1806 | Oid relOid = lfirst_oid(cell); |
1807 | Datum aclDatum; |
1808 | Form_pg_class pg_class_tuple; |
1809 | bool isNull; |
1810 | AclMode this_privileges; |
1811 | AclMode *col_privileges; |
1812 | int num_col_privileges; |
1813 | bool have_col_privileges; |
1814 | Acl *old_acl; |
1815 | Acl *old_rel_acl; |
1816 | int noldmembers; |
1817 | Oid *oldmembers; |
1818 | Oid ownerId; |
1819 | HeapTuple tuple; |
1820 | ListCell *cell_colprivs; |
1821 | |
1822 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); |
1823 | if (!HeapTupleIsValid(tuple)) |
1824 | elog(ERROR, "cache lookup failed for relation %u" , relOid); |
1825 | pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); |
1826 | |
1827 | /* Not sensible to grant on an index */ |
1828 | if (pg_class_tuple->relkind == RELKIND_INDEX || |
1829 | pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX) |
1830 | ereport(ERROR, |
1831 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1832 | errmsg("\"%s\" is an index" , |
1833 | NameStr(pg_class_tuple->relname)))); |
1834 | |
1835 | /* Composite types aren't tables either */ |
1836 | if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) |
1837 | ereport(ERROR, |
1838 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1839 | errmsg("\"%s\" is a composite type" , |
1840 | NameStr(pg_class_tuple->relname)))); |
1841 | |
1842 | /* Used GRANT SEQUENCE on a non-sequence? */ |
1843 | if (istmt->objtype == OBJECT_SEQUENCE && |
1844 | pg_class_tuple->relkind != RELKIND_SEQUENCE) |
1845 | ereport(ERROR, |
1846 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1847 | errmsg("\"%s\" is not a sequence" , |
1848 | NameStr(pg_class_tuple->relname)))); |
1849 | |
1850 | /* Adjust the default permissions based on object type */ |
1851 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
1852 | { |
1853 | if (pg_class_tuple->relkind == RELKIND_SEQUENCE) |
1854 | this_privileges = ACL_ALL_RIGHTS_SEQUENCE; |
1855 | else |
1856 | this_privileges = ACL_ALL_RIGHTS_RELATION; |
1857 | } |
1858 | else |
1859 | this_privileges = istmt->privileges; |
1860 | |
1861 | /* |
1862 | * The GRANT TABLE syntax can be used for sequences and non-sequences, |
1863 | * so we have to look at the relkind to determine the supported |
1864 | * permissions. The OR of table and sequence permissions were already |
1865 | * checked. |
1866 | */ |
1867 | if (istmt->objtype == OBJECT_TABLE) |
1868 | { |
1869 | if (pg_class_tuple->relkind == RELKIND_SEQUENCE) |
1870 | { |
1871 | /* |
1872 | * For backward compatibility, just throw a warning for |
1873 | * invalid sequence permissions when using the non-sequence |
1874 | * GRANT syntax. |
1875 | */ |
1876 | if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE)) |
1877 | { |
1878 | /* |
1879 | * Mention the object name because the user needs to know |
1880 | * which operations succeeded. This is required because |
1881 | * WARNING allows the command to continue. |
1882 | */ |
1883 | ereport(WARNING, |
1884 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1885 | errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges" , |
1886 | NameStr(pg_class_tuple->relname)))); |
1887 | this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE; |
1888 | } |
1889 | } |
1890 | else |
1891 | { |
1892 | if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) |
1893 | { |
1894 | /* |
1895 | * USAGE is the only permission supported by sequences but |
1896 | * not by non-sequences. Don't mention the object name |
1897 | * because we didn't in the combined TABLE | SEQUENCE |
1898 | * check. |
1899 | */ |
1900 | ereport(ERROR, |
1901 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1902 | errmsg("invalid privilege type %s for table" , |
1903 | "USAGE" ))); |
1904 | } |
1905 | } |
1906 | } |
1907 | |
1908 | /* |
1909 | * Set up array in which we'll accumulate any column privilege bits |
1910 | * that need modification. The array is indexed such that entry [0] |
1911 | * corresponds to FirstLowInvalidHeapAttributeNumber. |
1912 | */ |
1913 | num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1; |
1914 | col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode)); |
1915 | have_col_privileges = false; |
1916 | |
1917 | /* |
1918 | * If we are revoking relation privileges that are also column |
1919 | * privileges, we must implicitly revoke them from each column too, |
1920 | * per SQL spec. (We don't need to implicitly add column privileges |
1921 | * during GRANT because the permissions-checking code always checks |
1922 | * both relation and per-column privileges.) |
1923 | */ |
1924 | if (!istmt->is_grant && |
1925 | (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0) |
1926 | { |
1927 | expand_all_col_privileges(relOid, pg_class_tuple, |
1928 | this_privileges & ACL_ALL_RIGHTS_COLUMN, |
1929 | col_privileges, |
1930 | num_col_privileges); |
1931 | have_col_privileges = true; |
1932 | } |
1933 | |
1934 | /* |
1935 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
1936 | * substitute the proper default. |
1937 | */ |
1938 | ownerId = pg_class_tuple->relowner; |
1939 | aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, |
1940 | &isNull); |
1941 | if (isNull) |
1942 | { |
1943 | switch (pg_class_tuple->relkind) |
1944 | { |
1945 | case RELKIND_SEQUENCE: |
1946 | old_acl = acldefault(OBJECT_SEQUENCE, ownerId); |
1947 | break; |
1948 | default: |
1949 | old_acl = acldefault(OBJECT_TABLE, ownerId); |
1950 | break; |
1951 | } |
1952 | /* There are no old member roles according to the catalogs */ |
1953 | noldmembers = 0; |
1954 | oldmembers = NULL; |
1955 | } |
1956 | else |
1957 | { |
1958 | old_acl = DatumGetAclPCopy(aclDatum); |
1959 | /* Get the roles mentioned in the existing ACL */ |
1960 | noldmembers = aclmembers(old_acl, &oldmembers); |
1961 | } |
1962 | |
1963 | /* Need an extra copy of original rel ACL for column handling */ |
1964 | old_rel_acl = aclcopy(old_acl); |
1965 | |
1966 | /* |
1967 | * Handle relation-level privileges, if any were specified |
1968 | */ |
1969 | if (this_privileges != ACL_NO_RIGHTS) |
1970 | { |
1971 | AclMode avail_goptions; |
1972 | Acl *new_acl; |
1973 | Oid grantorId; |
1974 | HeapTuple newtuple; |
1975 | Datum values[Natts_pg_class]; |
1976 | bool nulls[Natts_pg_class]; |
1977 | bool replaces[Natts_pg_class]; |
1978 | int nnewmembers; |
1979 | Oid *newmembers; |
1980 | ObjectType objtype; |
1981 | |
1982 | /* Determine ID to do the grant as, and available grant options */ |
1983 | select_best_grantor(GetUserId(), this_privileges, |
1984 | old_acl, ownerId, |
1985 | &grantorId, &avail_goptions); |
1986 | |
1987 | switch (pg_class_tuple->relkind) |
1988 | { |
1989 | case RELKIND_SEQUENCE: |
1990 | objtype = OBJECT_SEQUENCE; |
1991 | break; |
1992 | default: |
1993 | objtype = OBJECT_TABLE; |
1994 | break; |
1995 | } |
1996 | |
1997 | /* |
1998 | * Restrict the privileges to what we can actually grant, and emit |
1999 | * the standards-mandated warning and error messages. |
2000 | */ |
2001 | this_privileges = |
2002 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2003 | istmt->all_privs, this_privileges, |
2004 | relOid, grantorId, objtype, |
2005 | NameStr(pg_class_tuple->relname), |
2006 | 0, NULL); |
2007 | |
2008 | /* |
2009 | * Generate new ACL. |
2010 | */ |
2011 | new_acl = merge_acl_with_grant(old_acl, |
2012 | istmt->is_grant, |
2013 | istmt->grant_option, |
2014 | istmt->behavior, |
2015 | istmt->grantees, |
2016 | this_privileges, |
2017 | grantorId, |
2018 | ownerId); |
2019 | |
2020 | /* |
2021 | * We need the members of both old and new ACLs so we can correct |
2022 | * the shared dependency information. |
2023 | */ |
2024 | nnewmembers = aclmembers(new_acl, &newmembers); |
2025 | |
2026 | /* finished building new ACL value, now insert it */ |
2027 | MemSet(values, 0, sizeof(values)); |
2028 | MemSet(nulls, false, sizeof(nulls)); |
2029 | MemSet(replaces, false, sizeof(replaces)); |
2030 | |
2031 | replaces[Anum_pg_class_relacl - 1] = true; |
2032 | values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl); |
2033 | |
2034 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), |
2035 | values, nulls, replaces); |
2036 | |
2037 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2038 | |
2039 | /* Update initial privileges for extensions */ |
2040 | recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl); |
2041 | |
2042 | /* Update the shared dependency ACL info */ |
2043 | updateAclDependencies(RelationRelationId, relOid, 0, |
2044 | ownerId, |
2045 | noldmembers, oldmembers, |
2046 | nnewmembers, newmembers); |
2047 | |
2048 | pfree(new_acl); |
2049 | } |
2050 | |
2051 | /* |
2052 | * Handle column-level privileges, if any were specified or implied. |
2053 | * We first expand the user-specified column privileges into the |
2054 | * array, and then iterate over all nonempty array entries. |
2055 | */ |
2056 | foreach(cell_colprivs, istmt->col_privs) |
2057 | { |
2058 | AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs); |
2059 | |
2060 | if (col_privs->priv_name == NULL) |
2061 | this_privileges = ACL_ALL_RIGHTS_COLUMN; |
2062 | else |
2063 | this_privileges = string_to_privilege(col_privs->priv_name); |
2064 | |
2065 | if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN)) |
2066 | ereport(ERROR, |
2067 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
2068 | errmsg("invalid privilege type %s for column" , |
2069 | privilege_to_string(this_privileges)))); |
2070 | |
2071 | if (pg_class_tuple->relkind == RELKIND_SEQUENCE && |
2072 | this_privileges & ~((AclMode) ACL_SELECT)) |
2073 | { |
2074 | /* |
2075 | * The only column privilege allowed on sequences is SELECT. |
2076 | * This is a warning not error because we do it that way for |
2077 | * relation-level privileges. |
2078 | */ |
2079 | ereport(WARNING, |
2080 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
2081 | errmsg("sequence \"%s\" only supports SELECT column privileges" , |
2082 | NameStr(pg_class_tuple->relname)))); |
2083 | |
2084 | this_privileges &= (AclMode) ACL_SELECT; |
2085 | } |
2086 | |
2087 | expand_col_privileges(col_privs->cols, relOid, |
2088 | this_privileges, |
2089 | col_privileges, |
2090 | num_col_privileges); |
2091 | have_col_privileges = true; |
2092 | } |
2093 | |
2094 | if (have_col_privileges) |
2095 | { |
2096 | AttrNumber i; |
2097 | |
2098 | for (i = 0; i < num_col_privileges; i++) |
2099 | { |
2100 | if (col_privileges[i] == ACL_NO_RIGHTS) |
2101 | continue; |
2102 | ExecGrant_Attribute(istmt, |
2103 | relOid, |
2104 | NameStr(pg_class_tuple->relname), |
2105 | i + FirstLowInvalidHeapAttributeNumber, |
2106 | ownerId, |
2107 | col_privileges[i], |
2108 | attRelation, |
2109 | old_rel_acl); |
2110 | } |
2111 | } |
2112 | |
2113 | pfree(old_rel_acl); |
2114 | pfree(col_privileges); |
2115 | |
2116 | ReleaseSysCache(tuple); |
2117 | |
2118 | /* prevent error when processing duplicate objects */ |
2119 | CommandCounterIncrement(); |
2120 | } |
2121 | |
2122 | table_close(attRelation, RowExclusiveLock); |
2123 | table_close(relation, RowExclusiveLock); |
2124 | } |
2125 | |
2126 | static void |
2127 | ExecGrant_Database(InternalGrant *istmt) |
2128 | { |
2129 | Relation relation; |
2130 | ListCell *cell; |
2131 | |
2132 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2133 | istmt->privileges = ACL_ALL_RIGHTS_DATABASE; |
2134 | |
2135 | relation = table_open(DatabaseRelationId, RowExclusiveLock); |
2136 | |
2137 | foreach(cell, istmt->objects) |
2138 | { |
2139 | Oid datId = lfirst_oid(cell); |
2140 | Form_pg_database pg_database_tuple; |
2141 | Datum aclDatum; |
2142 | bool isNull; |
2143 | AclMode avail_goptions; |
2144 | AclMode this_privileges; |
2145 | Acl *old_acl; |
2146 | Acl *new_acl; |
2147 | Oid grantorId; |
2148 | Oid ownerId; |
2149 | HeapTuple newtuple; |
2150 | Datum values[Natts_pg_database]; |
2151 | bool nulls[Natts_pg_database]; |
2152 | bool replaces[Natts_pg_database]; |
2153 | int noldmembers; |
2154 | int nnewmembers; |
2155 | Oid *oldmembers; |
2156 | Oid *newmembers; |
2157 | HeapTuple tuple; |
2158 | |
2159 | tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(datId)); |
2160 | if (!HeapTupleIsValid(tuple)) |
2161 | elog(ERROR, "cache lookup failed for database %u" , datId); |
2162 | |
2163 | pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple); |
2164 | |
2165 | /* |
2166 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2167 | * substitute the proper default. |
2168 | */ |
2169 | ownerId = pg_database_tuple->datdba; |
2170 | aclDatum = heap_getattr(tuple, Anum_pg_database_datacl, |
2171 | RelationGetDescr(relation), &isNull); |
2172 | if (isNull) |
2173 | { |
2174 | old_acl = acldefault(OBJECT_DATABASE, ownerId); |
2175 | /* There are no old member roles according to the catalogs */ |
2176 | noldmembers = 0; |
2177 | oldmembers = NULL; |
2178 | } |
2179 | else |
2180 | { |
2181 | old_acl = DatumGetAclPCopy(aclDatum); |
2182 | /* Get the roles mentioned in the existing ACL */ |
2183 | noldmembers = aclmembers(old_acl, &oldmembers); |
2184 | } |
2185 | |
2186 | /* Determine ID to do the grant as, and available grant options */ |
2187 | select_best_grantor(GetUserId(), istmt->privileges, |
2188 | old_acl, ownerId, |
2189 | &grantorId, &avail_goptions); |
2190 | |
2191 | /* |
2192 | * Restrict the privileges to what we can actually grant, and emit the |
2193 | * standards-mandated warning and error messages. |
2194 | */ |
2195 | this_privileges = |
2196 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2197 | istmt->all_privs, istmt->privileges, |
2198 | datId, grantorId, OBJECT_DATABASE, |
2199 | NameStr(pg_database_tuple->datname), |
2200 | 0, NULL); |
2201 | |
2202 | /* |
2203 | * Generate new ACL. |
2204 | */ |
2205 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2206 | istmt->grant_option, istmt->behavior, |
2207 | istmt->grantees, this_privileges, |
2208 | grantorId, ownerId); |
2209 | |
2210 | /* |
2211 | * We need the members of both old and new ACLs so we can correct the |
2212 | * shared dependency information. |
2213 | */ |
2214 | nnewmembers = aclmembers(new_acl, &newmembers); |
2215 | |
2216 | /* finished building new ACL value, now insert it */ |
2217 | MemSet(values, 0, sizeof(values)); |
2218 | MemSet(nulls, false, sizeof(nulls)); |
2219 | MemSet(replaces, false, sizeof(replaces)); |
2220 | |
2221 | replaces[Anum_pg_database_datacl - 1] = true; |
2222 | values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl); |
2223 | |
2224 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2225 | nulls, replaces); |
2226 | |
2227 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2228 | |
2229 | /* Update the shared dependency ACL info */ |
2230 | updateAclDependencies(DatabaseRelationId, pg_database_tuple->oid, 0, |
2231 | ownerId, |
2232 | noldmembers, oldmembers, |
2233 | nnewmembers, newmembers); |
2234 | |
2235 | ReleaseSysCache(tuple); |
2236 | |
2237 | pfree(new_acl); |
2238 | |
2239 | /* prevent error when processing duplicate objects */ |
2240 | CommandCounterIncrement(); |
2241 | } |
2242 | |
2243 | table_close(relation, RowExclusiveLock); |
2244 | } |
2245 | |
2246 | static void |
2247 | ExecGrant_Fdw(InternalGrant *istmt) |
2248 | { |
2249 | Relation relation; |
2250 | ListCell *cell; |
2251 | |
2252 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2253 | istmt->privileges = ACL_ALL_RIGHTS_FDW; |
2254 | |
2255 | relation = table_open(ForeignDataWrapperRelationId, RowExclusiveLock); |
2256 | |
2257 | foreach(cell, istmt->objects) |
2258 | { |
2259 | Oid fdwid = lfirst_oid(cell); |
2260 | Form_pg_foreign_data_wrapper pg_fdw_tuple; |
2261 | Datum aclDatum; |
2262 | bool isNull; |
2263 | AclMode avail_goptions; |
2264 | AclMode this_privileges; |
2265 | Acl *old_acl; |
2266 | Acl *new_acl; |
2267 | Oid grantorId; |
2268 | Oid ownerId; |
2269 | HeapTuple tuple; |
2270 | HeapTuple newtuple; |
2271 | Datum values[Natts_pg_foreign_data_wrapper]; |
2272 | bool nulls[Natts_pg_foreign_data_wrapper]; |
2273 | bool replaces[Natts_pg_foreign_data_wrapper]; |
2274 | int noldmembers; |
2275 | int nnewmembers; |
2276 | Oid *oldmembers; |
2277 | Oid *newmembers; |
2278 | |
2279 | tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, |
2280 | ObjectIdGetDatum(fdwid)); |
2281 | if (!HeapTupleIsValid(tuple)) |
2282 | elog(ERROR, "cache lookup failed for foreign-data wrapper %u" , fdwid); |
2283 | |
2284 | pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); |
2285 | |
2286 | /* |
2287 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2288 | * substitute the proper default. |
2289 | */ |
2290 | ownerId = pg_fdw_tuple->fdwowner; |
2291 | aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple, |
2292 | Anum_pg_foreign_data_wrapper_fdwacl, |
2293 | &isNull); |
2294 | if (isNull) |
2295 | { |
2296 | old_acl = acldefault(OBJECT_FDW, ownerId); |
2297 | /* There are no old member roles according to the catalogs */ |
2298 | noldmembers = 0; |
2299 | oldmembers = NULL; |
2300 | } |
2301 | else |
2302 | { |
2303 | old_acl = DatumGetAclPCopy(aclDatum); |
2304 | /* Get the roles mentioned in the existing ACL */ |
2305 | noldmembers = aclmembers(old_acl, &oldmembers); |
2306 | } |
2307 | |
2308 | /* Determine ID to do the grant as, and available grant options */ |
2309 | select_best_grantor(GetUserId(), istmt->privileges, |
2310 | old_acl, ownerId, |
2311 | &grantorId, &avail_goptions); |
2312 | |
2313 | /* |
2314 | * Restrict the privileges to what we can actually grant, and emit the |
2315 | * standards-mandated warning and error messages. |
2316 | */ |
2317 | this_privileges = |
2318 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2319 | istmt->all_privs, istmt->privileges, |
2320 | fdwid, grantorId, OBJECT_FDW, |
2321 | NameStr(pg_fdw_tuple->fdwname), |
2322 | 0, NULL); |
2323 | |
2324 | /* |
2325 | * Generate new ACL. |
2326 | */ |
2327 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2328 | istmt->grant_option, istmt->behavior, |
2329 | istmt->grantees, this_privileges, |
2330 | grantorId, ownerId); |
2331 | |
2332 | /* |
2333 | * We need the members of both old and new ACLs so we can correct the |
2334 | * shared dependency information. |
2335 | */ |
2336 | nnewmembers = aclmembers(new_acl, &newmembers); |
2337 | |
2338 | /* finished building new ACL value, now insert it */ |
2339 | MemSet(values, 0, sizeof(values)); |
2340 | MemSet(nulls, false, sizeof(nulls)); |
2341 | MemSet(replaces, false, sizeof(replaces)); |
2342 | |
2343 | replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; |
2344 | values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl); |
2345 | |
2346 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2347 | nulls, replaces); |
2348 | |
2349 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2350 | |
2351 | /* Update initial privileges for extensions */ |
2352 | recordExtensionInitPriv(fdwid, ForeignDataWrapperRelationId, 0, |
2353 | new_acl); |
2354 | |
2355 | /* Update the shared dependency ACL info */ |
2356 | updateAclDependencies(ForeignDataWrapperRelationId, |
2357 | pg_fdw_tuple->oid, 0, |
2358 | ownerId, |
2359 | noldmembers, oldmembers, |
2360 | nnewmembers, newmembers); |
2361 | |
2362 | ReleaseSysCache(tuple); |
2363 | |
2364 | pfree(new_acl); |
2365 | |
2366 | /* prevent error when processing duplicate objects */ |
2367 | CommandCounterIncrement(); |
2368 | } |
2369 | |
2370 | table_close(relation, RowExclusiveLock); |
2371 | } |
2372 | |
2373 | static void |
2374 | ExecGrant_ForeignServer(InternalGrant *istmt) |
2375 | { |
2376 | Relation relation; |
2377 | ListCell *cell; |
2378 | |
2379 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2380 | istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER; |
2381 | |
2382 | relation = table_open(ForeignServerRelationId, RowExclusiveLock); |
2383 | |
2384 | foreach(cell, istmt->objects) |
2385 | { |
2386 | Oid srvid = lfirst_oid(cell); |
2387 | Form_pg_foreign_server pg_server_tuple; |
2388 | Datum aclDatum; |
2389 | bool isNull; |
2390 | AclMode avail_goptions; |
2391 | AclMode this_privileges; |
2392 | Acl *old_acl; |
2393 | Acl *new_acl; |
2394 | Oid grantorId; |
2395 | Oid ownerId; |
2396 | HeapTuple tuple; |
2397 | HeapTuple newtuple; |
2398 | Datum values[Natts_pg_foreign_server]; |
2399 | bool nulls[Natts_pg_foreign_server]; |
2400 | bool replaces[Natts_pg_foreign_server]; |
2401 | int noldmembers; |
2402 | int nnewmembers; |
2403 | Oid *oldmembers; |
2404 | Oid *newmembers; |
2405 | |
2406 | tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvid)); |
2407 | if (!HeapTupleIsValid(tuple)) |
2408 | elog(ERROR, "cache lookup failed for foreign server %u" , srvid); |
2409 | |
2410 | pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple); |
2411 | |
2412 | /* |
2413 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2414 | * substitute the proper default. |
2415 | */ |
2416 | ownerId = pg_server_tuple->srvowner; |
2417 | aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple, |
2418 | Anum_pg_foreign_server_srvacl, |
2419 | &isNull); |
2420 | if (isNull) |
2421 | { |
2422 | old_acl = acldefault(OBJECT_FOREIGN_SERVER, ownerId); |
2423 | /* There are no old member roles according to the catalogs */ |
2424 | noldmembers = 0; |
2425 | oldmembers = NULL; |
2426 | } |
2427 | else |
2428 | { |
2429 | old_acl = DatumGetAclPCopy(aclDatum); |
2430 | /* Get the roles mentioned in the existing ACL */ |
2431 | noldmembers = aclmembers(old_acl, &oldmembers); |
2432 | } |
2433 | |
2434 | /* Determine ID to do the grant as, and available grant options */ |
2435 | select_best_grantor(GetUserId(), istmt->privileges, |
2436 | old_acl, ownerId, |
2437 | &grantorId, &avail_goptions); |
2438 | |
2439 | /* |
2440 | * Restrict the privileges to what we can actually grant, and emit the |
2441 | * standards-mandated warning and error messages. |
2442 | */ |
2443 | this_privileges = |
2444 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2445 | istmt->all_privs, istmt->privileges, |
2446 | srvid, grantorId, OBJECT_FOREIGN_SERVER, |
2447 | NameStr(pg_server_tuple->srvname), |
2448 | 0, NULL); |
2449 | |
2450 | /* |
2451 | * Generate new ACL. |
2452 | */ |
2453 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2454 | istmt->grant_option, istmt->behavior, |
2455 | istmt->grantees, this_privileges, |
2456 | grantorId, ownerId); |
2457 | |
2458 | /* |
2459 | * We need the members of both old and new ACLs so we can correct the |
2460 | * shared dependency information. |
2461 | */ |
2462 | nnewmembers = aclmembers(new_acl, &newmembers); |
2463 | |
2464 | /* finished building new ACL value, now insert it */ |
2465 | MemSet(values, 0, sizeof(values)); |
2466 | MemSet(nulls, false, sizeof(nulls)); |
2467 | MemSet(replaces, false, sizeof(replaces)); |
2468 | |
2469 | replaces[Anum_pg_foreign_server_srvacl - 1] = true; |
2470 | values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl); |
2471 | |
2472 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2473 | nulls, replaces); |
2474 | |
2475 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2476 | |
2477 | /* Update initial privileges for extensions */ |
2478 | recordExtensionInitPriv(srvid, ForeignServerRelationId, 0, new_acl); |
2479 | |
2480 | /* Update the shared dependency ACL info */ |
2481 | updateAclDependencies(ForeignServerRelationId, |
2482 | pg_server_tuple->oid, 0, |
2483 | ownerId, |
2484 | noldmembers, oldmembers, |
2485 | nnewmembers, newmembers); |
2486 | |
2487 | ReleaseSysCache(tuple); |
2488 | |
2489 | pfree(new_acl); |
2490 | |
2491 | /* prevent error when processing duplicate objects */ |
2492 | CommandCounterIncrement(); |
2493 | } |
2494 | |
2495 | table_close(relation, RowExclusiveLock); |
2496 | } |
2497 | |
2498 | static void |
2499 | ExecGrant_Function(InternalGrant *istmt) |
2500 | { |
2501 | Relation relation; |
2502 | ListCell *cell; |
2503 | |
2504 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2505 | istmt->privileges = ACL_ALL_RIGHTS_FUNCTION; |
2506 | |
2507 | relation = table_open(ProcedureRelationId, RowExclusiveLock); |
2508 | |
2509 | foreach(cell, istmt->objects) |
2510 | { |
2511 | Oid funcId = lfirst_oid(cell); |
2512 | Form_pg_proc pg_proc_tuple; |
2513 | Datum aclDatum; |
2514 | bool isNull; |
2515 | AclMode avail_goptions; |
2516 | AclMode this_privileges; |
2517 | Acl *old_acl; |
2518 | Acl *new_acl; |
2519 | Oid grantorId; |
2520 | Oid ownerId; |
2521 | HeapTuple tuple; |
2522 | HeapTuple newtuple; |
2523 | Datum values[Natts_pg_proc]; |
2524 | bool nulls[Natts_pg_proc]; |
2525 | bool replaces[Natts_pg_proc]; |
2526 | int noldmembers; |
2527 | int nnewmembers; |
2528 | Oid *oldmembers; |
2529 | Oid *newmembers; |
2530 | |
2531 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcId)); |
2532 | if (!HeapTupleIsValid(tuple)) |
2533 | elog(ERROR, "cache lookup failed for function %u" , funcId); |
2534 | |
2535 | pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); |
2536 | |
2537 | /* |
2538 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2539 | * substitute the proper default. |
2540 | */ |
2541 | ownerId = pg_proc_tuple->proowner; |
2542 | aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl, |
2543 | &isNull); |
2544 | if (isNull) |
2545 | { |
2546 | old_acl = acldefault(OBJECT_FUNCTION, ownerId); |
2547 | /* There are no old member roles according to the catalogs */ |
2548 | noldmembers = 0; |
2549 | oldmembers = NULL; |
2550 | } |
2551 | else |
2552 | { |
2553 | old_acl = DatumGetAclPCopy(aclDatum); |
2554 | /* Get the roles mentioned in the existing ACL */ |
2555 | noldmembers = aclmembers(old_acl, &oldmembers); |
2556 | } |
2557 | |
2558 | /* Determine ID to do the grant as, and available grant options */ |
2559 | select_best_grantor(GetUserId(), istmt->privileges, |
2560 | old_acl, ownerId, |
2561 | &grantorId, &avail_goptions); |
2562 | |
2563 | /* |
2564 | * Restrict the privileges to what we can actually grant, and emit the |
2565 | * standards-mandated warning and error messages. |
2566 | */ |
2567 | this_privileges = |
2568 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2569 | istmt->all_privs, istmt->privileges, |
2570 | funcId, grantorId, OBJECT_FUNCTION, |
2571 | NameStr(pg_proc_tuple->proname), |
2572 | 0, NULL); |
2573 | |
2574 | /* |
2575 | * Generate new ACL. |
2576 | */ |
2577 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2578 | istmt->grant_option, istmt->behavior, |
2579 | istmt->grantees, this_privileges, |
2580 | grantorId, ownerId); |
2581 | |
2582 | /* |
2583 | * We need the members of both old and new ACLs so we can correct the |
2584 | * shared dependency information. |
2585 | */ |
2586 | nnewmembers = aclmembers(new_acl, &newmembers); |
2587 | |
2588 | /* finished building new ACL value, now insert it */ |
2589 | MemSet(values, 0, sizeof(values)); |
2590 | MemSet(nulls, false, sizeof(nulls)); |
2591 | MemSet(replaces, false, sizeof(replaces)); |
2592 | |
2593 | replaces[Anum_pg_proc_proacl - 1] = true; |
2594 | values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl); |
2595 | |
2596 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2597 | nulls, replaces); |
2598 | |
2599 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2600 | |
2601 | /* Update initial privileges for extensions */ |
2602 | recordExtensionInitPriv(funcId, ProcedureRelationId, 0, new_acl); |
2603 | |
2604 | /* Update the shared dependency ACL info */ |
2605 | updateAclDependencies(ProcedureRelationId, funcId, 0, |
2606 | ownerId, |
2607 | noldmembers, oldmembers, |
2608 | nnewmembers, newmembers); |
2609 | |
2610 | ReleaseSysCache(tuple); |
2611 | |
2612 | pfree(new_acl); |
2613 | |
2614 | /* prevent error when processing duplicate objects */ |
2615 | CommandCounterIncrement(); |
2616 | } |
2617 | |
2618 | table_close(relation, RowExclusiveLock); |
2619 | } |
2620 | |
2621 | static void |
2622 | ExecGrant_Language(InternalGrant *istmt) |
2623 | { |
2624 | Relation relation; |
2625 | ListCell *cell; |
2626 | |
2627 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2628 | istmt->privileges = ACL_ALL_RIGHTS_LANGUAGE; |
2629 | |
2630 | relation = table_open(LanguageRelationId, RowExclusiveLock); |
2631 | |
2632 | foreach(cell, istmt->objects) |
2633 | { |
2634 | Oid langId = lfirst_oid(cell); |
2635 | Form_pg_language pg_language_tuple; |
2636 | Datum aclDatum; |
2637 | bool isNull; |
2638 | AclMode avail_goptions; |
2639 | AclMode this_privileges; |
2640 | Acl *old_acl; |
2641 | Acl *new_acl; |
2642 | Oid grantorId; |
2643 | Oid ownerId; |
2644 | HeapTuple tuple; |
2645 | HeapTuple newtuple; |
2646 | Datum values[Natts_pg_language]; |
2647 | bool nulls[Natts_pg_language]; |
2648 | bool replaces[Natts_pg_language]; |
2649 | int noldmembers; |
2650 | int nnewmembers; |
2651 | Oid *oldmembers; |
2652 | Oid *newmembers; |
2653 | |
2654 | tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(langId)); |
2655 | if (!HeapTupleIsValid(tuple)) |
2656 | elog(ERROR, "cache lookup failed for language %u" , langId); |
2657 | |
2658 | pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple); |
2659 | |
2660 | if (!pg_language_tuple->lanpltrusted) |
2661 | ereport(ERROR, |
2662 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2663 | errmsg("language \"%s\" is not trusted" , |
2664 | NameStr(pg_language_tuple->lanname)), |
2665 | errdetail("GRANT and REVOKE are not allowed on untrusted languages, " |
2666 | "because only superusers can use untrusted languages." ))); |
2667 | |
2668 | /* |
2669 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2670 | * substitute the proper default. |
2671 | */ |
2672 | ownerId = pg_language_tuple->lanowner; |
2673 | aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl, |
2674 | &isNull); |
2675 | if (isNull) |
2676 | { |
2677 | old_acl = acldefault(OBJECT_LANGUAGE, ownerId); |
2678 | /* There are no old member roles according to the catalogs */ |
2679 | noldmembers = 0; |
2680 | oldmembers = NULL; |
2681 | } |
2682 | else |
2683 | { |
2684 | old_acl = DatumGetAclPCopy(aclDatum); |
2685 | /* Get the roles mentioned in the existing ACL */ |
2686 | noldmembers = aclmembers(old_acl, &oldmembers); |
2687 | } |
2688 | |
2689 | /* Determine ID to do the grant as, and available grant options */ |
2690 | select_best_grantor(GetUserId(), istmt->privileges, |
2691 | old_acl, ownerId, |
2692 | &grantorId, &avail_goptions); |
2693 | |
2694 | /* |
2695 | * Restrict the privileges to what we can actually grant, and emit the |
2696 | * standards-mandated warning and error messages. |
2697 | */ |
2698 | this_privileges = |
2699 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2700 | istmt->all_privs, istmt->privileges, |
2701 | langId, grantorId, OBJECT_LANGUAGE, |
2702 | NameStr(pg_language_tuple->lanname), |
2703 | 0, NULL); |
2704 | |
2705 | /* |
2706 | * Generate new ACL. |
2707 | */ |
2708 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2709 | istmt->grant_option, istmt->behavior, |
2710 | istmt->grantees, this_privileges, |
2711 | grantorId, ownerId); |
2712 | |
2713 | /* |
2714 | * We need the members of both old and new ACLs so we can correct the |
2715 | * shared dependency information. |
2716 | */ |
2717 | nnewmembers = aclmembers(new_acl, &newmembers); |
2718 | |
2719 | /* finished building new ACL value, now insert it */ |
2720 | MemSet(values, 0, sizeof(values)); |
2721 | MemSet(nulls, false, sizeof(nulls)); |
2722 | MemSet(replaces, false, sizeof(replaces)); |
2723 | |
2724 | replaces[Anum_pg_language_lanacl - 1] = true; |
2725 | values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl); |
2726 | |
2727 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2728 | nulls, replaces); |
2729 | |
2730 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2731 | |
2732 | /* Update initial privileges for extensions */ |
2733 | recordExtensionInitPriv(langId, LanguageRelationId, 0, new_acl); |
2734 | |
2735 | /* Update the shared dependency ACL info */ |
2736 | updateAclDependencies(LanguageRelationId, pg_language_tuple->oid, 0, |
2737 | ownerId, |
2738 | noldmembers, oldmembers, |
2739 | nnewmembers, newmembers); |
2740 | |
2741 | ReleaseSysCache(tuple); |
2742 | |
2743 | pfree(new_acl); |
2744 | |
2745 | /* prevent error when processing duplicate objects */ |
2746 | CommandCounterIncrement(); |
2747 | } |
2748 | |
2749 | table_close(relation, RowExclusiveLock); |
2750 | } |
2751 | |
2752 | static void |
2753 | ExecGrant_Largeobject(InternalGrant *istmt) |
2754 | { |
2755 | Relation relation; |
2756 | ListCell *cell; |
2757 | |
2758 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2759 | istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT; |
2760 | |
2761 | relation = table_open(LargeObjectMetadataRelationId, |
2762 | RowExclusiveLock); |
2763 | |
2764 | foreach(cell, istmt->objects) |
2765 | { |
2766 | Oid loid = lfirst_oid(cell); |
2767 | Form_pg_largeobject_metadata form_lo_meta; |
2768 | char loname[NAMEDATALEN]; |
2769 | Datum aclDatum; |
2770 | bool isNull; |
2771 | AclMode avail_goptions; |
2772 | AclMode this_privileges; |
2773 | Acl *old_acl; |
2774 | Acl *new_acl; |
2775 | Oid grantorId; |
2776 | Oid ownerId; |
2777 | HeapTuple newtuple; |
2778 | Datum values[Natts_pg_largeobject_metadata]; |
2779 | bool nulls[Natts_pg_largeobject_metadata]; |
2780 | bool replaces[Natts_pg_largeobject_metadata]; |
2781 | int noldmembers; |
2782 | int nnewmembers; |
2783 | Oid *oldmembers; |
2784 | Oid *newmembers; |
2785 | ScanKeyData entry[1]; |
2786 | SysScanDesc scan; |
2787 | HeapTuple tuple; |
2788 | |
2789 | /* There's no syscache for pg_largeobject_metadata */ |
2790 | ScanKeyInit(&entry[0], |
2791 | Anum_pg_largeobject_metadata_oid, |
2792 | BTEqualStrategyNumber, F_OIDEQ, |
2793 | ObjectIdGetDatum(loid)); |
2794 | |
2795 | scan = systable_beginscan(relation, |
2796 | LargeObjectMetadataOidIndexId, true, |
2797 | NULL, 1, entry); |
2798 | |
2799 | tuple = systable_getnext(scan); |
2800 | if (!HeapTupleIsValid(tuple)) |
2801 | elog(ERROR, "could not find tuple for large object %u" , loid); |
2802 | |
2803 | form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple); |
2804 | |
2805 | /* |
2806 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2807 | * substitute the proper default. |
2808 | */ |
2809 | ownerId = form_lo_meta->lomowner; |
2810 | aclDatum = heap_getattr(tuple, |
2811 | Anum_pg_largeobject_metadata_lomacl, |
2812 | RelationGetDescr(relation), &isNull); |
2813 | if (isNull) |
2814 | { |
2815 | old_acl = acldefault(OBJECT_LARGEOBJECT, ownerId); |
2816 | /* There are no old member roles according to the catalogs */ |
2817 | noldmembers = 0; |
2818 | oldmembers = NULL; |
2819 | } |
2820 | else |
2821 | { |
2822 | old_acl = DatumGetAclPCopy(aclDatum); |
2823 | /* Get the roles mentioned in the existing ACL */ |
2824 | noldmembers = aclmembers(old_acl, &oldmembers); |
2825 | } |
2826 | |
2827 | /* Determine ID to do the grant as, and available grant options */ |
2828 | select_best_grantor(GetUserId(), istmt->privileges, |
2829 | old_acl, ownerId, |
2830 | &grantorId, &avail_goptions); |
2831 | |
2832 | /* |
2833 | * Restrict the privileges to what we can actually grant, and emit the |
2834 | * standards-mandated warning and error messages. |
2835 | */ |
2836 | snprintf(loname, sizeof(loname), "large object %u" , loid); |
2837 | this_privileges = |
2838 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2839 | istmt->all_privs, istmt->privileges, |
2840 | loid, grantorId, OBJECT_LARGEOBJECT, |
2841 | loname, 0, NULL); |
2842 | |
2843 | /* |
2844 | * Generate new ACL. |
2845 | */ |
2846 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2847 | istmt->grant_option, istmt->behavior, |
2848 | istmt->grantees, this_privileges, |
2849 | grantorId, ownerId); |
2850 | |
2851 | /* |
2852 | * We need the members of both old and new ACLs so we can correct the |
2853 | * shared dependency information. |
2854 | */ |
2855 | nnewmembers = aclmembers(new_acl, &newmembers); |
2856 | |
2857 | /* finished building new ACL value, now insert it */ |
2858 | MemSet(values, 0, sizeof(values)); |
2859 | MemSet(nulls, false, sizeof(nulls)); |
2860 | MemSet(replaces, false, sizeof(replaces)); |
2861 | |
2862 | replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true; |
2863 | values[Anum_pg_largeobject_metadata_lomacl - 1] |
2864 | = PointerGetDatum(new_acl); |
2865 | |
2866 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), |
2867 | values, nulls, replaces); |
2868 | |
2869 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2870 | |
2871 | /* Update initial privileges for extensions */ |
2872 | recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl); |
2873 | |
2874 | /* Update the shared dependency ACL info */ |
2875 | updateAclDependencies(LargeObjectRelationId, |
2876 | form_lo_meta->oid, 0, |
2877 | ownerId, |
2878 | noldmembers, oldmembers, |
2879 | nnewmembers, newmembers); |
2880 | |
2881 | systable_endscan(scan); |
2882 | |
2883 | pfree(new_acl); |
2884 | |
2885 | /* prevent error when processing duplicate objects */ |
2886 | CommandCounterIncrement(); |
2887 | } |
2888 | |
2889 | table_close(relation, RowExclusiveLock); |
2890 | } |
2891 | |
2892 | static void |
2893 | ExecGrant_Namespace(InternalGrant *istmt) |
2894 | { |
2895 | Relation relation; |
2896 | ListCell *cell; |
2897 | |
2898 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
2899 | istmt->privileges = ACL_ALL_RIGHTS_SCHEMA; |
2900 | |
2901 | relation = table_open(NamespaceRelationId, RowExclusiveLock); |
2902 | |
2903 | foreach(cell, istmt->objects) |
2904 | { |
2905 | Oid nspid = lfirst_oid(cell); |
2906 | Form_pg_namespace pg_namespace_tuple; |
2907 | Datum aclDatum; |
2908 | bool isNull; |
2909 | AclMode avail_goptions; |
2910 | AclMode this_privileges; |
2911 | Acl *old_acl; |
2912 | Acl *new_acl; |
2913 | Oid grantorId; |
2914 | Oid ownerId; |
2915 | HeapTuple tuple; |
2916 | HeapTuple newtuple; |
2917 | Datum values[Natts_pg_namespace]; |
2918 | bool nulls[Natts_pg_namespace]; |
2919 | bool replaces[Natts_pg_namespace]; |
2920 | int noldmembers; |
2921 | int nnewmembers; |
2922 | Oid *oldmembers; |
2923 | Oid *newmembers; |
2924 | |
2925 | tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid)); |
2926 | if (!HeapTupleIsValid(tuple)) |
2927 | elog(ERROR, "cache lookup failed for namespace %u" , nspid); |
2928 | |
2929 | pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); |
2930 | |
2931 | /* |
2932 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
2933 | * substitute the proper default. |
2934 | */ |
2935 | ownerId = pg_namespace_tuple->nspowner; |
2936 | aclDatum = SysCacheGetAttr(NAMESPACENAME, tuple, |
2937 | Anum_pg_namespace_nspacl, |
2938 | &isNull); |
2939 | if (isNull) |
2940 | { |
2941 | old_acl = acldefault(OBJECT_SCHEMA, ownerId); |
2942 | /* There are no old member roles according to the catalogs */ |
2943 | noldmembers = 0; |
2944 | oldmembers = NULL; |
2945 | } |
2946 | else |
2947 | { |
2948 | old_acl = DatumGetAclPCopy(aclDatum); |
2949 | /* Get the roles mentioned in the existing ACL */ |
2950 | noldmembers = aclmembers(old_acl, &oldmembers); |
2951 | } |
2952 | |
2953 | /* Determine ID to do the grant as, and available grant options */ |
2954 | select_best_grantor(GetUserId(), istmt->privileges, |
2955 | old_acl, ownerId, |
2956 | &grantorId, &avail_goptions); |
2957 | |
2958 | /* |
2959 | * Restrict the privileges to what we can actually grant, and emit the |
2960 | * standards-mandated warning and error messages. |
2961 | */ |
2962 | this_privileges = |
2963 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
2964 | istmt->all_privs, istmt->privileges, |
2965 | nspid, grantorId, OBJECT_SCHEMA, |
2966 | NameStr(pg_namespace_tuple->nspname), |
2967 | 0, NULL); |
2968 | |
2969 | /* |
2970 | * Generate new ACL. |
2971 | */ |
2972 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
2973 | istmt->grant_option, istmt->behavior, |
2974 | istmt->grantees, this_privileges, |
2975 | grantorId, ownerId); |
2976 | |
2977 | /* |
2978 | * We need the members of both old and new ACLs so we can correct the |
2979 | * shared dependency information. |
2980 | */ |
2981 | nnewmembers = aclmembers(new_acl, &newmembers); |
2982 | |
2983 | /* finished building new ACL value, now insert it */ |
2984 | MemSet(values, 0, sizeof(values)); |
2985 | MemSet(nulls, false, sizeof(nulls)); |
2986 | MemSet(replaces, false, sizeof(replaces)); |
2987 | |
2988 | replaces[Anum_pg_namespace_nspacl - 1] = true; |
2989 | values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl); |
2990 | |
2991 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
2992 | nulls, replaces); |
2993 | |
2994 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
2995 | |
2996 | /* Update initial privileges for extensions */ |
2997 | recordExtensionInitPriv(nspid, NamespaceRelationId, 0, new_acl); |
2998 | |
2999 | /* Update the shared dependency ACL info */ |
3000 | updateAclDependencies(NamespaceRelationId, pg_namespace_tuple->oid, 0, |
3001 | ownerId, |
3002 | noldmembers, oldmembers, |
3003 | nnewmembers, newmembers); |
3004 | |
3005 | ReleaseSysCache(tuple); |
3006 | |
3007 | pfree(new_acl); |
3008 | |
3009 | /* prevent error when processing duplicate objects */ |
3010 | CommandCounterIncrement(); |
3011 | } |
3012 | |
3013 | table_close(relation, RowExclusiveLock); |
3014 | } |
3015 | |
3016 | static void |
3017 | ExecGrant_Tablespace(InternalGrant *istmt) |
3018 | { |
3019 | Relation relation; |
3020 | ListCell *cell; |
3021 | |
3022 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
3023 | istmt->privileges = ACL_ALL_RIGHTS_TABLESPACE; |
3024 | |
3025 | relation = table_open(TableSpaceRelationId, RowExclusiveLock); |
3026 | |
3027 | foreach(cell, istmt->objects) |
3028 | { |
3029 | Oid tblId = lfirst_oid(cell); |
3030 | Form_pg_tablespace pg_tablespace_tuple; |
3031 | Datum aclDatum; |
3032 | bool isNull; |
3033 | AclMode avail_goptions; |
3034 | AclMode this_privileges; |
3035 | Acl *old_acl; |
3036 | Acl *new_acl; |
3037 | Oid grantorId; |
3038 | Oid ownerId; |
3039 | HeapTuple newtuple; |
3040 | Datum values[Natts_pg_tablespace]; |
3041 | bool nulls[Natts_pg_tablespace]; |
3042 | bool replaces[Natts_pg_tablespace]; |
3043 | int noldmembers; |
3044 | int nnewmembers; |
3045 | Oid *oldmembers; |
3046 | Oid *newmembers; |
3047 | HeapTuple tuple; |
3048 | |
3049 | /* Search syscache for pg_tablespace */ |
3050 | tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tblId)); |
3051 | if (!HeapTupleIsValid(tuple)) |
3052 | elog(ERROR, "cache lookup failed for tablespace %u" , tblId); |
3053 | |
3054 | pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple); |
3055 | |
3056 | /* |
3057 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
3058 | * substitute the proper default. |
3059 | */ |
3060 | ownerId = pg_tablespace_tuple->spcowner; |
3061 | aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl, |
3062 | RelationGetDescr(relation), &isNull); |
3063 | if (isNull) |
3064 | { |
3065 | old_acl = acldefault(OBJECT_TABLESPACE, ownerId); |
3066 | /* There are no old member roles according to the catalogs */ |
3067 | noldmembers = 0; |
3068 | oldmembers = NULL; |
3069 | } |
3070 | else |
3071 | { |
3072 | old_acl = DatumGetAclPCopy(aclDatum); |
3073 | /* Get the roles mentioned in the existing ACL */ |
3074 | noldmembers = aclmembers(old_acl, &oldmembers); |
3075 | } |
3076 | |
3077 | /* Determine ID to do the grant as, and available grant options */ |
3078 | select_best_grantor(GetUserId(), istmt->privileges, |
3079 | old_acl, ownerId, |
3080 | &grantorId, &avail_goptions); |
3081 | |
3082 | /* |
3083 | * Restrict the privileges to what we can actually grant, and emit the |
3084 | * standards-mandated warning and error messages. |
3085 | */ |
3086 | this_privileges = |
3087 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
3088 | istmt->all_privs, istmt->privileges, |
3089 | tblId, grantorId, OBJECT_TABLESPACE, |
3090 | NameStr(pg_tablespace_tuple->spcname), |
3091 | 0, NULL); |
3092 | |
3093 | /* |
3094 | * Generate new ACL. |
3095 | */ |
3096 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
3097 | istmt->grant_option, istmt->behavior, |
3098 | istmt->grantees, this_privileges, |
3099 | grantorId, ownerId); |
3100 | |
3101 | /* |
3102 | * We need the members of both old and new ACLs so we can correct the |
3103 | * shared dependency information. |
3104 | */ |
3105 | nnewmembers = aclmembers(new_acl, &newmembers); |
3106 | |
3107 | /* finished building new ACL value, now insert it */ |
3108 | MemSet(values, 0, sizeof(values)); |
3109 | MemSet(nulls, false, sizeof(nulls)); |
3110 | MemSet(replaces, false, sizeof(replaces)); |
3111 | |
3112 | replaces[Anum_pg_tablespace_spcacl - 1] = true; |
3113 | values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl); |
3114 | |
3115 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
3116 | nulls, replaces); |
3117 | |
3118 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
3119 | |
3120 | /* Update the shared dependency ACL info */ |
3121 | updateAclDependencies(TableSpaceRelationId, tblId, 0, |
3122 | ownerId, |
3123 | noldmembers, oldmembers, |
3124 | nnewmembers, newmembers); |
3125 | |
3126 | ReleaseSysCache(tuple); |
3127 | pfree(new_acl); |
3128 | |
3129 | /* prevent error when processing duplicate objects */ |
3130 | CommandCounterIncrement(); |
3131 | } |
3132 | |
3133 | table_close(relation, RowExclusiveLock); |
3134 | } |
3135 | |
3136 | static void |
3137 | ExecGrant_Type(InternalGrant *istmt) |
3138 | { |
3139 | Relation relation; |
3140 | ListCell *cell; |
3141 | |
3142 | if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) |
3143 | istmt->privileges = ACL_ALL_RIGHTS_TYPE; |
3144 | |
3145 | relation = table_open(TypeRelationId, RowExclusiveLock); |
3146 | |
3147 | foreach(cell, istmt->objects) |
3148 | { |
3149 | Oid typId = lfirst_oid(cell); |
3150 | Form_pg_type pg_type_tuple; |
3151 | Datum aclDatum; |
3152 | bool isNull; |
3153 | AclMode avail_goptions; |
3154 | AclMode this_privileges; |
3155 | Acl *old_acl; |
3156 | Acl *new_acl; |
3157 | Oid grantorId; |
3158 | Oid ownerId; |
3159 | HeapTuple newtuple; |
3160 | Datum values[Natts_pg_type]; |
3161 | bool nulls[Natts_pg_type]; |
3162 | bool replaces[Natts_pg_type]; |
3163 | int noldmembers; |
3164 | int nnewmembers; |
3165 | Oid *oldmembers; |
3166 | Oid *newmembers; |
3167 | HeapTuple tuple; |
3168 | |
3169 | /* Search syscache for pg_type */ |
3170 | tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typId)); |
3171 | if (!HeapTupleIsValid(tuple)) |
3172 | elog(ERROR, "cache lookup failed for type %u" , typId); |
3173 | |
3174 | pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple); |
3175 | |
3176 | if (pg_type_tuple->typelem != 0 && pg_type_tuple->typlen == -1) |
3177 | ereport(ERROR, |
3178 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
3179 | errmsg("cannot set privileges of array types" ), |
3180 | errhint("Set the privileges of the element type instead." ))); |
3181 | |
3182 | /* Used GRANT DOMAIN on a non-domain? */ |
3183 | if (istmt->objtype == OBJECT_DOMAIN && |
3184 | pg_type_tuple->typtype != TYPTYPE_DOMAIN) |
3185 | ereport(ERROR, |
3186 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
3187 | errmsg("\"%s\" is not a domain" , |
3188 | NameStr(pg_type_tuple->typname)))); |
3189 | |
3190 | /* |
3191 | * Get owner ID and working copy of existing ACL. If there's no ACL, |
3192 | * substitute the proper default. |
3193 | */ |
3194 | ownerId = pg_type_tuple->typowner; |
3195 | aclDatum = heap_getattr(tuple, Anum_pg_type_typacl, |
3196 | RelationGetDescr(relation), &isNull); |
3197 | if (isNull) |
3198 | { |
3199 | old_acl = acldefault(istmt->objtype, ownerId); |
3200 | /* There are no old member roles according to the catalogs */ |
3201 | noldmembers = 0; |
3202 | oldmembers = NULL; |
3203 | } |
3204 | else |
3205 | { |
3206 | old_acl = DatumGetAclPCopy(aclDatum); |
3207 | /* Get the roles mentioned in the existing ACL */ |
3208 | noldmembers = aclmembers(old_acl, &oldmembers); |
3209 | } |
3210 | |
3211 | /* Determine ID to do the grant as, and available grant options */ |
3212 | select_best_grantor(GetUserId(), istmt->privileges, |
3213 | old_acl, ownerId, |
3214 | &grantorId, &avail_goptions); |
3215 | |
3216 | /* |
3217 | * Restrict the privileges to what we can actually grant, and emit the |
3218 | * standards-mandated warning and error messages. |
3219 | */ |
3220 | this_privileges = |
3221 | restrict_and_check_grant(istmt->is_grant, avail_goptions, |
3222 | istmt->all_privs, istmt->privileges, |
3223 | typId, grantorId, OBJECT_TYPE, |
3224 | NameStr(pg_type_tuple->typname), |
3225 | 0, NULL); |
3226 | |
3227 | /* |
3228 | * Generate new ACL. |
3229 | */ |
3230 | new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, |
3231 | istmt->grant_option, istmt->behavior, |
3232 | istmt->grantees, this_privileges, |
3233 | grantorId, ownerId); |
3234 | |
3235 | /* |
3236 | * We need the members of both old and new ACLs so we can correct the |
3237 | * shared dependency information. |
3238 | */ |
3239 | nnewmembers = aclmembers(new_acl, &newmembers); |
3240 | |
3241 | /* finished building new ACL value, now insert it */ |
3242 | MemSet(values, 0, sizeof(values)); |
3243 | MemSet(nulls, false, sizeof(nulls)); |
3244 | MemSet(replaces, false, sizeof(replaces)); |
3245 | |
3246 | replaces[Anum_pg_type_typacl - 1] = true; |
3247 | values[Anum_pg_type_typacl - 1] = PointerGetDatum(new_acl); |
3248 | |
3249 | newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, |
3250 | nulls, replaces); |
3251 | |
3252 | CatalogTupleUpdate(relation, &newtuple->t_self, newtuple); |
3253 | |
3254 | /* Update initial privileges for extensions */ |
3255 | recordExtensionInitPriv(typId, TypeRelationId, 0, new_acl); |
3256 | |
3257 | /* Update the shared dependency ACL info */ |
3258 | updateAclDependencies(TypeRelationId, typId, 0, |
3259 | ownerId, |
3260 | noldmembers, oldmembers, |
3261 | nnewmembers, newmembers); |
3262 | |
3263 | ReleaseSysCache(tuple); |
3264 | pfree(new_acl); |
3265 | |
3266 | /* prevent error when processing duplicate objects */ |
3267 | CommandCounterIncrement(); |
3268 | } |
3269 | |
3270 | table_close(relation, RowExclusiveLock); |
3271 | } |
3272 | |
3273 | |
3274 | static AclMode |
3275 | string_to_privilege(const char *privname) |
3276 | { |
3277 | if (strcmp(privname, "insert" ) == 0) |
3278 | return ACL_INSERT; |
3279 | if (strcmp(privname, "select" ) == 0) |
3280 | return ACL_SELECT; |
3281 | if (strcmp(privname, "update" ) == 0) |
3282 | return ACL_UPDATE; |
3283 | if (strcmp(privname, "delete" ) == 0) |
3284 | return ACL_DELETE; |
3285 | if (strcmp(privname, "truncate" ) == 0) |
3286 | return ACL_TRUNCATE; |
3287 | if (strcmp(privname, "references" ) == 0) |
3288 | return ACL_REFERENCES; |
3289 | if (strcmp(privname, "trigger" ) == 0) |
3290 | return ACL_TRIGGER; |
3291 | if (strcmp(privname, "execute" ) == 0) |
3292 | return ACL_EXECUTE; |
3293 | if (strcmp(privname, "usage" ) == 0) |
3294 | return ACL_USAGE; |
3295 | if (strcmp(privname, "create" ) == 0) |
3296 | return ACL_CREATE; |
3297 | if (strcmp(privname, "temporary" ) == 0) |
3298 | return ACL_CREATE_TEMP; |
3299 | if (strcmp(privname, "temp" ) == 0) |
3300 | return ACL_CREATE_TEMP; |
3301 | if (strcmp(privname, "connect" ) == 0) |
3302 | return ACL_CONNECT; |
3303 | if (strcmp(privname, "rule" ) == 0) |
3304 | return 0; /* ignore old RULE privileges */ |
3305 | ereport(ERROR, |
3306 | (errcode(ERRCODE_SYNTAX_ERROR), |
3307 | errmsg("unrecognized privilege type \"%s\"" , privname))); |
3308 | return 0; /* appease compiler */ |
3309 | } |
3310 | |
3311 | static const char * |
3312 | privilege_to_string(AclMode privilege) |
3313 | { |
3314 | switch (privilege) |
3315 | { |
3316 | case ACL_INSERT: |
3317 | return "INSERT" ; |
3318 | case ACL_SELECT: |
3319 | return "SELECT" ; |
3320 | case ACL_UPDATE: |
3321 | return "UPDATE" ; |
3322 | case ACL_DELETE: |
3323 | return "DELETE" ; |
3324 | case ACL_TRUNCATE: |
3325 | return "TRUNCATE" ; |
3326 | case ACL_REFERENCES: |
3327 | return "REFERENCES" ; |
3328 | case ACL_TRIGGER: |
3329 | return "TRIGGER" ; |
3330 | case ACL_EXECUTE: |
3331 | return "EXECUTE" ; |
3332 | case ACL_USAGE: |
3333 | return "USAGE" ; |
3334 | case ACL_CREATE: |
3335 | return "CREATE" ; |
3336 | case ACL_CREATE_TEMP: |
3337 | return "TEMP" ; |
3338 | case ACL_CONNECT: |
3339 | return "CONNECT" ; |
3340 | default: |
3341 | elog(ERROR, "unrecognized privilege: %d" , (int) privilege); |
3342 | } |
3343 | return NULL; /* appease compiler */ |
3344 | } |
3345 | |
3346 | /* |
3347 | * Standardized reporting of aclcheck permissions failures. |
3348 | * |
3349 | * Note: we do not double-quote the %s's below, because many callers |
3350 | * supply strings that might be already quoted. |
3351 | */ |
3352 | void |
3353 | aclcheck_error(AclResult aclerr, ObjectType objtype, |
3354 | const char *objectname) |
3355 | { |
3356 | switch (aclerr) |
3357 | { |
3358 | case ACLCHECK_OK: |
3359 | /* no error, so return to caller */ |
3360 | break; |
3361 | case ACLCHECK_NO_PRIV: |
3362 | { |
3363 | const char *msg = "???" ; |
3364 | |
3365 | switch (objtype) |
3366 | { |
3367 | case OBJECT_AGGREGATE: |
3368 | msg = gettext_noop("permission denied for aggregate %s" ); |
3369 | break; |
3370 | case OBJECT_COLLATION: |
3371 | msg = gettext_noop("permission denied for collation %s" ); |
3372 | break; |
3373 | case OBJECT_COLUMN: |
3374 | msg = gettext_noop("permission denied for column %s" ); |
3375 | break; |
3376 | case OBJECT_CONVERSION: |
3377 | msg = gettext_noop("permission denied for conversion %s" ); |
3378 | break; |
3379 | case OBJECT_DATABASE: |
3380 | msg = gettext_noop("permission denied for database %s" ); |
3381 | break; |
3382 | case OBJECT_DOMAIN: |
3383 | msg = gettext_noop("permission denied for domain %s" ); |
3384 | break; |
3385 | case OBJECT_EVENT_TRIGGER: |
3386 | msg = gettext_noop("permission denied for event trigger %s" ); |
3387 | break; |
3388 | case OBJECT_EXTENSION: |
3389 | msg = gettext_noop("permission denied for extension %s" ); |
3390 | break; |
3391 | case OBJECT_FDW: |
3392 | msg = gettext_noop("permission denied for foreign-data wrapper %s" ); |
3393 | break; |
3394 | case OBJECT_FOREIGN_SERVER: |
3395 | msg = gettext_noop("permission denied for foreign server %s" ); |
3396 | break; |
3397 | case OBJECT_FOREIGN_TABLE: |
3398 | msg = gettext_noop("permission denied for foreign table %s" ); |
3399 | break; |
3400 | case OBJECT_FUNCTION: |
3401 | msg = gettext_noop("permission denied for function %s" ); |
3402 | break; |
3403 | case OBJECT_INDEX: |
3404 | msg = gettext_noop("permission denied for index %s" ); |
3405 | break; |
3406 | case OBJECT_LANGUAGE: |
3407 | msg = gettext_noop("permission denied for language %s" ); |
3408 | break; |
3409 | case OBJECT_LARGEOBJECT: |
3410 | msg = gettext_noop("permission denied for large object %s" ); |
3411 | break; |
3412 | case OBJECT_MATVIEW: |
3413 | msg = gettext_noop("permission denied for materialized view %s" ); |
3414 | break; |
3415 | case OBJECT_OPCLASS: |
3416 | msg = gettext_noop("permission denied for operator class %s" ); |
3417 | break; |
3418 | case OBJECT_OPERATOR: |
3419 | msg = gettext_noop("permission denied for operator %s" ); |
3420 | break; |
3421 | case OBJECT_OPFAMILY: |
3422 | msg = gettext_noop("permission denied for operator family %s" ); |
3423 | break; |
3424 | case OBJECT_POLICY: |
3425 | msg = gettext_noop("permission denied for policy %s" ); |
3426 | break; |
3427 | case OBJECT_PROCEDURE: |
3428 | msg = gettext_noop("permission denied for procedure %s" ); |
3429 | break; |
3430 | case OBJECT_PUBLICATION: |
3431 | msg = gettext_noop("permission denied for publication %s" ); |
3432 | break; |
3433 | case OBJECT_ROUTINE: |
3434 | msg = gettext_noop("permission denied for routine %s" ); |
3435 | break; |
3436 | case OBJECT_SCHEMA: |
3437 | msg = gettext_noop("permission denied for schema %s" ); |
3438 | break; |
3439 | case OBJECT_SEQUENCE: |
3440 | msg = gettext_noop("permission denied for sequence %s" ); |
3441 | break; |
3442 | case OBJECT_STATISTIC_EXT: |
3443 | msg = gettext_noop("permission denied for statistics object %s" ); |
3444 | break; |
3445 | case OBJECT_SUBSCRIPTION: |
3446 | msg = gettext_noop("permission denied for subscription %s" ); |
3447 | break; |
3448 | case OBJECT_TABLE: |
3449 | msg = gettext_noop("permission denied for table %s" ); |
3450 | break; |
3451 | case OBJECT_TABLESPACE: |
3452 | msg = gettext_noop("permission denied for tablespace %s" ); |
3453 | break; |
3454 | case OBJECT_TSCONFIGURATION: |
3455 | msg = gettext_noop("permission denied for text search configuration %s" ); |
3456 | break; |
3457 | case OBJECT_TSDICTIONARY: |
3458 | msg = gettext_noop("permission denied for text search dictionary %s" ); |
3459 | break; |
3460 | case OBJECT_TYPE: |
3461 | msg = gettext_noop("permission denied for type %s" ); |
3462 | break; |
3463 | case OBJECT_VIEW: |
3464 | msg = gettext_noop("permission denied for view %s" ); |
3465 | break; |
3466 | /* these currently aren't used */ |
3467 | case OBJECT_ACCESS_METHOD: |
3468 | case OBJECT_AMOP: |
3469 | case OBJECT_AMPROC: |
3470 | case OBJECT_ATTRIBUTE: |
3471 | case OBJECT_CAST: |
3472 | case OBJECT_DEFAULT: |
3473 | case OBJECT_DEFACL: |
3474 | case OBJECT_DOMCONSTRAINT: |
3475 | case OBJECT_PUBLICATION_REL: |
3476 | case OBJECT_ROLE: |
3477 | case OBJECT_RULE: |
3478 | case OBJECT_TABCONSTRAINT: |
3479 | case OBJECT_TRANSFORM: |
3480 | case OBJECT_TRIGGER: |
3481 | case OBJECT_TSPARSER: |
3482 | case OBJECT_TSTEMPLATE: |
3483 | case OBJECT_USER_MAPPING: |
3484 | elog(ERROR, "unsupported object type %d" , objtype); |
3485 | } |
3486 | |
3487 | ereport(ERROR, |
3488 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
3489 | errmsg(msg, objectname))); |
3490 | break; |
3491 | } |
3492 | case ACLCHECK_NOT_OWNER: |
3493 | { |
3494 | const char *msg = "???" ; |
3495 | |
3496 | switch (objtype) |
3497 | { |
3498 | case OBJECT_AGGREGATE: |
3499 | msg = gettext_noop("must be owner of aggregate %s" ); |
3500 | break; |
3501 | case OBJECT_COLLATION: |
3502 | msg = gettext_noop("must be owner of collation %s" ); |
3503 | break; |
3504 | case OBJECT_CONVERSION: |
3505 | msg = gettext_noop("must be owner of conversion %s" ); |
3506 | break; |
3507 | case OBJECT_DATABASE: |
3508 | msg = gettext_noop("must be owner of database %s" ); |
3509 | break; |
3510 | case OBJECT_DOMAIN: |
3511 | msg = gettext_noop("must be owner of domain %s" ); |
3512 | break; |
3513 | case OBJECT_EVENT_TRIGGER: |
3514 | msg = gettext_noop("must be owner of event trigger %s" ); |
3515 | break; |
3516 | case OBJECT_EXTENSION: |
3517 | msg = gettext_noop("must be owner of extension %s" ); |
3518 | break; |
3519 | case OBJECT_FDW: |
3520 | msg = gettext_noop("must be owner of foreign-data wrapper %s" ); |
3521 | break; |
3522 | case OBJECT_FOREIGN_SERVER: |
3523 | msg = gettext_noop("must be owner of foreign server %s" ); |
3524 | break; |
3525 | case OBJECT_FOREIGN_TABLE: |
3526 | msg = gettext_noop("must be owner of foreign table %s" ); |
3527 | break; |
3528 | case OBJECT_FUNCTION: |
3529 | msg = gettext_noop("must be owner of function %s" ); |
3530 | break; |
3531 | case OBJECT_INDEX: |
3532 | msg = gettext_noop("must be owner of index %s" ); |
3533 | break; |
3534 | case OBJECT_LANGUAGE: |
3535 | msg = gettext_noop("must be owner of language %s" ); |
3536 | break; |
3537 | case OBJECT_LARGEOBJECT: |
3538 | msg = gettext_noop("must be owner of large object %s" ); |
3539 | break; |
3540 | case OBJECT_MATVIEW: |
3541 | msg = gettext_noop("must be owner of materialized view %s" ); |
3542 | break; |
3543 | case OBJECT_OPCLASS: |
3544 | msg = gettext_noop("must be owner of operator class %s" ); |
3545 | break; |
3546 | case OBJECT_OPERATOR: |
3547 | msg = gettext_noop("must be owner of operator %s" ); |
3548 | break; |
3549 | case OBJECT_OPFAMILY: |
3550 | msg = gettext_noop("must be owner of operator family %s" ); |
3551 | break; |
3552 | case OBJECT_PROCEDURE: |
3553 | msg = gettext_noop("must be owner of procedure %s" ); |
3554 | break; |
3555 | case OBJECT_PUBLICATION: |
3556 | msg = gettext_noop("must be owner of publication %s" ); |
3557 | break; |
3558 | case OBJECT_ROUTINE: |
3559 | msg = gettext_noop("must be owner of routine %s" ); |
3560 | break; |
3561 | case OBJECT_SEQUENCE: |
3562 | msg = gettext_noop("must be owner of sequence %s" ); |
3563 | break; |
3564 | case OBJECT_SUBSCRIPTION: |
3565 | msg = gettext_noop("must be owner of subscription %s" ); |
3566 | break; |
3567 | case OBJECT_TABLE: |
3568 | msg = gettext_noop("must be owner of table %s" ); |
3569 | break; |
3570 | case OBJECT_TYPE: |
3571 | msg = gettext_noop("must be owner of type %s" ); |
3572 | break; |
3573 | case OBJECT_VIEW: |
3574 | msg = gettext_noop("must be owner of view %s" ); |
3575 | break; |
3576 | case OBJECT_SCHEMA: |
3577 | msg = gettext_noop("must be owner of schema %s" ); |
3578 | break; |
3579 | case OBJECT_STATISTIC_EXT: |
3580 | msg = gettext_noop("must be owner of statistics object %s" ); |
3581 | break; |
3582 | case OBJECT_TABLESPACE: |
3583 | msg = gettext_noop("must be owner of tablespace %s" ); |
3584 | break; |
3585 | case OBJECT_TSCONFIGURATION: |
3586 | msg = gettext_noop("must be owner of text search configuration %s" ); |
3587 | break; |
3588 | case OBJECT_TSDICTIONARY: |
3589 | msg = gettext_noop("must be owner of text search dictionary %s" ); |
3590 | break; |
3591 | |
3592 | /* |
3593 | * Special cases: For these, the error message talks |
3594 | * about "relation", because that's where the |
3595 | * ownership is attached. See also |
3596 | * check_object_ownership(). |
3597 | */ |
3598 | case OBJECT_COLUMN: |
3599 | case OBJECT_POLICY: |
3600 | case OBJECT_RULE: |
3601 | case OBJECT_TABCONSTRAINT: |
3602 | case OBJECT_TRIGGER: |
3603 | msg = gettext_noop("must be owner of relation %s" ); |
3604 | break; |
3605 | /* these currently aren't used */ |
3606 | case OBJECT_ACCESS_METHOD: |
3607 | case OBJECT_AMOP: |
3608 | case OBJECT_AMPROC: |
3609 | case OBJECT_ATTRIBUTE: |
3610 | case OBJECT_CAST: |
3611 | case OBJECT_DEFAULT: |
3612 | case OBJECT_DEFACL: |
3613 | case OBJECT_DOMCONSTRAINT: |
3614 | case OBJECT_PUBLICATION_REL: |
3615 | case OBJECT_ROLE: |
3616 | case OBJECT_TRANSFORM: |
3617 | case OBJECT_TSPARSER: |
3618 | case OBJECT_TSTEMPLATE: |
3619 | case OBJECT_USER_MAPPING: |
3620 | elog(ERROR, "unsupported object type %d" , objtype); |
3621 | } |
3622 | |
3623 | ereport(ERROR, |
3624 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
3625 | errmsg(msg, objectname))); |
3626 | break; |
3627 | } |
3628 | default: |
3629 | elog(ERROR, "unrecognized AclResult: %d" , (int) aclerr); |
3630 | break; |
3631 | } |
3632 | } |
3633 | |
3634 | |
3635 | void |
3636 | aclcheck_error_col(AclResult aclerr, ObjectType objtype, |
3637 | const char *objectname, const char *colname) |
3638 | { |
3639 | switch (aclerr) |
3640 | { |
3641 | case ACLCHECK_OK: |
3642 | /* no error, so return to caller */ |
3643 | break; |
3644 | case ACLCHECK_NO_PRIV: |
3645 | ereport(ERROR, |
3646 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
3647 | errmsg("permission denied for column \"%s\" of relation \"%s\"" , |
3648 | colname, objectname))); |
3649 | break; |
3650 | case ACLCHECK_NOT_OWNER: |
3651 | /* relation msg is OK since columns don't have separate owners */ |
3652 | aclcheck_error(aclerr, objtype, objectname); |
3653 | break; |
3654 | default: |
3655 | elog(ERROR, "unrecognized AclResult: %d" , (int) aclerr); |
3656 | break; |
3657 | } |
3658 | } |
3659 | |
3660 | |
3661 | /* |
3662 | * Special common handling for types: use element type instead of array type, |
3663 | * and format nicely |
3664 | */ |
3665 | void |
3666 | aclcheck_error_type(AclResult aclerr, Oid typeOid) |
3667 | { |
3668 | Oid element_type = get_element_type(typeOid); |
3669 | |
3670 | aclcheck_error(aclerr, OBJECT_TYPE, format_type_be(element_type ? element_type : typeOid)); |
3671 | } |
3672 | |
3673 | |
3674 | /* |
3675 | * Relay for the various pg_*_mask routines depending on object kind |
3676 | */ |
3677 | static AclMode |
3678 | pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid, |
3679 | AclMode mask, AclMaskHow how) |
3680 | { |
3681 | switch (objtype) |
3682 | { |
3683 | case OBJECT_COLUMN: |
3684 | return |
3685 | pg_class_aclmask(table_oid, roleid, mask, how) | |
3686 | pg_attribute_aclmask(table_oid, attnum, roleid, mask, how); |
3687 | case OBJECT_TABLE: |
3688 | case OBJECT_SEQUENCE: |
3689 | return pg_class_aclmask(table_oid, roleid, mask, how); |
3690 | case OBJECT_DATABASE: |
3691 | return pg_database_aclmask(table_oid, roleid, mask, how); |
3692 | case OBJECT_FUNCTION: |
3693 | return pg_proc_aclmask(table_oid, roleid, mask, how); |
3694 | case OBJECT_LANGUAGE: |
3695 | return pg_language_aclmask(table_oid, roleid, mask, how); |
3696 | case OBJECT_LARGEOBJECT: |
3697 | return pg_largeobject_aclmask_snapshot(table_oid, roleid, |
3698 | mask, how, NULL); |
3699 | case OBJECT_SCHEMA: |
3700 | return pg_namespace_aclmask(table_oid, roleid, mask, how); |
3701 | case OBJECT_STATISTIC_EXT: |
3702 | elog(ERROR, "grantable rights not supported for statistics objects" ); |
3703 | /* not reached, but keep compiler quiet */ |
3704 | return ACL_NO_RIGHTS; |
3705 | case OBJECT_TABLESPACE: |
3706 | return pg_tablespace_aclmask(table_oid, roleid, mask, how); |
3707 | case OBJECT_FDW: |
3708 | return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how); |
3709 | case OBJECT_FOREIGN_SERVER: |
3710 | return pg_foreign_server_aclmask(table_oid, roleid, mask, how); |
3711 | case OBJECT_EVENT_TRIGGER: |
3712 | elog(ERROR, "grantable rights not supported for event triggers" ); |
3713 | /* not reached, but keep compiler quiet */ |
3714 | return ACL_NO_RIGHTS; |
3715 | case OBJECT_TYPE: |
3716 | return pg_type_aclmask(table_oid, roleid, mask, how); |
3717 | default: |
3718 | elog(ERROR, "unrecognized objtype: %d" , |
3719 | (int) objtype); |
3720 | /* not reached, but keep compiler quiet */ |
3721 | return ACL_NO_RIGHTS; |
3722 | } |
3723 | } |
3724 | |
3725 | |
3726 | /* **************************************************************** |
3727 | * Exported routines for examining a user's privileges for various objects |
3728 | * |
3729 | * See aclmask() for a description of the common API for these functions. |
3730 | * |
3731 | * Note: we give lookup failure the full ereport treatment because the |
3732 | * has_xxx_privilege() family of functions allow users to pass any random |
3733 | * OID to these functions. |
3734 | * **************************************************************** |
3735 | */ |
3736 | |
3737 | /* |
3738 | * Exported routine for examining a user's privileges for a column |
3739 | * |
3740 | * Note: this considers only privileges granted specifically on the column. |
3741 | * It is caller's responsibility to take relation-level privileges into account |
3742 | * as appropriate. (For the same reason, we have no special case for |
3743 | * superuser-ness here.) |
3744 | */ |
3745 | AclMode |
3746 | pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid, |
3747 | AclMode mask, AclMaskHow how) |
3748 | { |
3749 | AclMode result; |
3750 | HeapTuple classTuple; |
3751 | HeapTuple attTuple; |
3752 | Form_pg_class classForm; |
3753 | Form_pg_attribute attributeForm; |
3754 | Datum aclDatum; |
3755 | bool isNull; |
3756 | Acl *acl; |
3757 | Oid ownerId; |
3758 | |
3759 | /* |
3760 | * First, get the column's ACL from its pg_attribute entry |
3761 | */ |
3762 | attTuple = SearchSysCache2(ATTNUM, |
3763 | ObjectIdGetDatum(table_oid), |
3764 | Int16GetDatum(attnum)); |
3765 | if (!HeapTupleIsValid(attTuple)) |
3766 | ereport(ERROR, |
3767 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
3768 | errmsg("attribute %d of relation with OID %u does not exist" , |
3769 | attnum, table_oid))); |
3770 | attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple); |
3771 | |
3772 | /* Throw error on dropped columns, too */ |
3773 | if (attributeForm->attisdropped) |
3774 | ereport(ERROR, |
3775 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
3776 | errmsg("attribute %d of relation with OID %u does not exist" , |
3777 | attnum, table_oid))); |
3778 | |
3779 | aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl, |
3780 | &isNull); |
3781 | |
3782 | /* |
3783 | * Here we hard-wire knowledge that the default ACL for a column grants no |
3784 | * privileges, so that we can fall out quickly in the very common case |
3785 | * where attacl is null. |
3786 | */ |
3787 | if (isNull) |
3788 | { |
3789 | ReleaseSysCache(attTuple); |
3790 | return 0; |
3791 | } |
3792 | |
3793 | /* |
3794 | * Must get the relation's ownerId from pg_class. Since we already found |
3795 | * a pg_attribute entry, the only likely reason for this to fail is that a |
3796 | * concurrent DROP of the relation committed since then (which could only |
3797 | * happen if we don't have lock on the relation). We prefer to report "no |
3798 | * privileges" rather than failing in such a case, so as to avoid unwanted |
3799 | * failures in has_column_privilege() tests. |
3800 | */ |
3801 | classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid)); |
3802 | if (!HeapTupleIsValid(classTuple)) |
3803 | { |
3804 | ReleaseSysCache(attTuple); |
3805 | return 0; |
3806 | } |
3807 | classForm = (Form_pg_class) GETSTRUCT(classTuple); |
3808 | |
3809 | ownerId = classForm->relowner; |
3810 | |
3811 | ReleaseSysCache(classTuple); |
3812 | |
3813 | /* detoast column's ACL if necessary */ |
3814 | acl = DatumGetAclP(aclDatum); |
3815 | |
3816 | result = aclmask(acl, roleid, ownerId, mask, how); |
3817 | |
3818 | /* if we have a detoasted copy, free it */ |
3819 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
3820 | pfree(acl); |
3821 | |
3822 | ReleaseSysCache(attTuple); |
3823 | |
3824 | return result; |
3825 | } |
3826 | |
3827 | /* |
3828 | * Exported routine for examining a user's privileges for a table |
3829 | */ |
3830 | AclMode |
3831 | pg_class_aclmask(Oid table_oid, Oid roleid, |
3832 | AclMode mask, AclMaskHow how) |
3833 | { |
3834 | AclMode result; |
3835 | HeapTuple tuple; |
3836 | Form_pg_class classForm; |
3837 | Datum aclDatum; |
3838 | bool isNull; |
3839 | Acl *acl; |
3840 | Oid ownerId; |
3841 | |
3842 | /* |
3843 | * Must get the relation's tuple from pg_class |
3844 | */ |
3845 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid)); |
3846 | if (!HeapTupleIsValid(tuple)) |
3847 | ereport(ERROR, |
3848 | (errcode(ERRCODE_UNDEFINED_TABLE), |
3849 | errmsg("relation with OID %u does not exist" , |
3850 | table_oid))); |
3851 | classForm = (Form_pg_class) GETSTRUCT(tuple); |
3852 | |
3853 | /* |
3854 | * Deny anyone permission to update a system catalog unless |
3855 | * pg_authid.rolsuper is set. Also allow it if allowSystemTableMods. |
3856 | * |
3857 | * As of 7.4 we have some updatable system views; those shouldn't be |
3858 | * protected in this way. Assume the view rules can take care of |
3859 | * themselves. ACL_USAGE is if we ever have system sequences. |
3860 | */ |
3861 | if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) && |
3862 | IsSystemClass(table_oid, classForm) && |
3863 | classForm->relkind != RELKIND_VIEW && |
3864 | !superuser_arg(roleid) && |
3865 | !allowSystemTableMods) |
3866 | { |
3867 | #ifdef ACLDEBUG |
3868 | elog(DEBUG2, "permission denied for system catalog update" ); |
3869 | #endif |
3870 | mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE); |
3871 | } |
3872 | |
3873 | /* |
3874 | * Otherwise, superusers bypass all permission-checking. |
3875 | */ |
3876 | if (superuser_arg(roleid)) |
3877 | { |
3878 | #ifdef ACLDEBUG |
3879 | elog(DEBUG2, "OID %u is superuser, home free" , roleid); |
3880 | #endif |
3881 | ReleaseSysCache(tuple); |
3882 | return mask; |
3883 | } |
3884 | |
3885 | /* |
3886 | * Normal case: get the relation's ACL from pg_class |
3887 | */ |
3888 | ownerId = classForm->relowner; |
3889 | |
3890 | aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, |
3891 | &isNull); |
3892 | if (isNull) |
3893 | { |
3894 | /* No ACL, so build default ACL */ |
3895 | switch (classForm->relkind) |
3896 | { |
3897 | case RELKIND_SEQUENCE: |
3898 | acl = acldefault(OBJECT_SEQUENCE, ownerId); |
3899 | break; |
3900 | default: |
3901 | acl = acldefault(OBJECT_TABLE, ownerId); |
3902 | break; |
3903 | } |
3904 | aclDatum = (Datum) 0; |
3905 | } |
3906 | else |
3907 | { |
3908 | /* detoast rel's ACL if necessary */ |
3909 | acl = DatumGetAclP(aclDatum); |
3910 | } |
3911 | |
3912 | result = aclmask(acl, roleid, ownerId, mask, how); |
3913 | |
3914 | /* if we have a detoasted copy, free it */ |
3915 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
3916 | pfree(acl); |
3917 | |
3918 | ReleaseSysCache(tuple); |
3919 | |
3920 | return result; |
3921 | } |
3922 | |
3923 | /* |
3924 | * Exported routine for examining a user's privileges for a database |
3925 | */ |
3926 | AclMode |
3927 | pg_database_aclmask(Oid db_oid, Oid roleid, |
3928 | AclMode mask, AclMaskHow how) |
3929 | { |
3930 | AclMode result; |
3931 | HeapTuple tuple; |
3932 | Datum aclDatum; |
3933 | bool isNull; |
3934 | Acl *acl; |
3935 | Oid ownerId; |
3936 | |
3937 | /* Superusers bypass all permission checking. */ |
3938 | if (superuser_arg(roleid)) |
3939 | return mask; |
3940 | |
3941 | /* |
3942 | * Get the database's ACL from pg_database |
3943 | */ |
3944 | tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid)); |
3945 | if (!HeapTupleIsValid(tuple)) |
3946 | ereport(ERROR, |
3947 | (errcode(ERRCODE_UNDEFINED_DATABASE), |
3948 | errmsg("database with OID %u does not exist" , db_oid))); |
3949 | |
3950 | ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba; |
3951 | |
3952 | aclDatum = SysCacheGetAttr(DATABASEOID, tuple, Anum_pg_database_datacl, |
3953 | &isNull); |
3954 | if (isNull) |
3955 | { |
3956 | /* No ACL, so build default ACL */ |
3957 | acl = acldefault(OBJECT_DATABASE, ownerId); |
3958 | aclDatum = (Datum) 0; |
3959 | } |
3960 | else |
3961 | { |
3962 | /* detoast ACL if necessary */ |
3963 | acl = DatumGetAclP(aclDatum); |
3964 | } |
3965 | |
3966 | result = aclmask(acl, roleid, ownerId, mask, how); |
3967 | |
3968 | /* if we have a detoasted copy, free it */ |
3969 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
3970 | pfree(acl); |
3971 | |
3972 | ReleaseSysCache(tuple); |
3973 | |
3974 | return result; |
3975 | } |
3976 | |
3977 | /* |
3978 | * Exported routine for examining a user's privileges for a function |
3979 | */ |
3980 | AclMode |
3981 | pg_proc_aclmask(Oid proc_oid, Oid roleid, |
3982 | AclMode mask, AclMaskHow how) |
3983 | { |
3984 | AclMode result; |
3985 | HeapTuple tuple; |
3986 | Datum aclDatum; |
3987 | bool isNull; |
3988 | Acl *acl; |
3989 | Oid ownerId; |
3990 | |
3991 | /* Superusers bypass all permission checking. */ |
3992 | if (superuser_arg(roleid)) |
3993 | return mask; |
3994 | |
3995 | /* |
3996 | * Get the function's ACL from pg_proc |
3997 | */ |
3998 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid)); |
3999 | if (!HeapTupleIsValid(tuple)) |
4000 | ereport(ERROR, |
4001 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
4002 | errmsg("function with OID %u does not exist" , proc_oid))); |
4003 | |
4004 | ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner; |
4005 | |
4006 | aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl, |
4007 | &isNull); |
4008 | if (isNull) |
4009 | { |
4010 | /* No ACL, so build default ACL */ |
4011 | acl = acldefault(OBJECT_FUNCTION, ownerId); |
4012 | aclDatum = (Datum) 0; |
4013 | } |
4014 | else |
4015 | { |
4016 | /* detoast ACL if necessary */ |
4017 | acl = DatumGetAclP(aclDatum); |
4018 | } |
4019 | |
4020 | result = aclmask(acl, roleid, ownerId, mask, how); |
4021 | |
4022 | /* if we have a detoasted copy, free it */ |
4023 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4024 | pfree(acl); |
4025 | |
4026 | ReleaseSysCache(tuple); |
4027 | |
4028 | return result; |
4029 | } |
4030 | |
4031 | /* |
4032 | * Exported routine for examining a user's privileges for a language |
4033 | */ |
4034 | AclMode |
4035 | pg_language_aclmask(Oid lang_oid, Oid roleid, |
4036 | AclMode mask, AclMaskHow how) |
4037 | { |
4038 | AclMode result; |
4039 | HeapTuple tuple; |
4040 | Datum aclDatum; |
4041 | bool isNull; |
4042 | Acl *acl; |
4043 | Oid ownerId; |
4044 | |
4045 | /* Superusers bypass all permission checking. */ |
4046 | if (superuser_arg(roleid)) |
4047 | return mask; |
4048 | |
4049 | /* |
4050 | * Get the language's ACL from pg_language |
4051 | */ |
4052 | tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lang_oid)); |
4053 | if (!HeapTupleIsValid(tuple)) |
4054 | ereport(ERROR, |
4055 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4056 | errmsg("language with OID %u does not exist" , lang_oid))); |
4057 | |
4058 | ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner; |
4059 | |
4060 | aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl, |
4061 | &isNull); |
4062 | if (isNull) |
4063 | { |
4064 | /* No ACL, so build default ACL */ |
4065 | acl = acldefault(OBJECT_LANGUAGE, ownerId); |
4066 | aclDatum = (Datum) 0; |
4067 | } |
4068 | else |
4069 | { |
4070 | /* detoast ACL if necessary */ |
4071 | acl = DatumGetAclP(aclDatum); |
4072 | } |
4073 | |
4074 | result = aclmask(acl, roleid, ownerId, mask, how); |
4075 | |
4076 | /* if we have a detoasted copy, free it */ |
4077 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4078 | pfree(acl); |
4079 | |
4080 | ReleaseSysCache(tuple); |
4081 | |
4082 | return result; |
4083 | } |
4084 | |
4085 | /* |
4086 | * Exported routine for examining a user's privileges for a largeobject |
4087 | * |
4088 | * When a large object is opened for reading, it is opened relative to the |
4089 | * caller's snapshot, but when it is opened for writing, a current |
4090 | * MVCC snapshot will be used. See doc/src/sgml/lobj.sgml. This function |
4091 | * takes a snapshot argument so that the permissions check can be made |
4092 | * relative to the same snapshot that will be used to read the underlying |
4093 | * data. The caller will actually pass NULL for an instantaneous MVCC |
4094 | * snapshot, since all we do with the snapshot argument is pass it through |
4095 | * to systable_beginscan(). |
4096 | */ |
4097 | AclMode |
4098 | pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid, |
4099 | AclMode mask, AclMaskHow how, |
4100 | Snapshot snapshot) |
4101 | { |
4102 | AclMode result; |
4103 | Relation pg_lo_meta; |
4104 | ScanKeyData entry[1]; |
4105 | SysScanDesc scan; |
4106 | HeapTuple tuple; |
4107 | Datum aclDatum; |
4108 | bool isNull; |
4109 | Acl *acl; |
4110 | Oid ownerId; |
4111 | |
4112 | /* Superusers bypass all permission checking. */ |
4113 | if (superuser_arg(roleid)) |
4114 | return mask; |
4115 | |
4116 | /* |
4117 | * Get the largeobject's ACL from pg_language_metadata |
4118 | */ |
4119 | pg_lo_meta = table_open(LargeObjectMetadataRelationId, |
4120 | AccessShareLock); |
4121 | |
4122 | ScanKeyInit(&entry[0], |
4123 | Anum_pg_largeobject_metadata_oid, |
4124 | BTEqualStrategyNumber, F_OIDEQ, |
4125 | ObjectIdGetDatum(lobj_oid)); |
4126 | |
4127 | scan = systable_beginscan(pg_lo_meta, |
4128 | LargeObjectMetadataOidIndexId, true, |
4129 | snapshot, 1, entry); |
4130 | |
4131 | tuple = systable_getnext(scan); |
4132 | if (!HeapTupleIsValid(tuple)) |
4133 | ereport(ERROR, |
4134 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4135 | errmsg("large object %u does not exist" , lobj_oid))); |
4136 | |
4137 | ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; |
4138 | |
4139 | aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl, |
4140 | RelationGetDescr(pg_lo_meta), &isNull); |
4141 | |
4142 | if (isNull) |
4143 | { |
4144 | /* No ACL, so build default ACL */ |
4145 | acl = acldefault(OBJECT_LARGEOBJECT, ownerId); |
4146 | aclDatum = (Datum) 0; |
4147 | } |
4148 | else |
4149 | { |
4150 | /* detoast ACL if necessary */ |
4151 | acl = DatumGetAclP(aclDatum); |
4152 | } |
4153 | |
4154 | result = aclmask(acl, roleid, ownerId, mask, how); |
4155 | |
4156 | /* if we have a detoasted copy, free it */ |
4157 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4158 | pfree(acl); |
4159 | |
4160 | systable_endscan(scan); |
4161 | |
4162 | table_close(pg_lo_meta, AccessShareLock); |
4163 | |
4164 | return result; |
4165 | } |
4166 | |
4167 | /* |
4168 | * Exported routine for examining a user's privileges for a namespace |
4169 | */ |
4170 | AclMode |
4171 | pg_namespace_aclmask(Oid nsp_oid, Oid roleid, |
4172 | AclMode mask, AclMaskHow how) |
4173 | { |
4174 | AclMode result; |
4175 | HeapTuple tuple; |
4176 | Datum aclDatum; |
4177 | bool isNull; |
4178 | Acl *acl; |
4179 | Oid ownerId; |
4180 | |
4181 | /* Superusers bypass all permission checking. */ |
4182 | if (superuser_arg(roleid)) |
4183 | return mask; |
4184 | |
4185 | /* |
4186 | * If we have been assigned this namespace as a temp namespace, check to |
4187 | * make sure we have CREATE TEMP permission on the database, and if so act |
4188 | * as though we have all standard (but not GRANT OPTION) permissions on |
4189 | * the namespace. If we don't have CREATE TEMP, act as though we have |
4190 | * only USAGE (and not CREATE) rights. |
4191 | * |
4192 | * This may seem redundant given the check in InitTempTableNamespace, but |
4193 | * it really isn't since current user ID may have changed since then. The |
4194 | * upshot of this behavior is that a SECURITY DEFINER function can create |
4195 | * temp tables that can then be accessed (if permission is granted) by |
4196 | * code in the same session that doesn't have permissions to create temp |
4197 | * tables. |
4198 | * |
4199 | * XXX Would it be safe to ereport a special error message as |
4200 | * InitTempTableNamespace does? Returning zero here means we'll get a |
4201 | * generic "permission denied for schema pg_temp_N" message, which is not |
4202 | * remarkably user-friendly. |
4203 | */ |
4204 | if (isTempNamespace(nsp_oid)) |
4205 | { |
4206 | if (pg_database_aclcheck(MyDatabaseId, roleid, |
4207 | ACL_CREATE_TEMP) == ACLCHECK_OK) |
4208 | return mask & ACL_ALL_RIGHTS_SCHEMA; |
4209 | else |
4210 | return mask & ACL_USAGE; |
4211 | } |
4212 | |
4213 | /* |
4214 | * Get the schema's ACL from pg_namespace |
4215 | */ |
4216 | tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid)); |
4217 | if (!HeapTupleIsValid(tuple)) |
4218 | ereport(ERROR, |
4219 | (errcode(ERRCODE_UNDEFINED_SCHEMA), |
4220 | errmsg("schema with OID %u does not exist" , nsp_oid))); |
4221 | |
4222 | ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner; |
4223 | |
4224 | aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl, |
4225 | &isNull); |
4226 | if (isNull) |
4227 | { |
4228 | /* No ACL, so build default ACL */ |
4229 | acl = acldefault(OBJECT_SCHEMA, ownerId); |
4230 | aclDatum = (Datum) 0; |
4231 | } |
4232 | else |
4233 | { |
4234 | /* detoast ACL if necessary */ |
4235 | acl = DatumGetAclP(aclDatum); |
4236 | } |
4237 | |
4238 | result = aclmask(acl, roleid, ownerId, mask, how); |
4239 | |
4240 | /* if we have a detoasted copy, free it */ |
4241 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4242 | pfree(acl); |
4243 | |
4244 | ReleaseSysCache(tuple); |
4245 | |
4246 | return result; |
4247 | } |
4248 | |
4249 | /* |
4250 | * Exported routine for examining a user's privileges for a tablespace |
4251 | */ |
4252 | AclMode |
4253 | pg_tablespace_aclmask(Oid spc_oid, Oid roleid, |
4254 | AclMode mask, AclMaskHow how) |
4255 | { |
4256 | AclMode result; |
4257 | HeapTuple tuple; |
4258 | Datum aclDatum; |
4259 | bool isNull; |
4260 | Acl *acl; |
4261 | Oid ownerId; |
4262 | |
4263 | /* Superusers bypass all permission checking. */ |
4264 | if (superuser_arg(roleid)) |
4265 | return mask; |
4266 | |
4267 | /* |
4268 | * Get the tablespace's ACL from pg_tablespace |
4269 | */ |
4270 | tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid)); |
4271 | if (!HeapTupleIsValid(tuple)) |
4272 | ereport(ERROR, |
4273 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4274 | errmsg("tablespace with OID %u does not exist" , spc_oid))); |
4275 | |
4276 | ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner; |
4277 | |
4278 | aclDatum = SysCacheGetAttr(TABLESPACEOID, tuple, |
4279 | Anum_pg_tablespace_spcacl, |
4280 | &isNull); |
4281 | |
4282 | if (isNull) |
4283 | { |
4284 | /* No ACL, so build default ACL */ |
4285 | acl = acldefault(OBJECT_TABLESPACE, ownerId); |
4286 | aclDatum = (Datum) 0; |
4287 | } |
4288 | else |
4289 | { |
4290 | /* detoast ACL if necessary */ |
4291 | acl = DatumGetAclP(aclDatum); |
4292 | } |
4293 | |
4294 | result = aclmask(acl, roleid, ownerId, mask, how); |
4295 | |
4296 | /* if we have a detoasted copy, free it */ |
4297 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4298 | pfree(acl); |
4299 | |
4300 | ReleaseSysCache(tuple); |
4301 | |
4302 | return result; |
4303 | } |
4304 | |
4305 | /* |
4306 | * Exported routine for examining a user's privileges for a foreign |
4307 | * data wrapper |
4308 | */ |
4309 | AclMode |
4310 | pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid, |
4311 | AclMode mask, AclMaskHow how) |
4312 | { |
4313 | AclMode result; |
4314 | HeapTuple tuple; |
4315 | Datum aclDatum; |
4316 | bool isNull; |
4317 | Acl *acl; |
4318 | Oid ownerId; |
4319 | |
4320 | Form_pg_foreign_data_wrapper fdwForm; |
4321 | |
4322 | /* Bypass permission checks for superusers */ |
4323 | if (superuser_arg(roleid)) |
4324 | return mask; |
4325 | |
4326 | /* |
4327 | * Must get the FDW's tuple from pg_foreign_data_wrapper |
4328 | */ |
4329 | tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdw_oid)); |
4330 | if (!HeapTupleIsValid(tuple)) |
4331 | ereport(ERROR, |
4332 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4333 | errmsg("foreign-data wrapper with OID %u does not exist" , |
4334 | fdw_oid))); |
4335 | fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); |
4336 | |
4337 | /* |
4338 | * Normal case: get the FDW's ACL from pg_foreign_data_wrapper |
4339 | */ |
4340 | ownerId = fdwForm->fdwowner; |
4341 | |
4342 | aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple, |
4343 | Anum_pg_foreign_data_wrapper_fdwacl, &isNull); |
4344 | if (isNull) |
4345 | { |
4346 | /* No ACL, so build default ACL */ |
4347 | acl = acldefault(OBJECT_FDW, ownerId); |
4348 | aclDatum = (Datum) 0; |
4349 | } |
4350 | else |
4351 | { |
4352 | /* detoast rel's ACL if necessary */ |
4353 | acl = DatumGetAclP(aclDatum); |
4354 | } |
4355 | |
4356 | result = aclmask(acl, roleid, ownerId, mask, how); |
4357 | |
4358 | /* if we have a detoasted copy, free it */ |
4359 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4360 | pfree(acl); |
4361 | |
4362 | ReleaseSysCache(tuple); |
4363 | |
4364 | return result; |
4365 | } |
4366 | |
4367 | /* |
4368 | * Exported routine for examining a user's privileges for a foreign |
4369 | * server. |
4370 | */ |
4371 | AclMode |
4372 | pg_foreign_server_aclmask(Oid srv_oid, Oid roleid, |
4373 | AclMode mask, AclMaskHow how) |
4374 | { |
4375 | AclMode result; |
4376 | HeapTuple tuple; |
4377 | Datum aclDatum; |
4378 | bool isNull; |
4379 | Acl *acl; |
4380 | Oid ownerId; |
4381 | |
4382 | Form_pg_foreign_server srvForm; |
4383 | |
4384 | /* Bypass permission checks for superusers */ |
4385 | if (superuser_arg(roleid)) |
4386 | return mask; |
4387 | |
4388 | /* |
4389 | * Must get the FDW's tuple from pg_foreign_data_wrapper |
4390 | */ |
4391 | tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid)); |
4392 | if (!HeapTupleIsValid(tuple)) |
4393 | ereport(ERROR, |
4394 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4395 | errmsg("foreign server with OID %u does not exist" , |
4396 | srv_oid))); |
4397 | srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); |
4398 | |
4399 | /* |
4400 | * Normal case: get the foreign server's ACL from pg_foreign_server |
4401 | */ |
4402 | ownerId = srvForm->srvowner; |
4403 | |
4404 | aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple, |
4405 | Anum_pg_foreign_server_srvacl, &isNull); |
4406 | if (isNull) |
4407 | { |
4408 | /* No ACL, so build default ACL */ |
4409 | acl = acldefault(OBJECT_FOREIGN_SERVER, ownerId); |
4410 | aclDatum = (Datum) 0; |
4411 | } |
4412 | else |
4413 | { |
4414 | /* detoast rel's ACL if necessary */ |
4415 | acl = DatumGetAclP(aclDatum); |
4416 | } |
4417 | |
4418 | result = aclmask(acl, roleid, ownerId, mask, how); |
4419 | |
4420 | /* if we have a detoasted copy, free it */ |
4421 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4422 | pfree(acl); |
4423 | |
4424 | ReleaseSysCache(tuple); |
4425 | |
4426 | return result; |
4427 | } |
4428 | |
4429 | /* |
4430 | * Exported routine for examining a user's privileges for a type. |
4431 | */ |
4432 | AclMode |
4433 | pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how) |
4434 | { |
4435 | AclMode result; |
4436 | HeapTuple tuple; |
4437 | Datum aclDatum; |
4438 | bool isNull; |
4439 | Acl *acl; |
4440 | Oid ownerId; |
4441 | |
4442 | Form_pg_type typeForm; |
4443 | |
4444 | /* Bypass permission checks for superusers */ |
4445 | if (superuser_arg(roleid)) |
4446 | return mask; |
4447 | |
4448 | /* |
4449 | * Must get the type's tuple from pg_type |
4450 | */ |
4451 | tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid)); |
4452 | if (!HeapTupleIsValid(tuple)) |
4453 | ereport(ERROR, |
4454 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4455 | errmsg("type with OID %u does not exist" , |
4456 | type_oid))); |
4457 | typeForm = (Form_pg_type) GETSTRUCT(tuple); |
4458 | |
4459 | /* |
4460 | * "True" array types don't manage permissions of their own; consult the |
4461 | * element type instead. |
4462 | */ |
4463 | if (OidIsValid(typeForm->typelem) && typeForm->typlen == -1) |
4464 | { |
4465 | Oid elttype_oid = typeForm->typelem; |
4466 | |
4467 | ReleaseSysCache(tuple); |
4468 | |
4469 | tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid)); |
4470 | /* this case is not a user-facing error, so elog not ereport */ |
4471 | if (!HeapTupleIsValid(tuple)) |
4472 | elog(ERROR, "cache lookup failed for type %u" , elttype_oid); |
4473 | typeForm = (Form_pg_type) GETSTRUCT(tuple); |
4474 | } |
4475 | |
4476 | /* |
4477 | * Now get the type's owner and ACL from the tuple |
4478 | */ |
4479 | ownerId = typeForm->typowner; |
4480 | |
4481 | aclDatum = SysCacheGetAttr(TYPEOID, tuple, |
4482 | Anum_pg_type_typacl, &isNull); |
4483 | if (isNull) |
4484 | { |
4485 | /* No ACL, so build default ACL */ |
4486 | acl = acldefault(OBJECT_TYPE, ownerId); |
4487 | aclDatum = (Datum) 0; |
4488 | } |
4489 | else |
4490 | { |
4491 | /* detoast rel's ACL if necessary */ |
4492 | acl = DatumGetAclP(aclDatum); |
4493 | } |
4494 | |
4495 | result = aclmask(acl, roleid, ownerId, mask, how); |
4496 | |
4497 | /* if we have a detoasted copy, free it */ |
4498 | if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) |
4499 | pfree(acl); |
4500 | |
4501 | ReleaseSysCache(tuple); |
4502 | |
4503 | return result; |
4504 | } |
4505 | |
4506 | /* |
4507 | * Exported routine for checking a user's access privileges to a column |
4508 | * |
4509 | * Returns ACLCHECK_OK if the user has any of the privileges identified by |
4510 | * 'mode'; otherwise returns a suitable error code (in practice, always |
4511 | * ACLCHECK_NO_PRIV). |
4512 | * |
4513 | * As with pg_attribute_aclmask, only privileges granted directly on the |
4514 | * column are considered here. |
4515 | */ |
4516 | AclResult |
4517 | pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, |
4518 | Oid roleid, AclMode mode) |
4519 | { |
4520 | if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0) |
4521 | return ACLCHECK_OK; |
4522 | else |
4523 | return ACLCHECK_NO_PRIV; |
4524 | } |
4525 | |
4526 | /* |
4527 | * Exported routine for checking a user's access privileges to any/all columns |
4528 | * |
4529 | * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the |
4530 | * privileges identified by 'mode' on any non-dropped column in the relation; |
4531 | * otherwise returns a suitable error code (in practice, always |
4532 | * ACLCHECK_NO_PRIV). |
4533 | * |
4534 | * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the |
4535 | * privileges identified by 'mode' on each non-dropped column in the relation |
4536 | * (and there must be at least one such column); otherwise returns a suitable |
4537 | * error code (in practice, always ACLCHECK_NO_PRIV). |
4538 | * |
4539 | * As with pg_attribute_aclmask, only privileges granted directly on the |
4540 | * column(s) are considered here. |
4541 | * |
4542 | * Note: system columns are not considered here; there are cases where that |
4543 | * might be appropriate but there are also cases where it wouldn't. |
4544 | */ |
4545 | AclResult |
4546 | pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode, |
4547 | AclMaskHow how) |
4548 | { |
4549 | AclResult result; |
4550 | HeapTuple classTuple; |
4551 | Form_pg_class classForm; |
4552 | AttrNumber nattrs; |
4553 | AttrNumber curr_att; |
4554 | |
4555 | /* |
4556 | * Must fetch pg_class row to check number of attributes. As in |
4557 | * pg_attribute_aclmask, we prefer to return "no privileges" instead of |
4558 | * throwing an error if we get any unexpected lookup errors. |
4559 | */ |
4560 | classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid)); |
4561 | if (!HeapTupleIsValid(classTuple)) |
4562 | return ACLCHECK_NO_PRIV; |
4563 | classForm = (Form_pg_class) GETSTRUCT(classTuple); |
4564 | |
4565 | nattrs = classForm->relnatts; |
4566 | |
4567 | ReleaseSysCache(classTuple); |
4568 | |
4569 | /* |
4570 | * Initialize result in case there are no non-dropped columns. We want to |
4571 | * report failure in such cases for either value of 'how'. |
4572 | */ |
4573 | result = ACLCHECK_NO_PRIV; |
4574 | |
4575 | for (curr_att = 1; curr_att <= nattrs; curr_att++) |
4576 | { |
4577 | HeapTuple attTuple; |
4578 | AclMode attmask; |
4579 | |
4580 | attTuple = SearchSysCache2(ATTNUM, |
4581 | ObjectIdGetDatum(table_oid), |
4582 | Int16GetDatum(curr_att)); |
4583 | if (!HeapTupleIsValid(attTuple)) |
4584 | continue; |
4585 | |
4586 | /* ignore dropped columns */ |
4587 | if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped) |
4588 | { |
4589 | ReleaseSysCache(attTuple); |
4590 | continue; |
4591 | } |
4592 | |
4593 | /* |
4594 | * Here we hard-wire knowledge that the default ACL for a column |
4595 | * grants no privileges, so that we can fall out quickly in the very |
4596 | * common case where attacl is null. |
4597 | */ |
4598 | if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL)) |
4599 | attmask = 0; |
4600 | else |
4601 | attmask = pg_attribute_aclmask(table_oid, curr_att, roleid, |
4602 | mode, ACLMASK_ANY); |
4603 | |
4604 | ReleaseSysCache(attTuple); |
4605 | |
4606 | if (attmask != 0) |
4607 | { |
4608 | result = ACLCHECK_OK; |
4609 | if (how == ACLMASK_ANY) |
4610 | break; /* succeed on any success */ |
4611 | } |
4612 | else |
4613 | { |
4614 | result = ACLCHECK_NO_PRIV; |
4615 | if (how == ACLMASK_ALL) |
4616 | break; /* fail on any failure */ |
4617 | } |
4618 | } |
4619 | |
4620 | return result; |
4621 | } |
4622 | |
4623 | /* |
4624 | * Exported routine for checking a user's access privileges to a table |
4625 | * |
4626 | * Returns ACLCHECK_OK if the user has any of the privileges identified by |
4627 | * 'mode'; otherwise returns a suitable error code (in practice, always |
4628 | * ACLCHECK_NO_PRIV). |
4629 | */ |
4630 | AclResult |
4631 | pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode) |
4632 | { |
4633 | if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY) != 0) |
4634 | return ACLCHECK_OK; |
4635 | else |
4636 | return ACLCHECK_NO_PRIV; |
4637 | } |
4638 | |
4639 | /* |
4640 | * Exported routine for checking a user's access privileges to a database |
4641 | */ |
4642 | AclResult |
4643 | pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode) |
4644 | { |
4645 | if (pg_database_aclmask(db_oid, roleid, mode, ACLMASK_ANY) != 0) |
4646 | return ACLCHECK_OK; |
4647 | else |
4648 | return ACLCHECK_NO_PRIV; |
4649 | } |
4650 | |
4651 | /* |
4652 | * Exported routine for checking a user's access privileges to a function |
4653 | */ |
4654 | AclResult |
4655 | pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode) |
4656 | { |
4657 | if (pg_proc_aclmask(proc_oid, roleid, mode, ACLMASK_ANY) != 0) |
4658 | return ACLCHECK_OK; |
4659 | else |
4660 | return ACLCHECK_NO_PRIV; |
4661 | } |
4662 | |
4663 | /* |
4664 | * Exported routine for checking a user's access privileges to a language |
4665 | */ |
4666 | AclResult |
4667 | pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode) |
4668 | { |
4669 | if (pg_language_aclmask(lang_oid, roleid, mode, ACLMASK_ANY) != 0) |
4670 | return ACLCHECK_OK; |
4671 | else |
4672 | return ACLCHECK_NO_PRIV; |
4673 | } |
4674 | |
4675 | /* |
4676 | * Exported routine for checking a user's access privileges to a largeobject |
4677 | */ |
4678 | AclResult |
4679 | pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode, |
4680 | Snapshot snapshot) |
4681 | { |
4682 | if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode, |
4683 | ACLMASK_ANY, snapshot) != 0) |
4684 | return ACLCHECK_OK; |
4685 | else |
4686 | return ACLCHECK_NO_PRIV; |
4687 | } |
4688 | |
4689 | /* |
4690 | * Exported routine for checking a user's access privileges to a namespace |
4691 | */ |
4692 | AclResult |
4693 | pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode) |
4694 | { |
4695 | if (pg_namespace_aclmask(nsp_oid, roleid, mode, ACLMASK_ANY) != 0) |
4696 | return ACLCHECK_OK; |
4697 | else |
4698 | return ACLCHECK_NO_PRIV; |
4699 | } |
4700 | |
4701 | /* |
4702 | * Exported routine for checking a user's access privileges to a tablespace |
4703 | */ |
4704 | AclResult |
4705 | pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode) |
4706 | { |
4707 | if (pg_tablespace_aclmask(spc_oid, roleid, mode, ACLMASK_ANY) != 0) |
4708 | return ACLCHECK_OK; |
4709 | else |
4710 | return ACLCHECK_NO_PRIV; |
4711 | } |
4712 | |
4713 | /* |
4714 | * Exported routine for checking a user's access privileges to a foreign |
4715 | * data wrapper |
4716 | */ |
4717 | AclResult |
4718 | pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode) |
4719 | { |
4720 | if (pg_foreign_data_wrapper_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0) |
4721 | return ACLCHECK_OK; |
4722 | else |
4723 | return ACLCHECK_NO_PRIV; |
4724 | } |
4725 | |
4726 | /* |
4727 | * Exported routine for checking a user's access privileges to a foreign |
4728 | * server |
4729 | */ |
4730 | AclResult |
4731 | pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode) |
4732 | { |
4733 | if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0) |
4734 | return ACLCHECK_OK; |
4735 | else |
4736 | return ACLCHECK_NO_PRIV; |
4737 | } |
4738 | |
4739 | /* |
4740 | * Exported routine for checking a user's access privileges to a type |
4741 | */ |
4742 | AclResult |
4743 | pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode) |
4744 | { |
4745 | if (pg_type_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0) |
4746 | return ACLCHECK_OK; |
4747 | else |
4748 | return ACLCHECK_NO_PRIV; |
4749 | } |
4750 | |
4751 | /* |
4752 | * Ownership check for a relation (specified by OID). |
4753 | */ |
4754 | bool |
4755 | pg_class_ownercheck(Oid class_oid, Oid roleid) |
4756 | { |
4757 | HeapTuple tuple; |
4758 | Oid ownerId; |
4759 | |
4760 | /* Superusers bypass all permission checking. */ |
4761 | if (superuser_arg(roleid)) |
4762 | return true; |
4763 | |
4764 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(class_oid)); |
4765 | if (!HeapTupleIsValid(tuple)) |
4766 | ereport(ERROR, |
4767 | (errcode(ERRCODE_UNDEFINED_TABLE), |
4768 | errmsg("relation with OID %u does not exist" , class_oid))); |
4769 | |
4770 | ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner; |
4771 | |
4772 | ReleaseSysCache(tuple); |
4773 | |
4774 | return has_privs_of_role(roleid, ownerId); |
4775 | } |
4776 | |
4777 | /* |
4778 | * Ownership check for a type (specified by OID). |
4779 | */ |
4780 | bool |
4781 | pg_type_ownercheck(Oid type_oid, Oid roleid) |
4782 | { |
4783 | HeapTuple tuple; |
4784 | Oid ownerId; |
4785 | |
4786 | /* Superusers bypass all permission checking. */ |
4787 | if (superuser_arg(roleid)) |
4788 | return true; |
4789 | |
4790 | tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid)); |
4791 | if (!HeapTupleIsValid(tuple)) |
4792 | ereport(ERROR, |
4793 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4794 | errmsg("type with OID %u does not exist" , type_oid))); |
4795 | |
4796 | ownerId = ((Form_pg_type) GETSTRUCT(tuple))->typowner; |
4797 | |
4798 | ReleaseSysCache(tuple); |
4799 | |
4800 | return has_privs_of_role(roleid, ownerId); |
4801 | } |
4802 | |
4803 | /* |
4804 | * Ownership check for an operator (specified by OID). |
4805 | */ |
4806 | bool |
4807 | pg_oper_ownercheck(Oid oper_oid, Oid roleid) |
4808 | { |
4809 | HeapTuple tuple; |
4810 | Oid ownerId; |
4811 | |
4812 | /* Superusers bypass all permission checking. */ |
4813 | if (superuser_arg(roleid)) |
4814 | return true; |
4815 | |
4816 | tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(oper_oid)); |
4817 | if (!HeapTupleIsValid(tuple)) |
4818 | ereport(ERROR, |
4819 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
4820 | errmsg("operator with OID %u does not exist" , oper_oid))); |
4821 | |
4822 | ownerId = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner; |
4823 | |
4824 | ReleaseSysCache(tuple); |
4825 | |
4826 | return has_privs_of_role(roleid, ownerId); |
4827 | } |
4828 | |
4829 | /* |
4830 | * Ownership check for a function (specified by OID). |
4831 | */ |
4832 | bool |
4833 | pg_proc_ownercheck(Oid proc_oid, Oid roleid) |
4834 | { |
4835 | HeapTuple tuple; |
4836 | Oid ownerId; |
4837 | |
4838 | /* Superusers bypass all permission checking. */ |
4839 | if (superuser_arg(roleid)) |
4840 | return true; |
4841 | |
4842 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid)); |
4843 | if (!HeapTupleIsValid(tuple)) |
4844 | ereport(ERROR, |
4845 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
4846 | errmsg("function with OID %u does not exist" , proc_oid))); |
4847 | |
4848 | ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner; |
4849 | |
4850 | ReleaseSysCache(tuple); |
4851 | |
4852 | return has_privs_of_role(roleid, ownerId); |
4853 | } |
4854 | |
4855 | /* |
4856 | * Ownership check for a procedural language (specified by OID) |
4857 | */ |
4858 | bool |
4859 | pg_language_ownercheck(Oid lan_oid, Oid roleid) |
4860 | { |
4861 | HeapTuple tuple; |
4862 | Oid ownerId; |
4863 | |
4864 | /* Superusers bypass all permission checking. */ |
4865 | if (superuser_arg(roleid)) |
4866 | return true; |
4867 | |
4868 | tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lan_oid)); |
4869 | if (!HeapTupleIsValid(tuple)) |
4870 | ereport(ERROR, |
4871 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
4872 | errmsg("language with OID %u does not exist" , lan_oid))); |
4873 | |
4874 | ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner; |
4875 | |
4876 | ReleaseSysCache(tuple); |
4877 | |
4878 | return has_privs_of_role(roleid, ownerId); |
4879 | } |
4880 | |
4881 | /* |
4882 | * Ownership check for a largeobject (specified by OID) |
4883 | * |
4884 | * This is only used for operations like ALTER LARGE OBJECT that are always |
4885 | * relative to an up-to-date snapshot. |
4886 | */ |
4887 | bool |
4888 | pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid) |
4889 | { |
4890 | Relation pg_lo_meta; |
4891 | ScanKeyData entry[1]; |
4892 | SysScanDesc scan; |
4893 | HeapTuple tuple; |
4894 | Oid ownerId; |
4895 | |
4896 | /* Superusers bypass all permission checking. */ |
4897 | if (superuser_arg(roleid)) |
4898 | return true; |
4899 | |
4900 | /* There's no syscache for pg_largeobject_metadata */ |
4901 | pg_lo_meta = table_open(LargeObjectMetadataRelationId, |
4902 | AccessShareLock); |
4903 | |
4904 | ScanKeyInit(&entry[0], |
4905 | Anum_pg_largeobject_metadata_oid, |
4906 | BTEqualStrategyNumber, F_OIDEQ, |
4907 | ObjectIdGetDatum(lobj_oid)); |
4908 | |
4909 | scan = systable_beginscan(pg_lo_meta, |
4910 | LargeObjectMetadataOidIndexId, true, |
4911 | NULL, 1, entry); |
4912 | |
4913 | tuple = systable_getnext(scan); |
4914 | if (!HeapTupleIsValid(tuple)) |
4915 | ereport(ERROR, |
4916 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4917 | errmsg("large object %u does not exist" , lobj_oid))); |
4918 | |
4919 | ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; |
4920 | |
4921 | systable_endscan(scan); |
4922 | table_close(pg_lo_meta, AccessShareLock); |
4923 | |
4924 | return has_privs_of_role(roleid, ownerId); |
4925 | } |
4926 | |
4927 | /* |
4928 | * Ownership check for a namespace (specified by OID). |
4929 | */ |
4930 | bool |
4931 | pg_namespace_ownercheck(Oid nsp_oid, Oid roleid) |
4932 | { |
4933 | HeapTuple tuple; |
4934 | Oid ownerId; |
4935 | |
4936 | /* Superusers bypass all permission checking. */ |
4937 | if (superuser_arg(roleid)) |
4938 | return true; |
4939 | |
4940 | tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid)); |
4941 | if (!HeapTupleIsValid(tuple)) |
4942 | ereport(ERROR, |
4943 | (errcode(ERRCODE_UNDEFINED_SCHEMA), |
4944 | errmsg("schema with OID %u does not exist" , nsp_oid))); |
4945 | |
4946 | ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner; |
4947 | |
4948 | ReleaseSysCache(tuple); |
4949 | |
4950 | return has_privs_of_role(roleid, ownerId); |
4951 | } |
4952 | |
4953 | /* |
4954 | * Ownership check for a tablespace (specified by OID). |
4955 | */ |
4956 | bool |
4957 | pg_tablespace_ownercheck(Oid spc_oid, Oid roleid) |
4958 | { |
4959 | HeapTuple spctuple; |
4960 | Oid spcowner; |
4961 | |
4962 | /* Superusers bypass all permission checking. */ |
4963 | if (superuser_arg(roleid)) |
4964 | return true; |
4965 | |
4966 | /* Search syscache for pg_tablespace */ |
4967 | spctuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid)); |
4968 | if (!HeapTupleIsValid(spctuple)) |
4969 | ereport(ERROR, |
4970 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4971 | errmsg("tablespace with OID %u does not exist" , spc_oid))); |
4972 | |
4973 | spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner; |
4974 | |
4975 | ReleaseSysCache(spctuple); |
4976 | |
4977 | return has_privs_of_role(roleid, spcowner); |
4978 | } |
4979 | |
4980 | /* |
4981 | * Ownership check for an operator class (specified by OID). |
4982 | */ |
4983 | bool |
4984 | pg_opclass_ownercheck(Oid opc_oid, Oid roleid) |
4985 | { |
4986 | HeapTuple tuple; |
4987 | Oid ownerId; |
4988 | |
4989 | /* Superusers bypass all permission checking. */ |
4990 | if (superuser_arg(roleid)) |
4991 | return true; |
4992 | |
4993 | tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opc_oid)); |
4994 | if (!HeapTupleIsValid(tuple)) |
4995 | ereport(ERROR, |
4996 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4997 | errmsg("operator class with OID %u does not exist" , |
4998 | opc_oid))); |
4999 | |
5000 | ownerId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcowner; |
5001 | |
5002 | ReleaseSysCache(tuple); |
5003 | |
5004 | return has_privs_of_role(roleid, ownerId); |
5005 | } |
5006 | |
5007 | /* |
5008 | * Ownership check for an operator family (specified by OID). |
5009 | */ |
5010 | bool |
5011 | pg_opfamily_ownercheck(Oid opf_oid, Oid roleid) |
5012 | { |
5013 | HeapTuple tuple; |
5014 | Oid ownerId; |
5015 | |
5016 | /* Superusers bypass all permission checking. */ |
5017 | if (superuser_arg(roleid)) |
5018 | return true; |
5019 | |
5020 | tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opf_oid)); |
5021 | if (!HeapTupleIsValid(tuple)) |
5022 | ereport(ERROR, |
5023 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5024 | errmsg("operator family with OID %u does not exist" , |
5025 | opf_oid))); |
5026 | |
5027 | ownerId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfowner; |
5028 | |
5029 | ReleaseSysCache(tuple); |
5030 | |
5031 | return has_privs_of_role(roleid, ownerId); |
5032 | } |
5033 | |
5034 | /* |
5035 | * Ownership check for a text search dictionary (specified by OID). |
5036 | */ |
5037 | bool |
5038 | pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid) |
5039 | { |
5040 | HeapTuple tuple; |
5041 | Oid ownerId; |
5042 | |
5043 | /* Superusers bypass all permission checking. */ |
5044 | if (superuser_arg(roleid)) |
5045 | return true; |
5046 | |
5047 | tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dict_oid)); |
5048 | if (!HeapTupleIsValid(tuple)) |
5049 | ereport(ERROR, |
5050 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5051 | errmsg("text search dictionary with OID %u does not exist" , |
5052 | dict_oid))); |
5053 | |
5054 | ownerId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictowner; |
5055 | |
5056 | ReleaseSysCache(tuple); |
5057 | |
5058 | return has_privs_of_role(roleid, ownerId); |
5059 | } |
5060 | |
5061 | /* |
5062 | * Ownership check for a text search configuration (specified by OID). |
5063 | */ |
5064 | bool |
5065 | pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid) |
5066 | { |
5067 | HeapTuple tuple; |
5068 | Oid ownerId; |
5069 | |
5070 | /* Superusers bypass all permission checking. */ |
5071 | if (superuser_arg(roleid)) |
5072 | return true; |
5073 | |
5074 | tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfg_oid)); |
5075 | if (!HeapTupleIsValid(tuple)) |
5076 | ereport(ERROR, |
5077 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5078 | errmsg("text search configuration with OID %u does not exist" , |
5079 | cfg_oid))); |
5080 | |
5081 | ownerId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgowner; |
5082 | |
5083 | ReleaseSysCache(tuple); |
5084 | |
5085 | return has_privs_of_role(roleid, ownerId); |
5086 | } |
5087 | |
5088 | /* |
5089 | * Ownership check for a foreign-data wrapper (specified by OID). |
5090 | */ |
5091 | bool |
5092 | pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid) |
5093 | { |
5094 | HeapTuple tuple; |
5095 | Oid ownerId; |
5096 | |
5097 | /* Superusers bypass all permission checking. */ |
5098 | if (superuser_arg(roleid)) |
5099 | return true; |
5100 | |
5101 | tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(srv_oid)); |
5102 | if (!HeapTupleIsValid(tuple)) |
5103 | ereport(ERROR, |
5104 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5105 | errmsg("foreign-data wrapper with OID %u does not exist" , |
5106 | srv_oid))); |
5107 | |
5108 | ownerId = ((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwowner; |
5109 | |
5110 | ReleaseSysCache(tuple); |
5111 | |
5112 | return has_privs_of_role(roleid, ownerId); |
5113 | } |
5114 | |
5115 | /* |
5116 | * Ownership check for a foreign server (specified by OID). |
5117 | */ |
5118 | bool |
5119 | pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid) |
5120 | { |
5121 | HeapTuple tuple; |
5122 | Oid ownerId; |
5123 | |
5124 | /* Superusers bypass all permission checking. */ |
5125 | if (superuser_arg(roleid)) |
5126 | return true; |
5127 | |
5128 | tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid)); |
5129 | if (!HeapTupleIsValid(tuple)) |
5130 | ereport(ERROR, |
5131 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5132 | errmsg("foreign server with OID %u does not exist" , |
5133 | srv_oid))); |
5134 | |
5135 | ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner; |
5136 | |
5137 | ReleaseSysCache(tuple); |
5138 | |
5139 | return has_privs_of_role(roleid, ownerId); |
5140 | } |
5141 | |
5142 | /* |
5143 | * Ownership check for an event trigger (specified by OID). |
5144 | */ |
5145 | bool |
5146 | pg_event_trigger_ownercheck(Oid et_oid, Oid roleid) |
5147 | { |
5148 | HeapTuple tuple; |
5149 | Oid ownerId; |
5150 | |
5151 | /* Superusers bypass all permission checking. */ |
5152 | if (superuser_arg(roleid)) |
5153 | return true; |
5154 | |
5155 | tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid)); |
5156 | if (!HeapTupleIsValid(tuple)) |
5157 | ereport(ERROR, |
5158 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5159 | errmsg("event trigger with OID %u does not exist" , |
5160 | et_oid))); |
5161 | |
5162 | ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner; |
5163 | |
5164 | ReleaseSysCache(tuple); |
5165 | |
5166 | return has_privs_of_role(roleid, ownerId); |
5167 | } |
5168 | |
5169 | /* |
5170 | * Ownership check for a database (specified by OID). |
5171 | */ |
5172 | bool |
5173 | pg_database_ownercheck(Oid db_oid, Oid roleid) |
5174 | { |
5175 | HeapTuple tuple; |
5176 | Oid dba; |
5177 | |
5178 | /* Superusers bypass all permission checking. */ |
5179 | if (superuser_arg(roleid)) |
5180 | return true; |
5181 | |
5182 | tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid)); |
5183 | if (!HeapTupleIsValid(tuple)) |
5184 | ereport(ERROR, |
5185 | (errcode(ERRCODE_UNDEFINED_DATABASE), |
5186 | errmsg("database with OID %u does not exist" , db_oid))); |
5187 | |
5188 | dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba; |
5189 | |
5190 | ReleaseSysCache(tuple); |
5191 | |
5192 | return has_privs_of_role(roleid, dba); |
5193 | } |
5194 | |
5195 | /* |
5196 | * Ownership check for a collation (specified by OID). |
5197 | */ |
5198 | bool |
5199 | pg_collation_ownercheck(Oid coll_oid, Oid roleid) |
5200 | { |
5201 | HeapTuple tuple; |
5202 | Oid ownerId; |
5203 | |
5204 | /* Superusers bypass all permission checking. */ |
5205 | if (superuser_arg(roleid)) |
5206 | return true; |
5207 | |
5208 | tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(coll_oid)); |
5209 | if (!HeapTupleIsValid(tuple)) |
5210 | ereport(ERROR, |
5211 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5212 | errmsg("collation with OID %u does not exist" , coll_oid))); |
5213 | |
5214 | ownerId = ((Form_pg_collation) GETSTRUCT(tuple))->collowner; |
5215 | |
5216 | ReleaseSysCache(tuple); |
5217 | |
5218 | return has_privs_of_role(roleid, ownerId); |
5219 | } |
5220 | |
5221 | /* |
5222 | * Ownership check for a conversion (specified by OID). |
5223 | */ |
5224 | bool |
5225 | pg_conversion_ownercheck(Oid conv_oid, Oid roleid) |
5226 | { |
5227 | HeapTuple tuple; |
5228 | Oid ownerId; |
5229 | |
5230 | /* Superusers bypass all permission checking. */ |
5231 | if (superuser_arg(roleid)) |
5232 | return true; |
5233 | |
5234 | tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(conv_oid)); |
5235 | if (!HeapTupleIsValid(tuple)) |
5236 | ereport(ERROR, |
5237 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5238 | errmsg("conversion with OID %u does not exist" , conv_oid))); |
5239 | |
5240 | ownerId = ((Form_pg_conversion) GETSTRUCT(tuple))->conowner; |
5241 | |
5242 | ReleaseSysCache(tuple); |
5243 | |
5244 | return has_privs_of_role(roleid, ownerId); |
5245 | } |
5246 | |
5247 | /* |
5248 | * Ownership check for an extension (specified by OID). |
5249 | */ |
5250 | bool |
5251 | pg_extension_ownercheck(Oid ext_oid, Oid roleid) |
5252 | { |
5253 | Relation pg_extension; |
5254 | ScanKeyData entry[1]; |
5255 | SysScanDesc scan; |
5256 | HeapTuple tuple; |
5257 | Oid ownerId; |
5258 | |
5259 | /* Superusers bypass all permission checking. */ |
5260 | if (superuser_arg(roleid)) |
5261 | return true; |
5262 | |
5263 | /* There's no syscache for pg_extension, so do it the hard way */ |
5264 | pg_extension = table_open(ExtensionRelationId, AccessShareLock); |
5265 | |
5266 | ScanKeyInit(&entry[0], |
5267 | Anum_pg_extension_oid, |
5268 | BTEqualStrategyNumber, F_OIDEQ, |
5269 | ObjectIdGetDatum(ext_oid)); |
5270 | |
5271 | scan = systable_beginscan(pg_extension, |
5272 | ExtensionOidIndexId, true, |
5273 | NULL, 1, entry); |
5274 | |
5275 | tuple = systable_getnext(scan); |
5276 | if (!HeapTupleIsValid(tuple)) |
5277 | ereport(ERROR, |
5278 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5279 | errmsg("extension with OID %u does not exist" , ext_oid))); |
5280 | |
5281 | ownerId = ((Form_pg_extension) GETSTRUCT(tuple))->extowner; |
5282 | |
5283 | systable_endscan(scan); |
5284 | table_close(pg_extension, AccessShareLock); |
5285 | |
5286 | return has_privs_of_role(roleid, ownerId); |
5287 | } |
5288 | |
5289 | /* |
5290 | * Ownership check for a publication (specified by OID). |
5291 | */ |
5292 | bool |
5293 | pg_publication_ownercheck(Oid pub_oid, Oid roleid) |
5294 | { |
5295 | HeapTuple tuple; |
5296 | Oid ownerId; |
5297 | |
5298 | /* Superusers bypass all permission checking. */ |
5299 | if (superuser_arg(roleid)) |
5300 | return true; |
5301 | |
5302 | tuple = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pub_oid)); |
5303 | if (!HeapTupleIsValid(tuple)) |
5304 | ereport(ERROR, |
5305 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5306 | errmsg("publication with OID %u does not exist" , pub_oid))); |
5307 | |
5308 | ownerId = ((Form_pg_publication) GETSTRUCT(tuple))->pubowner; |
5309 | |
5310 | ReleaseSysCache(tuple); |
5311 | |
5312 | return has_privs_of_role(roleid, ownerId); |
5313 | } |
5314 | |
5315 | /* |
5316 | * Ownership check for a subscription (specified by OID). |
5317 | */ |
5318 | bool |
5319 | pg_subscription_ownercheck(Oid sub_oid, Oid roleid) |
5320 | { |
5321 | HeapTuple tuple; |
5322 | Oid ownerId; |
5323 | |
5324 | /* Superusers bypass all permission checking. */ |
5325 | if (superuser_arg(roleid)) |
5326 | return true; |
5327 | |
5328 | tuple = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(sub_oid)); |
5329 | if (!HeapTupleIsValid(tuple)) |
5330 | ereport(ERROR, |
5331 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5332 | errmsg("subscription with OID %u does not exist" , sub_oid))); |
5333 | |
5334 | ownerId = ((Form_pg_subscription) GETSTRUCT(tuple))->subowner; |
5335 | |
5336 | ReleaseSysCache(tuple); |
5337 | |
5338 | return has_privs_of_role(roleid, ownerId); |
5339 | } |
5340 | |
5341 | /* |
5342 | * Ownership check for a statistics object (specified by OID). |
5343 | */ |
5344 | bool |
5345 | pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid) |
5346 | { |
5347 | HeapTuple tuple; |
5348 | Oid ownerId; |
5349 | |
5350 | /* Superusers bypass all permission checking. */ |
5351 | if (superuser_arg(roleid)) |
5352 | return true; |
5353 | |
5354 | tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stat_oid)); |
5355 | if (!HeapTupleIsValid(tuple)) |
5356 | ereport(ERROR, |
5357 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5358 | errmsg("statistics object with OID %u does not exist" , |
5359 | stat_oid))); |
5360 | |
5361 | ownerId = ((Form_pg_statistic_ext) GETSTRUCT(tuple))->stxowner; |
5362 | |
5363 | ReleaseSysCache(tuple); |
5364 | |
5365 | return has_privs_of_role(roleid, ownerId); |
5366 | } |
5367 | |
5368 | /* |
5369 | * Check whether specified role has CREATEROLE privilege (or is a superuser) |
5370 | * |
5371 | * Note: roles do not have owners per se; instead we use this test in |
5372 | * places where an ownership-like permissions test is needed for a role. |
5373 | * Be sure to apply it to the role trying to do the operation, not the |
5374 | * role being operated on! Also note that this generally should not be |
5375 | * considered enough privilege if the target role is a superuser. |
5376 | * (We don't handle that consideration here because we want to give a |
5377 | * separate error message for such cases, so the caller has to deal with it.) |
5378 | */ |
5379 | bool |
5380 | has_createrole_privilege(Oid roleid) |
5381 | { |
5382 | bool result = false; |
5383 | HeapTuple utup; |
5384 | |
5385 | /* Superusers bypass all permission checking. */ |
5386 | if (superuser_arg(roleid)) |
5387 | return true; |
5388 | |
5389 | utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); |
5390 | if (HeapTupleIsValid(utup)) |
5391 | { |
5392 | result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole; |
5393 | ReleaseSysCache(utup); |
5394 | } |
5395 | return result; |
5396 | } |
5397 | |
5398 | bool |
5399 | has_bypassrls_privilege(Oid roleid) |
5400 | { |
5401 | bool result = false; |
5402 | HeapTuple utup; |
5403 | |
5404 | /* Superusers bypass all permission checking. */ |
5405 | if (superuser_arg(roleid)) |
5406 | return true; |
5407 | |
5408 | utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); |
5409 | if (HeapTupleIsValid(utup)) |
5410 | { |
5411 | result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls; |
5412 | ReleaseSysCache(utup); |
5413 | } |
5414 | return result; |
5415 | } |
5416 | |
5417 | /* |
5418 | * Fetch pg_default_acl entry for given role, namespace and object type |
5419 | * (object type must be given in pg_default_acl's encoding). |
5420 | * Returns NULL if no such entry. |
5421 | */ |
5422 | static Acl * |
5423 | get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype) |
5424 | { |
5425 | Acl *result = NULL; |
5426 | HeapTuple tuple; |
5427 | |
5428 | tuple = SearchSysCache3(DEFACLROLENSPOBJ, |
5429 | ObjectIdGetDatum(roleId), |
5430 | ObjectIdGetDatum(nsp_oid), |
5431 | CharGetDatum(objtype)); |
5432 | |
5433 | if (HeapTupleIsValid(tuple)) |
5434 | { |
5435 | Datum aclDatum; |
5436 | bool isNull; |
5437 | |
5438 | aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple, |
5439 | Anum_pg_default_acl_defaclacl, |
5440 | &isNull); |
5441 | if (!isNull) |
5442 | result = DatumGetAclPCopy(aclDatum); |
5443 | ReleaseSysCache(tuple); |
5444 | } |
5445 | |
5446 | return result; |
5447 | } |
5448 | |
5449 | /* |
5450 | * Get default permissions for newly created object within given schema |
5451 | * |
5452 | * Returns NULL if built-in system defaults should be used. |
5453 | * |
5454 | * If the result is not NULL, caller must call recordDependencyOnNewAcl |
5455 | * once the OID of the new object is known. |
5456 | */ |
5457 | Acl * |
5458 | get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid) |
5459 | { |
5460 | Acl *result; |
5461 | Acl *glob_acl; |
5462 | Acl *schema_acl; |
5463 | Acl *def_acl; |
5464 | char defaclobjtype; |
5465 | |
5466 | /* |
5467 | * Use NULL during bootstrap, since pg_default_acl probably isn't there |
5468 | * yet. |
5469 | */ |
5470 | if (IsBootstrapProcessingMode()) |
5471 | return NULL; |
5472 | |
5473 | /* Check if object type is supported in pg_default_acl */ |
5474 | switch (objtype) |
5475 | { |
5476 | case OBJECT_TABLE: |
5477 | defaclobjtype = DEFACLOBJ_RELATION; |
5478 | break; |
5479 | |
5480 | case OBJECT_SEQUENCE: |
5481 | defaclobjtype = DEFACLOBJ_SEQUENCE; |
5482 | break; |
5483 | |
5484 | case OBJECT_FUNCTION: |
5485 | defaclobjtype = DEFACLOBJ_FUNCTION; |
5486 | break; |
5487 | |
5488 | case OBJECT_TYPE: |
5489 | defaclobjtype = DEFACLOBJ_TYPE; |
5490 | break; |
5491 | |
5492 | case OBJECT_SCHEMA: |
5493 | defaclobjtype = DEFACLOBJ_NAMESPACE; |
5494 | break; |
5495 | |
5496 | default: |
5497 | return NULL; |
5498 | } |
5499 | |
5500 | /* Look up the relevant pg_default_acl entries */ |
5501 | glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype); |
5502 | schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype); |
5503 | |
5504 | /* Quick out if neither entry exists */ |
5505 | if (glob_acl == NULL && schema_acl == NULL) |
5506 | return NULL; |
5507 | |
5508 | /* We need to know the hard-wired default value, too */ |
5509 | def_acl = acldefault(objtype, ownerId); |
5510 | |
5511 | /* If there's no global entry, substitute the hard-wired default */ |
5512 | if (glob_acl == NULL) |
5513 | glob_acl = def_acl; |
5514 | |
5515 | /* Merge in any per-schema privileges */ |
5516 | result = aclmerge(glob_acl, schema_acl, ownerId); |
5517 | |
5518 | /* |
5519 | * For efficiency, we want to return NULL if the result equals default. |
5520 | * This requires sorting both arrays to get an accurate comparison. |
5521 | */ |
5522 | aclitemsort(result); |
5523 | aclitemsort(def_acl); |
5524 | if (aclequal(result, def_acl)) |
5525 | result = NULL; |
5526 | |
5527 | return result; |
5528 | } |
5529 | |
5530 | /* |
5531 | * Record dependencies on roles mentioned in a new object's ACL. |
5532 | */ |
5533 | void |
5534 | recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId, |
5535 | Oid ownerId, Acl *acl) |
5536 | { |
5537 | int nmembers; |
5538 | Oid *members; |
5539 | |
5540 | /* Nothing to do if ACL is defaulted */ |
5541 | if (acl == NULL) |
5542 | return; |
5543 | |
5544 | /* Extract roles mentioned in ACL */ |
5545 | nmembers = aclmembers(acl, &members); |
5546 | |
5547 | /* Update the shared dependency ACL info */ |
5548 | updateAclDependencies(classId, objectId, objsubId, |
5549 | ownerId, |
5550 | 0, NULL, |
5551 | nmembers, members); |
5552 | } |
5553 | |
5554 | /* |
5555 | * Record initial privileges for the top-level object passed in. |
5556 | * |
5557 | * For the object passed in, this will record its ACL (if any) and the ACLs of |
5558 | * any sub-objects (eg: columns) into pg_init_privs. |
5559 | * |
5560 | * Any new kinds of objects which have ACLs associated with them and can be |
5561 | * added to an extension should be added to the if-else tree below. |
5562 | */ |
5563 | void |
5564 | recordExtObjInitPriv(Oid objoid, Oid classoid) |
5565 | { |
5566 | /* |
5567 | * pg_class / pg_attribute |
5568 | * |
5569 | * If this is a relation then we need to see if there are any sub-objects |
5570 | * (eg: columns) for it and, if so, be sure to call |
5571 | * recordExtensionInitPrivWorker() for each one. |
5572 | */ |
5573 | if (classoid == RelationRelationId) |
5574 | { |
5575 | Form_pg_class pg_class_tuple; |
5576 | Datum aclDatum; |
5577 | bool isNull; |
5578 | HeapTuple tuple; |
5579 | |
5580 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid)); |
5581 | if (!HeapTupleIsValid(tuple)) |
5582 | elog(ERROR, "cache lookup failed for relation %u" , objoid); |
5583 | pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); |
5584 | |
5585 | /* Indexes don't have permissions */ |
5586 | if (pg_class_tuple->relkind == RELKIND_INDEX || |
5587 | pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX) |
5588 | return; |
5589 | |
5590 | /* Composite types don't have permissions either */ |
5591 | if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) |
5592 | return; |
5593 | |
5594 | /* |
5595 | * If this isn't a sequence, index, or composite type then it's |
5596 | * possibly going to have columns associated with it that might have |
5597 | * ACLs. |
5598 | */ |
5599 | if (pg_class_tuple->relkind != RELKIND_SEQUENCE) |
5600 | { |
5601 | AttrNumber curr_att; |
5602 | AttrNumber nattrs = pg_class_tuple->relnatts; |
5603 | |
5604 | for (curr_att = 1; curr_att <= nattrs; curr_att++) |
5605 | { |
5606 | HeapTuple attTuple; |
5607 | Datum attaclDatum; |
5608 | |
5609 | attTuple = SearchSysCache2(ATTNUM, |
5610 | ObjectIdGetDatum(objoid), |
5611 | Int16GetDatum(curr_att)); |
5612 | |
5613 | if (!HeapTupleIsValid(attTuple)) |
5614 | continue; |
5615 | |
5616 | /* ignore dropped columns */ |
5617 | if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped) |
5618 | { |
5619 | ReleaseSysCache(attTuple); |
5620 | continue; |
5621 | } |
5622 | |
5623 | attaclDatum = SysCacheGetAttr(ATTNUM, attTuple, |
5624 | Anum_pg_attribute_attacl, |
5625 | &isNull); |
5626 | |
5627 | /* no need to do anything for a NULL ACL */ |
5628 | if (isNull) |
5629 | { |
5630 | ReleaseSysCache(attTuple); |
5631 | continue; |
5632 | } |
5633 | |
5634 | recordExtensionInitPrivWorker(objoid, classoid, curr_att, |
5635 | DatumGetAclP(attaclDatum)); |
5636 | |
5637 | ReleaseSysCache(attTuple); |
5638 | } |
5639 | } |
5640 | |
5641 | aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, |
5642 | &isNull); |
5643 | |
5644 | /* Add the record, if any, for the top-level object */ |
5645 | if (!isNull) |
5646 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5647 | DatumGetAclP(aclDatum)); |
5648 | |
5649 | ReleaseSysCache(tuple); |
5650 | } |
5651 | /* pg_foreign_data_wrapper */ |
5652 | else if (classoid == ForeignDataWrapperRelationId) |
5653 | { |
5654 | Datum aclDatum; |
5655 | bool isNull; |
5656 | HeapTuple tuple; |
5657 | |
5658 | tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, |
5659 | ObjectIdGetDatum(objoid)); |
5660 | if (!HeapTupleIsValid(tuple)) |
5661 | elog(ERROR, "cache lookup failed for foreign data wrapper %u" , |
5662 | objoid); |
5663 | |
5664 | aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple, |
5665 | Anum_pg_foreign_data_wrapper_fdwacl, |
5666 | &isNull); |
5667 | |
5668 | /* Add the record, if any, for the top-level object */ |
5669 | if (!isNull) |
5670 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5671 | DatumGetAclP(aclDatum)); |
5672 | |
5673 | ReleaseSysCache(tuple); |
5674 | } |
5675 | /* pg_foreign_server */ |
5676 | else if (classoid == ForeignServerRelationId) |
5677 | { |
5678 | Datum aclDatum; |
5679 | bool isNull; |
5680 | HeapTuple tuple; |
5681 | |
5682 | tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(objoid)); |
5683 | if (!HeapTupleIsValid(tuple)) |
5684 | elog(ERROR, "cache lookup failed for foreign data wrapper %u" , |
5685 | objoid); |
5686 | |
5687 | aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple, |
5688 | Anum_pg_foreign_server_srvacl, |
5689 | &isNull); |
5690 | |
5691 | /* Add the record, if any, for the top-level object */ |
5692 | if (!isNull) |
5693 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5694 | DatumGetAclP(aclDatum)); |
5695 | |
5696 | ReleaseSysCache(tuple); |
5697 | } |
5698 | /* pg_language */ |
5699 | else if (classoid == LanguageRelationId) |
5700 | { |
5701 | Datum aclDatum; |
5702 | bool isNull; |
5703 | HeapTuple tuple; |
5704 | |
5705 | tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(objoid)); |
5706 | if (!HeapTupleIsValid(tuple)) |
5707 | elog(ERROR, "cache lookup failed for language %u" , objoid); |
5708 | |
5709 | aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl, |
5710 | &isNull); |
5711 | |
5712 | /* Add the record, if any, for the top-level object */ |
5713 | if (!isNull) |
5714 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5715 | DatumGetAclP(aclDatum)); |
5716 | |
5717 | ReleaseSysCache(tuple); |
5718 | } |
5719 | /* pg_largeobject_metadata */ |
5720 | else if (classoid == LargeObjectMetadataRelationId) |
5721 | { |
5722 | Datum aclDatum; |
5723 | bool isNull; |
5724 | HeapTuple tuple; |
5725 | ScanKeyData entry[1]; |
5726 | SysScanDesc scan; |
5727 | Relation relation; |
5728 | |
5729 | relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock); |
5730 | |
5731 | /* There's no syscache for pg_largeobject_metadata */ |
5732 | ScanKeyInit(&entry[0], |
5733 | Anum_pg_largeobject_metadata_oid, |
5734 | BTEqualStrategyNumber, F_OIDEQ, |
5735 | ObjectIdGetDatum(objoid)); |
5736 | |
5737 | scan = systable_beginscan(relation, |
5738 | LargeObjectMetadataOidIndexId, true, |
5739 | NULL, 1, entry); |
5740 | |
5741 | tuple = systable_getnext(scan); |
5742 | if (!HeapTupleIsValid(tuple)) |
5743 | elog(ERROR, "could not find tuple for large object %u" , objoid); |
5744 | |
5745 | aclDatum = heap_getattr(tuple, |
5746 | Anum_pg_largeobject_metadata_lomacl, |
5747 | RelationGetDescr(relation), &isNull); |
5748 | |
5749 | /* Add the record, if any, for the top-level object */ |
5750 | if (!isNull) |
5751 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5752 | DatumGetAclP(aclDatum)); |
5753 | |
5754 | systable_endscan(scan); |
5755 | } |
5756 | /* pg_namespace */ |
5757 | else if (classoid == NamespaceRelationId) |
5758 | { |
5759 | Datum aclDatum; |
5760 | bool isNull; |
5761 | HeapTuple tuple; |
5762 | |
5763 | tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(objoid)); |
5764 | if (!HeapTupleIsValid(tuple)) |
5765 | elog(ERROR, "cache lookup failed for function %u" , objoid); |
5766 | |
5767 | aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, |
5768 | Anum_pg_namespace_nspacl, &isNull); |
5769 | |
5770 | /* Add the record, if any, for the top-level object */ |
5771 | if (!isNull) |
5772 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5773 | DatumGetAclP(aclDatum)); |
5774 | |
5775 | ReleaseSysCache(tuple); |
5776 | } |
5777 | /* pg_proc */ |
5778 | else if (classoid == ProcedureRelationId) |
5779 | { |
5780 | Datum aclDatum; |
5781 | bool isNull; |
5782 | HeapTuple tuple; |
5783 | |
5784 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(objoid)); |
5785 | if (!HeapTupleIsValid(tuple)) |
5786 | elog(ERROR, "cache lookup failed for function %u" , objoid); |
5787 | |
5788 | aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl, |
5789 | &isNull); |
5790 | |
5791 | /* Add the record, if any, for the top-level object */ |
5792 | if (!isNull) |
5793 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5794 | DatumGetAclP(aclDatum)); |
5795 | |
5796 | ReleaseSysCache(tuple); |
5797 | } |
5798 | /* pg_type */ |
5799 | else if (classoid == TypeRelationId) |
5800 | { |
5801 | Datum aclDatum; |
5802 | bool isNull; |
5803 | HeapTuple tuple; |
5804 | |
5805 | tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objoid)); |
5806 | if (!HeapTupleIsValid(tuple)) |
5807 | elog(ERROR, "cache lookup failed for function %u" , objoid); |
5808 | |
5809 | aclDatum = SysCacheGetAttr(TYPEOID, tuple, Anum_pg_type_typacl, |
5810 | &isNull); |
5811 | |
5812 | /* Add the record, if any, for the top-level object */ |
5813 | if (!isNull) |
5814 | recordExtensionInitPrivWorker(objoid, classoid, 0, |
5815 | DatumGetAclP(aclDatum)); |
5816 | |
5817 | ReleaseSysCache(tuple); |
5818 | } |
5819 | else if (classoid == AccessMethodRelationId || |
5820 | classoid == AggregateRelationId || |
5821 | classoid == CastRelationId || |
5822 | classoid == CollationRelationId || |
5823 | classoid == ConversionRelationId || |
5824 | classoid == EventTriggerRelationId || |
5825 | classoid == OperatorRelationId || |
5826 | classoid == OperatorClassRelationId || |
5827 | classoid == OperatorFamilyRelationId || |
5828 | classoid == NamespaceRelationId || |
5829 | classoid == TSConfigRelationId || |
5830 | classoid == TSDictionaryRelationId || |
5831 | classoid == TSParserRelationId || |
5832 | classoid == TSTemplateRelationId || |
5833 | classoid == TransformRelationId |
5834 | ) |
5835 | { |
5836 | /* no ACL for these object types, so do nothing. */ |
5837 | } |
5838 | |
5839 | /* |
5840 | * complain if we are given a class OID for a class that extensions don't |
5841 | * support or that we don't recognize. |
5842 | */ |
5843 | else |
5844 | { |
5845 | elog(ERROR, "unrecognized or unsupported class OID: %u" , classoid); |
5846 | } |
5847 | } |
5848 | |
5849 | /* |
5850 | * For the object passed in, remove its ACL and the ACLs of any object subIds |
5851 | * from pg_init_privs (via recordExtensionInitPrivWorker()). |
5852 | */ |
5853 | void |
5854 | removeExtObjInitPriv(Oid objoid, Oid classoid) |
5855 | { |
5856 | /* |
5857 | * If this is a relation then we need to see if there are any sub-objects |
5858 | * (eg: columns) for it and, if so, be sure to call |
5859 | * recordExtensionInitPrivWorker() for each one. |
5860 | */ |
5861 | if (classoid == RelationRelationId) |
5862 | { |
5863 | Form_pg_class pg_class_tuple; |
5864 | HeapTuple tuple; |
5865 | |
5866 | tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid)); |
5867 | if (!HeapTupleIsValid(tuple)) |
5868 | elog(ERROR, "cache lookup failed for relation %u" , objoid); |
5869 | pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); |
5870 | |
5871 | /* Indexes don't have permissions */ |
5872 | if (pg_class_tuple->relkind == RELKIND_INDEX || |
5873 | pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX) |
5874 | return; |
5875 | |
5876 | /* Composite types don't have permissions either */ |
5877 | if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) |
5878 | return; |
5879 | |
5880 | /* |
5881 | * If this isn't a sequence, index, or composite type then it's |
5882 | * possibly going to have columns associated with it that might have |
5883 | * ACLs. |
5884 | */ |
5885 | if (pg_class_tuple->relkind != RELKIND_SEQUENCE) |
5886 | { |
5887 | AttrNumber curr_att; |
5888 | AttrNumber nattrs = pg_class_tuple->relnatts; |
5889 | |
5890 | for (curr_att = 1; curr_att <= nattrs; curr_att++) |
5891 | { |
5892 | HeapTuple attTuple; |
5893 | |
5894 | attTuple = SearchSysCache2(ATTNUM, |
5895 | ObjectIdGetDatum(objoid), |
5896 | Int16GetDatum(curr_att)); |
5897 | |
5898 | if (!HeapTupleIsValid(attTuple)) |
5899 | continue; |
5900 | |
5901 | /* when removing, remove all entries, even dropped columns */ |
5902 | |
5903 | recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL); |
5904 | |
5905 | ReleaseSysCache(attTuple); |
5906 | } |
5907 | } |
5908 | |
5909 | ReleaseSysCache(tuple); |
5910 | } |
5911 | |
5912 | /* Remove the record, if any, for the top-level object */ |
5913 | recordExtensionInitPrivWorker(objoid, classoid, 0, NULL); |
5914 | } |
5915 | |
5916 | /* |
5917 | * Record initial ACL for an extension object |
5918 | * |
5919 | * Can be called at any time, we check if 'creating_extension' is set and, if |
5920 | * not, exit immediately. |
5921 | * |
5922 | * Pass in the object OID, the OID of the class (the OID of the table which |
5923 | * the object is defined in) and the 'sub' id of the object (objsubid), if |
5924 | * any. If there is no 'sub' id (they are currently only used for columns of |
5925 | * tables) then pass in '0'. Finally, pass in the complete ACL to store. |
5926 | * |
5927 | * If an ACL already exists for this object/sub-object then we will replace |
5928 | * it with what is passed in. |
5929 | * |
5930 | * Passing in NULL for 'new_acl' will result in the entry for the object being |
5931 | * removed, if one is found. |
5932 | */ |
5933 | static void |
5934 | recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) |
5935 | { |
5936 | /* |
5937 | * Generally, we only record the initial privileges when an extension is |
5938 | * being created, but because we don't actually use CREATE EXTENSION |
5939 | * during binary upgrades with pg_upgrade, there is a variable to let us |
5940 | * know that the GRANT and REVOKE statements being issued, while this |
5941 | * variable is true, are for the initial privileges of the extension |
5942 | * object and therefore we need to record them. |
5943 | */ |
5944 | if (!creating_extension && !binary_upgrade_record_init_privs) |
5945 | return; |
5946 | |
5947 | recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl); |
5948 | } |
5949 | |
5950 | /* |
5951 | * Record initial ACL for an extension object, worker. |
5952 | * |
5953 | * This will perform a wholesale replacement of the entire ACL for the object |
5954 | * passed in, therefore be sure to pass in the complete new ACL to use. |
5955 | * |
5956 | * Generally speaking, do *not* use this function directly but instead use |
5957 | * recordExtensionInitPriv(), which checks if 'creating_extension' is set. |
5958 | * This function does *not* check if 'creating_extension' is set as it is also |
5959 | * used when an object is added to or removed from an extension via ALTER |
5960 | * EXTENSION ... ADD/DROP. |
5961 | */ |
5962 | static void |
5963 | recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) |
5964 | { |
5965 | Relation relation; |
5966 | ScanKeyData key[3]; |
5967 | SysScanDesc scan; |
5968 | HeapTuple tuple; |
5969 | HeapTuple oldtuple; |
5970 | |
5971 | relation = table_open(InitPrivsRelationId, RowExclusiveLock); |
5972 | |
5973 | ScanKeyInit(&key[0], |
5974 | Anum_pg_init_privs_objoid, |
5975 | BTEqualStrategyNumber, F_OIDEQ, |
5976 | ObjectIdGetDatum(objoid)); |
5977 | ScanKeyInit(&key[1], |
5978 | Anum_pg_init_privs_classoid, |
5979 | BTEqualStrategyNumber, F_OIDEQ, |
5980 | ObjectIdGetDatum(classoid)); |
5981 | ScanKeyInit(&key[2], |
5982 | Anum_pg_init_privs_objsubid, |
5983 | BTEqualStrategyNumber, F_INT4EQ, |
5984 | Int32GetDatum(objsubid)); |
5985 | |
5986 | scan = systable_beginscan(relation, InitPrivsObjIndexId, true, |
5987 | NULL, 3, key); |
5988 | |
5989 | /* There should exist only one entry or none. */ |
5990 | oldtuple = systable_getnext(scan); |
5991 | |
5992 | /* If we find an entry, update it with the latest ACL. */ |
5993 | if (HeapTupleIsValid(oldtuple)) |
5994 | { |
5995 | Datum values[Natts_pg_init_privs]; |
5996 | bool nulls[Natts_pg_init_privs]; |
5997 | bool replace[Natts_pg_init_privs]; |
5998 | |
5999 | /* If we have a new ACL to set, then update the row with it. */ |
6000 | if (new_acl) |
6001 | { |
6002 | MemSet(values, 0, sizeof(values)); |
6003 | MemSet(nulls, false, sizeof(nulls)); |
6004 | MemSet(replace, false, sizeof(replace)); |
6005 | |
6006 | values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl); |
6007 | replace[Anum_pg_init_privs_initprivs - 1] = true; |
6008 | |
6009 | oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation), |
6010 | values, nulls, replace); |
6011 | |
6012 | CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple); |
6013 | } |
6014 | else |
6015 | { |
6016 | /* new_acl is NULL, so delete the entry we found. */ |
6017 | CatalogTupleDelete(relation, &oldtuple->t_self); |
6018 | } |
6019 | } |
6020 | else |
6021 | { |
6022 | Datum values[Natts_pg_init_privs]; |
6023 | bool nulls[Natts_pg_init_privs]; |
6024 | |
6025 | /* |
6026 | * Only add a new entry if the new ACL is non-NULL. |
6027 | * |
6028 | * If we are passed in a NULL ACL and no entry exists, we can just |
6029 | * fall through and do nothing. |
6030 | */ |
6031 | if (new_acl) |
6032 | { |
6033 | /* No entry found, so add it. */ |
6034 | MemSet(nulls, false, sizeof(nulls)); |
6035 | |
6036 | values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid); |
6037 | values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid); |
6038 | values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid); |
6039 | |
6040 | /* This function only handles initial privileges of extensions */ |
6041 | values[Anum_pg_init_privs_privtype - 1] = |
6042 | CharGetDatum(INITPRIVS_EXTENSION); |
6043 | |
6044 | values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl); |
6045 | |
6046 | tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); |
6047 | |
6048 | CatalogTupleInsert(relation, tuple); |
6049 | } |
6050 | } |
6051 | |
6052 | systable_endscan(scan); |
6053 | |
6054 | /* prevent error when processing objects multiple times */ |
6055 | CommandCounterIncrement(); |
6056 | |
6057 | table_close(relation, RowExclusiveLock); |
6058 | } |
6059 | |