Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * user.c
4 : * Commands for manipulating roles (formerly called users).
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * src/backend/commands/user.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "access/genam.h"
16 : #include "access/htup_details.h"
17 : #include "access/table.h"
18 : #include "access/xact.h"
19 : #include "catalog/binary_upgrade.h"
20 : #include "catalog/catalog.h"
21 : #include "catalog/dependency.h"
22 : #include "catalog/indexing.h"
23 : #include "catalog/objectaccess.h"
24 : #include "catalog/pg_auth_members.h"
25 : #include "catalog/pg_authid.h"
26 : #include "catalog/pg_database.h"
27 : #include "catalog/pg_db_role_setting.h"
28 : #include "commands/comment.h"
29 : #include "commands/dbcommands.h"
30 : #include "commands/defrem.h"
31 : #include "commands/seclabel.h"
32 : #include "commands/user.h"
33 : #include "libpq/crypt.h"
34 : #include "miscadmin.h"
35 : #include "port/pg_bitutils.h"
36 : #include "storage/lmgr.h"
37 : #include "utils/acl.h"
38 : #include "utils/builtins.h"
39 : #include "utils/catcache.h"
40 : #include "utils/fmgroids.h"
41 : #include "utils/syscache.h"
42 : #include "utils/varlena.h"
43 :
44 : /*
45 : * Removing a role grant - or the admin option on it - might recurse to
46 : * dependent grants. We use these values to reason about what would need to
47 : * be done in such cases.
48 : *
49 : * RRG_NOOP indicates a grant that would not need to be altered by the
50 : * operation.
51 : *
52 : * RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
53 : * admin_option set to false by the operation.
54 : *
55 : * Similarly, RRG_REMOVE_INHERIT_OPTION and RRG_REMOVE_SET_OPTION indicate
56 : * grants that would need to have the corresponding options set to false.
57 : *
58 : * RRG_DELETE_GRANT indicates a grant that would need to be removed entirely
59 : * by the operation.
60 : */
61 : typedef enum
62 : {
63 : RRG_NOOP,
64 : RRG_REMOVE_ADMIN_OPTION,
65 : RRG_REMOVE_INHERIT_OPTION,
66 : RRG_REMOVE_SET_OPTION,
67 : RRG_DELETE_GRANT,
68 : } RevokeRoleGrantAction;
69 :
70 : /* Potentially set by pg_upgrade_support functions */
71 : Oid binary_upgrade_next_pg_authid_oid = InvalidOid;
72 :
73 : typedef struct
74 : {
75 : unsigned specified;
76 : bool admin;
77 : bool inherit;
78 : bool set;
79 : } GrantRoleOptions;
80 :
81 : #define GRANT_ROLE_SPECIFIED_ADMIN 0x0001
82 : #define GRANT_ROLE_SPECIFIED_INHERIT 0x0002
83 : #define GRANT_ROLE_SPECIFIED_SET 0x0004
84 :
85 : /* GUC parameters */
86 : int Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;
87 : char *createrole_self_grant = "";
88 : static bool createrole_self_grant_enabled = false;
89 : static GrantRoleOptions createrole_self_grant_options;
90 :
91 : /* Hook to check passwords in CreateRole() and AlterRole() */
92 : check_password_hook_type check_password_hook = NULL;
93 :
94 : static void AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
95 : List *memberSpecs, List *memberIds,
96 : Oid grantorId, GrantRoleOptions *popt);
97 : static void DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
98 : List *memberSpecs, List *memberIds,
99 : Oid grantorId, GrantRoleOptions *popt,
100 : DropBehavior behavior);
101 : static void check_role_membership_authorization(Oid currentUserId, Oid roleid,
102 : bool is_grant);
103 : static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId,
104 : bool is_grant);
105 : static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
106 : static bool plan_single_revoke(CatCList *memlist,
107 : RevokeRoleGrantAction *actions,
108 : Oid member, Oid grantor,
109 : GrantRoleOptions *popt,
110 : DropBehavior behavior);
111 : static void plan_member_revoke(CatCList *memlist,
112 : RevokeRoleGrantAction *actions, Oid member);
113 : static void plan_recursive_revoke(CatCList *memlist,
114 : RevokeRoleGrantAction *actions,
115 : int index,
116 : bool revoke_admin_option_only,
117 : DropBehavior behavior);
118 : static void InitGrantRoleOptions(GrantRoleOptions *popt);
119 :
120 :
121 : /* Check if current user has createrole privileges */
122 : static bool
123 1140 : have_createrole_privilege(void)
124 : {
125 1140 : return has_createrole_privilege(GetUserId());
126 : }
127 :
128 :
129 : /*
130 : * CREATE ROLE
131 : */
132 : Oid
133 973 : CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
134 : {
135 : Relation pg_authid_rel;
136 : TupleDesc pg_authid_dsc;
137 : HeapTuple tuple;
138 973 : Datum new_record[Natts_pg_authid] = {0};
139 973 : bool new_record_nulls[Natts_pg_authid] = {0};
140 973 : Oid currentUserId = GetUserId();
141 : Oid roleid;
142 : ListCell *item;
143 : ListCell *option;
144 973 : char *password = NULL; /* user password */
145 973 : bool issuper = false; /* Make the user a superuser? */
146 973 : bool inherit = true; /* Auto inherit privileges? */
147 973 : bool createrole = false; /* Can this user create roles? */
148 973 : bool createdb = false; /* Can the user create databases? */
149 973 : bool canlogin = false; /* Can this user login? */
150 973 : bool isreplication = false; /* Is this a replication role? */
151 973 : bool bypassrls = false; /* Is this a row security enabled role? */
152 973 : int connlimit = -1; /* maximum connections allowed */
153 973 : List *addroleto = NIL; /* roles to make this a member of */
154 973 : List *rolemembers = NIL; /* roles to be members of this role */
155 973 : List *adminmembers = NIL; /* roles to be admins of this role */
156 973 : char *validUntil = NULL; /* time the login is valid until */
157 : Datum validUntil_datum; /* same, as timestamptz Datum */
158 : bool validUntil_null;
159 973 : DefElem *dpassword = NULL;
160 973 : DefElem *dissuper = NULL;
161 973 : DefElem *dinherit = NULL;
162 973 : DefElem *dcreaterole = NULL;
163 973 : DefElem *dcreatedb = NULL;
164 973 : DefElem *dcanlogin = NULL;
165 973 : DefElem *disreplication = NULL;
166 973 : DefElem *dconnlimit = NULL;
167 973 : DefElem *daddroleto = NULL;
168 973 : DefElem *drolemembers = NULL;
169 973 : DefElem *dadminmembers = NULL;
170 973 : DefElem *dvalidUntil = NULL;
171 973 : DefElem *dbypassRLS = NULL;
172 : GrantRoleOptions popt;
173 :
174 : /* Report error if name has \n or \r character. */
175 973 : if (strpbrk(stmt->role, "\n\r"))
176 1 : ereport(ERROR,
177 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
178 : errmsg("role name \"%s\" contains a newline or carriage return character", stmt->role)));
179 :
180 : /* The defaults can vary depending on the original statement type */
181 972 : switch (stmt->stmt_type)
182 : {
183 731 : case ROLESTMT_ROLE:
184 731 : break;
185 229 : case ROLESTMT_USER:
186 229 : canlogin = true;
187 : /* may eventually want inherit to default to false here */
188 229 : break;
189 12 : case ROLESTMT_GROUP:
190 12 : break;
191 : }
192 :
193 : /* Extract options from the statement node tree */
194 1575 : foreach(option, stmt->options)
195 : {
196 603 : DefElem *defel = (DefElem *) lfirst(option);
197 :
198 603 : if (strcmp(defel->defname, "password") == 0)
199 : {
200 64 : if (dpassword)
201 0 : errorConflictingDefElem(defel, pstate);
202 64 : dpassword = defel;
203 : }
204 539 : else if (strcmp(defel->defname, "sysid") == 0)
205 : {
206 3 : ereport(NOTICE,
207 : (errmsg("SYSID can no longer be specified")));
208 : }
209 536 : else if (strcmp(defel->defname, "superuser") == 0)
210 : {
211 97 : if (dissuper)
212 0 : errorConflictingDefElem(defel, pstate);
213 97 : dissuper = defel;
214 : }
215 439 : else if (strcmp(defel->defname, "inherit") == 0)
216 : {
217 32 : if (dinherit)
218 0 : errorConflictingDefElem(defel, pstate);
219 32 : dinherit = defel;
220 : }
221 407 : else if (strcmp(defel->defname, "createrole") == 0)
222 : {
223 45 : if (dcreaterole)
224 0 : errorConflictingDefElem(defel, pstate);
225 45 : dcreaterole = defel;
226 : }
227 362 : else if (strcmp(defel->defname, "createdb") == 0)
228 : {
229 35 : if (dcreatedb)
230 0 : errorConflictingDefElem(defel, pstate);
231 35 : dcreatedb = defel;
232 : }
233 327 : else if (strcmp(defel->defname, "canlogin") == 0)
234 : {
235 154 : if (dcanlogin)
236 0 : errorConflictingDefElem(defel, pstate);
237 154 : dcanlogin = defel;
238 : }
239 173 : else if (strcmp(defel->defname, "isreplication") == 0)
240 : {
241 46 : if (disreplication)
242 0 : errorConflictingDefElem(defel, pstate);
243 46 : disreplication = defel;
244 : }
245 127 : else if (strcmp(defel->defname, "connectionlimit") == 0)
246 : {
247 7 : if (dconnlimit)
248 0 : errorConflictingDefElem(defel, pstate);
249 7 : dconnlimit = defel;
250 : }
251 120 : else if (strcmp(defel->defname, "addroleto") == 0)
252 : {
253 50 : if (daddroleto)
254 0 : errorConflictingDefElem(defel, pstate);
255 50 : daddroleto = defel;
256 : }
257 70 : else if (strcmp(defel->defname, "rolemembers") == 0)
258 : {
259 14 : if (drolemembers)
260 0 : errorConflictingDefElem(defel, pstate);
261 14 : drolemembers = defel;
262 : }
263 56 : else if (strcmp(defel->defname, "adminmembers") == 0)
264 : {
265 11 : if (dadminmembers)
266 0 : errorConflictingDefElem(defel, pstate);
267 11 : dadminmembers = defel;
268 : }
269 45 : else if (strcmp(defel->defname, "validUntil") == 0)
270 : {
271 4 : if (dvalidUntil)
272 0 : errorConflictingDefElem(defel, pstate);
273 4 : dvalidUntil = defel;
274 : }
275 41 : else if (strcmp(defel->defname, "bypassrls") == 0)
276 : {
277 41 : if (dbypassRLS)
278 0 : errorConflictingDefElem(defel, pstate);
279 41 : dbypassRLS = defel;
280 : }
281 : else
282 0 : elog(ERROR, "option \"%s\" not recognized",
283 : defel->defname);
284 : }
285 :
286 972 : if (dpassword && dpassword->arg)
287 58 : password = strVal(dpassword->arg);
288 972 : if (dissuper)
289 97 : issuper = boolVal(dissuper->arg);
290 972 : if (dinherit)
291 32 : inherit = boolVal(dinherit->arg);
292 972 : if (dcreaterole)
293 45 : createrole = boolVal(dcreaterole->arg);
294 972 : if (dcreatedb)
295 35 : createdb = boolVal(dcreatedb->arg);
296 972 : if (dcanlogin)
297 154 : canlogin = boolVal(dcanlogin->arg);
298 972 : if (disreplication)
299 46 : isreplication = boolVal(disreplication->arg);
300 972 : if (dconnlimit)
301 : {
302 7 : connlimit = intVal(dconnlimit->arg);
303 7 : if (connlimit < -1)
304 0 : ereport(ERROR,
305 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
306 : errmsg("invalid connection limit: %d", connlimit)));
307 : }
308 972 : if (daddroleto)
309 50 : addroleto = (List *) daddroleto->arg;
310 972 : if (drolemembers)
311 14 : rolemembers = (List *) drolemembers->arg;
312 972 : if (dadminmembers)
313 11 : adminmembers = (List *) dadminmembers->arg;
314 972 : if (dvalidUntil)
315 4 : validUntil = strVal(dvalidUntil->arg);
316 972 : if (dbypassRLS)
317 41 : bypassrls = boolVal(dbypassRLS->arg);
318 :
319 : /* Check some permissions first */
320 972 : if (!superuser_arg(currentUserId))
321 : {
322 111 : if (!has_createrole_privilege(currentUserId))
323 0 : ereport(ERROR,
324 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
325 : errmsg("permission denied to create role"),
326 : errdetail("Only roles with the %s attribute may create roles.",
327 : "CREATEROLE")));
328 111 : if (issuper)
329 3 : ereport(ERROR,
330 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
331 : errmsg("permission denied to create role"),
332 : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
333 : "SUPERUSER", "SUPERUSER")));
334 108 : if (createdb && !have_createdb_privilege())
335 3 : ereport(ERROR,
336 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
337 : errmsg("permission denied to create role"),
338 : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
339 : "CREATEDB", "CREATEDB")));
340 105 : if (isreplication && !has_rolreplication(currentUserId))
341 6 : ereport(ERROR,
342 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
343 : errmsg("permission denied to create role"),
344 : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
345 : "REPLICATION", "REPLICATION")));
346 99 : if (bypassrls && !has_bypassrls_privilege(currentUserId))
347 3 : ereport(ERROR,
348 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
349 : errmsg("permission denied to create role"),
350 : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
351 : "BYPASSRLS", "BYPASSRLS")));
352 : }
353 :
354 : /*
355 : * Check that the user is not trying to create a role in the reserved
356 : * "pg_" namespace.
357 : */
358 957 : if (IsReservedName(stmt->role))
359 4 : ereport(ERROR,
360 : (errcode(ERRCODE_RESERVED_NAME),
361 : errmsg("role name \"%s\" is reserved",
362 : stmt->role),
363 : errdetail("Role names starting with \"pg_\" are reserved.")));
364 :
365 : /*
366 : * If built with appropriate switch, whine when regression-testing
367 : * conventions for role names are violated.
368 : */
369 : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
370 : if (strncmp(stmt->role, "regress_", 8) != 0)
371 : elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
372 : #endif
373 :
374 : /*
375 : * Check the pg_authid relation to be certain the role doesn't already
376 : * exist.
377 : */
378 953 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
379 953 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
380 :
381 953 : if (OidIsValid(get_role_oid(stmt->role, true)))
382 3 : ereport(ERROR,
383 : (errcode(ERRCODE_DUPLICATE_OBJECT),
384 : errmsg("role \"%s\" already exists",
385 : stmt->role)));
386 :
387 : /* Convert validuntil to internal form */
388 950 : if (validUntil)
389 : {
390 4 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
391 : CStringGetDatum(validUntil),
392 : ObjectIdGetDatum(InvalidOid),
393 : Int32GetDatum(-1));
394 4 : validUntil_null = false;
395 : }
396 : else
397 : {
398 946 : validUntil_datum = (Datum) 0;
399 946 : validUntil_null = true;
400 : }
401 :
402 : /*
403 : * Call the password checking hook if there is one defined
404 : */
405 950 : if (check_password_hook && password)
406 0 : (*check_password_hook) (stmt->role,
407 : password,
408 : get_password_type(password),
409 : validUntil_datum,
410 : validUntil_null);
411 :
412 : /*
413 : * Build a tuple to insert
414 : */
415 950 : new_record[Anum_pg_authid_rolname - 1] =
416 950 : DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
417 950 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
418 950 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
419 950 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
420 950 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
421 950 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
422 950 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
423 950 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
424 :
425 950 : if (password)
426 : {
427 : char *shadow_pass;
428 58 : const char *logdetail = NULL;
429 :
430 : /*
431 : * Don't allow an empty password. Libpq treats an empty password the
432 : * same as no password at all, and won't even try to authenticate. But
433 : * other clients might, so allowing it would be confusing. By clearing
434 : * the password when an empty string is specified, the account is
435 : * consistently locked for all clients.
436 : *
437 : * Note that this only covers passwords stored in the database itself.
438 : * There are also checks in the authentication code, to forbid an
439 : * empty password from being used with authentication methods that
440 : * fetch the password from an external system, like LDAP or PAM.
441 : */
442 113 : if (password[0] == '\0' ||
443 55 : plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK)
444 : {
445 3 : ereport(NOTICE,
446 : (errmsg("empty string is not a valid password, clearing password")));
447 3 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
448 : }
449 : else
450 : {
451 : /* Encrypt the password to the requested format. */
452 55 : shadow_pass = encrypt_password(Password_encryption, stmt->role,
453 : password);
454 52 : new_record[Anum_pg_authid_rolpassword - 1] =
455 52 : CStringGetTextDatum(shadow_pass);
456 : }
457 : }
458 : else
459 892 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
460 :
461 947 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
462 947 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
463 :
464 947 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
465 :
466 : /*
467 : * pg_largeobject_metadata contains pg_authid.oid's, so we use the
468 : * binary-upgrade override.
469 : */
470 947 : if (IsBinaryUpgrade)
471 : {
472 8 : if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
473 0 : ereport(ERROR,
474 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
475 : errmsg("pg_authid OID value not set when in binary upgrade mode")));
476 :
477 8 : roleid = binary_upgrade_next_pg_authid_oid;
478 8 : binary_upgrade_next_pg_authid_oid = InvalidOid;
479 : }
480 : else
481 : {
482 939 : roleid = GetNewOidWithIndex(pg_authid_rel, AuthIdOidIndexId,
483 : Anum_pg_authid_oid);
484 : }
485 :
486 947 : new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(roleid);
487 :
488 947 : tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
489 :
490 : /*
491 : * Insert new record in the pg_authid table
492 : */
493 947 : CatalogTupleInsert(pg_authid_rel, tuple);
494 :
495 : /*
496 : * Advance command counter so we can see new record; else tests in
497 : * AddRoleMems may fail.
498 : */
499 947 : if (addroleto || adminmembers || rolemembers)
500 72 : CommandCounterIncrement();
501 :
502 : /* Default grant. */
503 947 : InitGrantRoleOptions(&popt);
504 :
505 : /*
506 : * Add the new role to the specified existing roles.
507 : */
508 947 : if (addroleto)
509 : {
510 50 : RoleSpec *thisrole = makeNode(RoleSpec);
511 50 : List *thisrole_list = list_make1(thisrole);
512 50 : List *thisrole_oidlist = list_make1_oid(roleid);
513 :
514 50 : thisrole->roletype = ROLESPEC_CSTRING;
515 50 : thisrole->rolename = stmt->role;
516 50 : thisrole->location = -1;
517 :
518 64 : foreach(item, addroleto)
519 : {
520 50 : RoleSpec *oldrole = lfirst(item);
521 50 : HeapTuple oldroletup = get_rolespec_tuple(oldrole);
522 50 : Form_pg_authid oldroleform = (Form_pg_authid) GETSTRUCT(oldroletup);
523 50 : Oid oldroleid = oldroleform->oid;
524 50 : char *oldrolename = NameStr(oldroleform->rolname);
525 :
526 : /* can only add this role to roles for which you have rights */
527 50 : check_role_membership_authorization(currentUserId, oldroleid, true);
528 14 : AddRoleMems(currentUserId, oldrolename, oldroleid,
529 : thisrole_list,
530 : thisrole_oidlist,
531 : InvalidOid, &popt);
532 :
533 14 : ReleaseSysCache(oldroletup);
534 : }
535 : }
536 :
537 : /*
538 : * If the current user isn't a superuser, make them an admin of the new
539 : * role so that they can administer the new object they just created.
540 : * Superusers will be able to do that anyway.
541 : *
542 : * The grantor of record for this implicit grant is the bootstrap
543 : * superuser, which means that the CREATEROLE user cannot revoke the
544 : * grant. They can however grant the created role back to themselves with
545 : * different options, since they enjoy ADMIN OPTION on it.
546 : */
547 911 : if (!superuser())
548 : {
549 60 : RoleSpec *current_role = makeNode(RoleSpec);
550 : GrantRoleOptions poptself;
551 : List *memberSpecs;
552 60 : List *memberIds = list_make1_oid(currentUserId);
553 :
554 60 : current_role->roletype = ROLESPEC_CURRENT_ROLE;
555 60 : current_role->location = -1;
556 60 : memberSpecs = list_make1(current_role);
557 :
558 60 : poptself.specified = GRANT_ROLE_SPECIFIED_ADMIN
559 : | GRANT_ROLE_SPECIFIED_INHERIT
560 : | GRANT_ROLE_SPECIFIED_SET;
561 60 : poptself.admin = true;
562 60 : poptself.inherit = false;
563 60 : poptself.set = false;
564 :
565 60 : AddRoleMems(BOOTSTRAP_SUPERUSERID, stmt->role, roleid,
566 : memberSpecs, memberIds,
567 : BOOTSTRAP_SUPERUSERID, &poptself);
568 :
569 : /*
570 : * We must make the implicit grant visible to the code below, else the
571 : * additional grants will fail.
572 : */
573 60 : CommandCounterIncrement();
574 :
575 : /*
576 : * Because of the implicit grant above, a CREATEROLE user who creates
577 : * a role has the ability to grant that role back to themselves with
578 : * the INHERIT or SET options, if they wish to inherit the role's
579 : * privileges or be able to SET ROLE to it. The createrole_self_grant
580 : * GUC can be used to make this happen automatically. This has no
581 : * security implications since the same user is able to make the same
582 : * grant using an explicit GRANT statement; it's just convenient.
583 : */
584 60 : if (createrole_self_grant_enabled)
585 3 : AddRoleMems(currentUserId, stmt->role, roleid,
586 : memberSpecs, memberIds,
587 : currentUserId, &createrole_self_grant_options);
588 : }
589 :
590 : /*
591 : * Add the specified members to this new role. adminmembers get the admin
592 : * option, rolemembers don't.
593 : *
594 : * NB: No permissions check is required here. If you have enough rights to
595 : * create a role, you can add any members you like.
596 : */
597 911 : AddRoleMems(currentUserId, stmt->role, roleid,
598 : rolemembers, roleSpecsToIds(rolemembers),
599 : InvalidOid, &popt);
600 908 : popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
601 908 : popt.admin = true;
602 908 : AddRoleMems(currentUserId, stmt->role, roleid,
603 : adminmembers, roleSpecsToIds(adminmembers),
604 : InvalidOid, &popt);
605 :
606 : /* Post creation hook for new role */
607 905 : InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
608 :
609 : /*
610 : * Close pg_authid, but keep lock till commit.
611 : */
612 905 : table_close(pg_authid_rel, NoLock);
613 :
614 905 : return roleid;
615 : }
616 :
617 :
618 : /*
619 : * ALTER ROLE
620 : *
621 : * Note: the rolemembers option accepted here is intended to support the
622 : * backwards-compatible ALTER GROUP syntax. Although it will work to say
623 : * "ALTER ROLE role ROLE rolenames", we don't document it.
624 : */
625 : Oid
626 239 : AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
627 : {
628 239 : Datum new_record[Natts_pg_authid] = {0};
629 239 : bool new_record_nulls[Natts_pg_authid] = {0};
630 239 : bool new_record_repl[Natts_pg_authid] = {0};
631 : Relation pg_authid_rel;
632 : TupleDesc pg_authid_dsc;
633 : HeapTuple tuple,
634 : new_tuple;
635 : Form_pg_authid authform;
636 : ListCell *option;
637 : char *rolename;
638 239 : char *password = NULL; /* user password */
639 239 : int connlimit = -1; /* maximum connections allowed */
640 239 : char *validUntil = NULL; /* time the login is valid until */
641 : Datum validUntil_datum; /* same, as timestamptz Datum */
642 : bool validUntil_null;
643 239 : DefElem *dpassword = NULL;
644 239 : DefElem *dissuper = NULL;
645 239 : DefElem *dinherit = NULL;
646 239 : DefElem *dcreaterole = NULL;
647 239 : DefElem *dcreatedb = NULL;
648 239 : DefElem *dcanlogin = NULL;
649 239 : DefElem *disreplication = NULL;
650 239 : DefElem *dconnlimit = NULL;
651 239 : DefElem *drolemembers = NULL;
652 239 : DefElem *dvalidUntil = NULL;
653 239 : DefElem *dbypassRLS = NULL;
654 : Oid roleid;
655 239 : Oid currentUserId = GetUserId();
656 : GrantRoleOptions popt;
657 :
658 239 : check_rolespec_name(stmt->role,
659 239 : _("Cannot alter reserved roles."));
660 :
661 : /* Extract options from the statement node tree */
662 652 : foreach(option, stmt->options)
663 : {
664 413 : DefElem *defel = (DefElem *) lfirst(option);
665 :
666 413 : if (strcmp(defel->defname, "password") == 0)
667 : {
668 48 : if (dpassword)
669 0 : errorConflictingDefElem(defel, pstate);
670 48 : dpassword = defel;
671 : }
672 365 : else if (strcmp(defel->defname, "superuser") == 0)
673 : {
674 58 : if (dissuper)
675 0 : errorConflictingDefElem(defel, pstate);
676 58 : dissuper = defel;
677 : }
678 307 : else if (strcmp(defel->defname, "inherit") == 0)
679 : {
680 42 : if (dinherit)
681 0 : errorConflictingDefElem(defel, pstate);
682 42 : dinherit = defel;
683 : }
684 265 : else if (strcmp(defel->defname, "createrole") == 0)
685 : {
686 33 : if (dcreaterole)
687 0 : errorConflictingDefElem(defel, pstate);
688 33 : dcreaterole = defel;
689 : }
690 232 : else if (strcmp(defel->defname, "createdb") == 0)
691 : {
692 42 : if (dcreatedb)
693 0 : errorConflictingDefElem(defel, pstate);
694 42 : dcreatedb = defel;
695 : }
696 190 : else if (strcmp(defel->defname, "canlogin") == 0)
697 : {
698 45 : if (dcanlogin)
699 0 : errorConflictingDefElem(defel, pstate);
700 45 : dcanlogin = defel;
701 : }
702 145 : else if (strcmp(defel->defname, "isreplication") == 0)
703 : {
704 76 : if (disreplication)
705 0 : errorConflictingDefElem(defel, pstate);
706 76 : disreplication = defel;
707 : }
708 69 : else if (strcmp(defel->defname, "connectionlimit") == 0)
709 : {
710 6 : if (dconnlimit)
711 0 : errorConflictingDefElem(defel, pstate);
712 6 : dconnlimit = defel;
713 : }
714 63 : else if (strcmp(defel->defname, "rolemembers") == 0 &&
715 21 : stmt->action != 0)
716 : {
717 21 : if (drolemembers)
718 0 : errorConflictingDefElem(defel, pstate);
719 21 : drolemembers = defel;
720 : }
721 42 : else if (strcmp(defel->defname, "validUntil") == 0)
722 : {
723 0 : if (dvalidUntil)
724 0 : errorConflictingDefElem(defel, pstate);
725 0 : dvalidUntil = defel;
726 : }
727 42 : else if (strcmp(defel->defname, "bypassrls") == 0)
728 : {
729 42 : if (dbypassRLS)
730 0 : errorConflictingDefElem(defel, pstate);
731 42 : dbypassRLS = defel;
732 : }
733 : else
734 0 : elog(ERROR, "option \"%s\" not recognized",
735 : defel->defname);
736 : }
737 :
738 239 : if (dpassword && dpassword->arg)
739 48 : password = strVal(dpassword->arg);
740 239 : if (dconnlimit)
741 : {
742 6 : connlimit = intVal(dconnlimit->arg);
743 6 : if (connlimit < -1)
744 0 : ereport(ERROR,
745 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
746 : errmsg("invalid connection limit: %d", connlimit)));
747 : }
748 239 : if (dvalidUntil)
749 0 : validUntil = strVal(dvalidUntil->arg);
750 :
751 : /*
752 : * Scan the pg_authid relation to be certain the user exists.
753 : */
754 239 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
755 239 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
756 :
757 239 : tuple = get_rolespec_tuple(stmt->role);
758 231 : authform = (Form_pg_authid) GETSTRUCT(tuple);
759 231 : rolename = pstrdup(NameStr(authform->rolname));
760 231 : roleid = authform->oid;
761 :
762 : /* To mess with a superuser in any way you gotta be superuser. */
763 231 : if (!superuser() && authform->rolsuper)
764 0 : ereport(ERROR,
765 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
766 : errmsg("permission denied to alter role"),
767 : errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
768 : "SUPERUSER", "SUPERUSER")));
769 231 : if (!superuser() && dissuper)
770 9 : ereport(ERROR,
771 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
772 : errmsg("permission denied to alter role"),
773 : errdetail("Only roles with the %s attribute may change the %s attribute.",
774 : "SUPERUSER", "SUPERUSER")));
775 :
776 : /*
777 : * Most changes to a role require that you both have CREATEROLE privileges
778 : * and also ADMIN OPTION on the role.
779 : */
780 222 : if (!have_createrole_privilege() ||
781 204 : !is_admin_of_role(GetUserId(), roleid))
782 : {
783 : /* things an unprivileged user certainly can't do */
784 21 : if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
785 18 : dvalidUntil || disreplication || dbypassRLS)
786 3 : ereport(ERROR,
787 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
788 : errmsg("permission denied to alter role"),
789 : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
790 : "CREATEROLE", "ADMIN", rolename)));
791 :
792 : /* an unprivileged user can change their own password */
793 18 : if (dpassword && roleid != currentUserId)
794 3 : ereport(ERROR,
795 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
796 : errmsg("permission denied to alter role"),
797 : errdetail("To change another role's password, the current user must have the %s attribute and the %s option on the role.",
798 : "CREATEROLE", "ADMIN")));
799 : }
800 201 : else if (!superuser())
801 : {
802 : /*
803 : * Even if you have both CREATEROLE and ADMIN OPTION on a role, you
804 : * can only change the CREATEDB, REPLICATION, or BYPASSRLS attributes
805 : * if they are set for your own role (or you are the superuser).
806 : */
807 30 : if (dcreatedb && !have_createdb_privilege())
808 3 : ereport(ERROR,
809 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
810 : errmsg("permission denied to alter role"),
811 : errdetail("Only roles with the %s attribute may change the %s attribute.",
812 : "CREATEDB", "CREATEDB")));
813 27 : if (disreplication && !has_rolreplication(currentUserId))
814 3 : ereport(ERROR,
815 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
816 : errmsg("permission denied to alter role"),
817 : errdetail("Only roles with the %s attribute may change the %s attribute.",
818 : "REPLICATION", "REPLICATION")));
819 24 : if (dbypassRLS && !has_bypassrls_privilege(currentUserId))
820 3 : ereport(ERROR,
821 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
822 : errmsg("permission denied to alter role"),
823 : errdetail("Only roles with the %s attribute may change the %s attribute.",
824 : "BYPASSRLS", "BYPASSRLS")));
825 : }
826 :
827 : /* To add or drop members, you need ADMIN OPTION. */
828 207 : if (drolemembers && !is_admin_of_role(currentUserId, roleid))
829 6 : ereport(ERROR,
830 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
831 : errmsg("permission denied to alter role"),
832 : errdetail("Only roles with the %s option on role \"%s\" may add or drop members.",
833 : "ADMIN", rolename)));
834 :
835 : /* Convert validuntil to internal form */
836 201 : if (dvalidUntil)
837 : {
838 0 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
839 : CStringGetDatum(validUntil),
840 : ObjectIdGetDatum(InvalidOid),
841 : Int32GetDatum(-1));
842 0 : validUntil_null = false;
843 : }
844 : else
845 : {
846 : /* fetch existing setting in case hook needs it */
847 201 : validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
848 : Anum_pg_authid_rolvaliduntil,
849 : &validUntil_null);
850 : }
851 :
852 : /*
853 : * Call the password checking hook if there is one defined
854 : */
855 201 : if (check_password_hook && password)
856 7 : (*check_password_hook) (rolename,
857 : password,
858 : get_password_type(password),
859 : validUntil_datum,
860 : validUntil_null);
861 :
862 : /*
863 : * Build an updated tuple, perusing the information just obtained
864 : */
865 :
866 : /*
867 : * issuper/createrole/etc
868 : */
869 197 : if (dissuper)
870 : {
871 49 : bool should_be_super = boolVal(dissuper->arg);
872 :
873 49 : if (!should_be_super && roleid == BOOTSTRAP_SUPERUSERID)
874 0 : ereport(ERROR,
875 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
876 : errmsg("permission denied to alter role"),
877 : errdetail("The bootstrap superuser must have the %s attribute.",
878 : "SUPERUSER")));
879 :
880 49 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
881 49 : new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
882 : }
883 :
884 197 : if (dinherit)
885 : {
886 39 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
887 39 : new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
888 : }
889 :
890 197 : if (dcreaterole)
891 : {
892 33 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
893 33 : new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
894 : }
895 :
896 197 : if (dcreatedb)
897 : {
898 39 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
899 39 : new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
900 : }
901 :
902 197 : if (dcanlogin)
903 : {
904 42 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
905 42 : new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
906 : }
907 :
908 197 : if (disreplication)
909 : {
910 65 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
911 65 : new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
912 : }
913 :
914 197 : if (dconnlimit)
915 : {
916 3 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
917 3 : new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
918 : }
919 :
920 : /* password */
921 197 : if (password)
922 : {
923 : char *shadow_pass;
924 41 : const char *logdetail = NULL;
925 :
926 : /* Like in CREATE USER, don't allow an empty password. */
927 82 : if (password[0] == '\0' ||
928 41 : plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
929 : {
930 6 : ereport(NOTICE,
931 : (errmsg("empty string is not a valid password, clearing password")));
932 6 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
933 : }
934 : else
935 : {
936 : /* Encrypt the password to the requested format. */
937 35 : shadow_pass = encrypt_password(Password_encryption, rolename,
938 : password);
939 32 : new_record[Anum_pg_authid_rolpassword - 1] =
940 32 : CStringGetTextDatum(shadow_pass);
941 : }
942 38 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
943 : }
944 :
945 : /* unset password */
946 194 : if (dpassword && dpassword->arg == NULL)
947 : {
948 0 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
949 0 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
950 : }
951 :
952 : /* valid until */
953 194 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
954 194 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
955 194 : new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
956 :
957 194 : if (dbypassRLS)
958 : {
959 39 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
960 39 : new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
961 : }
962 :
963 194 : new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
964 : new_record_nulls, new_record_repl);
965 194 : CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
966 :
967 194 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
968 :
969 194 : ReleaseSysCache(tuple);
970 194 : heap_freetuple(new_tuple);
971 :
972 194 : InitGrantRoleOptions(&popt);
973 :
974 : /*
975 : * Advance command counter so we can see new record; else tests in
976 : * AddRoleMems may fail.
977 : */
978 194 : if (drolemembers)
979 : {
980 15 : List *rolemembers = (List *) drolemembers->arg;
981 :
982 15 : CommandCounterIncrement();
983 :
984 15 : if (stmt->action == +1) /* add members to role */
985 9 : AddRoleMems(currentUserId, rolename, roleid,
986 : rolemembers, roleSpecsToIds(rolemembers),
987 : InvalidOid, &popt);
988 6 : else if (stmt->action == -1) /* drop members from role */
989 6 : DelRoleMems(currentUserId, rolename, roleid,
990 : rolemembers, roleSpecsToIds(rolemembers),
991 : InvalidOid, &popt, DROP_RESTRICT);
992 : }
993 :
994 : /*
995 : * Close pg_authid, but keep lock till commit.
996 : */
997 194 : table_close(pg_authid_rel, NoLock);
998 :
999 194 : return roleid;
1000 : }
1001 :
1002 :
1003 : /*
1004 : * ALTER ROLE ... SET
1005 : */
1006 : Oid
1007 46 : AlterRoleSet(AlterRoleSetStmt *stmt)
1008 : {
1009 : HeapTuple roletuple;
1010 : Form_pg_authid roleform;
1011 46 : Oid databaseid = InvalidOid;
1012 46 : Oid roleid = InvalidOid;
1013 :
1014 46 : if (stmt->role)
1015 : {
1016 42 : check_rolespec_name(stmt->role,
1017 42 : _("Cannot alter reserved roles."));
1018 :
1019 42 : roletuple = get_rolespec_tuple(stmt->role);
1020 38 : roleform = (Form_pg_authid) GETSTRUCT(roletuple);
1021 38 : roleid = roleform->oid;
1022 :
1023 : /*
1024 : * Obtain a lock on the role and make sure it didn't go away in the
1025 : * meantime.
1026 : */
1027 38 : shdepLockAndCheckObject(AuthIdRelationId, roleid);
1028 :
1029 : /*
1030 : * To mess with a superuser you gotta be superuser; otherwise you need
1031 : * CREATEROLE plus admin option on the target role; unless you're just
1032 : * trying to change your own settings
1033 : */
1034 38 : if (roleform->rolsuper)
1035 : {
1036 16 : if (!superuser())
1037 0 : ereport(ERROR,
1038 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1039 : errmsg("permission denied to alter role"),
1040 : errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
1041 : "SUPERUSER", "SUPERUSER")));
1042 : }
1043 : else
1044 : {
1045 22 : if ((!have_createrole_privilege() ||
1046 17 : !is_admin_of_role(GetUserId(), roleid))
1047 5 : && roleid != GetUserId())
1048 0 : ereport(ERROR,
1049 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1050 : errmsg("permission denied to alter role"),
1051 : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
1052 : "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
1053 : }
1054 :
1055 38 : ReleaseSysCache(roletuple);
1056 : }
1057 :
1058 : /* look up and lock the database, if specified */
1059 42 : if (stmt->database != NULL)
1060 : {
1061 2 : databaseid = get_database_oid(stmt->database, false);
1062 2 : shdepLockAndCheckObject(DatabaseRelationId, databaseid);
1063 :
1064 2 : if (!stmt->role)
1065 : {
1066 : /*
1067 : * If no role is specified, then this is effectively the same as
1068 : * ALTER DATABASE ... SET, so use the same permission check.
1069 : */
1070 0 : if (!object_ownercheck(DatabaseRelationId, databaseid, GetUserId()))
1071 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
1072 0 : stmt->database);
1073 : }
1074 : }
1075 :
1076 42 : if (!stmt->role && !stmt->database)
1077 : {
1078 : /* Must be superuser to alter settings globally. */
1079 4 : if (!superuser())
1080 0 : ereport(ERROR,
1081 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1082 : errmsg("permission denied to alter setting"),
1083 : errdetail("Only roles with the %s attribute may alter settings globally.",
1084 : "SUPERUSER")));
1085 : }
1086 :
1087 42 : AlterSetting(databaseid, roleid, stmt->setstmt);
1088 :
1089 40 : return roleid;
1090 : }
1091 :
1092 :
1093 : /*
1094 : * DROP ROLE
1095 : */
1096 : void
1097 884 : DropRole(DropRoleStmt *stmt)
1098 : {
1099 : Relation pg_authid_rel,
1100 : pg_auth_members_rel;
1101 : ListCell *item;
1102 884 : List *role_oids = NIL;
1103 :
1104 884 : if (!have_createrole_privilege())
1105 0 : ereport(ERROR,
1106 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1107 : errmsg("permission denied to drop role"),
1108 : errdetail("Only roles with the %s attribute and the %s option on the target roles may drop roles.",
1109 : "CREATEROLE", "ADMIN")));
1110 :
1111 : /*
1112 : * Scan the pg_authid relation to find the Oid of the role(s) to be
1113 : * deleted and perform preliminary permissions and sanity checks.
1114 : */
1115 884 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
1116 884 : pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock);
1117 :
1118 1769 : foreach(item, stmt->roles)
1119 : {
1120 939 : RoleSpec *rolspec = lfirst(item);
1121 : char *role;
1122 : HeapTuple tuple,
1123 : tmp_tuple;
1124 : Form_pg_authid roleform;
1125 : ScanKeyData scankey;
1126 : SysScanDesc sscan;
1127 : Oid roleid;
1128 :
1129 939 : if (rolspec->roletype != ROLESPEC_CSTRING)
1130 0 : ereport(ERROR,
1131 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1132 : errmsg("cannot use special role specifier in DROP ROLE")));
1133 939 : role = rolspec->rolename;
1134 :
1135 939 : tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
1136 939 : if (!HeapTupleIsValid(tuple))
1137 : {
1138 150 : if (!stmt->missing_ok)
1139 : {
1140 45 : ereport(ERROR,
1141 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1142 : errmsg("role \"%s\" does not exist", role)));
1143 : }
1144 : else
1145 : {
1146 105 : ereport(NOTICE,
1147 : (errmsg("role \"%s\" does not exist, skipping",
1148 : role)));
1149 : }
1150 :
1151 105 : continue;
1152 : }
1153 :
1154 789 : roleform = (Form_pg_authid) GETSTRUCT(tuple);
1155 789 : roleid = roleform->oid;
1156 :
1157 789 : if (roleid == GetUserId())
1158 3 : ereport(ERROR,
1159 : (errcode(ERRCODE_OBJECT_IN_USE),
1160 : errmsg("current user cannot be dropped")));
1161 786 : if (roleid == GetOuterUserId())
1162 0 : ereport(ERROR,
1163 : (errcode(ERRCODE_OBJECT_IN_USE),
1164 : errmsg("current user cannot be dropped")));
1165 786 : if (roleid == GetSessionUserId())
1166 0 : ereport(ERROR,
1167 : (errcode(ERRCODE_OBJECT_IN_USE),
1168 : errmsg("session user cannot be dropped")));
1169 :
1170 : /*
1171 : * For safety's sake, we allow createrole holders to drop ordinary
1172 : * roles but not superuser roles, and only if they also have ADMIN
1173 : * OPTION.
1174 : */
1175 786 : if (roleform->rolsuper && !superuser())
1176 3 : ereport(ERROR,
1177 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1178 : errmsg("permission denied to drop role"),
1179 : errdetail("Only roles with the %s attribute may drop roles with the %s attribute.",
1180 : "SUPERUSER", "SUPERUSER")));
1181 783 : if (!is_admin_of_role(GetUserId(), roleid))
1182 3 : ereport(ERROR,
1183 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1184 : errmsg("permission denied to drop role"),
1185 : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may drop this role.",
1186 : "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
1187 :
1188 : /* DROP hook for the role being removed */
1189 780 : InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
1190 :
1191 : /* Don't leak the syscache tuple */
1192 780 : ReleaseSysCache(tuple);
1193 :
1194 : /*
1195 : * Lock the role, so nobody can add dependencies to her while we drop
1196 : * her. We keep the lock until the end of transaction.
1197 : */
1198 780 : LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
1199 :
1200 : /*
1201 : * If there is a pg_auth_members entry that has one of the roles to be
1202 : * dropped as the roleid or member, it should be silently removed, but
1203 : * if there is a pg_auth_members entry that has one of the roles to be
1204 : * dropped as the grantor, the operation should fail.
1205 : *
1206 : * It's possible, however, that a single pg_auth_members entry could
1207 : * fall into multiple categories - e.g. the user could do "GRANT foo
1208 : * TO bar GRANTED BY baz" and then "DROP ROLE baz, bar". We want such
1209 : * an operation to succeed regardless of the order in which the
1210 : * to-be-dropped roles are passed to DROP ROLE.
1211 : *
1212 : * To make that work, we remove all pg_auth_members entries that can
1213 : * be silently removed in this loop, and then below we'll make a
1214 : * second pass over the list of roles to be removed and check for any
1215 : * remaining dependencies.
1216 : */
1217 780 : ScanKeyInit(&scankey,
1218 : Anum_pg_auth_members_roleid,
1219 : BTEqualStrategyNumber, F_OIDEQ,
1220 : ObjectIdGetDatum(roleid));
1221 :
1222 780 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
1223 : true, NULL, 1, &scankey);
1224 :
1225 905 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1226 : {
1227 : Form_pg_auth_members authmem_form;
1228 :
1229 125 : authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
1230 125 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
1231 : authmem_form->oid, 0);
1232 125 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1233 : }
1234 :
1235 780 : systable_endscan(sscan);
1236 :
1237 780 : ScanKeyInit(&scankey,
1238 : Anum_pg_auth_members_member,
1239 : BTEqualStrategyNumber, F_OIDEQ,
1240 : ObjectIdGetDatum(roleid));
1241 :
1242 780 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
1243 : true, NULL, 1, &scankey);
1244 :
1245 914 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1246 : {
1247 : Form_pg_auth_members authmem_form;
1248 :
1249 134 : authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
1250 134 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
1251 : authmem_form->oid, 0);
1252 134 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1253 : }
1254 :
1255 780 : systable_endscan(sscan);
1256 :
1257 : /*
1258 : * Advance command counter so that later iterations of this loop will
1259 : * see the changes already made. This is essential if, for example,
1260 : * we are trying to drop both a role and one of its direct members ---
1261 : * we'll get an error if we try to delete the linking pg_auth_members
1262 : * tuple twice. (We do not need a CCI between the two delete loops
1263 : * above, because it's not allowed for a role to directly contain
1264 : * itself.)
1265 : */
1266 780 : CommandCounterIncrement();
1267 :
1268 : /* Looks tentatively OK, add it to the list if not there yet. */
1269 780 : role_oids = list_append_unique_oid(role_oids, roleid);
1270 : }
1271 :
1272 : /*
1273 : * Second pass over the roles to be removed.
1274 : */
1275 1545 : foreach(item, role_oids)
1276 : {
1277 777 : Oid roleid = lfirst_oid(item);
1278 : HeapTuple tuple;
1279 : Form_pg_authid roleform;
1280 : char *detail;
1281 : char *detail_log;
1282 :
1283 : /*
1284 : * Re-find the pg_authid tuple.
1285 : *
1286 : * Since we've taken a lock on the role OID, it shouldn't be possible
1287 : * for the tuple to have been deleted -- or for that matter updated --
1288 : * unless the user is manually modifying the system catalogs.
1289 : */
1290 777 : tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
1291 777 : if (!HeapTupleIsValid(tuple))
1292 0 : elog(ERROR, "could not find tuple for role %u", roleid);
1293 777 : roleform = (Form_pg_authid) GETSTRUCT(tuple);
1294 :
1295 : /*
1296 : * Check for pg_shdepend entries depending on this role.
1297 : *
1298 : * This needs to happen after we've completed removing any
1299 : * pg_auth_members entries that can be removed silently, in order to
1300 : * avoid spurious failures. See notes above for more details.
1301 : */
1302 777 : if (checkSharedDependencies(AuthIdRelationId, roleid,
1303 : &detail, &detail_log))
1304 62 : ereport(ERROR,
1305 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1306 : errmsg("role \"%s\" cannot be dropped because some objects depend on it",
1307 : NameStr(roleform->rolname)),
1308 : errdetail_internal("%s", detail),
1309 : errdetail_log("%s", detail_log)));
1310 :
1311 : /*
1312 : * Remove the role from the pg_authid table
1313 : */
1314 715 : CatalogTupleDelete(pg_authid_rel, &tuple->t_self);
1315 :
1316 715 : ReleaseSysCache(tuple);
1317 :
1318 : /*
1319 : * Remove any comments or security labels on this role.
1320 : */
1321 715 : DeleteSharedComments(roleid, AuthIdRelationId);
1322 715 : DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
1323 :
1324 : /*
1325 : * Remove settings for this role.
1326 : */
1327 715 : DropSetting(InvalidOid, roleid);
1328 : }
1329 :
1330 : /*
1331 : * Now we can clean up; but keep locks until commit.
1332 : */
1333 768 : table_close(pg_auth_members_rel, NoLock);
1334 768 : table_close(pg_authid_rel, NoLock);
1335 768 : }
1336 :
1337 : /*
1338 : * Rename role
1339 : */
1340 : ObjectAddress
1341 15 : RenameRole(const char *oldname, const char *newname)
1342 : {
1343 : HeapTuple oldtuple,
1344 : newtuple;
1345 : TupleDesc dsc;
1346 : Relation rel;
1347 : Datum datum;
1348 : bool isnull;
1349 : Datum repl_val[Natts_pg_authid];
1350 : bool repl_null[Natts_pg_authid];
1351 : bool repl_repl[Natts_pg_authid];
1352 : int i;
1353 : Oid roleid;
1354 : ObjectAddress address;
1355 : Form_pg_authid authform;
1356 :
1357 : /* Report error if name has \n or \r character. */
1358 15 : if (strpbrk(newname, "\n\r"))
1359 0 : ereport(ERROR,
1360 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1361 : errmsg("role name \"%s\" contains a newline or carriage return character", newname)));
1362 :
1363 15 : rel = table_open(AuthIdRelationId, RowExclusiveLock);
1364 15 : dsc = RelationGetDescr(rel);
1365 :
1366 15 : oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
1367 15 : if (!HeapTupleIsValid(oldtuple))
1368 0 : ereport(ERROR,
1369 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1370 : errmsg("role \"%s\" does not exist", oldname)));
1371 :
1372 : /*
1373 : * XXX Client applications probably store the session user somewhere, so
1374 : * renaming it could cause confusion. On the other hand, there may not be
1375 : * an actual problem besides a little confusion, so think about this and
1376 : * decide. Same for SET ROLE ... we don't restrict renaming the current
1377 : * effective userid, though.
1378 : */
1379 :
1380 15 : authform = (Form_pg_authid) GETSTRUCT(oldtuple);
1381 15 : roleid = authform->oid;
1382 :
1383 15 : if (roleid == GetSessionUserId())
1384 0 : ereport(ERROR,
1385 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1386 : errmsg("session user cannot be renamed")));
1387 15 : if (roleid == GetOuterUserId())
1388 0 : ereport(ERROR,
1389 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1390 : errmsg("current user cannot be renamed")));
1391 :
1392 : /*
1393 : * Check that the user is not trying to rename a system role and not
1394 : * trying to rename a role into the reserved "pg_" namespace.
1395 : */
1396 15 : if (IsReservedName(NameStr(authform->rolname)))
1397 0 : ereport(ERROR,
1398 : (errcode(ERRCODE_RESERVED_NAME),
1399 : errmsg("role name \"%s\" is reserved",
1400 : NameStr(authform->rolname)),
1401 : errdetail("Role names starting with \"pg_\" are reserved.")));
1402 :
1403 15 : if (IsReservedName(newname))
1404 0 : ereport(ERROR,
1405 : (errcode(ERRCODE_RESERVED_NAME),
1406 : errmsg("role name \"%s\" is reserved",
1407 : newname),
1408 : errdetail("Role names starting with \"pg_\" are reserved.")));
1409 :
1410 : /*
1411 : * If built with appropriate switch, whine when regression-testing
1412 : * conventions for role names are violated.
1413 : */
1414 : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
1415 : if (strncmp(newname, "regress_", 8) != 0)
1416 : elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
1417 : #endif
1418 :
1419 : /* make sure the new name doesn't exist */
1420 15 : if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
1421 0 : ereport(ERROR,
1422 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1423 : errmsg("role \"%s\" already exists", newname)));
1424 :
1425 : /*
1426 : * Only superusers can mess with superusers. Otherwise, a user with
1427 : * CREATEROLE can rename a role for which they have ADMIN OPTION.
1428 : */
1429 15 : if (authform->rolsuper)
1430 : {
1431 3 : if (!superuser())
1432 0 : ereport(ERROR,
1433 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1434 : errmsg("permission denied to rename role"),
1435 : errdetail("Only roles with the %s attribute may rename roles with the %s attribute.",
1436 : "SUPERUSER", "SUPERUSER")));
1437 : }
1438 : else
1439 : {
1440 12 : if (!have_createrole_privilege() ||
1441 12 : !is_admin_of_role(GetUserId(), roleid))
1442 3 : ereport(ERROR,
1443 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1444 : errmsg("permission denied to rename role"),
1445 : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may rename this role.",
1446 : "CREATEROLE", "ADMIN", NameStr(authform->rolname))));
1447 : }
1448 :
1449 : /* OK, construct the modified tuple */
1450 156 : for (i = 0; i < Natts_pg_authid; i++)
1451 144 : repl_repl[i] = false;
1452 :
1453 12 : repl_repl[Anum_pg_authid_rolname - 1] = true;
1454 12 : repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1455 : CStringGetDatum(newname));
1456 12 : repl_null[Anum_pg_authid_rolname - 1] = false;
1457 :
1458 12 : datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1459 :
1460 12 : if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
1461 : {
1462 : /* MD5 uses the username as salt, so just clear it on a rename */
1463 3 : repl_repl[Anum_pg_authid_rolpassword - 1] = true;
1464 3 : repl_null[Anum_pg_authid_rolpassword - 1] = true;
1465 :
1466 3 : ereport(NOTICE,
1467 : (errmsg("MD5 password cleared because of role rename")));
1468 : }
1469 :
1470 12 : newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1471 12 : CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
1472 :
1473 12 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
1474 :
1475 12 : ObjectAddressSet(address, AuthIdRelationId, roleid);
1476 :
1477 12 : ReleaseSysCache(oldtuple);
1478 :
1479 : /*
1480 : * Close pg_authid, but keep lock till commit.
1481 : */
1482 12 : table_close(rel, NoLock);
1483 :
1484 12 : return address;
1485 : }
1486 :
1487 : /*
1488 : * GrantRoleStmt
1489 : *
1490 : * Grant/Revoke roles to/from roles
1491 : */
1492 : void
1493 321 : GrantRole(ParseState *pstate, GrantRoleStmt *stmt)
1494 : {
1495 : Relation pg_authid_rel;
1496 : Oid grantor;
1497 : List *grantee_ids;
1498 : ListCell *item;
1499 : GrantRoleOptions popt;
1500 321 : Oid currentUserId = GetUserId();
1501 :
1502 : /* Parse options list. */
1503 321 : InitGrantRoleOptions(&popt);
1504 503 : foreach(item, stmt->opt)
1505 : {
1506 182 : DefElem *opt = (DefElem *) lfirst(item);
1507 182 : char *optval = defGetString(opt);
1508 :
1509 182 : if (strcmp(opt->defname, "admin") == 0)
1510 : {
1511 94 : popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
1512 :
1513 94 : if (parse_bool(optval, &popt.admin))
1514 94 : continue;
1515 : }
1516 88 : else if (strcmp(opt->defname, "inherit") == 0)
1517 : {
1518 47 : popt.specified |= GRANT_ROLE_SPECIFIED_INHERIT;
1519 47 : if (parse_bool(optval, &popt.inherit))
1520 47 : continue;
1521 : }
1522 41 : else if (strcmp(opt->defname, "set") == 0)
1523 : {
1524 41 : popt.specified |= GRANT_ROLE_SPECIFIED_SET;
1525 41 : if (parse_bool(optval, &popt.set))
1526 41 : continue;
1527 : }
1528 : else
1529 0 : ereport(ERROR,
1530 : errcode(ERRCODE_SYNTAX_ERROR),
1531 : errmsg("unrecognized role option \"%s\"", opt->defname),
1532 : parser_errposition(pstate, opt->location));
1533 :
1534 0 : ereport(ERROR,
1535 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1536 : errmsg("unrecognized value for role option \"%s\": \"%s\"",
1537 : opt->defname, optval),
1538 : parser_errposition(pstate, opt->location)));
1539 : }
1540 :
1541 : /* Lookup OID of grantor, if specified. */
1542 321 : if (stmt->grantor)
1543 60 : grantor = get_rolespec_oid(stmt->grantor, false);
1544 : else
1545 261 : grantor = InvalidOid;
1546 :
1547 318 : grantee_ids = roleSpecsToIds(stmt->grantee_roles);
1548 :
1549 : /* AccessShareLock is enough since we aren't modifying pg_authid */
1550 318 : pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
1551 :
1552 : /*
1553 : * Step through all of the granted roles and add, update, or remove
1554 : * entries in pg_auth_members as appropriate. If stmt->is_grant is true,
1555 : * we are adding new grants or, if they already exist, updating options on
1556 : * those grants. If stmt->is_grant is false, we are revoking grants or
1557 : * removing options from them.
1558 : */
1559 577 : foreach(item, stmt->granted_roles)
1560 : {
1561 319 : AccessPriv *priv = (AccessPriv *) lfirst(item);
1562 319 : char *rolename = priv->priv_name;
1563 : Oid roleid;
1564 :
1565 : /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
1566 319 : if (rolename == NULL || priv->cols != NIL)
1567 0 : ereport(ERROR,
1568 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1569 : errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
1570 :
1571 319 : roleid = get_role_oid(rolename, false);
1572 319 : check_role_membership_authorization(currentUserId,
1573 319 : roleid, stmt->is_grant);
1574 274 : if (stmt->is_grant)
1575 199 : AddRoleMems(currentUserId, rolename, roleid,
1576 : stmt->grantee_roles, grantee_ids,
1577 : grantor, &popt);
1578 : else
1579 75 : DelRoleMems(currentUserId, rolename, roleid,
1580 : stmt->grantee_roles, grantee_ids,
1581 : grantor, &popt, stmt->behavior);
1582 : }
1583 :
1584 : /*
1585 : * Close pg_authid, but keep lock till commit.
1586 : */
1587 258 : table_close(pg_authid_rel, NoLock);
1588 258 : }
1589 :
1590 : /*
1591 : * DropOwnedObjects
1592 : *
1593 : * Drop the objects owned by a given list of roles.
1594 : */
1595 : void
1596 74 : DropOwnedObjects(DropOwnedStmt *stmt)
1597 : {
1598 74 : List *role_ids = roleSpecsToIds(stmt->roles);
1599 : ListCell *cell;
1600 :
1601 : /* Check privileges */
1602 157 : foreach(cell, role_ids)
1603 : {
1604 89 : Oid roleid = lfirst_oid(cell);
1605 :
1606 89 : if (!has_privs_of_role(GetUserId(), roleid))
1607 6 : ereport(ERROR,
1608 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1609 : errmsg("permission denied to drop objects"),
1610 : errdetail("Only roles with privileges of role \"%s\" may drop objects owned by it.",
1611 : GetUserNameFromId(roleid, false))));
1612 : }
1613 :
1614 : /* Ok, do it */
1615 68 : shdepDropOwned(role_ids, stmt->behavior);
1616 65 : }
1617 :
1618 : /*
1619 : * ReassignOwnedObjects
1620 : *
1621 : * Give the objects owned by a given list of roles away to another user.
1622 : */
1623 : void
1624 23 : ReassignOwnedObjects(ReassignOwnedStmt *stmt)
1625 : {
1626 23 : List *role_ids = roleSpecsToIds(stmt->roles);
1627 : ListCell *cell;
1628 : Oid newrole;
1629 :
1630 : /* Check privileges */
1631 40 : foreach(cell, role_ids)
1632 : {
1633 23 : Oid roleid = lfirst_oid(cell);
1634 :
1635 23 : if (!has_privs_of_role(GetUserId(), roleid))
1636 6 : ereport(ERROR,
1637 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1638 : errmsg("permission denied to reassign objects"),
1639 : errdetail("Only roles with privileges of role \"%s\" may reassign objects owned by it.",
1640 : GetUserNameFromId(roleid, false))));
1641 : }
1642 :
1643 : /* Must have privileges on the receiving side too */
1644 17 : newrole = get_rolespec_oid(stmt->newrole, false);
1645 :
1646 17 : if (!has_privs_of_role(GetUserId(), newrole))
1647 3 : ereport(ERROR,
1648 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1649 : errmsg("permission denied to reassign objects"),
1650 : errdetail("Only roles with privileges of role \"%s\" may reassign objects to it.",
1651 : GetUserNameFromId(newrole, false))));
1652 :
1653 : /* Ok, do it */
1654 14 : shdepReassignOwned(role_ids, newrole);
1655 14 : }
1656 :
1657 : /*
1658 : * roleSpecsToIds
1659 : *
1660 : * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
1661 : *
1662 : * ROLESPEC_PUBLIC is not allowed.
1663 : */
1664 : List *
1665 2264 : roleSpecsToIds(List *memberNames)
1666 : {
1667 2264 : List *result = NIL;
1668 : ListCell *l;
1669 :
1670 2778 : foreach(l, memberNames)
1671 : {
1672 514 : RoleSpec *rolespec = lfirst_node(RoleSpec, l);
1673 : Oid roleid;
1674 :
1675 514 : roleid = get_rolespec_oid(rolespec, false);
1676 514 : result = lappend_oid(result, roleid);
1677 : }
1678 2264 : return result;
1679 : }
1680 :
1681 : /*
1682 : * AddRoleMems -- Add given members to the specified role
1683 : *
1684 : * currentUserId: OID of role performing the operation
1685 : * rolename: name of role to add to (used only for error messages)
1686 : * roleid: OID of role to add to
1687 : * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
1688 : * memberIds: OIDs of roles to add
1689 : * grantorId: OID that should be recorded as having granted the membership
1690 : * (InvalidOid if not set explicitly)
1691 : * popt: information about grant options
1692 : */
1693 : static void
1694 2104 : AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
1695 : List *memberSpecs, List *memberIds,
1696 : Oid grantorId, GrantRoleOptions *popt)
1697 : {
1698 : Relation pg_authmem_rel;
1699 : TupleDesc pg_authmem_dsc;
1700 : ListCell *specitem;
1701 : ListCell *iditem;
1702 :
1703 : Assert(list_length(memberSpecs) == list_length(memberIds));
1704 :
1705 : /* Validate grantor (and resolve implicit grantor if not specified). */
1706 2104 : grantorId = check_role_grantor(currentUserId, roleid, grantorId, true);
1707 :
1708 2101 : pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
1709 2101 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1710 :
1711 : /*
1712 : * Only allow changes to this role by one backend at a time, so that we
1713 : * can check integrity constraints like the lack of circular ADMIN OPTION
1714 : * grants without fear of race conditions.
1715 : */
1716 2101 : LockSharedObject(AuthIdRelationId, roleid, 0,
1717 : ShareUpdateExclusiveLock);
1718 :
1719 : /* Preliminary sanity checks. */
1720 2443 : forboth(specitem, memberSpecs, iditem, memberIds)
1721 : {
1722 351 : RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
1723 351 : Oid memberid = lfirst_oid(iditem);
1724 :
1725 : /*
1726 : * pg_database_owner is never a role member. Lifting this restriction
1727 : * would require a policy decision about membership loops. One could
1728 : * prevent loops, which would include making "ALTER DATABASE x OWNER
1729 : * TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
1730 : * proposed_datdba). Hence, gaining a membership could reduce what a
1731 : * role could do. Alternately, one could allow these memberships to
1732 : * complete loops. A role could then have actual WITH ADMIN OPTION on
1733 : * itself, prompting a decision about is_admin_of_role() treatment of
1734 : * the case.
1735 : *
1736 : * Lifting this restriction also has policy implications for ownership
1737 : * of shared objects (databases and tablespaces). We allow such
1738 : * ownership, but we might find cause to ban it in the future.
1739 : * Designing such a ban would more troublesome if the design had to
1740 : * address pg_database_owner being a member of role FOO that owns a
1741 : * shared object. (The effect of such ownership is that any owner of
1742 : * another database can act as the owner of affected shared objects.)
1743 : */
1744 351 : if (memberid == ROLE_PG_DATABASE_OWNER)
1745 3 : ereport(ERROR,
1746 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1747 : errmsg("role \"%s\" cannot be a member of any role",
1748 : get_rolespec_name(memberRole)));
1749 :
1750 : /*
1751 : * Refuse creation of membership loops, including the trivial case
1752 : * where a role is made a member of itself. We do this by checking to
1753 : * see if the target role is already a member of the proposed member
1754 : * role. We have to ignore possible superuserness, however, else we
1755 : * could never grant membership in a superuser-privileged role.
1756 : */
1757 348 : if (is_member_of_role_nosuper(roleid, memberid))
1758 6 : ereport(ERROR,
1759 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1760 : errmsg("role \"%s\" is a member of role \"%s\"",
1761 : rolename, get_rolespec_name(memberRole))));
1762 : }
1763 :
1764 : /*
1765 : * Disallow attempts to grant ADMIN OPTION back to a user who granted it
1766 : * to you, similar to what check_circularity does for ACLs. We want the
1767 : * chains of grants to remain acyclic, so that it's always possible to use
1768 : * REVOKE .. CASCADE to clean up all grants that depend on the one being
1769 : * revoked.
1770 : *
1771 : * NB: This check might look redundant with the check for membership loops
1772 : * above, but it isn't. That's checking for role-member loop (e.g. A is a
1773 : * member of B and B is a member of A) while this is checking for a
1774 : * member-grantor loop (e.g. A gave ADMIN OPTION on X to B and now B, who
1775 : * has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION on
1776 : * X back to A).
1777 : */
1778 2092 : if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID)
1779 : {
1780 : CatCList *memlist;
1781 : RevokeRoleGrantAction *actions;
1782 : int i;
1783 :
1784 : /* Get the list of members for this role. */
1785 72 : memlist = SearchSysCacheList1(AUTHMEMROLEMEM,
1786 : ObjectIdGetDatum(roleid));
1787 :
1788 : /*
1789 : * Figure out what would happen if we removed all existing grants to
1790 : * every role to which we've been asked to make a new grant.
1791 : */
1792 72 : actions = initialize_revoke_actions(memlist);
1793 114 : foreach(iditem, memberIds)
1794 : {
1795 42 : Oid memberid = lfirst_oid(iditem);
1796 :
1797 42 : if (memberid == BOOTSTRAP_SUPERUSERID)
1798 0 : ereport(ERROR,
1799 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1800 : errmsg("%s option cannot be granted back to your own grantor",
1801 : "ADMIN")));
1802 42 : plan_member_revoke(memlist, actions, memberid);
1803 : }
1804 :
1805 : /*
1806 : * If the result would be that the grantor role would no longer have
1807 : * the ability to perform the grant, then the proposed grant would
1808 : * create a circularity.
1809 : */
1810 87 : for (i = 0; i < memlist->n_members; ++i)
1811 : {
1812 : HeapTuple authmem_tuple;
1813 : Form_pg_auth_members authmem_form;
1814 :
1815 84 : authmem_tuple = &memlist->members[i]->tuple;
1816 84 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
1817 :
1818 84 : if (actions[i] == RRG_NOOP &&
1819 78 : authmem_form->member == grantorId &&
1820 69 : authmem_form->admin_option)
1821 69 : break;
1822 : }
1823 72 : if (i >= memlist->n_members)
1824 3 : ereport(ERROR,
1825 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1826 : errmsg("%s option cannot be granted back to your own grantor",
1827 : "ADMIN")));
1828 :
1829 69 : ReleaseSysCacheList(memlist);
1830 : }
1831 :
1832 : /* Now perform the catalog updates. */
1833 2428 : forboth(specitem, memberSpecs, iditem, memberIds)
1834 : {
1835 339 : RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
1836 339 : Oid memberid = lfirst_oid(iditem);
1837 : HeapTuple authmem_tuple;
1838 : HeapTuple tuple;
1839 339 : Datum new_record[Natts_pg_auth_members] = {0};
1840 339 : bool new_record_nulls[Natts_pg_auth_members] = {0};
1841 339 : bool new_record_repl[Natts_pg_auth_members] = {0};
1842 :
1843 : /* Common initialization for possible insert or update */
1844 339 : new_record[Anum_pg_auth_members_roleid - 1] =
1845 339 : ObjectIdGetDatum(roleid);
1846 339 : new_record[Anum_pg_auth_members_member - 1] =
1847 339 : ObjectIdGetDatum(memberid);
1848 339 : new_record[Anum_pg_auth_members_grantor - 1] =
1849 339 : ObjectIdGetDatum(grantorId);
1850 :
1851 : /* Find any existing tuple */
1852 339 : authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
1853 : ObjectIdGetDatum(roleid),
1854 : ObjectIdGetDatum(memberid),
1855 : ObjectIdGetDatum(grantorId));
1856 :
1857 : /*
1858 : * If we found a tuple, update it with new option values, unless there
1859 : * are no changes, in which case issue a WARNING.
1860 : *
1861 : * If we didn't find a tuple, just insert one.
1862 : */
1863 339 : if (HeapTupleIsValid(authmem_tuple))
1864 : {
1865 : Form_pg_auth_members authmem_form;
1866 17 : bool at_least_one_change = false;
1867 :
1868 17 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
1869 :
1870 17 : if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0
1871 0 : && authmem_form->admin_option != popt->admin)
1872 : {
1873 0 : new_record[Anum_pg_auth_members_admin_option - 1] =
1874 0 : BoolGetDatum(popt->admin);
1875 0 : new_record_repl[Anum_pg_auth_members_admin_option - 1] =
1876 : true;
1877 0 : at_least_one_change = true;
1878 : }
1879 :
1880 17 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0
1881 8 : && authmem_form->inherit_option != popt->inherit)
1882 : {
1883 8 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1884 8 : BoolGetDatum(popt->inherit);
1885 8 : new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
1886 : true;
1887 8 : at_least_one_change = true;
1888 : }
1889 :
1890 17 : if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0
1891 5 : && authmem_form->set_option != popt->set)
1892 : {
1893 5 : new_record[Anum_pg_auth_members_set_option - 1] =
1894 5 : BoolGetDatum(popt->set);
1895 5 : new_record_repl[Anum_pg_auth_members_set_option - 1] =
1896 : true;
1897 5 : at_least_one_change = true;
1898 : }
1899 :
1900 17 : if (!at_least_one_change)
1901 : {
1902 9 : ereport(NOTICE,
1903 : (errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"",
1904 : get_rolespec_name(memberRole), rolename,
1905 : GetUserNameFromId(grantorId, false))));
1906 9 : ReleaseSysCache(authmem_tuple);
1907 9 : continue;
1908 : }
1909 :
1910 8 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1911 : new_record,
1912 : new_record_nulls, new_record_repl);
1913 8 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
1914 :
1915 8 : ReleaseSysCache(authmem_tuple);
1916 : }
1917 : else
1918 : {
1919 : Oid objectId;
1920 322 : Oid *newmembers = palloc_object(Oid);
1921 :
1922 : /*
1923 : * The values for these options can be taken directly from 'popt'.
1924 : * Either they were specified, or the defaults as set by
1925 : * InitGrantRoleOptions are correct.
1926 : */
1927 322 : new_record[Anum_pg_auth_members_admin_option - 1] =
1928 322 : BoolGetDatum(popt->admin);
1929 322 : new_record[Anum_pg_auth_members_set_option - 1] =
1930 322 : BoolGetDatum(popt->set);
1931 :
1932 : /*
1933 : * If the user specified a value for the inherit option, use
1934 : * whatever was specified. Otherwise, set the default value based
1935 : * on the role-level property.
1936 : */
1937 322 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
1938 97 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1939 97 : BoolGetDatum(popt->inherit);
1940 : else
1941 : {
1942 : HeapTuple mrtup;
1943 : Form_pg_authid mrform;
1944 :
1945 225 : mrtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(memberid));
1946 225 : if (!HeapTupleIsValid(mrtup))
1947 0 : elog(ERROR, "cache lookup failed for role %u", memberid);
1948 225 : mrform = (Form_pg_authid) GETSTRUCT(mrtup);
1949 225 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1950 225 : BoolGetDatum(mrform->rolinherit);
1951 225 : ReleaseSysCache(mrtup);
1952 : }
1953 :
1954 : /* get an OID for the new row and insert it */
1955 322 : objectId = GetNewOidWithIndex(pg_authmem_rel, AuthMemOidIndexId,
1956 : Anum_pg_auth_members_oid);
1957 322 : new_record[Anum_pg_auth_members_oid - 1] = ObjectIdGetDatum(objectId);
1958 322 : tuple = heap_form_tuple(pg_authmem_dsc,
1959 : new_record, new_record_nulls);
1960 322 : CatalogTupleInsert(pg_authmem_rel, tuple);
1961 :
1962 : /* updateAclDependencies wants to pfree array inputs */
1963 322 : newmembers[0] = grantorId;
1964 322 : updateAclDependencies(AuthMemRelationId, objectId,
1965 : 0, InvalidOid,
1966 : 0, NULL,
1967 : 1, newmembers);
1968 : }
1969 :
1970 : /* CCI after each change, in case there are duplicates in list */
1971 330 : CommandCounterIncrement();
1972 : }
1973 :
1974 : /*
1975 : * Close pg_authmem, but keep lock till commit.
1976 : */
1977 2089 : table_close(pg_authmem_rel, NoLock);
1978 2089 : }
1979 :
1980 : /*
1981 : * DelRoleMems -- Remove given members from the specified role
1982 : *
1983 : * rolename: name of role to del from (used only for error messages)
1984 : * roleid: OID of role to del from
1985 : * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
1986 : * memberIds: OIDs of roles to del
1987 : * grantorId: who is revoking the membership
1988 : * popt: information about grant options
1989 : * behavior: RESTRICT or CASCADE behavior for recursive removal
1990 : */
1991 : static void
1992 81 : DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
1993 : List *memberSpecs, List *memberIds,
1994 : Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior)
1995 : {
1996 : Relation pg_authmem_rel;
1997 : TupleDesc pg_authmem_dsc;
1998 : ListCell *specitem;
1999 : ListCell *iditem;
2000 : CatCList *memlist;
2001 : RevokeRoleGrantAction *actions;
2002 : int i;
2003 :
2004 : Assert(list_length(memberSpecs) == list_length(memberIds));
2005 :
2006 : /* Validate grantor (and resolve implicit grantor if not specified). */
2007 81 : grantorId = check_role_grantor(currentUserId, roleid, grantorId, false);
2008 :
2009 81 : pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
2010 81 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
2011 :
2012 : /*
2013 : * Only allow changes to this role by one backend at a time, so that we
2014 : * can check for things like dependent privileges without fear of race
2015 : * conditions.
2016 : */
2017 81 : LockSharedObject(AuthIdRelationId, roleid, 0,
2018 : ShareUpdateExclusiveLock);
2019 :
2020 81 : memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid));
2021 81 : actions = initialize_revoke_actions(memlist);
2022 :
2023 : /*
2024 : * We may need to recurse to dependent privileges if DROP_CASCADE was
2025 : * specified, or refuse to perform the operation if dependent privileges
2026 : * exist and DROP_RESTRICT was specified. plan_single_revoke() will figure
2027 : * out what to do with each catalog tuple.
2028 : */
2029 156 : forboth(specitem, memberSpecs, iditem, memberIds)
2030 : {
2031 81 : RoleSpec *memberRole = lfirst(specitem);
2032 81 : Oid memberid = lfirst_oid(iditem);
2033 :
2034 81 : if (!plan_single_revoke(memlist, actions, memberid, grantorId,
2035 : popt, behavior))
2036 : {
2037 3 : ereport(WARNING,
2038 : (errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"",
2039 : get_rolespec_name(memberRole), rolename,
2040 : GetUserNameFromId(grantorId, false))));
2041 3 : continue;
2042 : }
2043 : }
2044 :
2045 : /*
2046 : * We now know what to do with each catalog tuple: it should either be
2047 : * left alone, deleted, or just have the admin_option flag cleared.
2048 : * Perform the appropriate action in each case.
2049 : */
2050 218 : for (i = 0; i < memlist->n_members; ++i)
2051 : {
2052 : HeapTuple authmem_tuple;
2053 : Form_pg_auth_members authmem_form;
2054 :
2055 143 : if (actions[i] == RRG_NOOP)
2056 62 : continue;
2057 :
2058 81 : authmem_tuple = &memlist->members[i]->tuple;
2059 81 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2060 :
2061 81 : if (actions[i] == RRG_DELETE_GRANT)
2062 : {
2063 : /*
2064 : * Remove the entry altogether, after first removing its
2065 : * dependencies
2066 : */
2067 57 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
2068 : authmem_form->oid, 0);
2069 57 : CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
2070 : }
2071 : else
2072 : {
2073 : /* Just turn off the specified option */
2074 : HeapTuple tuple;
2075 24 : Datum new_record[Natts_pg_auth_members] = {0};
2076 24 : bool new_record_nulls[Natts_pg_auth_members] = {0};
2077 24 : bool new_record_repl[Natts_pg_auth_members] = {0};
2078 :
2079 : /* Build a tuple to update with */
2080 24 : if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
2081 : {
2082 15 : new_record[Anum_pg_auth_members_admin_option - 1] =
2083 15 : BoolGetDatum(false);
2084 15 : new_record_repl[Anum_pg_auth_members_admin_option - 1] =
2085 : true;
2086 : }
2087 9 : else if (actions[i] == RRG_REMOVE_INHERIT_OPTION)
2088 : {
2089 6 : new_record[Anum_pg_auth_members_inherit_option - 1] =
2090 6 : BoolGetDatum(false);
2091 6 : new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
2092 : true;
2093 : }
2094 3 : else if (actions[i] == RRG_REMOVE_SET_OPTION)
2095 : {
2096 3 : new_record[Anum_pg_auth_members_set_option - 1] =
2097 3 : BoolGetDatum(false);
2098 3 : new_record_repl[Anum_pg_auth_members_set_option - 1] =
2099 : true;
2100 : }
2101 : else
2102 0 : elog(ERROR, "unknown role revoke action");
2103 :
2104 24 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
2105 : new_record,
2106 : new_record_nulls, new_record_repl);
2107 24 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
2108 : }
2109 : }
2110 :
2111 75 : ReleaseSysCacheList(memlist);
2112 :
2113 : /*
2114 : * Close pg_authmem, but keep lock till commit.
2115 : */
2116 75 : table_close(pg_authmem_rel, NoLock);
2117 75 : }
2118 :
2119 : /*
2120 : * Check that currentUserId has permission to modify the membership list for
2121 : * roleid. Throw an error if not.
2122 : */
2123 : static void
2124 369 : check_role_membership_authorization(Oid currentUserId, Oid roleid,
2125 : bool is_grant)
2126 : {
2127 : /*
2128 : * The charter of pg_database_owner is to have exactly one, implicit,
2129 : * situation-dependent member. There's no technical need for this
2130 : * restriction. (One could lift it and take the further step of making
2131 : * object_ownercheck(DatabaseRelationId, ...) equivalent to
2132 : * has_privs_of_role(roleid, ROLE_PG_DATABASE_OWNER), in which case
2133 : * explicit, situation-independent members could act as the owner of any
2134 : * database.)
2135 : */
2136 369 : if (is_grant && roleid == ROLE_PG_DATABASE_OWNER)
2137 6 : ereport(ERROR,
2138 : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2139 : errmsg("role \"%s\" cannot have explicit members",
2140 : GetUserNameFromId(roleid, false)));
2141 :
2142 : /* To mess with a superuser role, you gotta be superuser. */
2143 363 : if (superuser_arg(roleid))
2144 : {
2145 8 : if (!superuser_arg(currentUserId))
2146 : {
2147 3 : if (is_grant)
2148 3 : ereport(ERROR,
2149 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2150 : errmsg("permission denied to grant role \"%s\"",
2151 : GetUserNameFromId(roleid, false)),
2152 : errdetail("Only roles with the %s attribute may grant roles with the %s attribute.",
2153 : "SUPERUSER", "SUPERUSER")));
2154 : else
2155 0 : ereport(ERROR,
2156 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2157 : errmsg("permission denied to revoke role \"%s\"",
2158 : GetUserNameFromId(roleid, false)),
2159 : errdetail("Only roles with the %s attribute may revoke roles with the %s attribute.",
2160 : "SUPERUSER", "SUPERUSER")));
2161 : }
2162 : }
2163 : else
2164 : {
2165 : /*
2166 : * Otherwise, must have admin option on the role to be changed.
2167 : */
2168 355 : if (!is_admin_of_role(currentUserId, roleid))
2169 : {
2170 72 : if (is_grant)
2171 72 : ereport(ERROR,
2172 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2173 : errmsg("permission denied to grant role \"%s\"",
2174 : GetUserNameFromId(roleid, false)),
2175 : errdetail("Only roles with the %s option on role \"%s\" may grant this role.",
2176 : "ADMIN", GetUserNameFromId(roleid, false))));
2177 : else
2178 0 : ereport(ERROR,
2179 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2180 : errmsg("permission denied to revoke role \"%s\"",
2181 : GetUserNameFromId(roleid, false)),
2182 : errdetail("Only roles with the %s option on role \"%s\" may revoke this role.",
2183 : "ADMIN", GetUserNameFromId(roleid, false))));
2184 : }
2185 : }
2186 288 : }
2187 :
2188 : /*
2189 : * Sanity-check, or infer, the grantor for a GRANT or REVOKE statement
2190 : * targeting a role.
2191 : *
2192 : * The grantor must always be either a role with ADMIN OPTION on the role in
2193 : * which membership is being granted, or the bootstrap superuser. This is
2194 : * similar to the restriction enforced by select_best_grantor, except that
2195 : * roles don't have owners, so we regard the bootstrap superuser as the
2196 : * implicit owner.
2197 : *
2198 : * If the grantor was not explicitly specified by the user, grantorId should
2199 : * be passed as InvalidOid, and this function will infer the user to be
2200 : * recorded as the grantor. In many cases, this will be the current user, but
2201 : * things get more complicated when the current user doesn't possess ADMIN
2202 : * OPTION on the role but rather relies on having SUPERUSER privileges, or
2203 : * on inheriting the privileges of a role which does have ADMIN OPTION. See
2204 : * below for details.
2205 : *
2206 : * If the grantor was specified by the user, then it must be a user that
2207 : * can legally be recorded as the grantor, as per the rule stated above.
2208 : * This is an integrity constraint, not a permissions check, and thus even
2209 : * superusers are subject to this restriction. However, there is also a
2210 : * permissions check: to specify a role as the grantor, the current user
2211 : * must possess the privileges of that role. Superusers will always pass
2212 : * this check, but for non-superusers it may lead to an error.
2213 : *
2214 : * The return value is the OID to be regarded as the grantor when executing
2215 : * the operation.
2216 : */
2217 : static Oid
2218 2185 : check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant)
2219 : {
2220 : /* If the grantor ID was not specified, pick one to use. */
2221 2185 : if (!OidIsValid(grantorId))
2222 : {
2223 : /*
2224 : * Grants where the grantor is recorded as the bootstrap superuser do
2225 : * not depend on any other existing grants, so always default to this
2226 : * interpretation when possible.
2227 : */
2228 2065 : if (superuser_arg(currentUserId))
2229 1897 : return BOOTSTRAP_SUPERUSERID;
2230 :
2231 : /*
2232 : * Otherwise, the grantor must either have ADMIN OPTION on the role or
2233 : * inherit the privileges of a role which does. In the former case,
2234 : * record the grantor as the current user; in the latter, pick one of
2235 : * the roles that is "most directly" inherited by the current role
2236 : * (i.e. fewest "hops").
2237 : *
2238 : * (We shouldn't fail to find a best grantor, because we've already
2239 : * established that the current user has permission to perform the
2240 : * operation.)
2241 : */
2242 168 : grantorId = select_best_admin(currentUserId, roleid);
2243 168 : if (!OidIsValid(grantorId))
2244 0 : elog(ERROR, "no possible grantors");
2245 168 : return grantorId;
2246 : }
2247 :
2248 : /*
2249 : * If an explicit grantor is specified, it must be a role whose privileges
2250 : * the current user possesses.
2251 : *
2252 : * It should also be a role that has ADMIN OPTION on the target role, but
2253 : * we check this condition only in case of GRANT. For REVOKE, no matching
2254 : * grant should exist anyway, but if it somehow does, let the user get rid
2255 : * of it.
2256 : */
2257 120 : if (is_grant)
2258 : {
2259 108 : if (!has_privs_of_role(currentUserId, grantorId))
2260 0 : ereport(ERROR,
2261 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2262 : errmsg("permission denied to grant privileges as role \"%s\"",
2263 : GetUserNameFromId(grantorId, false)),
2264 : errdetail("Only roles with privileges of role \"%s\" may grant privileges as this role.",
2265 : GetUserNameFromId(grantorId, false))));
2266 :
2267 153 : if (grantorId != BOOTSTRAP_SUPERUSERID &&
2268 45 : select_best_admin(grantorId, roleid) != grantorId)
2269 3 : ereport(ERROR,
2270 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2271 : errmsg("permission denied to grant privileges as role \"%s\"",
2272 : GetUserNameFromId(grantorId, false)),
2273 : errdetail("The grantor must have the %s option on role \"%s\".",
2274 : "ADMIN", GetUserNameFromId(roleid, false))));
2275 : }
2276 : else
2277 : {
2278 12 : if (!has_privs_of_role(currentUserId, grantorId))
2279 0 : ereport(ERROR,
2280 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2281 : errmsg("permission denied to revoke privileges granted by role \"%s\"",
2282 : GetUserNameFromId(grantorId, false)),
2283 : errdetail("Only roles with privileges of role \"%s\" may revoke privileges granted by this role.",
2284 : GetUserNameFromId(grantorId, false))));
2285 : }
2286 :
2287 : /*
2288 : * If a grantor was specified explicitly, always attribute the grant to
2289 : * that role (unless we error out above).
2290 : */
2291 117 : return grantorId;
2292 : }
2293 :
2294 : /*
2295 : * Initialize an array of RevokeRoleGrantAction objects.
2296 : *
2297 : * 'memlist' should be a list of all grants for the target role.
2298 : *
2299 : * This constructs an array indicating that no actions are to be performed;
2300 : * that is, every element is initially RRG_NOOP.
2301 : */
2302 : static RevokeRoleGrantAction *
2303 153 : initialize_revoke_actions(CatCList *memlist)
2304 : {
2305 : RevokeRoleGrantAction *result;
2306 : int i;
2307 :
2308 153 : if (memlist->n_members == 0)
2309 0 : return NULL;
2310 :
2311 153 : result = palloc_array(RevokeRoleGrantAction, memlist->n_members);
2312 416 : for (i = 0; i < memlist->n_members; i++)
2313 263 : result[i] = RRG_NOOP;
2314 153 : return result;
2315 : }
2316 :
2317 : /*
2318 : * Figure out what we would need to do in order to revoke a grant, or just the
2319 : * admin option on a grant, given that there might be dependent privileges.
2320 : *
2321 : * 'memlist' should be a list of all grants for the target role.
2322 : *
2323 : * Whatever actions prove to be necessary will be signalled by updating
2324 : * 'actions'.
2325 : *
2326 : * If behavior is DROP_RESTRICT, an error will occur if there are dependent
2327 : * role membership grants; if DROP_CASCADE, those grants will be scheduled
2328 : * for deletion.
2329 : *
2330 : * The return value is true if the matching grant was found in the list,
2331 : * and false if not.
2332 : */
2333 : static bool
2334 81 : plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2335 : Oid member, Oid grantor, GrantRoleOptions *popt,
2336 : DropBehavior behavior)
2337 : {
2338 : int i;
2339 :
2340 : /*
2341 : * If popt.specified == 0, we're revoking the grant entirely; otherwise,
2342 : * we expect just one bit to be set, and we're revoking the corresponding
2343 : * option. As of this writing, there's no syntax that would allow for an
2344 : * attempt to revoke multiple options at once, and the logic below
2345 : * wouldn't work properly if such syntax were added, so assert that our
2346 : * caller isn't trying to do that.
2347 : */
2348 : Assert(pg_popcount32(popt->specified) <= 1);
2349 :
2350 140 : for (i = 0; i < memlist->n_members; ++i)
2351 : {
2352 : HeapTuple authmem_tuple;
2353 : Form_pg_auth_members authmem_form;
2354 :
2355 137 : authmem_tuple = &memlist->members[i]->tuple;
2356 137 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2357 :
2358 137 : if (authmem_form->member == member &&
2359 87 : authmem_form->grantor == grantor)
2360 : {
2361 78 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
2362 : {
2363 : /*
2364 : * Revoking the INHERIT option doesn't change anything for
2365 : * dependent privileges, so we don't need to recurse.
2366 : */
2367 6 : actions[i] = RRG_REMOVE_INHERIT_OPTION;
2368 : }
2369 72 : else if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0)
2370 : {
2371 : /* Here too, no need to recurse. */
2372 3 : actions[i] = RRG_REMOVE_SET_OPTION;
2373 : }
2374 : else
2375 : {
2376 : bool revoke_admin_option_only;
2377 :
2378 : /*
2379 : * Revoking the grant entirely, or ADMIN option on a grant,
2380 : * implicates dependent privileges, so we may need to recurse.
2381 : */
2382 69 : revoke_admin_option_only =
2383 69 : (popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0;
2384 69 : plan_recursive_revoke(memlist, actions, i,
2385 : revoke_admin_option_only, behavior);
2386 : }
2387 72 : return true;
2388 : }
2389 : }
2390 :
2391 3 : return false;
2392 : }
2393 :
2394 : /*
2395 : * Figure out what we would need to do in order to revoke all grants to
2396 : * a given member, given that there might be dependent privileges.
2397 : *
2398 : * 'memlist' should be a list of all grants for the target role.
2399 : *
2400 : * Whatever actions prove to be necessary will be signalled by updating
2401 : * 'actions'.
2402 : */
2403 : static void
2404 42 : plan_member_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2405 : Oid member)
2406 : {
2407 : int i;
2408 :
2409 93 : for (i = 0; i < memlist->n_members; ++i)
2410 : {
2411 : HeapTuple authmem_tuple;
2412 : Form_pg_auth_members authmem_form;
2413 :
2414 51 : authmem_tuple = &memlist->members[i]->tuple;
2415 51 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2416 :
2417 51 : if (authmem_form->member == member)
2418 3 : plan_recursive_revoke(memlist, actions, i, false, DROP_CASCADE);
2419 : }
2420 42 : }
2421 :
2422 : /*
2423 : * Workhorse for figuring out recursive revocation of role grants.
2424 : *
2425 : * This is similar to what recursive_revoke() does for ACLs.
2426 : */
2427 : static void
2428 84 : plan_recursive_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2429 : int index,
2430 : bool revoke_admin_option_only, DropBehavior behavior)
2431 : {
2432 84 : bool would_still_have_admin_option = false;
2433 : HeapTuple authmem_tuple;
2434 : Form_pg_auth_members authmem_form;
2435 : int i;
2436 :
2437 : /* If it's already been done, we can just return. */
2438 84 : if (actions[index] == RRG_DELETE_GRANT)
2439 0 : return;
2440 84 : if (actions[index] == RRG_REMOVE_ADMIN_OPTION &&
2441 : revoke_admin_option_only)
2442 0 : return;
2443 :
2444 : /* Locate tuple data. */
2445 84 : authmem_tuple = &memlist->members[index]->tuple;
2446 84 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2447 :
2448 : /*
2449 : * If the existing tuple does not have admin_option set, then we do not
2450 : * need to recurse. If we're just supposed to clear that bit we don't need
2451 : * to do anything at all; if we're supposed to remove the grant, we need
2452 : * to do something, but only to the tuple, and not any others.
2453 : */
2454 84 : if (!revoke_admin_option_only)
2455 : {
2456 66 : actions[index] = RRG_DELETE_GRANT;
2457 66 : if (!authmem_form->admin_option)
2458 45 : return;
2459 : }
2460 : else
2461 : {
2462 18 : if (!authmem_form->admin_option)
2463 0 : return;
2464 18 : actions[index] = RRG_REMOVE_ADMIN_OPTION;
2465 : }
2466 :
2467 : /* Determine whether the member would still have ADMIN OPTION. */
2468 114 : for (i = 0; i < memlist->n_members; ++i)
2469 : {
2470 : HeapTuple am_cascade_tuple;
2471 : Form_pg_auth_members am_cascade_form;
2472 :
2473 75 : am_cascade_tuple = &memlist->members[i]->tuple;
2474 75 : am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
2475 :
2476 75 : if (am_cascade_form->member == authmem_form->member &&
2477 39 : am_cascade_form->admin_option && actions[i] == RRG_NOOP)
2478 : {
2479 0 : would_still_have_admin_option = true;
2480 0 : break;
2481 : }
2482 : }
2483 :
2484 : /* If the member would still have ADMIN OPTION, we need not recurse. */
2485 39 : if (would_still_have_admin_option)
2486 0 : return;
2487 :
2488 : /*
2489 : * Recurse to grants that are not yet slated for deletion which have this
2490 : * member as the grantor.
2491 : */
2492 108 : for (i = 0; i < memlist->n_members; ++i)
2493 : {
2494 : HeapTuple am_cascade_tuple;
2495 : Form_pg_auth_members am_cascade_form;
2496 :
2497 75 : am_cascade_tuple = &memlist->members[i]->tuple;
2498 75 : am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
2499 :
2500 75 : if (am_cascade_form->grantor == authmem_form->member &&
2501 18 : actions[i] != RRG_DELETE_GRANT)
2502 : {
2503 18 : if (behavior == DROP_RESTRICT)
2504 6 : ereport(ERROR,
2505 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
2506 : errmsg("dependent privileges exist"),
2507 : errhint("Use CASCADE to revoke them too.")));
2508 :
2509 12 : plan_recursive_revoke(memlist, actions, i, false, behavior);
2510 : }
2511 : }
2512 : }
2513 :
2514 : /*
2515 : * Initialize a GrantRoleOptions object with default values.
2516 : */
2517 : static void
2518 1462 : InitGrantRoleOptions(GrantRoleOptions *popt)
2519 : {
2520 1462 : popt->specified = 0;
2521 1462 : popt->admin = false;
2522 1462 : popt->inherit = false;
2523 1462 : popt->set = true;
2524 1462 : }
2525 :
2526 : /*
2527 : * GUC check_hook for createrole_self_grant
2528 : */
2529 : bool
2530 1195 : check_createrole_self_grant(char **newval, void **extra, GucSource source)
2531 : {
2532 : char *rawstring;
2533 : List *elemlist;
2534 : ListCell *l;
2535 1195 : unsigned options = 0;
2536 : unsigned *result;
2537 :
2538 : /* Need a modifiable copy of string */
2539 1195 : rawstring = pstrdup(*newval);
2540 :
2541 1195 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
2542 : {
2543 : /* syntax error in list */
2544 0 : GUC_check_errdetail("List syntax is invalid.");
2545 0 : pfree(rawstring);
2546 0 : list_free(elemlist);
2547 0 : return false;
2548 : }
2549 :
2550 1201 : foreach(l, elemlist)
2551 : {
2552 6 : char *tok = (char *) lfirst(l);
2553 :
2554 6 : if (pg_strcasecmp(tok, "SET") == 0)
2555 3 : options |= GRANT_ROLE_SPECIFIED_SET;
2556 3 : else if (pg_strcasecmp(tok, "INHERIT") == 0)
2557 3 : options |= GRANT_ROLE_SPECIFIED_INHERIT;
2558 : else
2559 : {
2560 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
2561 0 : pfree(rawstring);
2562 0 : list_free(elemlist);
2563 0 : return false;
2564 : }
2565 : }
2566 :
2567 1195 : pfree(rawstring);
2568 1195 : list_free(elemlist);
2569 :
2570 1195 : result = (unsigned *) guc_malloc(LOG, sizeof(unsigned));
2571 1195 : if (!result)
2572 0 : return false;
2573 1195 : *result = options;
2574 1195 : *extra = result;
2575 :
2576 1195 : return true;
2577 : }
2578 :
2579 : /*
2580 : * GUC assign_hook for createrole_self_grant
2581 : */
2582 : void
2583 1195 : assign_createrole_self_grant(const char *newval, void *extra)
2584 : {
2585 1195 : unsigned options = *(unsigned *) extra;
2586 :
2587 1195 : createrole_self_grant_enabled = (options != 0);
2588 1195 : createrole_self_grant_options.specified = GRANT_ROLE_SPECIFIED_ADMIN
2589 : | GRANT_ROLE_SPECIFIED_INHERIT
2590 : | GRANT_ROLE_SPECIFIED_SET;
2591 1195 : createrole_self_grant_options.admin = false;
2592 1195 : createrole_self_grant_options.inherit =
2593 1195 : (options & GRANT_ROLE_SPECIFIED_INHERIT) != 0;
2594 1195 : createrole_self_grant_options.set =
2595 1195 : (options & GRANT_ROLE_SPECIFIED_SET) != 0;
2596 1195 : }
|