Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * user.c
4 : * Commands for manipulating roles (formerly called users).
5 : *
6 : * Portions Copyright (c) 1996-2017, 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/heapam.h"
17 : #include "access/htup_details.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/seclabel.h"
31 : #include "commands/user.h"
32 : #include "libpq/crypt.h"
33 : #include "miscadmin.h"
34 : #include "storage/lmgr.h"
35 : #include "utils/acl.h"
36 : #include "utils/builtins.h"
37 : #include "utils/fmgroids.h"
38 : #include "utils/syscache.h"
39 : #include "utils/timestamp.h"
40 : #include "utils/tqual.h"
41 :
42 : /* Potentially set by pg_upgrade_support functions */
43 : Oid binary_upgrade_next_pg_authid_oid = InvalidOid;
44 :
45 :
46 : /* GUC parameter */
47 : int Password_encryption = PASSWORD_TYPE_MD5;
48 :
49 : /* Hook to check passwords in CreateRole() and AlterRole() */
50 : check_password_hook_type check_password_hook = NULL;
51 :
52 : static void AddRoleMems(const char *rolename, Oid roleid,
53 : List *memberSpecs, List *memberIds,
54 : Oid grantorId, bool admin_opt);
55 : static void DelRoleMems(const char *rolename, Oid roleid,
56 : List *memberSpecs, List *memberIds,
57 : bool admin_opt);
58 :
59 :
60 : /* Check if current user has createrole privileges */
61 : static bool
62 298 : have_createrole_privilege(void)
63 : {
64 298 : return has_createrole_privilege(GetUserId());
65 : }
66 :
67 :
68 : /*
69 : * CREATE ROLE
70 : */
71 : Oid
72 118 : CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
73 : {
74 : Relation pg_authid_rel;
75 : TupleDesc pg_authid_dsc;
76 : HeapTuple tuple;
77 : Datum new_record[Natts_pg_authid];
78 : bool new_record_nulls[Natts_pg_authid];
79 : Oid roleid;
80 : ListCell *item;
81 : ListCell *option;
82 118 : char *password = NULL; /* user password */
83 118 : bool issuper = false; /* Make the user a superuser? */
84 118 : bool inherit = true; /* Auto inherit privileges? */
85 118 : bool createrole = false; /* Can this user create roles? */
86 118 : bool createdb = false; /* Can the user create databases? */
87 118 : bool canlogin = false; /* Can this user login? */
88 118 : bool isreplication = false; /* Is this a replication role? */
89 118 : bool bypassrls = false; /* Is this a row security enabled role? */
90 118 : int connlimit = -1; /* maximum connections allowed */
91 118 : List *addroleto = NIL; /* roles to make this a member of */
92 118 : List *rolemembers = NIL; /* roles to be members of this role */
93 118 : List *adminmembers = NIL; /* roles to be admins of this role */
94 118 : char *validUntil = NULL; /* time the login is valid until */
95 : Datum validUntil_datum; /* same, as timestamptz Datum */
96 : bool validUntil_null;
97 118 : DefElem *dpassword = NULL;
98 118 : DefElem *dissuper = NULL;
99 118 : DefElem *dinherit = NULL;
100 118 : DefElem *dcreaterole = NULL;
101 118 : DefElem *dcreatedb = NULL;
102 118 : DefElem *dcanlogin = NULL;
103 118 : DefElem *disreplication = NULL;
104 118 : DefElem *dconnlimit = NULL;
105 118 : DefElem *daddroleto = NULL;
106 118 : DefElem *drolemembers = NULL;
107 118 : DefElem *dadminmembers = NULL;
108 118 : DefElem *dvalidUntil = NULL;
109 118 : DefElem *dbypassRLS = NULL;
110 :
111 : /* The defaults can vary depending on the original statement type */
112 118 : switch (stmt->stmt_type)
113 : {
114 : case ROLESTMT_ROLE:
115 76 : break;
116 : case ROLESTMT_USER:
117 38 : canlogin = true;
118 : /* may eventually want inherit to default to false here */
119 38 : break;
120 : case ROLESTMT_GROUP:
121 4 : break;
122 : }
123 :
124 : /* Extract options from the statement node tree */
125 172 : foreach(option, stmt->options)
126 : {
127 54 : DefElem *defel = (DefElem *) lfirst(option);
128 :
129 54 : if (strcmp(defel->defname, "password") == 0)
130 : {
131 6 : if (dpassword)
132 0 : ereport(ERROR,
133 : (errcode(ERRCODE_SYNTAX_ERROR),
134 : errmsg("conflicting or redundant options"),
135 : parser_errposition(pstate, defel->location)));
136 6 : dpassword = defel;
137 : }
138 48 : else if (strcmp(defel->defname, "sysid") == 0)
139 : {
140 0 : ereport(NOTICE,
141 : (errmsg("SYSID can no longer be specified")));
142 : }
143 48 : else if (strcmp(defel->defname, "superuser") == 0)
144 : {
145 14 : if (dissuper)
146 0 : ereport(ERROR,
147 : (errcode(ERRCODE_SYNTAX_ERROR),
148 : errmsg("conflicting or redundant options"),
149 : parser_errposition(pstate, defel->location)));
150 14 : dissuper = defel;
151 : }
152 34 : else if (strcmp(defel->defname, "inherit") == 0)
153 : {
154 1 : if (dinherit)
155 0 : ereport(ERROR,
156 : (errcode(ERRCODE_SYNTAX_ERROR),
157 : errmsg("conflicting or redundant options"),
158 : parser_errposition(pstate, defel->location)));
159 1 : dinherit = defel;
160 : }
161 33 : else if (strcmp(defel->defname, "createrole") == 0)
162 : {
163 3 : if (dcreaterole)
164 0 : ereport(ERROR,
165 : (errcode(ERRCODE_SYNTAX_ERROR),
166 : errmsg("conflicting or redundant options"),
167 : parser_errposition(pstate, defel->location)));
168 3 : dcreaterole = defel;
169 : }
170 30 : else if (strcmp(defel->defname, "createdb") == 0)
171 : {
172 2 : if (dcreatedb)
173 0 : ereport(ERROR,
174 : (errcode(ERRCODE_SYNTAX_ERROR),
175 : errmsg("conflicting or redundant options"),
176 : parser_errposition(pstate, defel->location)));
177 2 : dcreatedb = defel;
178 : }
179 28 : else if (strcmp(defel->defname, "canlogin") == 0)
180 : {
181 22 : if (dcanlogin)
182 0 : ereport(ERROR,
183 : (errcode(ERRCODE_SYNTAX_ERROR),
184 : errmsg("conflicting or redundant options"),
185 : parser_errposition(pstate, defel->location)));
186 22 : dcanlogin = defel;
187 : }
188 6 : else if (strcmp(defel->defname, "isreplication") == 0)
189 : {
190 1 : if (disreplication)
191 0 : ereport(ERROR,
192 : (errcode(ERRCODE_SYNTAX_ERROR),
193 : errmsg("conflicting or redundant options"),
194 : parser_errposition(pstate, defel->location)));
195 1 : disreplication = defel;
196 : }
197 5 : else if (strcmp(defel->defname, "connectionlimit") == 0)
198 : {
199 0 : if (dconnlimit)
200 0 : ereport(ERROR,
201 : (errcode(ERRCODE_SYNTAX_ERROR),
202 : errmsg("conflicting or redundant options"),
203 : parser_errposition(pstate, defel->location)));
204 0 : dconnlimit = defel;
205 : }
206 5 : else if (strcmp(defel->defname, "addroleto") == 0)
207 : {
208 2 : if (daddroleto)
209 0 : ereport(ERROR,
210 : (errcode(ERRCODE_SYNTAX_ERROR),
211 : errmsg("conflicting or redundant options"),
212 : parser_errposition(pstate, defel->location)));
213 2 : daddroleto = defel;
214 : }
215 3 : else if (strcmp(defel->defname, "rolemembers") == 0)
216 : {
217 1 : if (drolemembers)
218 0 : ereport(ERROR,
219 : (errcode(ERRCODE_SYNTAX_ERROR),
220 : errmsg("conflicting or redundant options"),
221 : parser_errposition(pstate, defel->location)));
222 1 : drolemembers = defel;
223 : }
224 2 : else if (strcmp(defel->defname, "adminmembers") == 0)
225 : {
226 0 : if (dadminmembers)
227 0 : ereport(ERROR,
228 : (errcode(ERRCODE_SYNTAX_ERROR),
229 : errmsg("conflicting or redundant options"),
230 : parser_errposition(pstate, defel->location)));
231 0 : dadminmembers = defel;
232 : }
233 2 : else if (strcmp(defel->defname, "validUntil") == 0)
234 : {
235 0 : if (dvalidUntil)
236 0 : ereport(ERROR,
237 : (errcode(ERRCODE_SYNTAX_ERROR),
238 : errmsg("conflicting or redundant options"),
239 : parser_errposition(pstate, defel->location)));
240 0 : dvalidUntil = defel;
241 : }
242 2 : else if (strcmp(defel->defname, "bypassrls") == 0)
243 : {
244 2 : if (dbypassRLS)
245 0 : ereport(ERROR,
246 : (errcode(ERRCODE_SYNTAX_ERROR),
247 : errmsg("conflicting or redundant options"),
248 : parser_errposition(pstate, defel->location)));
249 2 : dbypassRLS = defel;
250 : }
251 : else
252 0 : elog(ERROR, "option \"%s\" not recognized",
253 : defel->defname);
254 : }
255 :
256 118 : if (dpassword && dpassword->arg)
257 5 : password = strVal(dpassword->arg);
258 118 : if (dissuper)
259 14 : issuper = intVal(dissuper->arg) != 0;
260 118 : if (dinherit)
261 1 : inherit = intVal(dinherit->arg) != 0;
262 118 : if (dcreaterole)
263 3 : createrole = intVal(dcreaterole->arg) != 0;
264 118 : if (dcreatedb)
265 2 : createdb = intVal(dcreatedb->arg) != 0;
266 118 : if (dcanlogin)
267 22 : canlogin = intVal(dcanlogin->arg) != 0;
268 118 : if (disreplication)
269 1 : isreplication = intVal(disreplication->arg) != 0;
270 118 : if (dconnlimit)
271 : {
272 0 : connlimit = intVal(dconnlimit->arg);
273 0 : if (connlimit < -1)
274 0 : ereport(ERROR,
275 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
276 : errmsg("invalid connection limit: %d", connlimit)));
277 : }
278 118 : if (daddroleto)
279 2 : addroleto = (List *) daddroleto->arg;
280 118 : if (drolemembers)
281 1 : rolemembers = (List *) drolemembers->arg;
282 118 : if (dadminmembers)
283 0 : adminmembers = (List *) dadminmembers->arg;
284 118 : if (dvalidUntil)
285 0 : validUntil = strVal(dvalidUntil->arg);
286 118 : if (dbypassRLS)
287 2 : bypassrls = intVal(dbypassRLS->arg) != 0;
288 :
289 : /* Check some permissions first */
290 118 : if (issuper)
291 : {
292 11 : if (!superuser())
293 0 : ereport(ERROR,
294 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
295 : errmsg("must be superuser to create superusers")));
296 : }
297 107 : else if (isreplication)
298 : {
299 1 : if (!superuser())
300 0 : ereport(ERROR,
301 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
302 : errmsg("must be superuser to create replication users")));
303 : }
304 106 : else if (bypassrls)
305 : {
306 2 : if (!superuser())
307 0 : ereport(ERROR,
308 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
309 : errmsg("must be superuser to change bypassrls attribute")));
310 : }
311 : else
312 : {
313 104 : if (!have_createrole_privilege())
314 0 : ereport(ERROR,
315 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
316 : errmsg("permission denied to create role")));
317 : }
318 :
319 : /*
320 : * Check that the user is not trying to create a role in the reserved
321 : * "pg_" namespace.
322 : */
323 118 : if (IsReservedName(stmt->role))
324 4 : ereport(ERROR,
325 : (errcode(ERRCODE_RESERVED_NAME),
326 : errmsg("role name \"%s\" is reserved",
327 : stmt->role),
328 : errdetail("Role names starting with \"pg_\" are reserved.")));
329 :
330 : /*
331 : * Check the pg_authid relation to be certain the role doesn't already
332 : * exist.
333 : */
334 114 : pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
335 114 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
336 :
337 114 : if (OidIsValid(get_role_oid(stmt->role, true)))
338 1 : ereport(ERROR,
339 : (errcode(ERRCODE_DUPLICATE_OBJECT),
340 : errmsg("role \"%s\" already exists",
341 : stmt->role)));
342 :
343 : /* Convert validuntil to internal form */
344 113 : if (validUntil)
345 : {
346 0 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
347 : CStringGetDatum(validUntil),
348 : ObjectIdGetDatum(InvalidOid),
349 : Int32GetDatum(-1));
350 0 : validUntil_null = false;
351 : }
352 : else
353 : {
354 113 : validUntil_datum = (Datum) 0;
355 113 : validUntil_null = true;
356 : }
357 :
358 : /*
359 : * Call the password checking hook if there is one defined
360 : */
361 113 : if (check_password_hook && password)
362 0 : (*check_password_hook) (stmt->role,
363 : password,
364 : get_password_type(password),
365 : validUntil_datum,
366 : validUntil_null);
367 :
368 : /*
369 : * Build a tuple to insert
370 : */
371 113 : MemSet(new_record, 0, sizeof(new_record));
372 113 : MemSet(new_record_nulls, false, sizeof(new_record_nulls));
373 :
374 113 : new_record[Anum_pg_authid_rolname - 1] =
375 113 : DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
376 :
377 113 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
378 113 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
379 113 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
380 113 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
381 113 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
382 113 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
383 113 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
384 :
385 113 : if (password)
386 : {
387 : char *shadow_pass;
388 : char *logdetail;
389 :
390 : /*
391 : * Don't allow an empty password. Libpq treats an empty password the
392 : * same as no password at all, and won't even try to authenticate. But
393 : * other clients might, so allowing it would be confusing. By clearing
394 : * the password when an empty string is specified, the account is
395 : * consistently locked for all clients.
396 : *
397 : * Note that this only covers passwords stored in the database itself.
398 : * There are also checks in the authentication code, to forbid an
399 : * empty password from being used with authentication methods that
400 : * fetch the password from an external system, like LDAP or PAM.
401 : */
402 9 : if (password[0] == '\0' ||
403 4 : plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK)
404 : {
405 1 : ereport(NOTICE,
406 : (errmsg("empty string is not a valid password, clearing password")));
407 1 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
408 : }
409 : else
410 : {
411 : /* Encrypt the password to the requested format. */
412 4 : shadow_pass = encrypt_password(Password_encryption, stmt->role,
413 : password);
414 4 : new_record[Anum_pg_authid_rolpassword - 1] =
415 4 : CStringGetTextDatum(shadow_pass);
416 : }
417 : }
418 : else
419 108 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
420 :
421 113 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
422 113 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
423 :
424 113 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
425 :
426 113 : tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
427 :
428 : /*
429 : * pg_largeobject_metadata contains pg_authid.oid's, so we use the
430 : * binary-upgrade override.
431 : */
432 113 : if (IsBinaryUpgrade)
433 : {
434 0 : if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
435 0 : ereport(ERROR,
436 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
437 : errmsg("pg_authid OID value not set when in binary upgrade mode")));
438 :
439 0 : HeapTupleSetOid(tuple, binary_upgrade_next_pg_authid_oid);
440 0 : binary_upgrade_next_pg_authid_oid = InvalidOid;
441 : }
442 :
443 : /*
444 : * Insert new record in the pg_authid table
445 : */
446 113 : roleid = CatalogTupleInsert(pg_authid_rel, tuple);
447 :
448 : /*
449 : * Advance command counter so we can see new record; else tests in
450 : * AddRoleMems may fail.
451 : */
452 113 : if (addroleto || adminmembers || rolemembers)
453 3 : CommandCounterIncrement();
454 :
455 : /*
456 : * Add the new role to the specified existing roles.
457 : */
458 115 : foreach(item, addroleto)
459 : {
460 2 : RoleSpec *oldrole = lfirst(item);
461 2 : HeapTuple oldroletup = get_rolespec_tuple(oldrole);
462 2 : Oid oldroleid = HeapTupleGetOid(oldroletup);
463 2 : char *oldrolename = NameStr(((Form_pg_authid) GETSTRUCT(oldroletup))->rolname);
464 :
465 4 : AddRoleMems(oldrolename, oldroleid,
466 2 : list_make1(makeString(stmt->role)),
467 : list_make1_oid(roleid),
468 : GetUserId(), false);
469 :
470 2 : ReleaseSysCache(oldroletup);
471 : }
472 :
473 : /*
474 : * Add the specified members to this new role. adminmembers get the admin
475 : * option, rolemembers don't.
476 : */
477 113 : AddRoleMems(stmt->role, roleid,
478 : adminmembers, roleSpecsToIds(adminmembers),
479 : GetUserId(), true);
480 113 : AddRoleMems(stmt->role, roleid,
481 : rolemembers, roleSpecsToIds(rolemembers),
482 : GetUserId(), false);
483 :
484 : /* Post creation hook for new role */
485 113 : InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
486 :
487 : /*
488 : * Close pg_authid, but keep lock till commit.
489 : */
490 113 : heap_close(pg_authid_rel, NoLock);
491 :
492 113 : return roleid;
493 : }
494 :
495 :
496 : /*
497 : * ALTER ROLE
498 : *
499 : * Note: the rolemembers option accepted here is intended to support the
500 : * backwards-compatible ALTER GROUP syntax. Although it will work to say
501 : * "ALTER ROLE role ROLE rolenames", we don't document it.
502 : */
503 : Oid
504 52 : AlterRole(AlterRoleStmt *stmt)
505 : {
506 : Datum new_record[Natts_pg_authid];
507 : bool new_record_nulls[Natts_pg_authid];
508 : bool new_record_repl[Natts_pg_authid];
509 : Relation pg_authid_rel;
510 : TupleDesc pg_authid_dsc;
511 : HeapTuple tuple,
512 : new_tuple;
513 : Form_pg_authid authform;
514 : ListCell *option;
515 52 : char *rolename = NULL;
516 52 : char *password = NULL; /* user password */
517 52 : int issuper = -1; /* Make the user a superuser? */
518 52 : int inherit = -1; /* Auto inherit privileges? */
519 52 : int createrole = -1; /* Can this user create roles? */
520 52 : int createdb = -1; /* Can the user create databases? */
521 52 : int canlogin = -1; /* Can this user login? */
522 52 : int isreplication = -1; /* Is this a replication role? */
523 52 : int connlimit = -1; /* maximum connections allowed */
524 52 : List *rolemembers = NIL; /* roles to be added/removed */
525 52 : char *validUntil = NULL; /* time the login is valid until */
526 : Datum validUntil_datum; /* same, as timestamptz Datum */
527 : bool validUntil_null;
528 52 : int bypassrls = -1;
529 52 : DefElem *dpassword = NULL;
530 52 : DefElem *dissuper = NULL;
531 52 : DefElem *dinherit = NULL;
532 52 : DefElem *dcreaterole = NULL;
533 52 : DefElem *dcreatedb = NULL;
534 52 : DefElem *dcanlogin = NULL;
535 52 : DefElem *disreplication = NULL;
536 52 : DefElem *dconnlimit = NULL;
537 52 : DefElem *drolemembers = NULL;
538 52 : DefElem *dvalidUntil = NULL;
539 52 : DefElem *dbypassRLS = NULL;
540 : Oid roleid;
541 :
542 52 : check_rolespec_name(stmt->role,
543 : "Cannot alter reserved roles.");
544 :
545 : /* Extract options from the statement node tree */
546 104 : foreach(option, stmt->options)
547 : {
548 52 : DefElem *defel = (DefElem *) lfirst(option);
549 :
550 52 : if (strcmp(defel->defname, "password") == 0)
551 : {
552 6 : if (dpassword)
553 0 : ereport(ERROR,
554 : (errcode(ERRCODE_SYNTAX_ERROR),
555 : errmsg("conflicting or redundant options")));
556 6 : dpassword = defel;
557 : }
558 46 : else if (strcmp(defel->defname, "superuser") == 0)
559 : {
560 5 : if (dissuper)
561 0 : ereport(ERROR,
562 : (errcode(ERRCODE_SYNTAX_ERROR),
563 : errmsg("conflicting or redundant options")));
564 5 : dissuper = defel;
565 : }
566 41 : else if (strcmp(defel->defname, "inherit") == 0)
567 : {
568 2 : if (dinherit)
569 0 : ereport(ERROR,
570 : (errcode(ERRCODE_SYNTAX_ERROR),
571 : errmsg("conflicting or redundant options")));
572 2 : dinherit = defel;
573 : }
574 39 : else if (strcmp(defel->defname, "createrole") == 0)
575 : {
576 2 : if (dcreaterole)
577 0 : ereport(ERROR,
578 : (errcode(ERRCODE_SYNTAX_ERROR),
579 : errmsg("conflicting or redundant options")));
580 2 : dcreaterole = defel;
581 : }
582 37 : else if (strcmp(defel->defname, "createdb") == 0)
583 : {
584 2 : if (dcreatedb)
585 0 : ereport(ERROR,
586 : (errcode(ERRCODE_SYNTAX_ERROR),
587 : errmsg("conflicting or redundant options")));
588 2 : dcreatedb = defel;
589 : }
590 35 : else if (strcmp(defel->defname, "canlogin") == 0)
591 : {
592 4 : if (dcanlogin)
593 0 : ereport(ERROR,
594 : (errcode(ERRCODE_SYNTAX_ERROR),
595 : errmsg("conflicting or redundant options")));
596 4 : dcanlogin = defel;
597 : }
598 31 : else if (strcmp(defel->defname, "isreplication") == 0)
599 : {
600 26 : if (disreplication)
601 0 : ereport(ERROR,
602 : (errcode(ERRCODE_SYNTAX_ERROR),
603 : errmsg("conflicting or redundant options")));
604 26 : disreplication = defel;
605 : }
606 5 : else if (strcmp(defel->defname, "connectionlimit") == 0)
607 : {
608 0 : if (dconnlimit)
609 0 : ereport(ERROR,
610 : (errcode(ERRCODE_SYNTAX_ERROR),
611 : errmsg("conflicting or redundant options")));
612 0 : dconnlimit = defel;
613 : }
614 8 : else if (strcmp(defel->defname, "rolemembers") == 0 &&
615 3 : stmt->action != 0)
616 : {
617 3 : if (drolemembers)
618 0 : ereport(ERROR,
619 : (errcode(ERRCODE_SYNTAX_ERROR),
620 : errmsg("conflicting or redundant options")));
621 3 : drolemembers = defel;
622 : }
623 2 : else if (strcmp(defel->defname, "validUntil") == 0)
624 : {
625 0 : if (dvalidUntil)
626 0 : ereport(ERROR,
627 : (errcode(ERRCODE_SYNTAX_ERROR),
628 : errmsg("conflicting or redundant options")));
629 0 : dvalidUntil = defel;
630 : }
631 2 : else if (strcmp(defel->defname, "bypassrls") == 0)
632 : {
633 2 : if (dbypassRLS)
634 0 : ereport(ERROR,
635 : (errcode(ERRCODE_SYNTAX_ERROR),
636 : errmsg("conflicting or redundant options")));
637 2 : dbypassRLS = defel;
638 : }
639 : else
640 0 : elog(ERROR, "option \"%s\" not recognized",
641 : defel->defname);
642 : }
643 :
644 52 : if (dpassword && dpassword->arg)
645 6 : password = strVal(dpassword->arg);
646 52 : if (dissuper)
647 5 : issuper = intVal(dissuper->arg);
648 52 : if (dinherit)
649 2 : inherit = intVal(dinherit->arg);
650 52 : if (dcreaterole)
651 2 : createrole = intVal(dcreaterole->arg);
652 52 : if (dcreatedb)
653 2 : createdb = intVal(dcreatedb->arg);
654 52 : if (dcanlogin)
655 4 : canlogin = intVal(dcanlogin->arg);
656 52 : if (disreplication)
657 26 : isreplication = intVal(disreplication->arg);
658 52 : if (dconnlimit)
659 : {
660 0 : connlimit = intVal(dconnlimit->arg);
661 0 : if (connlimit < -1)
662 0 : ereport(ERROR,
663 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
664 : errmsg("invalid connection limit: %d", connlimit)));
665 : }
666 52 : if (drolemembers)
667 3 : rolemembers = (List *) drolemembers->arg;
668 52 : if (dvalidUntil)
669 0 : validUntil = strVal(dvalidUntil->arg);
670 52 : if (dbypassRLS)
671 2 : bypassrls = intVal(dbypassRLS->arg);
672 :
673 : /*
674 : * Scan the pg_authid relation to be certain the user exists.
675 : */
676 52 : pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
677 52 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
678 :
679 52 : tuple = get_rolespec_tuple(stmt->role);
680 44 : authform = (Form_pg_authid) GETSTRUCT(tuple);
681 44 : rolename = pstrdup(NameStr(authform->rolname));
682 44 : roleid = HeapTupleGetOid(tuple);
683 :
684 : /*
685 : * To mess with a superuser you gotta be superuser; else you need
686 : * createrole, or just want to change your own password
687 : */
688 44 : if (authform->rolsuper || issuper >= 0)
689 : {
690 26 : if (!superuser())
691 0 : ereport(ERROR,
692 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
693 : errmsg("must be superuser to alter superusers")));
694 : }
695 31 : else if (authform->rolreplication || isreplication >= 0)
696 : {
697 20 : if (!superuser())
698 0 : ereport(ERROR,
699 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
700 : errmsg("must be superuser to alter replication users")));
701 : }
702 21 : else if (authform->rolbypassrls || bypassrls >= 0)
703 : {
704 4 : if (!superuser())
705 0 : ereport(ERROR,
706 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
707 : errmsg("must be superuser to change bypassrls attribute")));
708 : }
709 19 : else if (!have_createrole_privilege())
710 : {
711 0 : if (!(inherit < 0 &&
712 0 : createrole < 0 &&
713 0 : createdb < 0 &&
714 0 : canlogin < 0 &&
715 0 : isreplication < 0 &&
716 0 : !dconnlimit &&
717 0 : !rolemembers &&
718 0 : !validUntil &&
719 : dpassword &&
720 0 : roleid == GetUserId()))
721 0 : ereport(ERROR,
722 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
723 : errmsg("permission denied")));
724 : }
725 :
726 : /* Convert validuntil to internal form */
727 44 : if (validUntil)
728 : {
729 0 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
730 : CStringGetDatum(validUntil),
731 : ObjectIdGetDatum(InvalidOid),
732 : Int32GetDatum(-1));
733 0 : validUntil_null = false;
734 : }
735 : else
736 : {
737 : /* fetch existing setting in case hook needs it */
738 44 : validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
739 : Anum_pg_authid_rolvaliduntil,
740 : &validUntil_null);
741 : }
742 :
743 : /*
744 : * Call the password checking hook if there is one defined
745 : */
746 44 : if (check_password_hook && password)
747 0 : (*check_password_hook) (rolename,
748 : password,
749 : get_password_type(password),
750 : validUntil_datum,
751 : validUntil_null);
752 :
753 : /*
754 : * Build an updated tuple, perusing the information just obtained
755 : */
756 44 : MemSet(new_record, 0, sizeof(new_record));
757 44 : MemSet(new_record_nulls, false, sizeof(new_record_nulls));
758 44 : MemSet(new_record_repl, false, sizeof(new_record_repl));
759 :
760 : /*
761 : * issuper/createrole/etc
762 : */
763 44 : if (issuper >= 0)
764 : {
765 5 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
766 5 : new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
767 : }
768 :
769 44 : if (inherit >= 0)
770 : {
771 2 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
772 2 : new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
773 : }
774 :
775 44 : if (createrole >= 0)
776 : {
777 2 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
778 2 : new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
779 : }
780 :
781 44 : if (createdb >= 0)
782 : {
783 2 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
784 2 : new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
785 : }
786 :
787 44 : if (canlogin >= 0)
788 : {
789 4 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
790 4 : new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
791 : }
792 :
793 44 : if (isreplication >= 0)
794 : {
795 18 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
796 18 : new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
797 : }
798 :
799 44 : if (dconnlimit)
800 : {
801 0 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
802 0 : new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
803 : }
804 :
805 : /* password */
806 44 : if (password)
807 : {
808 : char *shadow_pass;
809 : char *logdetail;
810 :
811 : /* Like in CREATE USER, don't allow an empty password. */
812 12 : if (password[0] == '\0' ||
813 6 : plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
814 : {
815 2 : ereport(NOTICE,
816 : (errmsg("empty string is not a valid password, clearing password")));
817 2 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
818 : }
819 : else
820 : {
821 : /* Encrypt the password to the requested format. */
822 4 : shadow_pass = encrypt_password(Password_encryption, rolename,
823 : password);
824 4 : new_record[Anum_pg_authid_rolpassword - 1] =
825 4 : CStringGetTextDatum(shadow_pass);
826 : }
827 6 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
828 : }
829 :
830 : /* unset password */
831 44 : if (dpassword && dpassword->arg == NULL)
832 : {
833 0 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
834 0 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
835 : }
836 :
837 : /* valid until */
838 44 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
839 44 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
840 44 : new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
841 :
842 44 : if (bypassrls >= 0)
843 : {
844 2 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
845 2 : new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
846 : }
847 :
848 44 : new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
849 : new_record_nulls, new_record_repl);
850 44 : CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
851 :
852 44 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
853 :
854 44 : ReleaseSysCache(tuple);
855 44 : heap_freetuple(new_tuple);
856 :
857 : /*
858 : * Advance command counter so we can see new record; else tests in
859 : * AddRoleMems may fail.
860 : */
861 44 : if (rolemembers)
862 3 : CommandCounterIncrement();
863 :
864 44 : if (stmt->action == +1) /* add members to role */
865 43 : AddRoleMems(rolename, roleid,
866 : rolemembers, roleSpecsToIds(rolemembers),
867 : GetUserId(), false);
868 1 : else if (stmt->action == -1) /* drop members from role */
869 1 : DelRoleMems(rolename, roleid,
870 : rolemembers, roleSpecsToIds(rolemembers),
871 : false);
872 :
873 : /*
874 : * Close pg_authid, but keep lock till commit.
875 : */
876 44 : heap_close(pg_authid_rel, NoLock);
877 :
878 44 : return roleid;
879 : }
880 :
881 :
882 : /*
883 : * ALTER ROLE ... SET
884 : */
885 : Oid
886 28 : AlterRoleSet(AlterRoleSetStmt *stmt)
887 : {
888 : HeapTuple roletuple;
889 28 : Oid databaseid = InvalidOid;
890 28 : Oid roleid = InvalidOid;
891 :
892 28 : if (stmt->role)
893 : {
894 24 : check_rolespec_name(stmt->role,
895 : "Cannot alter reserved roles.");
896 :
897 24 : roletuple = get_rolespec_tuple(stmt->role);
898 20 : roleid = HeapTupleGetOid(roletuple);
899 :
900 : /*
901 : * Obtain a lock on the role and make sure it didn't go away in the
902 : * meantime.
903 : */
904 20 : shdepLockAndCheckObject(AuthIdRelationId, HeapTupleGetOid(roletuple));
905 :
906 : /*
907 : * To mess with a superuser you gotta be superuser; else you need
908 : * createrole, or just want to change your own settings
909 : */
910 20 : if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
911 : {
912 11 : if (!superuser())
913 0 : ereport(ERROR,
914 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
915 : errmsg("must be superuser to alter superusers")));
916 : }
917 : else
918 : {
919 9 : if (!have_createrole_privilege() &&
920 0 : HeapTupleGetOid(roletuple) != GetUserId())
921 0 : ereport(ERROR,
922 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
923 : errmsg("permission denied")));
924 : }
925 :
926 20 : ReleaseSysCache(roletuple);
927 : }
928 :
929 : /* look up and lock the database, if specified */
930 24 : if (stmt->database != NULL)
931 : {
932 0 : databaseid = get_database_oid(stmt->database, false);
933 0 : shdepLockAndCheckObject(DatabaseRelationId, databaseid);
934 :
935 0 : if (!stmt->role)
936 : {
937 : /*
938 : * If no role is specified, then this is effectively the same as
939 : * ALTER DATABASE ... SET, so use the same permission check.
940 : */
941 0 : if (!pg_database_ownercheck(databaseid, GetUserId()))
942 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
943 0 : stmt->database);
944 : }
945 : }
946 :
947 24 : if (!stmt->role && !stmt->database)
948 : {
949 : /* Must be superuser to alter settings globally. */
950 4 : if (!superuser())
951 0 : ereport(ERROR,
952 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
953 : errmsg("must be superuser to alter settings globally")));
954 : }
955 :
956 24 : AlterSetting(databaseid, roleid, stmt->setstmt);
957 :
958 24 : return roleid;
959 : }
960 :
961 :
962 : /*
963 : * DROP ROLE
964 : */
965 : void
966 141 : DropRole(DropRoleStmt *stmt)
967 : {
968 : Relation pg_authid_rel,
969 : pg_auth_members_rel;
970 : ListCell *item;
971 :
972 141 : if (!have_createrole_privilege())
973 0 : ereport(ERROR,
974 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
975 : errmsg("permission denied to drop role")));
976 :
977 : /*
978 : * Scan the pg_authid relation to find the Oid of the role(s) to be
979 : * deleted.
980 : */
981 141 : pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
982 141 : pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
983 :
984 277 : foreach(item, stmt->roles)
985 : {
986 157 : RoleSpec *rolspec = lfirst(item);
987 : char *role;
988 : HeapTuple tuple,
989 : tmp_tuple;
990 : ScanKeyData scankey;
991 : char *detail;
992 : char *detail_log;
993 : SysScanDesc sscan;
994 : Oid roleid;
995 :
996 157 : if (rolspec->roletype != ROLESPEC_CSTRING)
997 0 : ereport(ERROR,
998 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
999 : errmsg("cannot use special role specifier in DROP ROLE")));
1000 157 : role = rolspec->rolename;
1001 :
1002 157 : tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
1003 157 : if (!HeapTupleIsValid(tuple))
1004 : {
1005 37 : if (!stmt->missing_ok)
1006 : {
1007 7 : ereport(ERROR,
1008 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1009 : errmsg("role \"%s\" does not exist", role)));
1010 : }
1011 : else
1012 : {
1013 30 : ereport(NOTICE,
1014 : (errmsg("role \"%s\" does not exist, skipping",
1015 : role)));
1016 : }
1017 :
1018 30 : continue;
1019 : }
1020 :
1021 120 : roleid = HeapTupleGetOid(tuple);
1022 :
1023 120 : if (roleid == GetUserId())
1024 0 : ereport(ERROR,
1025 : (errcode(ERRCODE_OBJECT_IN_USE),
1026 : errmsg("current user cannot be dropped")));
1027 120 : if (roleid == GetOuterUserId())
1028 0 : ereport(ERROR,
1029 : (errcode(ERRCODE_OBJECT_IN_USE),
1030 : errmsg("current user cannot be dropped")));
1031 120 : if (roleid == GetSessionUserId())
1032 0 : ereport(ERROR,
1033 : (errcode(ERRCODE_OBJECT_IN_USE),
1034 : errmsg("session user cannot be dropped")));
1035 :
1036 : /*
1037 : * For safety's sake, we allow createrole holders to drop ordinary
1038 : * roles but not superuser roles. This is mainly to avoid the
1039 : * scenario where you accidentally drop the last superuser.
1040 : */
1041 133 : if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
1042 13 : !superuser())
1043 0 : ereport(ERROR,
1044 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1045 : errmsg("must be superuser to drop superusers")));
1046 :
1047 : /* DROP hook for the role being removed */
1048 120 : InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
1049 :
1050 : /*
1051 : * Lock the role, so nobody can add dependencies to her while we drop
1052 : * her. We keep the lock until the end of transaction.
1053 : */
1054 120 : LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
1055 :
1056 : /* Check for pg_shdepend entries depending on this role */
1057 120 : if (checkSharedDependencies(AuthIdRelationId, roleid,
1058 : &detail, &detail_log))
1059 14 : ereport(ERROR,
1060 : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1061 : errmsg("role \"%s\" cannot be dropped because some objects depend on it",
1062 : role),
1063 : errdetail_internal("%s", detail),
1064 : errdetail_log("%s", detail_log)));
1065 :
1066 : /*
1067 : * Remove the role from the pg_authid table
1068 : */
1069 106 : CatalogTupleDelete(pg_authid_rel, &tuple->t_self);
1070 :
1071 106 : ReleaseSysCache(tuple);
1072 :
1073 : /*
1074 : * Remove role from the pg_auth_members table. We have to remove all
1075 : * tuples that show it as either a role or a member.
1076 : *
1077 : * XXX what about grantor entries? Maybe we should do one heap scan.
1078 : */
1079 106 : ScanKeyInit(&scankey,
1080 : Anum_pg_auth_members_roleid,
1081 : BTEqualStrategyNumber, F_OIDEQ,
1082 : ObjectIdGetDatum(roleid));
1083 :
1084 106 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
1085 : true, NULL, 1, &scankey);
1086 :
1087 218 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1088 : {
1089 6 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1090 : }
1091 :
1092 106 : systable_endscan(sscan);
1093 :
1094 106 : ScanKeyInit(&scankey,
1095 : Anum_pg_auth_members_member,
1096 : BTEqualStrategyNumber, F_OIDEQ,
1097 : ObjectIdGetDatum(roleid));
1098 :
1099 106 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
1100 : true, NULL, 1, &scankey);
1101 :
1102 217 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1103 : {
1104 5 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1105 : }
1106 :
1107 106 : systable_endscan(sscan);
1108 :
1109 : /*
1110 : * Remove any comments or security labels on this role.
1111 : */
1112 106 : DeleteSharedComments(roleid, AuthIdRelationId);
1113 106 : DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
1114 :
1115 : /*
1116 : * Remove settings for this role.
1117 : */
1118 106 : DropSetting(InvalidOid, roleid);
1119 :
1120 : /*
1121 : * Advance command counter so that later iterations of this loop will
1122 : * see the changes already made. This is essential if, for example,
1123 : * we are trying to drop both a role and one of its direct members ---
1124 : * we'll get an error if we try to delete the linking pg_auth_members
1125 : * tuple twice. (We do not need a CCI between the two delete loops
1126 : * above, because it's not allowed for a role to directly contain
1127 : * itself.)
1128 : */
1129 106 : CommandCounterIncrement();
1130 : }
1131 :
1132 : /*
1133 : * Now we can clean up; but keep locks until commit.
1134 : */
1135 120 : heap_close(pg_auth_members_rel, NoLock);
1136 120 : heap_close(pg_authid_rel, NoLock);
1137 120 : }
1138 :
1139 : /*
1140 : * Rename role
1141 : */
1142 : ObjectAddress
1143 3 : RenameRole(const char *oldname, const char *newname)
1144 : {
1145 : HeapTuple oldtuple,
1146 : newtuple;
1147 : TupleDesc dsc;
1148 : Relation rel;
1149 : Datum datum;
1150 : bool isnull;
1151 : Datum repl_val[Natts_pg_authid];
1152 : bool repl_null[Natts_pg_authid];
1153 : bool repl_repl[Natts_pg_authid];
1154 : int i;
1155 : Oid roleid;
1156 : ObjectAddress address;
1157 : Form_pg_authid authform;
1158 :
1159 3 : rel = heap_open(AuthIdRelationId, RowExclusiveLock);
1160 3 : dsc = RelationGetDescr(rel);
1161 :
1162 3 : oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
1163 3 : if (!HeapTupleIsValid(oldtuple))
1164 0 : ereport(ERROR,
1165 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1166 : errmsg("role \"%s\" does not exist", oldname)));
1167 :
1168 : /*
1169 : * XXX Client applications probably store the session user somewhere, so
1170 : * renaming it could cause confusion. On the other hand, there may not be
1171 : * an actual problem besides a little confusion, so think about this and
1172 : * decide. Same for SET ROLE ... we don't restrict renaming the current
1173 : * effective userid, though.
1174 : */
1175 :
1176 3 : roleid = HeapTupleGetOid(oldtuple);
1177 3 : authform = (Form_pg_authid) GETSTRUCT(oldtuple);
1178 :
1179 3 : if (roleid == GetSessionUserId())
1180 0 : ereport(ERROR,
1181 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1182 : errmsg("session user cannot be renamed")));
1183 3 : if (roleid == GetOuterUserId())
1184 0 : ereport(ERROR,
1185 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1186 : errmsg("current user cannot be renamed")));
1187 :
1188 : /*
1189 : * Check that the user is not trying to rename a system role and not
1190 : * trying to rename a role into the reserved "pg_" namespace.
1191 : */
1192 3 : if (IsReservedName(NameStr(authform->rolname)))
1193 0 : ereport(ERROR,
1194 : (errcode(ERRCODE_RESERVED_NAME),
1195 : errmsg("role name \"%s\" is reserved",
1196 : NameStr(authform->rolname)),
1197 : errdetail("Role names starting with \"pg_\" are reserved.")));
1198 :
1199 3 : if (IsReservedName(newname))
1200 0 : ereport(ERROR,
1201 : (errcode(ERRCODE_RESERVED_NAME),
1202 : errmsg("role name \"%s\" is reserved",
1203 : newname),
1204 : errdetail("Role names starting with \"pg_\" are reserved.")));
1205 :
1206 : /* make sure the new name doesn't exist */
1207 3 : if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
1208 0 : ereport(ERROR,
1209 : (errcode(ERRCODE_DUPLICATE_OBJECT),
1210 : errmsg("role \"%s\" already exists", newname)));
1211 :
1212 : /*
1213 : * createrole is enough privilege unless you want to mess with a superuser
1214 : */
1215 3 : if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
1216 : {
1217 1 : if (!superuser())
1218 0 : ereport(ERROR,
1219 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1220 : errmsg("must be superuser to rename superusers")));
1221 : }
1222 : else
1223 : {
1224 2 : if (!have_createrole_privilege())
1225 0 : ereport(ERROR,
1226 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1227 : errmsg("permission denied to rename role")));
1228 : }
1229 :
1230 : /* OK, construct the modified tuple */
1231 36 : for (i = 0; i < Natts_pg_authid; i++)
1232 33 : repl_repl[i] = false;
1233 :
1234 3 : repl_repl[Anum_pg_authid_rolname - 1] = true;
1235 3 : repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1236 : CStringGetDatum(newname));
1237 3 : repl_null[Anum_pg_authid_rolname - 1] = false;
1238 :
1239 3 : datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1240 :
1241 3 : if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
1242 : {
1243 : /* MD5 uses the username as salt, so just clear it on a rename */
1244 1 : repl_repl[Anum_pg_authid_rolpassword - 1] = true;
1245 1 : repl_null[Anum_pg_authid_rolpassword - 1] = true;
1246 :
1247 1 : ereport(NOTICE,
1248 : (errmsg("MD5 password cleared because of role rename")));
1249 : }
1250 :
1251 3 : newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1252 3 : CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
1253 :
1254 3 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
1255 :
1256 3 : ObjectAddressSet(address, AuthIdRelationId, roleid);
1257 :
1258 3 : ReleaseSysCache(oldtuple);
1259 :
1260 : /*
1261 : * Close pg_authid, but keep lock till commit.
1262 : */
1263 3 : heap_close(rel, NoLock);
1264 :
1265 3 : return address;
1266 : }
1267 :
1268 : /*
1269 : * GrantRoleStmt
1270 : *
1271 : * Grant/Revoke roles to/from roles
1272 : */
1273 : void
1274 20 : GrantRole(GrantRoleStmt *stmt)
1275 : {
1276 : Relation pg_authid_rel;
1277 : Oid grantor;
1278 : List *grantee_ids;
1279 : ListCell *item;
1280 :
1281 20 : if (stmt->grantor)
1282 0 : grantor = get_rolespec_oid(stmt->grantor, false);
1283 : else
1284 20 : grantor = GetUserId();
1285 :
1286 20 : grantee_ids = roleSpecsToIds(stmt->grantee_roles);
1287 :
1288 : /* AccessShareLock is enough since we aren't modifying pg_authid */
1289 20 : pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
1290 :
1291 : /*
1292 : * Step through all of the granted roles and add/remove entries for the
1293 : * grantees, or, if admin_opt is set, then just add/remove the admin
1294 : * option.
1295 : *
1296 : * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1297 : */
1298 36 : foreach(item, stmt->granted_roles)
1299 : {
1300 20 : AccessPriv *priv = (AccessPriv *) lfirst(item);
1301 20 : char *rolename = priv->priv_name;
1302 : Oid roleid;
1303 :
1304 : /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
1305 20 : if (rolename == NULL || priv->cols != NIL)
1306 0 : ereport(ERROR,
1307 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1308 : errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
1309 :
1310 20 : roleid = get_role_oid(rolename, false);
1311 20 : if (stmt->is_grant)
1312 18 : AddRoleMems(rolename, roleid,
1313 : stmt->grantee_roles, grantee_ids,
1314 18 : grantor, stmt->admin_opt);
1315 : else
1316 2 : DelRoleMems(rolename, roleid,
1317 : stmt->grantee_roles, grantee_ids,
1318 2 : stmt->admin_opt);
1319 : }
1320 :
1321 : /*
1322 : * Close pg_authid, but keep lock till commit.
1323 : */
1324 16 : heap_close(pg_authid_rel, NoLock);
1325 16 : }
1326 :
1327 : /*
1328 : * DropOwnedObjects
1329 : *
1330 : * Drop the objects owned by a given list of roles.
1331 : */
1332 : void
1333 16 : DropOwnedObjects(DropOwnedStmt *stmt)
1334 : {
1335 16 : List *role_ids = roleSpecsToIds(stmt->roles);
1336 : ListCell *cell;
1337 :
1338 : /* Check privileges */
1339 37 : foreach(cell, role_ids)
1340 : {
1341 23 : Oid roleid = lfirst_oid(cell);
1342 :
1343 23 : if (!has_privs_of_role(GetUserId(), roleid))
1344 2 : ereport(ERROR,
1345 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1346 : errmsg("permission denied to drop objects")));
1347 : }
1348 :
1349 : /* Ok, do it */
1350 14 : shdepDropOwned(role_ids, stmt->behavior);
1351 13 : }
1352 :
1353 : /*
1354 : * ReassignOwnedObjects
1355 : *
1356 : * Give the objects owned by a given list of roles away to another user.
1357 : */
1358 : void
1359 4 : ReassignOwnedObjects(ReassignOwnedStmt *stmt)
1360 : {
1361 4 : List *role_ids = roleSpecsToIds(stmt->roles);
1362 : ListCell *cell;
1363 : Oid newrole;
1364 :
1365 : /* Check privileges */
1366 7 : foreach(cell, role_ids)
1367 : {
1368 4 : Oid roleid = lfirst_oid(cell);
1369 :
1370 4 : if (!has_privs_of_role(GetUserId(), roleid))
1371 1 : ereport(ERROR,
1372 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1373 : errmsg("permission denied to reassign objects")));
1374 : }
1375 :
1376 : /* Must have privileges on the receiving side too */
1377 3 : newrole = get_rolespec_oid(stmt->newrole, false);
1378 :
1379 3 : if (!has_privs_of_role(GetUserId(), newrole))
1380 1 : ereport(ERROR,
1381 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1382 : errmsg("permission denied to reassign objects")));
1383 :
1384 : /* Ok, do it */
1385 2 : shdepReassignOwned(role_ids, newrole);
1386 2 : }
1387 :
1388 : /*
1389 : * roleSpecsToIds
1390 : *
1391 : * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
1392 : *
1393 : * ROLESPEC_PUBLIC is not allowed.
1394 : */
1395 : List *
1396 313 : roleSpecsToIds(List *memberNames)
1397 : {
1398 313 : List *result = NIL;
1399 : ListCell *l;
1400 :
1401 365 : foreach(l, memberNames)
1402 : {
1403 52 : RoleSpec *rolespec = lfirst_node(RoleSpec, l);
1404 : Oid roleid;
1405 :
1406 52 : roleid = get_rolespec_oid(rolespec, false);
1407 52 : result = lappend_oid(result, roleid);
1408 : }
1409 313 : return result;
1410 : }
1411 :
1412 : /*
1413 : * AddRoleMems -- Add given members to the specified role
1414 : *
1415 : * rolename: name of role to add to (used only for error messages)
1416 : * roleid: OID of role to add to
1417 : * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
1418 : * memberIds: OIDs of roles to add
1419 : * grantorId: who is granting the membership
1420 : * admin_opt: granting admin option?
1421 : *
1422 : * Note: caller is responsible for calling auth_file_update_needed().
1423 : */
1424 : static void
1425 289 : AddRoleMems(const char *rolename, Oid roleid,
1426 : List *memberSpecs, List *memberIds,
1427 : Oid grantorId, bool admin_opt)
1428 : {
1429 : Relation pg_authmem_rel;
1430 : TupleDesc pg_authmem_dsc;
1431 : ListCell *specitem;
1432 : ListCell *iditem;
1433 :
1434 289 : Assert(list_length(memberSpecs) == list_length(memberIds));
1435 :
1436 : /* Skip permission check if nothing to do */
1437 289 : if (!memberIds)
1438 551 : return;
1439 :
1440 : /*
1441 : * Check permissions: must have createrole or admin option on the role to
1442 : * be changed. To mess with a superuser role, you gotta be superuser.
1443 : */
1444 23 : if (superuser_arg(roleid))
1445 : {
1446 3 : if (!superuser())
1447 0 : ereport(ERROR,
1448 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1449 : errmsg("must be superuser to alter superusers")));
1450 : }
1451 : else
1452 : {
1453 27 : if (!have_createrole_privilege() &&
1454 7 : !is_admin_of_role(grantorId, roleid))
1455 4 : ereport(ERROR,
1456 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1457 : errmsg("must have admin option on role \"%s\"",
1458 : rolename)));
1459 : }
1460 :
1461 : /*
1462 : * The role membership grantor of record has little significance at
1463 : * present. Nonetheless, inasmuch as users might look to it for a crude
1464 : * audit trail, let only superusers impute the grant to a third party.
1465 : *
1466 : * Before lifting this restriction, give the member == role case of
1467 : * is_admin_of_role() a fresh look. Ensure that the current role cannot
1468 : * use an explicit grantor specification to take advantage of the session
1469 : * user's self-admin right.
1470 : */
1471 19 : if (grantorId != GetUserId() && !superuser())
1472 0 : ereport(ERROR,
1473 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1474 : errmsg("must be superuser to set grantor")));
1475 :
1476 19 : pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1477 19 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1478 :
1479 39 : forboth(specitem, memberSpecs, iditem, memberIds)
1480 : {
1481 20 : RoleSpec *memberRole = lfirst(specitem);
1482 20 : Oid memberid = lfirst_oid(iditem);
1483 : HeapTuple authmem_tuple;
1484 : HeapTuple tuple;
1485 : Datum new_record[Natts_pg_auth_members];
1486 : bool new_record_nulls[Natts_pg_auth_members];
1487 : bool new_record_repl[Natts_pg_auth_members];
1488 :
1489 : /*
1490 : * Refuse creation of membership loops, including the trivial case
1491 : * where a role is made a member of itself. We do this by checking to
1492 : * see if the target role is already a member of the proposed member
1493 : * role. We have to ignore possible superuserness, however, else we
1494 : * could never grant membership in a superuser-privileged role.
1495 : */
1496 20 : if (is_member_of_role_nosuper(roleid, memberid))
1497 0 : ereport(ERROR,
1498 : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1499 : (errmsg("role \"%s\" is a member of role \"%s\"",
1500 : rolename, get_rolespec_name(memberRole)))));
1501 :
1502 : /*
1503 : * Check if entry for this role/member already exists; if so, give
1504 : * warning unless we are adding admin option.
1505 : */
1506 20 : authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
1507 : ObjectIdGetDatum(roleid),
1508 : ObjectIdGetDatum(memberid));
1509 20 : if (HeapTupleIsValid(authmem_tuple) &&
1510 0 : (!admin_opt ||
1511 0 : ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1512 : {
1513 3 : ereport(NOTICE,
1514 : (errmsg("role \"%s\" is already a member of role \"%s\"",
1515 : get_rolespec_name(memberRole), rolename)));
1516 3 : ReleaseSysCache(authmem_tuple);
1517 3 : continue;
1518 : }
1519 :
1520 : /* Build a tuple to insert or update */
1521 17 : MemSet(new_record, 0, sizeof(new_record));
1522 17 : MemSet(new_record_nulls, false, sizeof(new_record_nulls));
1523 17 : MemSet(new_record_repl, false, sizeof(new_record_repl));
1524 :
1525 17 : new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1526 17 : new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1527 17 : new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1528 17 : new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1529 :
1530 17 : if (HeapTupleIsValid(authmem_tuple))
1531 : {
1532 0 : new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
1533 0 : new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
1534 0 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1535 : new_record,
1536 : new_record_nulls, new_record_repl);
1537 0 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
1538 0 : ReleaseSysCache(authmem_tuple);
1539 : }
1540 : else
1541 : {
1542 17 : tuple = heap_form_tuple(pg_authmem_dsc,
1543 : new_record, new_record_nulls);
1544 17 : CatalogTupleInsert(pg_authmem_rel, tuple);
1545 : }
1546 :
1547 : /* CCI after each change, in case there are duplicates in list */
1548 17 : CommandCounterIncrement();
1549 : }
1550 :
1551 : /*
1552 : * Close pg_authmem, but keep lock till commit.
1553 : */
1554 19 : heap_close(pg_authmem_rel, NoLock);
1555 : }
1556 :
1557 : /*
1558 : * DelRoleMems -- Remove given members from the specified role
1559 : *
1560 : * rolename: name of role to del from (used only for error messages)
1561 : * roleid: OID of role to del from
1562 : * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
1563 : * memberIds: OIDs of roles to del
1564 : * admin_opt: remove admin option only?
1565 : *
1566 : * Note: caller is responsible for calling auth_file_update_needed().
1567 : */
1568 : static void
1569 3 : DelRoleMems(const char *rolename, Oid roleid,
1570 : List *memberSpecs, List *memberIds,
1571 : bool admin_opt)
1572 : {
1573 : Relation pg_authmem_rel;
1574 : TupleDesc pg_authmem_dsc;
1575 : ListCell *specitem;
1576 : ListCell *iditem;
1577 :
1578 3 : Assert(list_length(memberSpecs) == list_length(memberIds));
1579 :
1580 : /* Skip permission check if nothing to do */
1581 3 : if (!memberIds)
1582 3 : return;
1583 :
1584 : /*
1585 : * Check permissions: must have createrole or admin option on the role to
1586 : * be changed. To mess with a superuser role, you gotta be superuser.
1587 : */
1588 3 : if (superuser_arg(roleid))
1589 : {
1590 0 : if (!superuser())
1591 0 : ereport(ERROR,
1592 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1593 : errmsg("must be superuser to alter superusers")));
1594 : }
1595 : else
1596 : {
1597 4 : if (!have_createrole_privilege() &&
1598 1 : !is_admin_of_role(GetUserId(), roleid))
1599 0 : ereport(ERROR,
1600 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1601 : errmsg("must have admin option on role \"%s\"",
1602 : rolename)));
1603 : }
1604 :
1605 3 : pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1606 3 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1607 :
1608 6 : forboth(specitem, memberSpecs, iditem, memberIds)
1609 : {
1610 3 : RoleSpec *memberRole = lfirst(specitem);
1611 3 : Oid memberid = lfirst_oid(iditem);
1612 : HeapTuple authmem_tuple;
1613 :
1614 : /*
1615 : * Find entry for this role/member
1616 : */
1617 3 : authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
1618 : ObjectIdGetDatum(roleid),
1619 : ObjectIdGetDatum(memberid));
1620 3 : if (!HeapTupleIsValid(authmem_tuple))
1621 : {
1622 0 : ereport(WARNING,
1623 : (errmsg("role \"%s\" is not a member of role \"%s\"",
1624 : get_rolespec_name(memberRole), rolename)));
1625 0 : continue;
1626 : }
1627 :
1628 3 : if (!admin_opt)
1629 : {
1630 : /* Remove the entry altogether */
1631 3 : CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
1632 : }
1633 : else
1634 : {
1635 : /* Just turn off the admin option */
1636 : HeapTuple tuple;
1637 : Datum new_record[Natts_pg_auth_members];
1638 : bool new_record_nulls[Natts_pg_auth_members];
1639 : bool new_record_repl[Natts_pg_auth_members];
1640 :
1641 : /* Build a tuple to update with */
1642 0 : MemSet(new_record, 0, sizeof(new_record));
1643 0 : MemSet(new_record_nulls, false, sizeof(new_record_nulls));
1644 0 : MemSet(new_record_repl, false, sizeof(new_record_repl));
1645 :
1646 0 : new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1647 0 : new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
1648 :
1649 0 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1650 : new_record,
1651 : new_record_nulls, new_record_repl);
1652 0 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
1653 : }
1654 :
1655 3 : ReleaseSysCache(authmem_tuple);
1656 :
1657 : /* CCI after each change, in case there are duplicates in list */
1658 3 : CommandCounterIncrement();
1659 : }
1660 :
1661 : /*
1662 : * Close pg_authmem, but keep lock till commit.
1663 : */
1664 3 : heap_close(pg_authmem_rel, NoLock);
1665 : }
|