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