YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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