/src/postgres/src/backend/commands/user.c
Line | Count | Source |
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 | 2 | #define GRANT_ROLE_SPECIFIED_ADMIN 0x0001 |
82 | 4 | #define GRANT_ROLE_SPECIFIED_INHERIT 0x0002 |
83 | 4 | #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 | | have_createrole_privilege(void) |
124 | 0 | { |
125 | 0 | return has_createrole_privilege(GetUserId()); |
126 | 0 | } |
127 | | |
128 | | |
129 | | /* |
130 | | * CREATE ROLE |
131 | | */ |
132 | | Oid |
133 | | CreateRole(ParseState *pstate, CreateRoleStmt *stmt) |
134 | 0 | { |
135 | 0 | Relation pg_authid_rel; |
136 | 0 | TupleDesc pg_authid_dsc; |
137 | 0 | HeapTuple tuple; |
138 | 0 | Datum new_record[Natts_pg_authid] = {0}; |
139 | 0 | bool new_record_nulls[Natts_pg_authid] = {0}; |
140 | 0 | Oid currentUserId = GetUserId(); |
141 | 0 | Oid roleid; |
142 | 0 | ListCell *item; |
143 | 0 | ListCell *option; |
144 | 0 | char *password = NULL; /* user password */ |
145 | 0 | bool issuper = false; /* Make the user a superuser? */ |
146 | 0 | bool inherit = true; /* Auto inherit privileges? */ |
147 | 0 | bool createrole = false; /* Can this user create roles? */ |
148 | 0 | bool createdb = false; /* Can the user create databases? */ |
149 | 0 | bool canlogin = false; /* Can this user login? */ |
150 | 0 | bool isreplication = false; /* Is this a replication role? */ |
151 | 0 | bool bypassrls = false; /* Is this a row security enabled role? */ |
152 | 0 | int connlimit = -1; /* maximum connections allowed */ |
153 | 0 | List *addroleto = NIL; /* roles to make this a member of */ |
154 | 0 | List *rolemembers = NIL; /* roles to be members of this role */ |
155 | 0 | List *adminmembers = NIL; /* roles to be admins of this role */ |
156 | 0 | char *validUntil = NULL; /* time the login is valid until */ |
157 | 0 | Datum validUntil_datum; /* same, as timestamptz Datum */ |
158 | 0 | bool validUntil_null; |
159 | 0 | DefElem *dpassword = NULL; |
160 | 0 | DefElem *dissuper = NULL; |
161 | 0 | DefElem *dinherit = NULL; |
162 | 0 | DefElem *dcreaterole = NULL; |
163 | 0 | DefElem *dcreatedb = NULL; |
164 | 0 | DefElem *dcanlogin = NULL; |
165 | 0 | DefElem *disreplication = NULL; |
166 | 0 | DefElem *dconnlimit = NULL; |
167 | 0 | DefElem *daddroleto = NULL; |
168 | 0 | DefElem *drolemembers = NULL; |
169 | 0 | DefElem *dadminmembers = NULL; |
170 | 0 | DefElem *dvalidUntil = NULL; |
171 | 0 | DefElem *dbypassRLS = NULL; |
172 | 0 | GrantRoleOptions popt; |
173 | | |
174 | | /* Report error if name has \n or \r character. */ |
175 | 0 | if (strpbrk(stmt->role, "\n\r")) |
176 | 0 | ereport(ERROR, |
177 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
178 | 0 | 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 | 0 | switch (stmt->stmt_type) |
182 | 0 | { |
183 | 0 | case ROLESTMT_ROLE: |
184 | 0 | break; |
185 | 0 | case ROLESTMT_USER: |
186 | 0 | canlogin = true; |
187 | | /* may eventually want inherit to default to false here */ |
188 | 0 | break; |
189 | 0 | case ROLESTMT_GROUP: |
190 | 0 | break; |
191 | 0 | } |
192 | | |
193 | | /* Extract options from the statement node tree */ |
194 | 0 | foreach(option, stmt->options) |
195 | 0 | { |
196 | 0 | DefElem *defel = (DefElem *) lfirst(option); |
197 | |
|
198 | 0 | if (strcmp(defel->defname, "password") == 0) |
199 | 0 | { |
200 | 0 | if (dpassword) |
201 | 0 | errorConflictingDefElem(defel, pstate); |
202 | 0 | dpassword = defel; |
203 | 0 | } |
204 | 0 | else if (strcmp(defel->defname, "sysid") == 0) |
205 | 0 | { |
206 | 0 | ereport(NOTICE, |
207 | 0 | (errmsg("SYSID can no longer be specified"))); |
208 | 0 | } |
209 | 0 | else if (strcmp(defel->defname, "superuser") == 0) |
210 | 0 | { |
211 | 0 | if (dissuper) |
212 | 0 | errorConflictingDefElem(defel, pstate); |
213 | 0 | dissuper = defel; |
214 | 0 | } |
215 | 0 | else if (strcmp(defel->defname, "inherit") == 0) |
216 | 0 | { |
217 | 0 | if (dinherit) |
218 | 0 | errorConflictingDefElem(defel, pstate); |
219 | 0 | dinherit = defel; |
220 | 0 | } |
221 | 0 | else if (strcmp(defel->defname, "createrole") == 0) |
222 | 0 | { |
223 | 0 | if (dcreaterole) |
224 | 0 | errorConflictingDefElem(defel, pstate); |
225 | 0 | dcreaterole = defel; |
226 | 0 | } |
227 | 0 | else if (strcmp(defel->defname, "createdb") == 0) |
228 | 0 | { |
229 | 0 | if (dcreatedb) |
230 | 0 | errorConflictingDefElem(defel, pstate); |
231 | 0 | dcreatedb = defel; |
232 | 0 | } |
233 | 0 | else if (strcmp(defel->defname, "canlogin") == 0) |
234 | 0 | { |
235 | 0 | if (dcanlogin) |
236 | 0 | errorConflictingDefElem(defel, pstate); |
237 | 0 | dcanlogin = defel; |
238 | 0 | } |
239 | 0 | else if (strcmp(defel->defname, "isreplication") == 0) |
240 | 0 | { |
241 | 0 | if (disreplication) |
242 | 0 | errorConflictingDefElem(defel, pstate); |
243 | 0 | disreplication = defel; |
244 | 0 | } |
245 | 0 | else if (strcmp(defel->defname, "connectionlimit") == 0) |
246 | 0 | { |
247 | 0 | if (dconnlimit) |
248 | 0 | errorConflictingDefElem(defel, pstate); |
249 | 0 | dconnlimit = defel; |
250 | 0 | } |
251 | 0 | else if (strcmp(defel->defname, "addroleto") == 0) |
252 | 0 | { |
253 | 0 | if (daddroleto) |
254 | 0 | errorConflictingDefElem(defel, pstate); |
255 | 0 | daddroleto = defel; |
256 | 0 | } |
257 | 0 | else if (strcmp(defel->defname, "rolemembers") == 0) |
258 | 0 | { |
259 | 0 | if (drolemembers) |
260 | 0 | errorConflictingDefElem(defel, pstate); |
261 | 0 | drolemembers = defel; |
262 | 0 | } |
263 | 0 | else if (strcmp(defel->defname, "adminmembers") == 0) |
264 | 0 | { |
265 | 0 | if (dadminmembers) |
266 | 0 | errorConflictingDefElem(defel, pstate); |
267 | 0 | dadminmembers = defel; |
268 | 0 | } |
269 | 0 | else if (strcmp(defel->defname, "validUntil") == 0) |
270 | 0 | { |
271 | 0 | if (dvalidUntil) |
272 | 0 | errorConflictingDefElem(defel, pstate); |
273 | 0 | dvalidUntil = defel; |
274 | 0 | } |
275 | 0 | else if (strcmp(defel->defname, "bypassrls") == 0) |
276 | 0 | { |
277 | 0 | if (dbypassRLS) |
278 | 0 | errorConflictingDefElem(defel, pstate); |
279 | 0 | dbypassRLS = defel; |
280 | 0 | } |
281 | 0 | else |
282 | 0 | elog(ERROR, "option \"%s\" not recognized", |
283 | 0 | defel->defname); |
284 | 0 | } |
285 | | |
286 | 0 | if (dpassword && dpassword->arg) |
287 | 0 | password = strVal(dpassword->arg); |
288 | 0 | if (dissuper) |
289 | 0 | issuper = boolVal(dissuper->arg); |
290 | 0 | if (dinherit) |
291 | 0 | inherit = boolVal(dinherit->arg); |
292 | 0 | if (dcreaterole) |
293 | 0 | createrole = boolVal(dcreaterole->arg); |
294 | 0 | if (dcreatedb) |
295 | 0 | createdb = boolVal(dcreatedb->arg); |
296 | 0 | if (dcanlogin) |
297 | 0 | canlogin = boolVal(dcanlogin->arg); |
298 | 0 | if (disreplication) |
299 | 0 | isreplication = boolVal(disreplication->arg); |
300 | 0 | if (dconnlimit) |
301 | 0 | { |
302 | 0 | connlimit = intVal(dconnlimit->arg); |
303 | 0 | if (connlimit < -1) |
304 | 0 | ereport(ERROR, |
305 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
306 | 0 | errmsg("invalid connection limit: %d", connlimit))); |
307 | 0 | } |
308 | 0 | if (daddroleto) |
309 | 0 | addroleto = (List *) daddroleto->arg; |
310 | 0 | if (drolemembers) |
311 | 0 | rolemembers = (List *) drolemembers->arg; |
312 | 0 | if (dadminmembers) |
313 | 0 | adminmembers = (List *) dadminmembers->arg; |
314 | 0 | if (dvalidUntil) |
315 | 0 | validUntil = strVal(dvalidUntil->arg); |
316 | 0 | if (dbypassRLS) |
317 | 0 | bypassrls = boolVal(dbypassRLS->arg); |
318 | | |
319 | | /* Check some permissions first */ |
320 | 0 | if (!superuser_arg(currentUserId)) |
321 | 0 | { |
322 | 0 | if (!has_createrole_privilege(currentUserId)) |
323 | 0 | ereport(ERROR, |
324 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
325 | 0 | errmsg("permission denied to create role"), |
326 | 0 | errdetail("Only roles with the %s attribute may create roles.", |
327 | 0 | "CREATEROLE"))); |
328 | 0 | if (issuper) |
329 | 0 | ereport(ERROR, |
330 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
331 | 0 | errmsg("permission denied to create role"), |
332 | 0 | errdetail("Only roles with the %s attribute may create roles with the %s attribute.", |
333 | 0 | "SUPERUSER", "SUPERUSER"))); |
334 | 0 | if (createdb && !have_createdb_privilege()) |
335 | 0 | ereport(ERROR, |
336 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
337 | 0 | errmsg("permission denied to create role"), |
338 | 0 | errdetail("Only roles with the %s attribute may create roles with the %s attribute.", |
339 | 0 | "CREATEDB", "CREATEDB"))); |
340 | 0 | if (isreplication && !has_rolreplication(currentUserId)) |
341 | 0 | ereport(ERROR, |
342 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
343 | 0 | errmsg("permission denied to create role"), |
344 | 0 | errdetail("Only roles with the %s attribute may create roles with the %s attribute.", |
345 | 0 | "REPLICATION", "REPLICATION"))); |
346 | 0 | if (bypassrls && !has_bypassrls_privilege(currentUserId)) |
347 | 0 | ereport(ERROR, |
348 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
349 | 0 | errmsg("permission denied to create role"), |
350 | 0 | errdetail("Only roles with the %s attribute may create roles with the %s attribute.", |
351 | 0 | "BYPASSRLS", "BYPASSRLS"))); |
352 | 0 | } |
353 | | |
354 | | /* |
355 | | * Check that the user is not trying to create a role in the reserved |
356 | | * "pg_" namespace. |
357 | | */ |
358 | 0 | if (IsReservedName(stmt->role)) |
359 | 0 | ereport(ERROR, |
360 | 0 | (errcode(ERRCODE_RESERVED_NAME), |
361 | 0 | errmsg("role name \"%s\" is reserved", |
362 | 0 | stmt->role), |
363 | 0 | 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 | 0 | pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock); |
379 | 0 | pg_authid_dsc = RelationGetDescr(pg_authid_rel); |
380 | |
|
381 | 0 | if (OidIsValid(get_role_oid(stmt->role, true))) |
382 | 0 | ereport(ERROR, |
383 | 0 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
384 | 0 | errmsg("role \"%s\" already exists", |
385 | 0 | stmt->role))); |
386 | | |
387 | | /* Convert validuntil to internal form */ |
388 | 0 | if (validUntil) |
389 | 0 | { |
390 | 0 | validUntil_datum = DirectFunctionCall3(timestamptz_in, |
391 | 0 | CStringGetDatum(validUntil), |
392 | 0 | ObjectIdGetDatum(InvalidOid), |
393 | 0 | Int32GetDatum(-1)); |
394 | 0 | validUntil_null = false; |
395 | 0 | } |
396 | 0 | else |
397 | 0 | { |
398 | 0 | validUntil_datum = (Datum) 0; |
399 | 0 | validUntil_null = true; |
400 | 0 | } |
401 | | |
402 | | /* |
403 | | * Call the password checking hook if there is one defined |
404 | | */ |
405 | 0 | if (check_password_hook && password) |
406 | 0 | (*check_password_hook) (stmt->role, |
407 | 0 | password, |
408 | 0 | get_password_type(password), |
409 | 0 | validUntil_datum, |
410 | 0 | validUntil_null); |
411 | | |
412 | | /* |
413 | | * Build a tuple to insert |
414 | | */ |
415 | 0 | new_record[Anum_pg_authid_rolname - 1] = |
416 | 0 | DirectFunctionCall1(namein, CStringGetDatum(stmt->role)); |
417 | 0 | new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper); |
418 | 0 | new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit); |
419 | 0 | new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole); |
420 | 0 | new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb); |
421 | 0 | new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); |
422 | 0 | new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication); |
423 | 0 | new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); |
424 | |
|
425 | 0 | if (password) |
426 | 0 | { |
427 | 0 | char *shadow_pass; |
428 | 0 | 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 | 0 | if (password[0] == '\0' || |
443 | 0 | plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK) |
444 | 0 | { |
445 | 0 | ereport(NOTICE, |
446 | 0 | (errmsg("empty string is not a valid password, clearing password"))); |
447 | 0 | new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; |
448 | 0 | } |
449 | 0 | else |
450 | 0 | { |
451 | | /* Encrypt the password to the requested format. */ |
452 | 0 | shadow_pass = encrypt_password(Password_encryption, stmt->role, |
453 | 0 | password); |
454 | 0 | new_record[Anum_pg_authid_rolpassword - 1] = |
455 | 0 | CStringGetTextDatum(shadow_pass); |
456 | 0 | } |
457 | 0 | } |
458 | 0 | else |
459 | 0 | new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; |
460 | | |
461 | 0 | new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum; |
462 | 0 | new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; |
463 | |
|
464 | 0 | 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 | 0 | if (IsBinaryUpgrade) |
471 | 0 | { |
472 | 0 | if (!OidIsValid(binary_upgrade_next_pg_authid_oid)) |
473 | 0 | ereport(ERROR, |
474 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
475 | 0 | errmsg("pg_authid OID value not set when in binary upgrade mode"))); |
476 | | |
477 | 0 | roleid = binary_upgrade_next_pg_authid_oid; |
478 | 0 | binary_upgrade_next_pg_authid_oid = InvalidOid; |
479 | 0 | } |
480 | 0 | else |
481 | 0 | { |
482 | 0 | roleid = GetNewOidWithIndex(pg_authid_rel, AuthIdOidIndexId, |
483 | 0 | Anum_pg_authid_oid); |
484 | 0 | } |
485 | | |
486 | 0 | new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(roleid); |
487 | |
|
488 | 0 | 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 | 0 | 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 | 0 | if (addroleto || adminmembers || rolemembers) |
500 | 0 | CommandCounterIncrement(); |
501 | | |
502 | | /* Default grant. */ |
503 | 0 | InitGrantRoleOptions(&popt); |
504 | | |
505 | | /* |
506 | | * Add the new role to the specified existing roles. |
507 | | */ |
508 | 0 | if (addroleto) |
509 | 0 | { |
510 | 0 | RoleSpec *thisrole = makeNode(RoleSpec); |
511 | 0 | List *thisrole_list = list_make1(thisrole); |
512 | 0 | List *thisrole_oidlist = list_make1_oid(roleid); |
513 | |
|
514 | 0 | thisrole->roletype = ROLESPEC_CSTRING; |
515 | 0 | thisrole->rolename = stmt->role; |
516 | 0 | thisrole->location = -1; |
517 | |
|
518 | 0 | foreach(item, addroleto) |
519 | 0 | { |
520 | 0 | RoleSpec *oldrole = lfirst(item); |
521 | 0 | HeapTuple oldroletup = get_rolespec_tuple(oldrole); |
522 | 0 | Form_pg_authid oldroleform = (Form_pg_authid) GETSTRUCT(oldroletup); |
523 | 0 | Oid oldroleid = oldroleform->oid; |
524 | 0 | char *oldrolename = NameStr(oldroleform->rolname); |
525 | | |
526 | | /* can only add this role to roles for which you have rights */ |
527 | 0 | check_role_membership_authorization(currentUserId, oldroleid, true); |
528 | 0 | AddRoleMems(currentUserId, oldrolename, oldroleid, |
529 | 0 | thisrole_list, |
530 | 0 | thisrole_oidlist, |
531 | 0 | InvalidOid, &popt); |
532 | |
|
533 | 0 | ReleaseSysCache(oldroletup); |
534 | 0 | } |
535 | 0 | } |
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 | 0 | if (!superuser()) |
548 | 0 | { |
549 | 0 | RoleSpec *current_role = makeNode(RoleSpec); |
550 | 0 | GrantRoleOptions poptself; |
551 | 0 | List *memberSpecs; |
552 | 0 | List *memberIds = list_make1_oid(currentUserId); |
553 | |
|
554 | 0 | current_role->roletype = ROLESPEC_CURRENT_ROLE; |
555 | 0 | current_role->location = -1; |
556 | 0 | memberSpecs = list_make1(current_role); |
557 | |
|
558 | 0 | poptself.specified = GRANT_ROLE_SPECIFIED_ADMIN |
559 | 0 | | GRANT_ROLE_SPECIFIED_INHERIT |
560 | 0 | | GRANT_ROLE_SPECIFIED_SET; |
561 | 0 | poptself.admin = true; |
562 | 0 | poptself.inherit = false; |
563 | 0 | poptself.set = false; |
564 | |
|
565 | 0 | AddRoleMems(BOOTSTRAP_SUPERUSERID, stmt->role, roleid, |
566 | 0 | memberSpecs, memberIds, |
567 | 0 | 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 | 0 | 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 | 0 | if (createrole_self_grant_enabled) |
585 | 0 | AddRoleMems(currentUserId, stmt->role, roleid, |
586 | 0 | memberSpecs, memberIds, |
587 | 0 | currentUserId, &createrole_self_grant_options); |
588 | 0 | } |
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 | 0 | AddRoleMems(currentUserId, stmt->role, roleid, |
598 | 0 | rolemembers, roleSpecsToIds(rolemembers), |
599 | 0 | InvalidOid, &popt); |
600 | 0 | popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN; |
601 | 0 | popt.admin = true; |
602 | 0 | AddRoleMems(currentUserId, stmt->role, roleid, |
603 | 0 | adminmembers, roleSpecsToIds(adminmembers), |
604 | 0 | InvalidOid, &popt); |
605 | | |
606 | | /* Post creation hook for new role */ |
607 | 0 | InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0); |
608 | | |
609 | | /* |
610 | | * Close pg_authid, but keep lock till commit. |
611 | | */ |
612 | 0 | table_close(pg_authid_rel, NoLock); |
613 | |
|
614 | 0 | return roleid; |
615 | 0 | } |
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 | | AlterRole(ParseState *pstate, AlterRoleStmt *stmt) |
627 | 0 | { |
628 | 0 | Datum new_record[Natts_pg_authid] = {0}; |
629 | 0 | bool new_record_nulls[Natts_pg_authid] = {0}; |
630 | 0 | bool new_record_repl[Natts_pg_authid] = {0}; |
631 | 0 | Relation pg_authid_rel; |
632 | 0 | TupleDesc pg_authid_dsc; |
633 | 0 | HeapTuple tuple, |
634 | 0 | new_tuple; |
635 | 0 | Form_pg_authid authform; |
636 | 0 | ListCell *option; |
637 | 0 | char *rolename; |
638 | 0 | char *password = NULL; /* user password */ |
639 | 0 | int connlimit = -1; /* maximum connections allowed */ |
640 | 0 | char *validUntil = NULL; /* time the login is valid until */ |
641 | 0 | Datum validUntil_datum; /* same, as timestamptz Datum */ |
642 | 0 | bool validUntil_null; |
643 | 0 | DefElem *dpassword = NULL; |
644 | 0 | DefElem *dissuper = NULL; |
645 | 0 | DefElem *dinherit = NULL; |
646 | 0 | DefElem *dcreaterole = NULL; |
647 | 0 | DefElem *dcreatedb = NULL; |
648 | 0 | DefElem *dcanlogin = NULL; |
649 | 0 | DefElem *disreplication = NULL; |
650 | 0 | DefElem *dconnlimit = NULL; |
651 | 0 | DefElem *drolemembers = NULL; |
652 | 0 | DefElem *dvalidUntil = NULL; |
653 | 0 | DefElem *dbypassRLS = NULL; |
654 | 0 | Oid roleid; |
655 | 0 | Oid currentUserId = GetUserId(); |
656 | 0 | GrantRoleOptions popt; |
657 | |
|
658 | 0 | check_rolespec_name(stmt->role, |
659 | 0 | _("Cannot alter reserved roles.")); |
660 | | |
661 | | /* Extract options from the statement node tree */ |
662 | 0 | foreach(option, stmt->options) |
663 | 0 | { |
664 | 0 | DefElem *defel = (DefElem *) lfirst(option); |
665 | |
|
666 | 0 | if (strcmp(defel->defname, "password") == 0) |
667 | 0 | { |
668 | 0 | if (dpassword) |
669 | 0 | errorConflictingDefElem(defel, pstate); |
670 | 0 | dpassword = defel; |
671 | 0 | } |
672 | 0 | else if (strcmp(defel->defname, "superuser") == 0) |
673 | 0 | { |
674 | 0 | if (dissuper) |
675 | 0 | errorConflictingDefElem(defel, pstate); |
676 | 0 | dissuper = defel; |
677 | 0 | } |
678 | 0 | else if (strcmp(defel->defname, "inherit") == 0) |
679 | 0 | { |
680 | 0 | if (dinherit) |
681 | 0 | errorConflictingDefElem(defel, pstate); |
682 | 0 | dinherit = defel; |
683 | 0 | } |
684 | 0 | else if (strcmp(defel->defname, "createrole") == 0) |
685 | 0 | { |
686 | 0 | if (dcreaterole) |
687 | 0 | errorConflictingDefElem(defel, pstate); |
688 | 0 | dcreaterole = defel; |
689 | 0 | } |
690 | 0 | else if (strcmp(defel->defname, "createdb") == 0) |
691 | 0 | { |
692 | 0 | if (dcreatedb) |
693 | 0 | errorConflictingDefElem(defel, pstate); |
694 | 0 | dcreatedb = defel; |
695 | 0 | } |
696 | 0 | else if (strcmp(defel->defname, "canlogin") == 0) |
697 | 0 | { |
698 | 0 | if (dcanlogin) |
699 | 0 | errorConflictingDefElem(defel, pstate); |
700 | 0 | dcanlogin = defel; |
701 | 0 | } |
702 | 0 | else if (strcmp(defel->defname, "isreplication") == 0) |
703 | 0 | { |
704 | 0 | if (disreplication) |
705 | 0 | errorConflictingDefElem(defel, pstate); |
706 | 0 | disreplication = defel; |
707 | 0 | } |
708 | 0 | else if (strcmp(defel->defname, "connectionlimit") == 0) |
709 | 0 | { |
710 | 0 | if (dconnlimit) |
711 | 0 | errorConflictingDefElem(defel, pstate); |
712 | 0 | dconnlimit = defel; |
713 | 0 | } |
714 | 0 | else if (strcmp(defel->defname, "rolemembers") == 0 && |
715 | 0 | stmt->action != 0) |
716 | 0 | { |
717 | 0 | if (drolemembers) |
718 | 0 | errorConflictingDefElem(defel, pstate); |
719 | 0 | drolemembers = defel; |
720 | 0 | } |
721 | 0 | else if (strcmp(defel->defname, "validUntil") == 0) |
722 | 0 | { |
723 | 0 | if (dvalidUntil) |
724 | 0 | errorConflictingDefElem(defel, pstate); |
725 | 0 | dvalidUntil = defel; |
726 | 0 | } |
727 | 0 | else if (strcmp(defel->defname, "bypassrls") == 0) |
728 | 0 | { |
729 | 0 | if (dbypassRLS) |
730 | 0 | errorConflictingDefElem(defel, pstate); |
731 | 0 | dbypassRLS = defel; |
732 | 0 | } |
733 | 0 | else |
734 | 0 | elog(ERROR, "option \"%s\" not recognized", |
735 | 0 | defel->defname); |
736 | 0 | } |
737 | | |
738 | 0 | if (dpassword && dpassword->arg) |
739 | 0 | password = strVal(dpassword->arg); |
740 | 0 | if (dconnlimit) |
741 | 0 | { |
742 | 0 | connlimit = intVal(dconnlimit->arg); |
743 | 0 | if (connlimit < -1) |
744 | 0 | ereport(ERROR, |
745 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
746 | 0 | errmsg("invalid connection limit: %d", connlimit))); |
747 | 0 | } |
748 | 0 | if (dvalidUntil) |
749 | 0 | validUntil = strVal(dvalidUntil->arg); |
750 | | |
751 | | /* |
752 | | * Scan the pg_authid relation to be certain the user exists. |
753 | | */ |
754 | 0 | pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock); |
755 | 0 | pg_authid_dsc = RelationGetDescr(pg_authid_rel); |
756 | |
|
757 | 0 | tuple = get_rolespec_tuple(stmt->role); |
758 | 0 | authform = (Form_pg_authid) GETSTRUCT(tuple); |
759 | 0 | rolename = pstrdup(NameStr(authform->rolname)); |
760 | 0 | roleid = authform->oid; |
761 | | |
762 | | /* To mess with a superuser in any way you gotta be superuser. */ |
763 | 0 | if (!superuser() && authform->rolsuper) |
764 | 0 | ereport(ERROR, |
765 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
766 | 0 | errmsg("permission denied to alter role"), |
767 | 0 | errdetail("Only roles with the %s attribute may alter roles with the %s attribute.", |
768 | 0 | "SUPERUSER", "SUPERUSER"))); |
769 | 0 | if (!superuser() && dissuper) |
770 | 0 | ereport(ERROR, |
771 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
772 | 0 | errmsg("permission denied to alter role"), |
773 | 0 | errdetail("Only roles with the %s attribute may change the %s attribute.", |
774 | 0 | "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 | 0 | if (!have_createrole_privilege() || |
781 | 0 | !is_admin_of_role(GetUserId(), roleid)) |
782 | 0 | { |
783 | | /* things an unprivileged user certainly can't do */ |
784 | 0 | if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit || |
785 | 0 | dvalidUntil || disreplication || dbypassRLS) |
786 | 0 | ereport(ERROR, |
787 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
788 | 0 | errmsg("permission denied to alter role"), |
789 | 0 | errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.", |
790 | 0 | "CREATEROLE", "ADMIN", rolename))); |
791 | | |
792 | | /* an unprivileged user can change their own password */ |
793 | 0 | if (dpassword && roleid != currentUserId) |
794 | 0 | ereport(ERROR, |
795 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
796 | 0 | errmsg("permission denied to alter role"), |
797 | 0 | errdetail("To change another role's password, the current user must have the %s attribute and the %s option on the role.", |
798 | 0 | "CREATEROLE", "ADMIN"))); |
799 | 0 | } |
800 | 0 | else if (!superuser()) |
801 | 0 | { |
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 | 0 | if (dcreatedb && !have_createdb_privilege()) |
808 | 0 | ereport(ERROR, |
809 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
810 | 0 | errmsg("permission denied to alter role"), |
811 | 0 | errdetail("Only roles with the %s attribute may change the %s attribute.", |
812 | 0 | "CREATEDB", "CREATEDB"))); |
813 | 0 | if (disreplication && !has_rolreplication(currentUserId)) |
814 | 0 | ereport(ERROR, |
815 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
816 | 0 | errmsg("permission denied to alter role"), |
817 | 0 | errdetail("Only roles with the %s attribute may change the %s attribute.", |
818 | 0 | "REPLICATION", "REPLICATION"))); |
819 | 0 | if (dbypassRLS && !has_bypassrls_privilege(currentUserId)) |
820 | 0 | ereport(ERROR, |
821 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
822 | 0 | errmsg("permission denied to alter role"), |
823 | 0 | errdetail("Only roles with the %s attribute may change the %s attribute.", |
824 | 0 | "BYPASSRLS", "BYPASSRLS"))); |
825 | 0 | } |
826 | | |
827 | | /* To add or drop members, you need ADMIN OPTION. */ |
828 | 0 | if (drolemembers && !has_admin_privs_of_role(currentUserId, roleid)) |
829 | 0 | ereport(ERROR, |
830 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
831 | 0 | errmsg("permission denied to alter role"), |
832 | 0 | errdetail("Only roles with the %s option on role \"%s\" may add or drop members.", |
833 | 0 | "ADMIN", rolename))); |
834 | | |
835 | | /* Convert validuntil to internal form */ |
836 | 0 | if (dvalidUntil) |
837 | 0 | { |
838 | 0 | validUntil_datum = DirectFunctionCall3(timestamptz_in, |
839 | 0 | CStringGetDatum(validUntil), |
840 | 0 | ObjectIdGetDatum(InvalidOid), |
841 | 0 | Int32GetDatum(-1)); |
842 | 0 | validUntil_null = false; |
843 | 0 | } |
844 | 0 | else |
845 | 0 | { |
846 | | /* fetch existing setting in case hook needs it */ |
847 | 0 | validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple, |
848 | 0 | Anum_pg_authid_rolvaliduntil, |
849 | 0 | &validUntil_null); |
850 | 0 | } |
851 | | |
852 | | /* |
853 | | * Call the password checking hook if there is one defined |
854 | | */ |
855 | 0 | if (check_password_hook && password) |
856 | 0 | (*check_password_hook) (rolename, |
857 | 0 | password, |
858 | 0 | get_password_type(password), |
859 | 0 | validUntil_datum, |
860 | 0 | validUntil_null); |
861 | | |
862 | | /* |
863 | | * Build an updated tuple, perusing the information just obtained |
864 | | */ |
865 | | |
866 | | /* |
867 | | * issuper/createrole/etc |
868 | | */ |
869 | 0 | if (dissuper) |
870 | 0 | { |
871 | 0 | bool should_be_super = boolVal(dissuper->arg); |
872 | |
|
873 | 0 | if (!should_be_super && roleid == BOOTSTRAP_SUPERUSERID) |
874 | 0 | ereport(ERROR, |
875 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
876 | 0 | errmsg("permission denied to alter role"), |
877 | 0 | errdetail("The bootstrap superuser must have the %s attribute.", |
878 | 0 | "SUPERUSER"))); |
879 | | |
880 | 0 | new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super); |
881 | 0 | new_record_repl[Anum_pg_authid_rolsuper - 1] = true; |
882 | 0 | } |
883 | | |
884 | 0 | if (dinherit) |
885 | 0 | { |
886 | 0 | new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg)); |
887 | 0 | new_record_repl[Anum_pg_authid_rolinherit - 1] = true; |
888 | 0 | } |
889 | |
|
890 | 0 | if (dcreaterole) |
891 | 0 | { |
892 | 0 | new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg)); |
893 | 0 | new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true; |
894 | 0 | } |
895 | |
|
896 | 0 | if (dcreatedb) |
897 | 0 | { |
898 | 0 | new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg)); |
899 | 0 | new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true; |
900 | 0 | } |
901 | |
|
902 | 0 | if (dcanlogin) |
903 | 0 | { |
904 | 0 | new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg)); |
905 | 0 | new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true; |
906 | 0 | } |
907 | |
|
908 | 0 | if (disreplication) |
909 | 0 | { |
910 | 0 | new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg)); |
911 | 0 | new_record_repl[Anum_pg_authid_rolreplication - 1] = true; |
912 | 0 | } |
913 | |
|
914 | 0 | if (dconnlimit) |
915 | 0 | { |
916 | 0 | new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); |
917 | 0 | new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true; |
918 | 0 | } |
919 | | |
920 | | /* password */ |
921 | 0 | if (password) |
922 | 0 | { |
923 | 0 | char *shadow_pass; |
924 | 0 | const char *logdetail = NULL; |
925 | | |
926 | | /* Like in CREATE USER, don't allow an empty password. */ |
927 | 0 | if (password[0] == '\0' || |
928 | 0 | plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK) |
929 | 0 | { |
930 | 0 | ereport(NOTICE, |
931 | 0 | (errmsg("empty string is not a valid password, clearing password"))); |
932 | 0 | new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; |
933 | 0 | } |
934 | 0 | else |
935 | 0 | { |
936 | | /* Encrypt the password to the requested format. */ |
937 | 0 | shadow_pass = encrypt_password(Password_encryption, rolename, |
938 | 0 | password); |
939 | 0 | new_record[Anum_pg_authid_rolpassword - 1] = |
940 | 0 | CStringGetTextDatum(shadow_pass); |
941 | 0 | } |
942 | 0 | new_record_repl[Anum_pg_authid_rolpassword - 1] = true; |
943 | 0 | } |
944 | | |
945 | | /* unset password */ |
946 | 0 | if (dpassword && dpassword->arg == NULL) |
947 | 0 | { |
948 | 0 | new_record_repl[Anum_pg_authid_rolpassword - 1] = true; |
949 | 0 | new_record_nulls[Anum_pg_authid_rolpassword - 1] = true; |
950 | 0 | } |
951 | | |
952 | | /* valid until */ |
953 | 0 | new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum; |
954 | 0 | new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; |
955 | 0 | new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true; |
956 | |
|
957 | 0 | if (dbypassRLS) |
958 | 0 | { |
959 | 0 | new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg)); |
960 | 0 | new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true; |
961 | 0 | } |
962 | |
|
963 | 0 | new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, |
964 | 0 | new_record_nulls, new_record_repl); |
965 | 0 | CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple); |
966 | |
|
967 | 0 | InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); |
968 | |
|
969 | 0 | ReleaseSysCache(tuple); |
970 | 0 | heap_freetuple(new_tuple); |
971 | |
|
972 | 0 | InitGrantRoleOptions(&popt); |
973 | | |
974 | | /* |
975 | | * Advance command counter so we can see new record; else tests in |
976 | | * AddRoleMems may fail. |
977 | | */ |
978 | 0 | if (drolemembers) |
979 | 0 | { |
980 | 0 | List *rolemembers = (List *) drolemembers->arg; |
981 | |
|
982 | 0 | CommandCounterIncrement(); |
983 | |
|
984 | 0 | if (stmt->action == +1) /* add members to role */ |
985 | 0 | AddRoleMems(currentUserId, rolename, roleid, |
986 | 0 | rolemembers, roleSpecsToIds(rolemembers), |
987 | 0 | InvalidOid, &popt); |
988 | 0 | else if (stmt->action == -1) /* drop members from role */ |
989 | 0 | DelRoleMems(currentUserId, rolename, roleid, |
990 | 0 | rolemembers, roleSpecsToIds(rolemembers), |
991 | 0 | InvalidOid, &popt, DROP_RESTRICT); |
992 | 0 | } |
993 | | |
994 | | /* |
995 | | * Close pg_authid, but keep lock till commit. |
996 | | */ |
997 | 0 | table_close(pg_authid_rel, NoLock); |
998 | |
|
999 | 0 | return roleid; |
1000 | 0 | } |
1001 | | |
1002 | | |
1003 | | /* |
1004 | | * ALTER ROLE ... SET |
1005 | | */ |
1006 | | Oid |
1007 | | AlterRoleSet(AlterRoleSetStmt *stmt) |
1008 | 0 | { |
1009 | 0 | HeapTuple roletuple; |
1010 | 0 | Form_pg_authid roleform; |
1011 | 0 | Oid databaseid = InvalidOid; |
1012 | 0 | Oid roleid = InvalidOid; |
1013 | |
|
1014 | 0 | if (stmt->role) |
1015 | 0 | { |
1016 | 0 | check_rolespec_name(stmt->role, |
1017 | 0 | _("Cannot alter reserved roles.")); |
1018 | |
|
1019 | 0 | roletuple = get_rolespec_tuple(stmt->role); |
1020 | 0 | roleform = (Form_pg_authid) GETSTRUCT(roletuple); |
1021 | 0 | 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 | 0 | 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 | 0 | if (roleform->rolsuper) |
1035 | 0 | { |
1036 | 0 | if (!superuser()) |
1037 | 0 | ereport(ERROR, |
1038 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1039 | 0 | errmsg("permission denied to alter role"), |
1040 | 0 | errdetail("Only roles with the %s attribute may alter roles with the %s attribute.", |
1041 | 0 | "SUPERUSER", "SUPERUSER"))); |
1042 | 0 | } |
1043 | 0 | else |
1044 | 0 | { |
1045 | 0 | if ((!have_createrole_privilege() || |
1046 | 0 | !is_admin_of_role(GetUserId(), roleid)) |
1047 | 0 | && roleid != GetUserId()) |
1048 | 0 | ereport(ERROR, |
1049 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1050 | 0 | errmsg("permission denied to alter role"), |
1051 | 0 | errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.", |
1052 | 0 | "CREATEROLE", "ADMIN", NameStr(roleform->rolname)))); |
1053 | 0 | } |
1054 | | |
1055 | 0 | ReleaseSysCache(roletuple); |
1056 | 0 | } |
1057 | | |
1058 | | /* look up and lock the database, if specified */ |
1059 | 0 | if (stmt->database != NULL) |
1060 | 0 | { |
1061 | 0 | databaseid = get_database_oid(stmt->database, false); |
1062 | 0 | shdepLockAndCheckObject(DatabaseRelationId, databaseid); |
1063 | |
|
1064 | 0 | if (!stmt->role) |
1065 | 0 | { |
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 | 0 | } |
1074 | 0 | } |
1075 | |
|
1076 | 0 | if (!stmt->role && !stmt->database) |
1077 | 0 | { |
1078 | | /* Must be superuser to alter settings globally. */ |
1079 | 0 | if (!superuser()) |
1080 | 0 | ereport(ERROR, |
1081 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1082 | 0 | errmsg("permission denied to alter setting"), |
1083 | 0 | errdetail("Only roles with the %s attribute may alter settings globally.", |
1084 | 0 | "SUPERUSER"))); |
1085 | 0 | } |
1086 | | |
1087 | 0 | AlterSetting(databaseid, roleid, stmt->setstmt); |
1088 | |
|
1089 | 0 | return roleid; |
1090 | 0 | } |
1091 | | |
1092 | | |
1093 | | /* |
1094 | | * DROP ROLE |
1095 | | */ |
1096 | | void |
1097 | | DropRole(DropRoleStmt *stmt) |
1098 | 0 | { |
1099 | 0 | Relation pg_authid_rel, |
1100 | 0 | pg_auth_members_rel; |
1101 | 0 | ListCell *item; |
1102 | 0 | List *role_oids = NIL; |
1103 | |
|
1104 | 0 | if (!have_createrole_privilege()) |
1105 | 0 | ereport(ERROR, |
1106 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1107 | 0 | errmsg("permission denied to drop role"), |
1108 | 0 | errdetail("Only roles with the %s attribute and the %s option on the target roles may drop roles.", |
1109 | 0 | "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 | 0 | pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock); |
1116 | 0 | pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock); |
1117 | |
|
1118 | 0 | foreach(item, stmt->roles) |
1119 | 0 | { |
1120 | 0 | RoleSpec *rolspec = lfirst(item); |
1121 | 0 | char *role; |
1122 | 0 | HeapTuple tuple, |
1123 | 0 | tmp_tuple; |
1124 | 0 | Form_pg_authid roleform; |
1125 | 0 | ScanKeyData scankey; |
1126 | 0 | SysScanDesc sscan; |
1127 | 0 | Oid roleid; |
1128 | |
|
1129 | 0 | if (rolspec->roletype != ROLESPEC_CSTRING) |
1130 | 0 | ereport(ERROR, |
1131 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1132 | 0 | errmsg("cannot use special role specifier in DROP ROLE"))); |
1133 | 0 | role = rolspec->rolename; |
1134 | |
|
1135 | 0 | tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role)); |
1136 | 0 | if (!HeapTupleIsValid(tuple)) |
1137 | 0 | { |
1138 | 0 | if (!stmt->missing_ok) |
1139 | 0 | { |
1140 | 0 | ereport(ERROR, |
1141 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1142 | 0 | errmsg("role \"%s\" does not exist", role))); |
1143 | 0 | } |
1144 | 0 | else |
1145 | 0 | { |
1146 | 0 | ereport(NOTICE, |
1147 | 0 | (errmsg("role \"%s\" does not exist, skipping", |
1148 | 0 | role))); |
1149 | 0 | } |
1150 | | |
1151 | 0 | continue; |
1152 | 0 | } |
1153 | | |
1154 | 0 | roleform = (Form_pg_authid) GETSTRUCT(tuple); |
1155 | 0 | roleid = roleform->oid; |
1156 | |
|
1157 | 0 | if (roleid == GetUserId()) |
1158 | 0 | ereport(ERROR, |
1159 | 0 | (errcode(ERRCODE_OBJECT_IN_USE), |
1160 | 0 | errmsg("current user cannot be dropped"))); |
1161 | 0 | if (roleid == GetOuterUserId()) |
1162 | 0 | ereport(ERROR, |
1163 | 0 | (errcode(ERRCODE_OBJECT_IN_USE), |
1164 | 0 | errmsg("current user cannot be dropped"))); |
1165 | 0 | if (roleid == GetSessionUserId()) |
1166 | 0 | ereport(ERROR, |
1167 | 0 | (errcode(ERRCODE_OBJECT_IN_USE), |
1168 | 0 | 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 | 0 | if (roleform->rolsuper && !superuser()) |
1176 | 0 | ereport(ERROR, |
1177 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1178 | 0 | errmsg("permission denied to drop role"), |
1179 | 0 | errdetail("Only roles with the %s attribute may drop roles with the %s attribute.", |
1180 | 0 | "SUPERUSER", "SUPERUSER"))); |
1181 | 0 | if (!is_admin_of_role(GetUserId(), roleid)) |
1182 | 0 | ereport(ERROR, |
1183 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1184 | 0 | errmsg("permission denied to drop role"), |
1185 | 0 | errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may drop this role.", |
1186 | 0 | "CREATEROLE", "ADMIN", NameStr(roleform->rolname)))); |
1187 | | |
1188 | | /* DROP hook for the role being removed */ |
1189 | 0 | InvokeObjectDropHook(AuthIdRelationId, roleid, 0); |
1190 | | |
1191 | | /* Don't leak the syscache tuple */ |
1192 | 0 | 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 | 0 | 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 | 0 | ScanKeyInit(&scankey, |
1218 | 0 | Anum_pg_auth_members_roleid, |
1219 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
1220 | 0 | ObjectIdGetDatum(roleid)); |
1221 | |
|
1222 | 0 | sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId, |
1223 | 0 | true, NULL, 1, &scankey); |
1224 | |
|
1225 | 0 | while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan))) |
1226 | 0 | { |
1227 | 0 | Form_pg_auth_members authmem_form; |
1228 | |
|
1229 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple); |
1230 | 0 | deleteSharedDependencyRecordsFor(AuthMemRelationId, |
1231 | 0 | authmem_form->oid, 0); |
1232 | 0 | CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self); |
1233 | 0 | } |
1234 | |
|
1235 | 0 | systable_endscan(sscan); |
1236 | |
|
1237 | 0 | ScanKeyInit(&scankey, |
1238 | 0 | Anum_pg_auth_members_member, |
1239 | 0 | BTEqualStrategyNumber, F_OIDEQ, |
1240 | 0 | ObjectIdGetDatum(roleid)); |
1241 | |
|
1242 | 0 | sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId, |
1243 | 0 | true, NULL, 1, &scankey); |
1244 | |
|
1245 | 0 | while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan))) |
1246 | 0 | { |
1247 | 0 | Form_pg_auth_members authmem_form; |
1248 | |
|
1249 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple); |
1250 | 0 | deleteSharedDependencyRecordsFor(AuthMemRelationId, |
1251 | 0 | authmem_form->oid, 0); |
1252 | 0 | CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self); |
1253 | 0 | } |
1254 | |
|
1255 | 0 | 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 | 0 | CommandCounterIncrement(); |
1267 | | |
1268 | | /* Looks tentatively OK, add it to the list if not there yet. */ |
1269 | 0 | role_oids = list_append_unique_oid(role_oids, roleid); |
1270 | 0 | } |
1271 | | |
1272 | | /* |
1273 | | * Second pass over the roles to be removed. |
1274 | | */ |
1275 | 0 | foreach(item, role_oids) |
1276 | 0 | { |
1277 | 0 | Oid roleid = lfirst_oid(item); |
1278 | 0 | HeapTuple tuple; |
1279 | 0 | Form_pg_authid roleform; |
1280 | 0 | char *detail; |
1281 | 0 | 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 | 0 | tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); |
1291 | 0 | if (!HeapTupleIsValid(tuple)) |
1292 | 0 | elog(ERROR, "could not find tuple for role %u", roleid); |
1293 | 0 | 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 | 0 | if (checkSharedDependencies(AuthIdRelationId, roleid, |
1303 | 0 | &detail, &detail_log)) |
1304 | 0 | ereport(ERROR, |
1305 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
1306 | 0 | errmsg("role \"%s\" cannot be dropped because some objects depend on it", |
1307 | 0 | NameStr(roleform->rolname)), |
1308 | 0 | errdetail_internal("%s", detail), |
1309 | 0 | errdetail_log("%s", detail_log))); |
1310 | | |
1311 | | /* |
1312 | | * Remove the role from the pg_authid table |
1313 | | */ |
1314 | 0 | CatalogTupleDelete(pg_authid_rel, &tuple->t_self); |
1315 | |
|
1316 | 0 | ReleaseSysCache(tuple); |
1317 | | |
1318 | | /* |
1319 | | * Remove any comments or security labels on this role. |
1320 | | */ |
1321 | 0 | DeleteSharedComments(roleid, AuthIdRelationId); |
1322 | 0 | DeleteSharedSecurityLabel(roleid, AuthIdRelationId); |
1323 | | |
1324 | | /* |
1325 | | * Remove settings for this role. |
1326 | | */ |
1327 | 0 | DropSetting(InvalidOid, roleid); |
1328 | 0 | } |
1329 | | |
1330 | | /* |
1331 | | * Now we can clean up; but keep locks until commit. |
1332 | | */ |
1333 | 0 | table_close(pg_auth_members_rel, NoLock); |
1334 | 0 | table_close(pg_authid_rel, NoLock); |
1335 | 0 | } |
1336 | | |
1337 | | /* |
1338 | | * Rename role |
1339 | | */ |
1340 | | ObjectAddress |
1341 | | RenameRole(const char *oldname, const char *newname) |
1342 | 0 | { |
1343 | 0 | HeapTuple oldtuple, |
1344 | 0 | newtuple; |
1345 | 0 | TupleDesc dsc; |
1346 | 0 | Relation rel; |
1347 | 0 | Datum datum; |
1348 | 0 | bool isnull; |
1349 | 0 | Datum repl_val[Natts_pg_authid]; |
1350 | 0 | bool repl_null[Natts_pg_authid]; |
1351 | 0 | bool repl_repl[Natts_pg_authid]; |
1352 | 0 | int i; |
1353 | 0 | Oid roleid; |
1354 | 0 | ObjectAddress address; |
1355 | 0 | Form_pg_authid authform; |
1356 | | |
1357 | | /* Report error if name has \n or \r character. */ |
1358 | 0 | if (strpbrk(newname, "\n\r")) |
1359 | 0 | ereport(ERROR, |
1360 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1361 | 0 | errmsg("role name \"%s\" contains a newline or carriage return character", newname))); |
1362 | | |
1363 | 0 | rel = table_open(AuthIdRelationId, RowExclusiveLock); |
1364 | 0 | dsc = RelationGetDescr(rel); |
1365 | |
|
1366 | 0 | oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname)); |
1367 | 0 | if (!HeapTupleIsValid(oldtuple)) |
1368 | 0 | ereport(ERROR, |
1369 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
1370 | 0 | 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 | 0 | authform = (Form_pg_authid) GETSTRUCT(oldtuple); |
1381 | 0 | roleid = authform->oid; |
1382 | |
|
1383 | 0 | if (roleid == GetSessionUserId()) |
1384 | 0 | ereport(ERROR, |
1385 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1386 | 0 | errmsg("session user cannot be renamed"))); |
1387 | 0 | if (roleid == GetOuterUserId()) |
1388 | 0 | ereport(ERROR, |
1389 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1390 | 0 | 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 | 0 | if (IsReservedName(NameStr(authform->rolname))) |
1397 | 0 | ereport(ERROR, |
1398 | 0 | (errcode(ERRCODE_RESERVED_NAME), |
1399 | 0 | errmsg("role name \"%s\" is reserved", |
1400 | 0 | NameStr(authform->rolname)), |
1401 | 0 | errdetail("Role names starting with \"pg_\" are reserved."))); |
1402 | | |
1403 | 0 | if (IsReservedName(newname)) |
1404 | 0 | ereport(ERROR, |
1405 | 0 | (errcode(ERRCODE_RESERVED_NAME), |
1406 | 0 | errmsg("role name \"%s\" is reserved", |
1407 | 0 | newname), |
1408 | 0 | 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 | 0 | if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname))) |
1421 | 0 | ereport(ERROR, |
1422 | 0 | (errcode(ERRCODE_DUPLICATE_OBJECT), |
1423 | 0 | 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 | 0 | if (authform->rolsuper) |
1430 | 0 | { |
1431 | 0 | if (!superuser()) |
1432 | 0 | ereport(ERROR, |
1433 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1434 | 0 | errmsg("permission denied to rename role"), |
1435 | 0 | errdetail("Only roles with the %s attribute may rename roles with the %s attribute.", |
1436 | 0 | "SUPERUSER", "SUPERUSER"))); |
1437 | 0 | } |
1438 | 0 | else |
1439 | 0 | { |
1440 | 0 | if (!have_createrole_privilege() || |
1441 | 0 | !is_admin_of_role(GetUserId(), roleid)) |
1442 | 0 | ereport(ERROR, |
1443 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1444 | 0 | errmsg("permission denied to rename role"), |
1445 | 0 | errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may rename this role.", |
1446 | 0 | "CREATEROLE", "ADMIN", NameStr(authform->rolname)))); |
1447 | 0 | } |
1448 | | |
1449 | | /* OK, construct the modified tuple */ |
1450 | 0 | for (i = 0; i < Natts_pg_authid; i++) |
1451 | 0 | repl_repl[i] = false; |
1452 | |
|
1453 | 0 | repl_repl[Anum_pg_authid_rolname - 1] = true; |
1454 | 0 | repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein, |
1455 | 0 | CStringGetDatum(newname)); |
1456 | 0 | repl_null[Anum_pg_authid_rolname - 1] = false; |
1457 | |
|
1458 | 0 | datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull); |
1459 | |
|
1460 | 0 | if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5) |
1461 | 0 | { |
1462 | | /* MD5 uses the username as salt, so just clear it on a rename */ |
1463 | 0 | repl_repl[Anum_pg_authid_rolpassword - 1] = true; |
1464 | 0 | repl_null[Anum_pg_authid_rolpassword - 1] = true; |
1465 | |
|
1466 | 0 | ereport(NOTICE, |
1467 | 0 | (errmsg("MD5 password cleared because of role rename"))); |
1468 | 0 | } |
1469 | | |
1470 | 0 | newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl); |
1471 | 0 | CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple); |
1472 | |
|
1473 | 0 | InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); |
1474 | |
|
1475 | 0 | ObjectAddressSet(address, AuthIdRelationId, roleid); |
1476 | |
|
1477 | 0 | ReleaseSysCache(oldtuple); |
1478 | | |
1479 | | /* |
1480 | | * Close pg_authid, but keep lock till commit. |
1481 | | */ |
1482 | 0 | table_close(rel, NoLock); |
1483 | |
|
1484 | 0 | return address; |
1485 | 0 | } |
1486 | | |
1487 | | /* |
1488 | | * GrantRoleStmt |
1489 | | * |
1490 | | * Grant/Revoke roles to/from roles |
1491 | | */ |
1492 | | void |
1493 | | GrantRole(ParseState *pstate, GrantRoleStmt *stmt) |
1494 | 0 | { |
1495 | 0 | Relation pg_authid_rel; |
1496 | 0 | Oid grantor; |
1497 | 0 | List *grantee_ids; |
1498 | 0 | ListCell *item; |
1499 | 0 | GrantRoleOptions popt; |
1500 | 0 | Oid currentUserId = GetUserId(); |
1501 | | |
1502 | | /* Parse options list. */ |
1503 | 0 | InitGrantRoleOptions(&popt); |
1504 | 0 | foreach(item, stmt->opt) |
1505 | 0 | { |
1506 | 0 | DefElem *opt = (DefElem *) lfirst(item); |
1507 | 0 | char *optval = defGetString(opt); |
1508 | |
|
1509 | 0 | if (strcmp(opt->defname, "admin") == 0) |
1510 | 0 | { |
1511 | 0 | popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN; |
1512 | |
|
1513 | 0 | if (parse_bool(optval, &popt.admin)) |
1514 | 0 | continue; |
1515 | 0 | } |
1516 | 0 | else if (strcmp(opt->defname, "inherit") == 0) |
1517 | 0 | { |
1518 | 0 | popt.specified |= GRANT_ROLE_SPECIFIED_INHERIT; |
1519 | 0 | if (parse_bool(optval, &popt.inherit)) |
1520 | 0 | continue; |
1521 | 0 | } |
1522 | 0 | else if (strcmp(opt->defname, "set") == 0) |
1523 | 0 | { |
1524 | 0 | popt.specified |= GRANT_ROLE_SPECIFIED_SET; |
1525 | 0 | if (parse_bool(optval, &popt.set)) |
1526 | 0 | continue; |
1527 | 0 | } |
1528 | 0 | else |
1529 | 0 | ereport(ERROR, |
1530 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
1531 | 0 | errmsg("unrecognized role option \"%s\"", opt->defname), |
1532 | 0 | parser_errposition(pstate, opt->location)); |
1533 | | |
1534 | 0 | ereport(ERROR, |
1535 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1536 | 0 | errmsg("unrecognized value for role option \"%s\": \"%s\"", |
1537 | 0 | opt->defname, optval), |
1538 | 0 | parser_errposition(pstate, opt->location))); |
1539 | 0 | } |
1540 | | |
1541 | | /* Lookup OID of grantor, if specified. */ |
1542 | 0 | if (stmt->grantor) |
1543 | 0 | grantor = get_rolespec_oid(stmt->grantor, false); |
1544 | 0 | else |
1545 | 0 | grantor = InvalidOid; |
1546 | |
|
1547 | 0 | grantee_ids = roleSpecsToIds(stmt->grantee_roles); |
1548 | | |
1549 | | /* AccessShareLock is enough since we aren't modifying pg_authid */ |
1550 | 0 | 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 | 0 | foreach(item, stmt->granted_roles) |
1560 | 0 | { |
1561 | 0 | AccessPriv *priv = (AccessPriv *) lfirst(item); |
1562 | 0 | char *rolename = priv->priv_name; |
1563 | 0 | Oid roleid; |
1564 | | |
1565 | | /* Must reject priv(columns) and ALL PRIVILEGES(columns) */ |
1566 | 0 | if (rolename == NULL || priv->cols != NIL) |
1567 | 0 | ereport(ERROR, |
1568 | 0 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1569 | 0 | errmsg("column names cannot be included in GRANT/REVOKE ROLE"))); |
1570 | | |
1571 | 0 | roleid = get_role_oid(rolename, false); |
1572 | 0 | check_role_membership_authorization(currentUserId, |
1573 | 0 | roleid, stmt->is_grant); |
1574 | 0 | if (stmt->is_grant) |
1575 | 0 | AddRoleMems(currentUserId, rolename, roleid, |
1576 | 0 | stmt->grantee_roles, grantee_ids, |
1577 | 0 | grantor, &popt); |
1578 | 0 | else |
1579 | 0 | DelRoleMems(currentUserId, rolename, roleid, |
1580 | 0 | stmt->grantee_roles, grantee_ids, |
1581 | 0 | grantor, &popt, stmt->behavior); |
1582 | 0 | } |
1583 | | |
1584 | | /* |
1585 | | * Close pg_authid, but keep lock till commit. |
1586 | | */ |
1587 | 0 | table_close(pg_authid_rel, NoLock); |
1588 | 0 | } |
1589 | | |
1590 | | /* |
1591 | | * DropOwnedObjects |
1592 | | * |
1593 | | * Drop the objects owned by a given list of roles. |
1594 | | */ |
1595 | | void |
1596 | | DropOwnedObjects(DropOwnedStmt *stmt) |
1597 | 0 | { |
1598 | 0 | List *role_ids = roleSpecsToIds(stmt->roles); |
1599 | 0 | ListCell *cell; |
1600 | | |
1601 | | /* Check privileges */ |
1602 | 0 | foreach(cell, role_ids) |
1603 | 0 | { |
1604 | 0 | Oid roleid = lfirst_oid(cell); |
1605 | |
|
1606 | 0 | if (!has_privs_of_role(GetUserId(), roleid)) |
1607 | 0 | ereport(ERROR, |
1608 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1609 | 0 | errmsg("permission denied to drop objects"), |
1610 | 0 | errdetail("Only roles with privileges of role \"%s\" may drop objects owned by it.", |
1611 | 0 | GetUserNameFromId(roleid, false)))); |
1612 | 0 | } |
1613 | | |
1614 | | /* Ok, do it */ |
1615 | 0 | shdepDropOwned(role_ids, stmt->behavior); |
1616 | 0 | } |
1617 | | |
1618 | | /* |
1619 | | * ReassignOwnedObjects |
1620 | | * |
1621 | | * Give the objects owned by a given list of roles away to another user. |
1622 | | */ |
1623 | | void |
1624 | | ReassignOwnedObjects(ReassignOwnedStmt *stmt) |
1625 | 0 | { |
1626 | 0 | List *role_ids = roleSpecsToIds(stmt->roles); |
1627 | 0 | ListCell *cell; |
1628 | 0 | Oid newrole; |
1629 | | |
1630 | | /* Check privileges */ |
1631 | 0 | foreach(cell, role_ids) |
1632 | 0 | { |
1633 | 0 | Oid roleid = lfirst_oid(cell); |
1634 | |
|
1635 | 0 | if (!has_privs_of_role(GetUserId(), roleid)) |
1636 | 0 | ereport(ERROR, |
1637 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1638 | 0 | errmsg("permission denied to reassign objects"), |
1639 | 0 | errdetail("Only roles with privileges of role \"%s\" may reassign objects owned by it.", |
1640 | 0 | GetUserNameFromId(roleid, false)))); |
1641 | 0 | } |
1642 | | |
1643 | | /* Must have privileges on the receiving side too */ |
1644 | 0 | newrole = get_rolespec_oid(stmt->newrole, false); |
1645 | |
|
1646 | 0 | if (!has_privs_of_role(GetUserId(), newrole)) |
1647 | 0 | ereport(ERROR, |
1648 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
1649 | 0 | errmsg("permission denied to reassign objects"), |
1650 | 0 | errdetail("Only roles with privileges of role \"%s\" may reassign objects to it.", |
1651 | 0 | GetUserNameFromId(newrole, false)))); |
1652 | | |
1653 | | /* Ok, do it */ |
1654 | 0 | shdepReassignOwned(role_ids, newrole); |
1655 | 0 | } |
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 | | roleSpecsToIds(List *memberNames) |
1666 | 0 | { |
1667 | 0 | List *result = NIL; |
1668 | 0 | ListCell *l; |
1669 | |
|
1670 | 0 | foreach(l, memberNames) |
1671 | 0 | { |
1672 | 0 | RoleSpec *rolespec = lfirst_node(RoleSpec, l); |
1673 | 0 | Oid roleid; |
1674 | |
|
1675 | 0 | roleid = get_rolespec_oid(rolespec, false); |
1676 | 0 | result = lappend_oid(result, roleid); |
1677 | 0 | } |
1678 | 0 | return result; |
1679 | 0 | } |
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 | | AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid, |
1695 | | List *memberSpecs, List *memberIds, |
1696 | | Oid grantorId, GrantRoleOptions *popt) |
1697 | 0 | { |
1698 | 0 | Relation pg_authmem_rel; |
1699 | 0 | TupleDesc pg_authmem_dsc; |
1700 | 0 | ListCell *specitem; |
1701 | 0 | ListCell *iditem; |
1702 | |
|
1703 | 0 | Assert(list_length(memberSpecs) == list_length(memberIds)); |
1704 | | |
1705 | | /* Validate grantor (and resolve implicit grantor if not specified). */ |
1706 | 0 | grantorId = check_role_grantor(currentUserId, roleid, grantorId, true); |
1707 | |
|
1708 | 0 | pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock); |
1709 | 0 | 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 | 0 | LockSharedObject(AuthIdRelationId, roleid, 0, |
1717 | 0 | ShareUpdateExclusiveLock); |
1718 | | |
1719 | | /* Preliminary sanity checks. */ |
1720 | 0 | forboth(specitem, memberSpecs, iditem, memberIds) |
1721 | 0 | { |
1722 | 0 | RoleSpec *memberRole = lfirst_node(RoleSpec, specitem); |
1723 | 0 | 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 | 0 | if (memberid == ROLE_PG_DATABASE_OWNER) |
1745 | 0 | ereport(ERROR, |
1746 | 0 | errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1747 | 0 | errmsg("role \"%s\" cannot be a member of any role", |
1748 | 0 | 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 | 0 | if (is_member_of_role_nosuper(roleid, memberid)) |
1758 | 0 | ereport(ERROR, |
1759 | 0 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1760 | 0 | errmsg("role \"%s\" is a member of role \"%s\"", |
1761 | 0 | rolename, get_rolespec_name(memberRole)))); |
1762 | 0 | } |
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 | 0 | if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID) |
1779 | 0 | { |
1780 | 0 | CatCList *memlist; |
1781 | 0 | RevokeRoleGrantAction *actions; |
1782 | 0 | int i; |
1783 | | |
1784 | | /* Get the list of members for this role. */ |
1785 | 0 | memlist = SearchSysCacheList1(AUTHMEMROLEMEM, |
1786 | 0 | 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 | 0 | actions = initialize_revoke_actions(memlist); |
1793 | 0 | foreach(iditem, memberIds) |
1794 | 0 | { |
1795 | 0 | Oid memberid = lfirst_oid(iditem); |
1796 | |
|
1797 | 0 | if (memberid == BOOTSTRAP_SUPERUSERID) |
1798 | 0 | ereport(ERROR, |
1799 | 0 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1800 | 0 | errmsg("%s option cannot be granted back to your own grantor", |
1801 | 0 | "ADMIN"))); |
1802 | 0 | plan_member_revoke(memlist, actions, memberid); |
1803 | 0 | } |
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 | 0 | for (i = 0; i < memlist->n_members; ++i) |
1811 | 0 | { |
1812 | 0 | HeapTuple authmem_tuple; |
1813 | 0 | Form_pg_auth_members authmem_form; |
1814 | |
|
1815 | 0 | authmem_tuple = &memlist->members[i]->tuple; |
1816 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple); |
1817 | |
|
1818 | 0 | if (actions[i] == RRG_NOOP && |
1819 | 0 | authmem_form->member == grantorId && |
1820 | 0 | authmem_form->admin_option) |
1821 | 0 | break; |
1822 | 0 | } |
1823 | 0 | if (i >= memlist->n_members) |
1824 | 0 | ereport(ERROR, |
1825 | 0 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1826 | 0 | errmsg("%s option cannot be granted back to your own grantor", |
1827 | 0 | "ADMIN"))); |
1828 | | |
1829 | 0 | ReleaseSysCacheList(memlist); |
1830 | 0 | } |
1831 | | |
1832 | | /* Now perform the catalog updates. */ |
1833 | 0 | forboth(specitem, memberSpecs, iditem, memberIds) |
1834 | 0 | { |
1835 | 0 | RoleSpec *memberRole = lfirst_node(RoleSpec, specitem); |
1836 | 0 | Oid memberid = lfirst_oid(iditem); |
1837 | 0 | HeapTuple authmem_tuple; |
1838 | 0 | HeapTuple tuple; |
1839 | 0 | Datum new_record[Natts_pg_auth_members] = {0}; |
1840 | 0 | bool new_record_nulls[Natts_pg_auth_members] = {0}; |
1841 | 0 | bool new_record_repl[Natts_pg_auth_members] = {0}; |
1842 | | |
1843 | | /* Common initialization for possible insert or update */ |
1844 | 0 | new_record[Anum_pg_auth_members_roleid - 1] = |
1845 | 0 | ObjectIdGetDatum(roleid); |
1846 | 0 | new_record[Anum_pg_auth_members_member - 1] = |
1847 | 0 | ObjectIdGetDatum(memberid); |
1848 | 0 | new_record[Anum_pg_auth_members_grantor - 1] = |
1849 | 0 | ObjectIdGetDatum(grantorId); |
1850 | | |
1851 | | /* Find any existing tuple */ |
1852 | 0 | authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM, |
1853 | 0 | ObjectIdGetDatum(roleid), |
1854 | 0 | ObjectIdGetDatum(memberid), |
1855 | 0 | 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 | 0 | if (HeapTupleIsValid(authmem_tuple)) |
1864 | 0 | { |
1865 | 0 | Form_pg_auth_members authmem_form; |
1866 | 0 | bool at_least_one_change = false; |
1867 | |
|
1868 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple); |
1869 | |
|
1870 | 0 | if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0 |
1871 | 0 | && authmem_form->admin_option != popt->admin) |
1872 | 0 | { |
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 | 0 | true; |
1877 | 0 | at_least_one_change = true; |
1878 | 0 | } |
1879 | |
|
1880 | 0 | if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0 |
1881 | 0 | && authmem_form->inherit_option != popt->inherit) |
1882 | 0 | { |
1883 | 0 | new_record[Anum_pg_auth_members_inherit_option - 1] = |
1884 | 0 | BoolGetDatum(popt->inherit); |
1885 | 0 | new_record_repl[Anum_pg_auth_members_inherit_option - 1] = |
1886 | 0 | true; |
1887 | 0 | at_least_one_change = true; |
1888 | 0 | } |
1889 | |
|
1890 | 0 | if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0 |
1891 | 0 | && authmem_form->set_option != popt->set) |
1892 | 0 | { |
1893 | 0 | new_record[Anum_pg_auth_members_set_option - 1] = |
1894 | 0 | BoolGetDatum(popt->set); |
1895 | 0 | new_record_repl[Anum_pg_auth_members_set_option - 1] = |
1896 | 0 | true; |
1897 | 0 | at_least_one_change = true; |
1898 | 0 | } |
1899 | |
|
1900 | 0 | if (!at_least_one_change) |
1901 | 0 | { |
1902 | 0 | ereport(NOTICE, |
1903 | 0 | (errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"", |
1904 | 0 | get_rolespec_name(memberRole), rolename, |
1905 | 0 | GetUserNameFromId(grantorId, false)))); |
1906 | 0 | ReleaseSysCache(authmem_tuple); |
1907 | 0 | continue; |
1908 | 0 | } |
1909 | | |
1910 | 0 | tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc, |
1911 | 0 | new_record, |
1912 | 0 | new_record_nulls, new_record_repl); |
1913 | 0 | CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple); |
1914 | |
|
1915 | 0 | ReleaseSysCache(authmem_tuple); |
1916 | 0 | } |
1917 | 0 | else |
1918 | 0 | { |
1919 | 0 | Oid objectId; |
1920 | 0 | 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 | 0 | new_record[Anum_pg_auth_members_admin_option - 1] = |
1928 | 0 | BoolGetDatum(popt->admin); |
1929 | 0 | new_record[Anum_pg_auth_members_set_option - 1] = |
1930 | 0 | 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 | 0 | if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0) |
1938 | 0 | new_record[Anum_pg_auth_members_inherit_option - 1] = |
1939 | 0 | BoolGetDatum(popt->inherit); |
1940 | 0 | else |
1941 | 0 | { |
1942 | 0 | HeapTuple mrtup; |
1943 | 0 | Form_pg_authid mrform; |
1944 | |
|
1945 | 0 | mrtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(memberid)); |
1946 | 0 | if (!HeapTupleIsValid(mrtup)) |
1947 | 0 | elog(ERROR, "cache lookup failed for role %u", memberid); |
1948 | 0 | mrform = (Form_pg_authid) GETSTRUCT(mrtup); |
1949 | 0 | new_record[Anum_pg_auth_members_inherit_option - 1] = |
1950 | 0 | BoolGetDatum(mrform->rolinherit); |
1951 | 0 | ReleaseSysCache(mrtup); |
1952 | 0 | } |
1953 | | |
1954 | | /* get an OID for the new row and insert it */ |
1955 | 0 | objectId = GetNewOidWithIndex(pg_authmem_rel, AuthMemOidIndexId, |
1956 | 0 | Anum_pg_auth_members_oid); |
1957 | 0 | new_record[Anum_pg_auth_members_oid - 1] = ObjectIdGetDatum(objectId); |
1958 | 0 | tuple = heap_form_tuple(pg_authmem_dsc, |
1959 | 0 | new_record, new_record_nulls); |
1960 | 0 | CatalogTupleInsert(pg_authmem_rel, tuple); |
1961 | | |
1962 | | /* updateAclDependencies wants to pfree array inputs */ |
1963 | 0 | newmembers[0] = grantorId; |
1964 | 0 | updateAclDependencies(AuthMemRelationId, objectId, |
1965 | 0 | 0, InvalidOid, |
1966 | 0 | 0, NULL, |
1967 | 0 | 1, newmembers); |
1968 | 0 | } |
1969 | | |
1970 | | /* CCI after each change, in case there are duplicates in list */ |
1971 | 0 | CommandCounterIncrement(); |
1972 | 0 | } |
1973 | | |
1974 | | /* |
1975 | | * Close pg_authmem, but keep lock till commit. |
1976 | | */ |
1977 | 0 | table_close(pg_authmem_rel, NoLock); |
1978 | 0 | } |
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 | | DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid, |
1993 | | List *memberSpecs, List *memberIds, |
1994 | | Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior) |
1995 | 0 | { |
1996 | 0 | Relation pg_authmem_rel; |
1997 | 0 | TupleDesc pg_authmem_dsc; |
1998 | 0 | ListCell *specitem; |
1999 | 0 | ListCell *iditem; |
2000 | 0 | CatCList *memlist; |
2001 | 0 | RevokeRoleGrantAction *actions; |
2002 | 0 | int i; |
2003 | |
|
2004 | 0 | Assert(list_length(memberSpecs) == list_length(memberIds)); |
2005 | | |
2006 | | /* Validate grantor (and resolve implicit grantor if not specified). */ |
2007 | 0 | grantorId = check_role_grantor(currentUserId, roleid, grantorId, false); |
2008 | |
|
2009 | 0 | pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock); |
2010 | 0 | 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 | 0 | LockSharedObject(AuthIdRelationId, roleid, 0, |
2018 | 0 | ShareUpdateExclusiveLock); |
2019 | |
|
2020 | 0 | memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid)); |
2021 | 0 | 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 | 0 | forboth(specitem, memberSpecs, iditem, memberIds) |
2030 | 0 | { |
2031 | 0 | RoleSpec *memberRole = lfirst(specitem); |
2032 | 0 | Oid memberid = lfirst_oid(iditem); |
2033 | |
|
2034 | 0 | if (!plan_single_revoke(memlist, actions, memberid, grantorId, |
2035 | 0 | popt, behavior)) |
2036 | 0 | { |
2037 | 0 | ereport(WARNING, |
2038 | 0 | (errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"", |
2039 | 0 | get_rolespec_name(memberRole), rolename, |
2040 | 0 | GetUserNameFromId(grantorId, false)))); |
2041 | 0 | continue; |
2042 | 0 | } |
2043 | 0 | } |
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 | 0 | for (i = 0; i < memlist->n_members; ++i) |
2051 | 0 | { |
2052 | 0 | HeapTuple authmem_tuple; |
2053 | 0 | Form_pg_auth_members authmem_form; |
2054 | |
|
2055 | 0 | if (actions[i] == RRG_NOOP) |
2056 | 0 | continue; |
2057 | | |
2058 | 0 | authmem_tuple = &memlist->members[i]->tuple; |
2059 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple); |
2060 | |
|
2061 | 0 | if (actions[i] == RRG_DELETE_GRANT) |
2062 | 0 | { |
2063 | | /* |
2064 | | * Remove the entry altogether, after first removing its |
2065 | | * dependencies |
2066 | | */ |
2067 | 0 | deleteSharedDependencyRecordsFor(AuthMemRelationId, |
2068 | 0 | authmem_form->oid, 0); |
2069 | 0 | CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self); |
2070 | 0 | } |
2071 | 0 | else |
2072 | 0 | { |
2073 | | /* Just turn off the specified option */ |
2074 | 0 | HeapTuple tuple; |
2075 | 0 | Datum new_record[Natts_pg_auth_members] = {0}; |
2076 | 0 | bool new_record_nulls[Natts_pg_auth_members] = {0}; |
2077 | 0 | bool new_record_repl[Natts_pg_auth_members] = {0}; |
2078 | | |
2079 | | /* Build a tuple to update with */ |
2080 | 0 | if (actions[i] == RRG_REMOVE_ADMIN_OPTION) |
2081 | 0 | { |
2082 | 0 | new_record[Anum_pg_auth_members_admin_option - 1] = |
2083 | 0 | BoolGetDatum(false); |
2084 | 0 | new_record_repl[Anum_pg_auth_members_admin_option - 1] = |
2085 | 0 | true; |
2086 | 0 | } |
2087 | 0 | else if (actions[i] == RRG_REMOVE_INHERIT_OPTION) |
2088 | 0 | { |
2089 | 0 | new_record[Anum_pg_auth_members_inherit_option - 1] = |
2090 | 0 | BoolGetDatum(false); |
2091 | 0 | new_record_repl[Anum_pg_auth_members_inherit_option - 1] = |
2092 | 0 | true; |
2093 | 0 | } |
2094 | 0 | else if (actions[i] == RRG_REMOVE_SET_OPTION) |
2095 | 0 | { |
2096 | 0 | new_record[Anum_pg_auth_members_set_option - 1] = |
2097 | 0 | BoolGetDatum(false); |
2098 | 0 | new_record_repl[Anum_pg_auth_members_set_option - 1] = |
2099 | 0 | true; |
2100 | 0 | } |
2101 | 0 | else |
2102 | 0 | elog(ERROR, "unknown role revoke action"); |
2103 | | |
2104 | 0 | tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc, |
2105 | 0 | new_record, |
2106 | 0 | new_record_nulls, new_record_repl); |
2107 | 0 | CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple); |
2108 | 0 | } |
2109 | 0 | } |
2110 | | |
2111 | 0 | ReleaseSysCacheList(memlist); |
2112 | | |
2113 | | /* |
2114 | | * Close pg_authmem, but keep lock till commit. |
2115 | | */ |
2116 | 0 | table_close(pg_authmem_rel, NoLock); |
2117 | 0 | } |
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 | | check_role_membership_authorization(Oid currentUserId, Oid roleid, |
2125 | | bool is_grant) |
2126 | 0 | { |
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 | 0 | if (is_grant && roleid == ROLE_PG_DATABASE_OWNER) |
2137 | 0 | ereport(ERROR, |
2138 | 0 | errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2139 | 0 | errmsg("role \"%s\" cannot have explicit members", |
2140 | 0 | GetUserNameFromId(roleid, false))); |
2141 | | |
2142 | | /* To mess with a superuser role, you gotta be superuser. */ |
2143 | 0 | if (superuser_arg(roleid)) |
2144 | 0 | { |
2145 | 0 | if (!superuser_arg(currentUserId)) |
2146 | 0 | { |
2147 | 0 | if (is_grant) |
2148 | 0 | ereport(ERROR, |
2149 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2150 | 0 | errmsg("permission denied to grant role \"%s\"", |
2151 | 0 | GetUserNameFromId(roleid, false)), |
2152 | 0 | errdetail("Only roles with the %s attribute may grant roles with the %s attribute.", |
2153 | 0 | "SUPERUSER", "SUPERUSER"))); |
2154 | 0 | else |
2155 | 0 | ereport(ERROR, |
2156 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2157 | 0 | errmsg("permission denied to revoke role \"%s\"", |
2158 | 0 | GetUserNameFromId(roleid, false)), |
2159 | 0 | errdetail("Only roles with the %s attribute may revoke roles with the %s attribute.", |
2160 | 0 | "SUPERUSER", "SUPERUSER"))); |
2161 | 0 | } |
2162 | 0 | } |
2163 | 0 | else |
2164 | 0 | { |
2165 | | /* |
2166 | | * Otherwise, must have admin option on the role to be changed. |
2167 | | */ |
2168 | 0 | if (!has_admin_privs_of_role(currentUserId, roleid)) |
2169 | 0 | { |
2170 | 0 | if (is_grant) |
2171 | 0 | ereport(ERROR, |
2172 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2173 | 0 | errmsg("permission denied to grant role \"%s\"", |
2174 | 0 | GetUserNameFromId(roleid, false)), |
2175 | 0 | errdetail("Only roles with the %s option on role \"%s\" may grant this role.", |
2176 | 0 | "ADMIN", GetUserNameFromId(roleid, false)))); |
2177 | 0 | else |
2178 | 0 | ereport(ERROR, |
2179 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2180 | 0 | errmsg("permission denied to revoke role \"%s\"", |
2181 | 0 | GetUserNameFromId(roleid, false)), |
2182 | 0 | errdetail("Only roles with the %s option on role \"%s\" may revoke this role.", |
2183 | 0 | "ADMIN", GetUserNameFromId(roleid, false)))); |
2184 | 0 | } |
2185 | 0 | } |
2186 | 0 | } |
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 | | check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant) |
2219 | 0 | { |
2220 | | /* If the grantor ID was not specified, pick one to use. */ |
2221 | 0 | if (!OidIsValid(grantorId)) |
2222 | 0 | { |
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 | 0 | if (superuser_arg(currentUserId)) |
2229 | 0 | 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 | 0 | grantorId = select_best_admin(currentUserId, roleid); |
2243 | 0 | if (!OidIsValid(grantorId)) |
2244 | 0 | elog(ERROR, "no possible grantors"); |
2245 | 0 | return grantorId; |
2246 | 0 | } |
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 | 0 | if (is_grant) |
2258 | 0 | { |
2259 | 0 | if (!has_privs_of_role(currentUserId, grantorId)) |
2260 | 0 | ereport(ERROR, |
2261 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2262 | 0 | errmsg("permission denied to grant privileges as role \"%s\"", |
2263 | 0 | GetUserNameFromId(grantorId, false)), |
2264 | 0 | errdetail("Only roles with privileges of role \"%s\" may grant privileges as this role.", |
2265 | 0 | GetUserNameFromId(grantorId, false)))); |
2266 | | |
2267 | 0 | if (grantorId != BOOTSTRAP_SUPERUSERID && |
2268 | 0 | select_best_admin(grantorId, roleid) != grantorId) |
2269 | 0 | ereport(ERROR, |
2270 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2271 | 0 | errmsg("permission denied to grant privileges as role \"%s\"", |
2272 | 0 | GetUserNameFromId(grantorId, false)), |
2273 | 0 | errdetail("The grantor must have the %s option on role \"%s\".", |
2274 | 0 | "ADMIN", GetUserNameFromId(roleid, false)))); |
2275 | 0 | } |
2276 | 0 | else |
2277 | 0 | { |
2278 | 0 | if (!has_privs_of_role(currentUserId, grantorId)) |
2279 | 0 | ereport(ERROR, |
2280 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
2281 | 0 | errmsg("permission denied to revoke privileges granted by role \"%s\"", |
2282 | 0 | GetUserNameFromId(grantorId, false)), |
2283 | 0 | errdetail("Only roles with privileges of role \"%s\" may revoke privileges granted by this role.", |
2284 | 0 | GetUserNameFromId(grantorId, false)))); |
2285 | 0 | } |
2286 | | |
2287 | | /* |
2288 | | * If a grantor was specified explicitly, always attribute the grant to |
2289 | | * that role (unless we error out above). |
2290 | | */ |
2291 | 0 | return grantorId; |
2292 | 0 | } |
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 | | initialize_revoke_actions(CatCList *memlist) |
2304 | 0 | { |
2305 | 0 | RevokeRoleGrantAction *result; |
2306 | 0 | int i; |
2307 | |
|
2308 | 0 | if (memlist->n_members == 0) |
2309 | 0 | return NULL; |
2310 | | |
2311 | 0 | result = palloc_array(RevokeRoleGrantAction, memlist->n_members); |
2312 | 0 | for (i = 0; i < memlist->n_members; i++) |
2313 | 0 | result[i] = RRG_NOOP; |
2314 | 0 | return result; |
2315 | 0 | } |
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 | | plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions, |
2335 | | Oid member, Oid grantor, GrantRoleOptions *popt, |
2336 | | DropBehavior behavior) |
2337 | 0 | { |
2338 | 0 | 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 | 0 | Assert(pg_popcount32(popt->specified) <= 1); |
2349 | |
|
2350 | 0 | for (i = 0; i < memlist->n_members; ++i) |
2351 | 0 | { |
2352 | 0 | HeapTuple authmem_tuple; |
2353 | 0 | Form_pg_auth_members authmem_form; |
2354 | |
|
2355 | 0 | authmem_tuple = &memlist->members[i]->tuple; |
2356 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple); |
2357 | |
|
2358 | 0 | if (authmem_form->member == member && |
2359 | 0 | authmem_form->grantor == grantor) |
2360 | 0 | { |
2361 | 0 | if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0) |
2362 | 0 | { |
2363 | | /* |
2364 | | * Revoking the INHERIT option doesn't change anything for |
2365 | | * dependent privileges, so we don't need to recurse. |
2366 | | */ |
2367 | 0 | actions[i] = RRG_REMOVE_INHERIT_OPTION; |
2368 | 0 | } |
2369 | 0 | else if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0) |
2370 | 0 | { |
2371 | | /* Here too, no need to recurse. */ |
2372 | 0 | actions[i] = RRG_REMOVE_SET_OPTION; |
2373 | 0 | } |
2374 | 0 | else |
2375 | 0 | { |
2376 | 0 | 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 | 0 | revoke_admin_option_only = |
2383 | 0 | (popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0; |
2384 | 0 | plan_recursive_revoke(memlist, actions, i, |
2385 | 0 | revoke_admin_option_only, behavior); |
2386 | 0 | } |
2387 | 0 | return true; |
2388 | 0 | } |
2389 | 0 | } |
2390 | | |
2391 | 0 | return false; |
2392 | 0 | } |
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 | | plan_member_revoke(CatCList *memlist, RevokeRoleGrantAction *actions, |
2405 | | Oid member) |
2406 | 0 | { |
2407 | 0 | int i; |
2408 | |
|
2409 | 0 | for (i = 0; i < memlist->n_members; ++i) |
2410 | 0 | { |
2411 | 0 | HeapTuple authmem_tuple; |
2412 | 0 | Form_pg_auth_members authmem_form; |
2413 | |
|
2414 | 0 | authmem_tuple = &memlist->members[i]->tuple; |
2415 | 0 | authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple); |
2416 | |
|
2417 | 0 | if (authmem_form->member == member) |
2418 | 0 | plan_recursive_revoke(memlist, actions, i, false, DROP_CASCADE); |
2419 | 0 | } |
2420 | 0 | } |
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 | | plan_recursive_revoke(CatCList *memlist, RevokeRoleGrantAction *actions, |
2429 | | int index, |
2430 | | bool revoke_admin_option_only, DropBehavior behavior) |
2431 | 0 | { |
2432 | 0 | bool would_still_have_admin_option = false; |
2433 | 0 | HeapTuple authmem_tuple; |
2434 | 0 | Form_pg_auth_members authmem_form; |
2435 | 0 | int i; |
2436 | | |
2437 | | /* If it's already been done, we can just return. */ |
2438 | 0 | if (actions[index] == RRG_DELETE_GRANT) |
2439 | 0 | return; |
2440 | 0 | if (actions[index] == RRG_REMOVE_ADMIN_OPTION && |
2441 | 0 | revoke_admin_option_only) |
2442 | 0 | return; |
2443 | | |
2444 | | /* Locate tuple data. */ |
2445 | 0 | authmem_tuple = &memlist->members[index]->tuple; |
2446 | 0 | 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 | 0 | if (!revoke_admin_option_only) |
2455 | 0 | { |
2456 | 0 | actions[index] = RRG_DELETE_GRANT; |
2457 | 0 | if (!authmem_form->admin_option) |
2458 | 0 | return; |
2459 | 0 | } |
2460 | 0 | else |
2461 | 0 | { |
2462 | 0 | if (!authmem_form->admin_option) |
2463 | 0 | return; |
2464 | 0 | actions[index] = RRG_REMOVE_ADMIN_OPTION; |
2465 | 0 | } |
2466 | | |
2467 | | /* Determine whether the member would still have ADMIN OPTION. */ |
2468 | 0 | for (i = 0; i < memlist->n_members; ++i) |
2469 | 0 | { |
2470 | 0 | HeapTuple am_cascade_tuple; |
2471 | 0 | Form_pg_auth_members am_cascade_form; |
2472 | |
|
2473 | 0 | am_cascade_tuple = &memlist->members[i]->tuple; |
2474 | 0 | am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple); |
2475 | |
|
2476 | 0 | if (am_cascade_form->member == authmem_form->member && |
2477 | 0 | am_cascade_form->admin_option && actions[i] == RRG_NOOP) |
2478 | 0 | { |
2479 | 0 | would_still_have_admin_option = true; |
2480 | 0 | break; |
2481 | 0 | } |
2482 | 0 | } |
2483 | | |
2484 | | /* If the member would still have ADMIN OPTION, we need not recurse. */ |
2485 | 0 | 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 | 0 | for (i = 0; i < memlist->n_members; ++i) |
2493 | 0 | { |
2494 | 0 | HeapTuple am_cascade_tuple; |
2495 | 0 | Form_pg_auth_members am_cascade_form; |
2496 | |
|
2497 | 0 | am_cascade_tuple = &memlist->members[i]->tuple; |
2498 | 0 | am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple); |
2499 | |
|
2500 | 0 | if (am_cascade_form->grantor == authmem_form->member && |
2501 | 0 | actions[i] != RRG_DELETE_GRANT) |
2502 | 0 | { |
2503 | 0 | if (behavior == DROP_RESTRICT) |
2504 | 0 | ereport(ERROR, |
2505 | 0 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
2506 | 0 | errmsg("dependent privileges exist"), |
2507 | 0 | errhint("Use CASCADE to revoke them too."))); |
2508 | | |
2509 | 0 | plan_recursive_revoke(memlist, actions, i, false, behavior); |
2510 | 0 | } |
2511 | 0 | } |
2512 | 0 | } |
2513 | | |
2514 | | /* |
2515 | | * Initialize a GrantRoleOptions object with default values. |
2516 | | */ |
2517 | | static void |
2518 | | InitGrantRoleOptions(GrantRoleOptions *popt) |
2519 | 0 | { |
2520 | 0 | popt->specified = 0; |
2521 | 0 | popt->admin = false; |
2522 | 0 | popt->inherit = false; |
2523 | 0 | popt->set = true; |
2524 | 0 | } |
2525 | | |
2526 | | /* |
2527 | | * GUC check_hook for createrole_self_grant |
2528 | | */ |
2529 | | bool |
2530 | | check_createrole_self_grant(char **newval, void **extra, GucSource source) |
2531 | 2 | { |
2532 | 2 | char *rawstring; |
2533 | 2 | List *elemlist; |
2534 | 2 | ListCell *l; |
2535 | 2 | unsigned options = 0; |
2536 | 2 | unsigned *result; |
2537 | | |
2538 | | /* Need a modifiable copy of string */ |
2539 | 2 | rawstring = pstrdup(*newval); |
2540 | | |
2541 | 2 | if (!SplitIdentifierString(rawstring, ',', &elemlist)) |
2542 | 0 | { |
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 | 0 | } |
2549 | | |
2550 | 2 | foreach(l, elemlist) |
2551 | 0 | { |
2552 | 0 | char *tok = (char *) lfirst(l); |
2553 | |
|
2554 | 0 | if (pg_strcasecmp(tok, "SET") == 0) |
2555 | 0 | options |= GRANT_ROLE_SPECIFIED_SET; |
2556 | 0 | else if (pg_strcasecmp(tok, "INHERIT") == 0) |
2557 | 0 | options |= GRANT_ROLE_SPECIFIED_INHERIT; |
2558 | 0 | else |
2559 | 0 | { |
2560 | 0 | GUC_check_errdetail("Unrecognized key word: \"%s\".", tok); |
2561 | 0 | pfree(rawstring); |
2562 | 0 | list_free(elemlist); |
2563 | 0 | return false; |
2564 | 0 | } |
2565 | 0 | } |
2566 | | |
2567 | 2 | pfree(rawstring); |
2568 | 2 | list_free(elemlist); |
2569 | | |
2570 | 2 | result = (unsigned *) guc_malloc(LOG, sizeof(unsigned)); |
2571 | 2 | if (!result) |
2572 | 0 | return false; |
2573 | 2 | *result = options; |
2574 | 2 | *extra = result; |
2575 | | |
2576 | 2 | return true; |
2577 | 2 | } |
2578 | | |
2579 | | /* |
2580 | | * GUC assign_hook for createrole_self_grant |
2581 | | */ |
2582 | | void |
2583 | | assign_createrole_self_grant(const char *newval, void *extra) |
2584 | 2 | { |
2585 | 2 | unsigned options = *(unsigned *) extra; |
2586 | | |
2587 | 2 | createrole_self_grant_enabled = (options != 0); |
2588 | 2 | createrole_self_grant_options.specified = GRANT_ROLE_SPECIFIED_ADMIN |
2589 | 2 | | GRANT_ROLE_SPECIFIED_INHERIT |
2590 | 2 | | GRANT_ROLE_SPECIFIED_SET; |
2591 | 2 | createrole_self_grant_options.admin = false; |
2592 | 2 | createrole_self_grant_options.inherit = |
2593 | 2 | (options & GRANT_ROLE_SPECIFIED_INHERIT) != 0; |
2594 | 2 | createrole_self_grant_options.set = |
2595 | 2 | (options & GRANT_ROLE_SPECIFIED_SET) != 0; |
2596 | 2 | } |