Coverage Report

Created: 2026-08-14 06:37

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