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