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