]> granicus.if.org Git - postgresql/blob - src/backend/commands/user.c
Disregard superuserness when checking to see if a role GRANT would
[postgresql] / src / backend / commands / user.c
1 /*-------------------------------------------------------------------------
2  *
3  * user.c
4  *        Commands for manipulating roles (formerly called users).
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.164 2005/11/04 17:25:15 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "access/genam.h"
16 #include "access/heapam.h"
17 #include "catalog/dependency.h"
18 #include "catalog/indexing.h"
19 #include "catalog/pg_auth_members.h"
20 #include "catalog/pg_authid.h"
21 #include "commands/user.h"
22 #include "libpq/crypt.h"
23 #include "miscadmin.h"
24 #include "utils/acl.h"
25 #include "utils/builtins.h"
26 #include "utils/flatfiles.h"
27 #include "utils/fmgroids.h"
28 #include "utils/guc.h"
29 #include "utils/lsyscache.h"
30 #include "utils/syscache.h"
31
32
33 extern bool Password_encryption;
34
35 static List *roleNamesToIds(List *memberNames);
36 static void AddRoleMems(const char *rolename, Oid roleid,
37                         List *memberNames, List *memberIds,
38                         Oid grantorId, bool admin_opt);
39 static void DelRoleMems(const char *rolename, Oid roleid,
40                         List *memberNames, List *memberIds,
41                         bool admin_opt);
42
43
44 /* Check if current user has createrole privileges */
45 static bool
46 have_createrole_privilege(void)
47 {
48         bool            result = false;
49         HeapTuple       utup;
50
51         /* Superusers can always do everything */
52         if (superuser())
53                 return true;
54
55         utup = SearchSysCache(AUTHOID,
56                                                   ObjectIdGetDatum(GetUserId()),
57                                                   0, 0, 0);
58         if (HeapTupleIsValid(utup))
59         {
60                 result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
61                 ReleaseSysCache(utup);
62         }
63         return result;
64 }
65
66
67 /*
68  * CREATE ROLE
69  */
70 void
71 CreateRole(CreateRoleStmt *stmt)
72 {
73         Relation        pg_authid_rel;
74         TupleDesc       pg_authid_dsc;
75         HeapTuple       tuple;
76         Datum           new_record[Natts_pg_authid];
77         char            new_record_nulls[Natts_pg_authid];
78         Oid                     roleid;
79         ListCell   *item;
80         ListCell   *option;
81         char       *password = NULL;    /* user password */
82         bool            encrypt_password = Password_encryption; /* encrypt password? */
83         char            encrypted_password[MD5_PASSWD_LEN + 1];
84         bool            issuper = false;        /* Make the user a superuser? */
85         bool            inherit = true; /* Auto inherit privileges? */
86         bool            createrole = false;             /* Can this user create roles? */
87         bool            createdb = false;               /* Can the user create databases? */
88         bool            canlogin = false;               /* Can this user login? */
89         int                     connlimit = -1; /* maximum connections allowed */
90         List       *addroleto = NIL;    /* roles to make this a member of */
91         List       *rolemembers = NIL;          /* roles to be members of this role */
92         List       *adminmembers = NIL;         /* roles to be admins of this role */
93         char       *validUntil = NULL;          /* time the login is valid until */
94         DefElem    *dpassword = NULL;
95         DefElem    *dissuper = NULL;
96         DefElem    *dinherit = NULL;
97         DefElem    *dcreaterole = NULL;
98         DefElem    *dcreatedb = NULL;
99         DefElem    *dcanlogin = NULL;
100         DefElem    *dconnlimit = NULL;
101         DefElem    *daddroleto = NULL;
102         DefElem    *drolemembers = NULL;
103         DefElem    *dadminmembers = NULL;
104         DefElem    *dvalidUntil = NULL;
105
106         /* The defaults can vary depending on the original statement type */
107         switch (stmt->stmt_type)
108         {
109                 case ROLESTMT_ROLE:
110                         break;
111                 case ROLESTMT_USER:
112                         canlogin = true;
113                         /* may eventually want inherit to default to false here */
114                         break;
115                 case ROLESTMT_GROUP:
116                         break;
117         }
118
119         /* Extract options from the statement node tree */
120         foreach(option, stmt->options)
121         {
122                 DefElem    *defel = (DefElem *) lfirst(option);
123
124                 if (strcmp(defel->defname, "password") == 0 ||
125                         strcmp(defel->defname, "encryptedPassword") == 0 ||
126                         strcmp(defel->defname, "unencryptedPassword") == 0)
127                 {
128                         if (dpassword)
129                                 ereport(ERROR,
130                                                 (errcode(ERRCODE_SYNTAX_ERROR),
131                                                  errmsg("conflicting or redundant options")));
132                         dpassword = defel;
133                         if (strcmp(defel->defname, "encryptedPassword") == 0)
134                                 encrypt_password = true;
135                         else if (strcmp(defel->defname, "unencryptedPassword") == 0)
136                                 encrypt_password = false;
137                 }
138                 else if (strcmp(defel->defname, "sysid") == 0)
139                 {
140                         ereport(NOTICE,
141                                         (errmsg("SYSID can no longer be specified")));
142                 }
143                 else if (strcmp(defel->defname, "superuser") == 0)
144                 {
145                         if (dissuper)
146                                 ereport(ERROR,
147                                                 (errcode(ERRCODE_SYNTAX_ERROR),
148                                                  errmsg("conflicting or redundant options")));
149                         dissuper = defel;
150                 }
151                 else if (strcmp(defel->defname, "inherit") == 0)
152                 {
153                         if (dinherit)
154                                 ereport(ERROR,
155                                                 (errcode(ERRCODE_SYNTAX_ERROR),
156                                                  errmsg("conflicting or redundant options")));
157                         dinherit = defel;
158                 }
159                 else if (strcmp(defel->defname, "createrole") == 0)
160                 {
161                         if (dcreaterole)
162                                 ereport(ERROR,
163                                                 (errcode(ERRCODE_SYNTAX_ERROR),
164                                                  errmsg("conflicting or redundant options")));
165                         dcreaterole = defel;
166                 }
167                 else if (strcmp(defel->defname, "createdb") == 0)
168                 {
169                         if (dcreatedb)
170                                 ereport(ERROR,
171                                                 (errcode(ERRCODE_SYNTAX_ERROR),
172                                                  errmsg("conflicting or redundant options")));
173                         dcreatedb = defel;
174                 }
175                 else if (strcmp(defel->defname, "canlogin") == 0)
176                 {
177                         if (dcanlogin)
178                                 ereport(ERROR,
179                                                 (errcode(ERRCODE_SYNTAX_ERROR),
180                                                  errmsg("conflicting or redundant options")));
181                         dcanlogin = defel;
182                 }
183                 else if (strcmp(defel->defname, "connectionlimit") == 0)
184                 {
185                         if (dconnlimit)
186                                 ereport(ERROR,
187                                                 (errcode(ERRCODE_SYNTAX_ERROR),
188                                                  errmsg("conflicting or redundant options")));
189                         dconnlimit = defel;
190                 }
191                 else if (strcmp(defel->defname, "addroleto") == 0)
192                 {
193                         if (daddroleto)
194                                 ereport(ERROR,
195                                                 (errcode(ERRCODE_SYNTAX_ERROR),
196                                                  errmsg("conflicting or redundant options")));
197                         daddroleto = defel;
198                 }
199                 else if (strcmp(defel->defname, "rolemembers") == 0)
200                 {
201                         if (drolemembers)
202                                 ereport(ERROR,
203                                                 (errcode(ERRCODE_SYNTAX_ERROR),
204                                                  errmsg("conflicting or redundant options")));
205                         drolemembers = defel;
206                 }
207                 else if (strcmp(defel->defname, "adminmembers") == 0)
208                 {
209                         if (dadminmembers)
210                                 ereport(ERROR,
211                                                 (errcode(ERRCODE_SYNTAX_ERROR),
212                                                  errmsg("conflicting or redundant options")));
213                         dadminmembers = defel;
214                 }
215                 else if (strcmp(defel->defname, "validUntil") == 0)
216                 {
217                         if (dvalidUntil)
218                                 ereport(ERROR,
219                                                 (errcode(ERRCODE_SYNTAX_ERROR),
220                                                  errmsg("conflicting or redundant options")));
221                         dvalidUntil = defel;
222                 }
223                 else
224                         elog(ERROR, "option \"%s\" not recognized",
225                                  defel->defname);
226         }
227
228         if (dpassword)
229                 password = strVal(dpassword->arg);
230         if (dissuper)
231                 issuper = intVal(dissuper->arg) != 0;
232         if (dinherit)
233                 inherit = intVal(dinherit->arg) != 0;
234         if (dcreaterole)
235                 createrole = intVal(dcreaterole->arg) != 0;
236         if (dcreatedb)
237                 createdb = intVal(dcreatedb->arg) != 0;
238         if (dcanlogin)
239                 canlogin = intVal(dcanlogin->arg) != 0;
240         if (dconnlimit)
241                 connlimit = intVal(dconnlimit->arg);
242         if (daddroleto)
243                 addroleto = (List *) daddroleto->arg;
244         if (drolemembers)
245                 rolemembers = (List *) drolemembers->arg;
246         if (dadminmembers)
247                 adminmembers = (List *) dadminmembers->arg;
248         if (dvalidUntil)
249                 validUntil = strVal(dvalidUntil->arg);
250
251         /* Check some permissions first */
252         if (issuper)
253         {
254                 if (!superuser())
255                         ereport(ERROR,
256                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
257                                          errmsg("must be superuser to create superusers")));
258         }
259         else
260         {
261                 if (!have_createrole_privilege())
262                         ereport(ERROR,
263                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
264                                          errmsg("permission denied to create role")));
265         }
266
267         if (strcmp(stmt->role, "public") == 0 ||
268                 strcmp(stmt->role, "none") == 0)
269                 ereport(ERROR,
270                                 (errcode(ERRCODE_RESERVED_NAME),
271                                  errmsg("role name \"%s\" is reserved",
272                                                 stmt->role)));
273
274         /*
275          * Check the pg_authid relation to be certain the role doesn't already
276          * exist.  Note we secure exclusive lock because we need to protect our
277          * eventual update of the flat auth file.
278          */
279         pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
280         pg_authid_dsc = RelationGetDescr(pg_authid_rel);
281
282         tuple = SearchSysCache(AUTHNAME,
283                                                    PointerGetDatum(stmt->role),
284                                                    0, 0, 0);
285         if (HeapTupleIsValid(tuple))
286                 ereport(ERROR,
287                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
288                                  errmsg("role \"%s\" already exists",
289                                                 stmt->role)));
290
291         /*
292          * Build a tuple to insert
293          */
294         MemSet(new_record, 0, sizeof(new_record));
295         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
296
297         new_record[Anum_pg_authid_rolname - 1] =
298                 DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
299
300         new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
301         new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
302         new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
303         new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
304         /* superuser gets catupdate right by default */
305         new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
306         new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
307         new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
308
309         if (password)
310         {
311                 if (!encrypt_password || isMD5(password))
312                         new_record[Anum_pg_authid_rolpassword - 1] =
313                                 DirectFunctionCall1(textin, CStringGetDatum(password));
314                 else
315                 {
316                         if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
317                                                                 encrypted_password))
318                                 elog(ERROR, "password encryption failed");
319                         new_record[Anum_pg_authid_rolpassword - 1] =
320                                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
321                 }
322         }
323         else
324                 new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n';
325
326         if (validUntil)
327                 new_record[Anum_pg_authid_rolvaliduntil - 1] =
328                         DirectFunctionCall3(timestamptz_in,
329                                                                 CStringGetDatum(validUntil),
330                                                                 ObjectIdGetDatum(InvalidOid),
331                                                                 Int32GetDatum(-1));
332
333         else
334                 new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = 'n';
335
336         new_record_nulls[Anum_pg_authid_rolconfig - 1] = 'n';
337
338         tuple = heap_formtuple(pg_authid_dsc, new_record, new_record_nulls);
339
340         /*
341          * Insert new record in the pg_authid table
342          */
343         roleid = simple_heap_insert(pg_authid_rel, tuple);
344         CatalogUpdateIndexes(pg_authid_rel, tuple);
345
346         /*
347          * Advance command counter so we can see new record; else tests in
348          * AddRoleMems may fail.
349          */
350         if (addroleto || adminmembers || rolemembers)
351                 CommandCounterIncrement();
352
353         /*
354          * Add the new role to the specified existing roles.
355          */
356         foreach(item, addroleto)
357         {
358                 char       *oldrolename = strVal(lfirst(item));
359                 Oid                     oldroleid = get_roleid_checked(oldrolename);
360
361                 AddRoleMems(oldrolename, oldroleid,
362                                         list_make1(makeString(stmt->role)),
363                                         list_make1_oid(roleid),
364                                         GetUserId(), false);
365         }
366
367         /*
368          * Add the specified members to this new role. adminmembers get the admin
369          * option, rolemembers don't.
370          */
371         AddRoleMems(stmt->role, roleid,
372                                 adminmembers, roleNamesToIds(adminmembers),
373                                 GetUserId(), true);
374         AddRoleMems(stmt->role, roleid,
375                                 rolemembers, roleNamesToIds(rolemembers),
376                                 GetUserId(), false);
377
378         /*
379          * Now we can clean up; but keep lock until commit (to avoid possible
380          * deadlock when commit code tries to acquire lock).
381          */
382         heap_close(pg_authid_rel, NoLock);
383
384         /*
385          * Set flag to update flat auth file at commit.
386          */
387         auth_file_update_needed();
388 }
389
390
391 /*
392  * ALTER ROLE
393  *
394  * Note: the rolemembers option accepted here is intended to support the
395  * backwards-compatible ALTER GROUP syntax.  Although it will work to say
396  * "ALTER ROLE role ROLE rolenames", we don't document it.
397  */
398 void
399 AlterRole(AlterRoleStmt *stmt)
400 {
401         Datum           new_record[Natts_pg_authid];
402         char            new_record_nulls[Natts_pg_authid];
403         char            new_record_repl[Natts_pg_authid];
404         Relation        pg_authid_rel;
405         TupleDesc       pg_authid_dsc;
406         HeapTuple       tuple,
407                                 new_tuple;
408         ListCell   *option;
409         char       *password = NULL;    /* user password */
410         bool            encrypt_password = Password_encryption; /* encrypt password? */
411         char            encrypted_password[MD5_PASSWD_LEN + 1];
412         int                     issuper = -1;   /* Make the user a superuser? */
413         int                     inherit = -1;   /* Auto inherit privileges? */
414         int                     createrole = -1;        /* Can this user create roles? */
415         int                     createdb = -1;  /* Can the user create databases? */
416         int                     canlogin = -1;  /* Can this user login? */
417         int                     connlimit = -1; /* maximum connections allowed */
418         List       *rolemembers = NIL;          /* roles to be added/removed */
419         char       *validUntil = NULL;          /* time the login is valid until */
420         DefElem    *dpassword = NULL;
421         DefElem    *dissuper = NULL;
422         DefElem    *dinherit = NULL;
423         DefElem    *dcreaterole = NULL;
424         DefElem    *dcreatedb = NULL;
425         DefElem    *dcanlogin = NULL;
426         DefElem    *dconnlimit = NULL;
427         DefElem    *drolemembers = NULL;
428         DefElem    *dvalidUntil = NULL;
429         Oid                     roleid;
430
431         /* Extract options from the statement node tree */
432         foreach(option, stmt->options)
433         {
434                 DefElem    *defel = (DefElem *) lfirst(option);
435
436                 if (strcmp(defel->defname, "password") == 0 ||
437                         strcmp(defel->defname, "encryptedPassword") == 0 ||
438                         strcmp(defel->defname, "unencryptedPassword") == 0)
439                 {
440                         if (dpassword)
441                                 ereport(ERROR,
442                                                 (errcode(ERRCODE_SYNTAX_ERROR),
443                                                  errmsg("conflicting or redundant options")));
444                         dpassword = defel;
445                         if (strcmp(defel->defname, "encryptedPassword") == 0)
446                                 encrypt_password = true;
447                         else if (strcmp(defel->defname, "unencryptedPassword") == 0)
448                                 encrypt_password = false;
449                 }
450                 else if (strcmp(defel->defname, "superuser") == 0)
451                 {
452                         if (dissuper)
453                                 ereport(ERROR,
454                                                 (errcode(ERRCODE_SYNTAX_ERROR),
455                                                  errmsg("conflicting or redundant options")));
456                         dissuper = defel;
457                 }
458                 else if (strcmp(defel->defname, "inherit") == 0)
459                 {
460                         if (dinherit)
461                                 ereport(ERROR,
462                                                 (errcode(ERRCODE_SYNTAX_ERROR),
463                                                  errmsg("conflicting or redundant options")));
464                         dinherit = defel;
465                 }
466                 else if (strcmp(defel->defname, "createrole") == 0)
467                 {
468                         if (dcreaterole)
469                                 ereport(ERROR,
470                                                 (errcode(ERRCODE_SYNTAX_ERROR),
471                                                  errmsg("conflicting or redundant options")));
472                         dcreaterole = defel;
473                 }
474                 else if (strcmp(defel->defname, "createdb") == 0)
475                 {
476                         if (dcreatedb)
477                                 ereport(ERROR,
478                                                 (errcode(ERRCODE_SYNTAX_ERROR),
479                                                  errmsg("conflicting or redundant options")));
480                         dcreatedb = defel;
481                 }
482                 else if (strcmp(defel->defname, "canlogin") == 0)
483                 {
484                         if (dcanlogin)
485                                 ereport(ERROR,
486                                                 (errcode(ERRCODE_SYNTAX_ERROR),
487                                                  errmsg("conflicting or redundant options")));
488                         dcanlogin = defel;
489                 }
490                 else if (strcmp(defel->defname, "connectionlimit") == 0)
491                 {
492                         if (dconnlimit)
493                                 ereport(ERROR,
494                                                 (errcode(ERRCODE_SYNTAX_ERROR),
495                                                  errmsg("conflicting or redundant options")));
496                         dconnlimit = defel;
497                 }
498                 else if (strcmp(defel->defname, "rolemembers") == 0 &&
499                                  stmt->action != 0)
500                 {
501                         if (drolemembers)
502                                 ereport(ERROR,
503                                                 (errcode(ERRCODE_SYNTAX_ERROR),
504                                                  errmsg("conflicting or redundant options")));
505                         drolemembers = defel;
506                 }
507                 else if (strcmp(defel->defname, "validUntil") == 0)
508                 {
509                         if (dvalidUntil)
510                                 ereport(ERROR,
511                                                 (errcode(ERRCODE_SYNTAX_ERROR),
512                                                  errmsg("conflicting or redundant options")));
513                         dvalidUntil = defel;
514                 }
515                 else
516                         elog(ERROR, "option \"%s\" not recognized",
517                                  defel->defname);
518         }
519
520         if (dpassword)
521                 password = strVal(dpassword->arg);
522         if (dissuper)
523                 issuper = intVal(dissuper->arg);
524         if (dinherit)
525                 inherit = intVal(dinherit->arg);
526         if (dcreaterole)
527                 createrole = intVal(dcreaterole->arg);
528         if (dcreatedb)
529                 createdb = intVal(dcreatedb->arg);
530         if (dcanlogin)
531                 canlogin = intVal(dcanlogin->arg);
532         if (dconnlimit)
533                 connlimit = intVal(dconnlimit->arg);
534         if (drolemembers)
535                 rolemembers = (List *) drolemembers->arg;
536         if (dvalidUntil)
537                 validUntil = strVal(dvalidUntil->arg);
538
539         /*
540          * Scan the pg_authid relation to be certain the user exists. Note we
541          * secure exclusive lock to protect our update of the flat auth file.
542          */
543         pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
544         pg_authid_dsc = RelationGetDescr(pg_authid_rel);
545
546         tuple = SearchSysCache(AUTHNAME,
547                                                    PointerGetDatum(stmt->role),
548                                                    0, 0, 0);
549         if (!HeapTupleIsValid(tuple))
550                 ereport(ERROR,
551                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
552                                  errmsg("role \"%s\" does not exist", stmt->role)));
553
554         roleid = HeapTupleGetOid(tuple);
555
556         /*
557          * To mess with a superuser you gotta be superuser; else you need
558          * createrole, or just want to change your own password
559          */
560         if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
561         {
562                 if (!superuser())
563                         ereport(ERROR,
564                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
565                                          errmsg("must be superuser to alter superusers")));
566         }
567         else if (!have_createrole_privilege())
568         {
569                 if (!(inherit < 0 &&
570                           createrole < 0 &&
571                           createdb < 0 &&
572                           canlogin < 0 &&
573                           !dconnlimit &&
574                           !rolemembers &&
575                           !validUntil &&
576                           password &&
577                           roleid == GetUserId()))
578                         ereport(ERROR,
579                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
580                                          errmsg("permission denied")));
581         }
582
583         /*
584          * Build an updated tuple, perusing the information just obtained
585          */
586         MemSet(new_record, 0, sizeof(new_record));
587         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
588         MemSet(new_record_repl, ' ', sizeof(new_record_repl));
589
590         /*
591          * issuper/createrole/catupdate/etc
592          *
593          * XXX It's rather unclear how to handle catupdate.  It's probably best to
594          * keep it equal to the superuser status, otherwise you could end up with
595          * a situation where no existing superuser can alter the catalogs,
596          * including pg_authid!
597          */
598         if (issuper >= 0)
599         {
600                 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
601                 new_record_repl[Anum_pg_authid_rolsuper - 1] = 'r';
602
603                 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
604                 new_record_repl[Anum_pg_authid_rolcatupdate - 1] = 'r';
605         }
606
607         if (inherit >= 0)
608         {
609                 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
610                 new_record_repl[Anum_pg_authid_rolinherit - 1] = 'r';
611         }
612
613         if (createrole >= 0)
614         {
615                 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
616                 new_record_repl[Anum_pg_authid_rolcreaterole - 1] = 'r';
617         }
618
619         if (createdb >= 0)
620         {
621                 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
622                 new_record_repl[Anum_pg_authid_rolcreatedb - 1] = 'r';
623         }
624
625         if (canlogin >= 0)
626         {
627                 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
628                 new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r';
629         }
630
631         if (dconnlimit)
632         {
633                 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
634                 new_record_repl[Anum_pg_authid_rolconnlimit - 1] = 'r';
635         }
636
637         /* password */
638         if (password)
639         {
640                 if (!encrypt_password || isMD5(password))
641                         new_record[Anum_pg_authid_rolpassword - 1] =
642                                 DirectFunctionCall1(textin, CStringGetDatum(password));
643                 else
644                 {
645                         if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
646                                                                 encrypted_password))
647                                 elog(ERROR, "password encryption failed");
648                         new_record[Anum_pg_authid_rolpassword - 1] =
649                                 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
650                 }
651                 new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r';
652         }
653
654         /* valid until */
655         if (validUntil)
656         {
657                 new_record[Anum_pg_authid_rolvaliduntil - 1] =
658                         DirectFunctionCall3(timestamptz_in,
659                                                                 CStringGetDatum(validUntil),
660                                                                 ObjectIdGetDatum(InvalidOid),
661                                                                 Int32GetDatum(-1));
662                 new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = 'r';
663         }
664
665         new_tuple = heap_modifytuple(tuple, pg_authid_dsc, new_record,
666                                                                  new_record_nulls, new_record_repl);
667         simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
668
669         /* Update indexes */
670         CatalogUpdateIndexes(pg_authid_rel, new_tuple);
671
672         ReleaseSysCache(tuple);
673         heap_freetuple(new_tuple);
674
675         /*
676          * Advance command counter so we can see new record; else tests in
677          * AddRoleMems may fail.
678          */
679         if (rolemembers)
680                 CommandCounterIncrement();
681
682         if (stmt->action == +1)         /* add members to role */
683                 AddRoleMems(stmt->role, roleid,
684                                         rolemembers, roleNamesToIds(rolemembers),
685                                         GetUserId(), false);
686         else if (stmt->action == -1)    /* drop members from role */
687                 DelRoleMems(stmt->role, roleid,
688                                         rolemembers, roleNamesToIds(rolemembers),
689                                         false);
690
691         /*
692          * Now we can clean up; but keep lock until commit (to avoid possible
693          * deadlock when commit code tries to acquire lock).
694          */
695         heap_close(pg_authid_rel, NoLock);
696
697         /*
698          * Set flag to update flat auth file at commit.
699          */
700         auth_file_update_needed();
701 }
702
703
704 /*
705  * ALTER ROLE ... SET
706  */
707 void
708 AlterRoleSet(AlterRoleSetStmt *stmt)
709 {
710         char       *valuestr;
711         HeapTuple       oldtuple,
712                                 newtuple;
713         Relation        rel;
714         Datum           repl_val[Natts_pg_authid];
715         char            repl_null[Natts_pg_authid];
716         char            repl_repl[Natts_pg_authid];
717         int                     i;
718
719         valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
720
721         /*
722          * RowExclusiveLock is sufficient, because we don't need to update the
723          * flat auth file.
724          */
725         rel = heap_open(AuthIdRelationId, RowExclusiveLock);
726         oldtuple = SearchSysCache(AUTHNAME,
727                                                           PointerGetDatum(stmt->role),
728                                                           0, 0, 0);
729         if (!HeapTupleIsValid(oldtuple))
730                 ereport(ERROR,
731                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
732                                  errmsg("role \"%s\" does not exist", stmt->role)));
733
734         /*
735          * To mess with a superuser you gotta be superuser; else you need
736          * createrole, or just want to change your own settings
737          */
738         if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
739         {
740                 if (!superuser())
741                         ereport(ERROR,
742                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
743                                          errmsg("must be superuser to alter superusers")));
744         }
745         else
746         {
747                 if (!have_createrole_privilege() &&
748                         HeapTupleGetOid(oldtuple) != GetUserId())
749                         ereport(ERROR,
750                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
751                                          errmsg("permission denied")));
752         }
753
754         for (i = 0; i < Natts_pg_authid; i++)
755                 repl_repl[i] = ' ';
756
757         repl_repl[Anum_pg_authid_rolconfig - 1] = 'r';
758         if (strcmp(stmt->variable, "all") == 0 && valuestr == NULL)
759         {
760                 /* RESET ALL */
761                 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
762         }
763         else
764         {
765                 Datum           datum;
766                 bool            isnull;
767                 ArrayType  *array;
768
769                 repl_null[Anum_pg_authid_rolconfig - 1] = ' ';
770
771                 datum = SysCacheGetAttr(AUTHNAME, oldtuple,
772                                                                 Anum_pg_authid_rolconfig, &isnull);
773
774                 array = isnull ? NULL : DatumGetArrayTypeP(datum);
775
776                 if (valuestr)
777                         array = GUCArrayAdd(array, stmt->variable, valuestr);
778                 else
779                         array = GUCArrayDelete(array, stmt->variable);
780
781                 if (array)
782                         repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array);
783                 else
784                         repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
785         }
786
787         newtuple = heap_modifytuple(oldtuple, RelationGetDescr(rel),
788                                                                 repl_val, repl_null, repl_repl);
789
790         simple_heap_update(rel, &oldtuple->t_self, newtuple);
791         CatalogUpdateIndexes(rel, newtuple);
792
793         ReleaseSysCache(oldtuple);
794         heap_close(rel, RowExclusiveLock);
795 }
796
797
798 /*
799  * DROP ROLE
800  */
801 void
802 DropRole(DropRoleStmt *stmt)
803 {
804         Relation        pg_authid_rel,
805                                 pg_auth_members_rel;
806         ListCell   *item;
807
808         if (!have_createrole_privilege())
809                 ereport(ERROR,
810                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
811                                  errmsg("permission denied to drop role")));
812
813         /*
814          * Scan the pg_authid relation to find the Oid of the role(s) to be
815          * deleted.  Note we secure exclusive lock on pg_authid, because we need
816          * to protect our update of the flat auth file.  A regular writer's lock
817          * on pg_auth_members is sufficient though.
818          */
819         pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
820         pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
821
822         foreach(item, stmt->roles)
823         {
824                 const char *role = strVal(lfirst(item));
825                 HeapTuple       tuple,
826                                         tmp_tuple;
827                 ScanKeyData scankey;
828                 char       *detail;
829                 SysScanDesc sscan;
830                 Oid                     roleid;
831
832                 tuple = SearchSysCache(AUTHNAME,
833                                                            PointerGetDatum(role),
834                                                            0, 0, 0);
835                 if (!HeapTupleIsValid(tuple))
836                         ereport(ERROR,
837                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
838                                          errmsg("role \"%s\" does not exist", role)));
839
840                 roleid = HeapTupleGetOid(tuple);
841
842                 if (roleid == GetUserId())
843                         ereport(ERROR,
844                                         (errcode(ERRCODE_OBJECT_IN_USE),
845                                          errmsg("current user cannot be dropped")));
846                 if (roleid == GetOuterUserId())
847                         ereport(ERROR,
848                                         (errcode(ERRCODE_OBJECT_IN_USE),
849                                          errmsg("current user cannot be dropped")));
850                 if (roleid == GetSessionUserId())
851                         ereport(ERROR,
852                                         (errcode(ERRCODE_OBJECT_IN_USE),
853                                          errmsg("session user cannot be dropped")));
854
855                 /*
856                  * For safety's sake, we allow createrole holders to drop ordinary
857                  * roles but not superuser roles.  This is mainly to avoid the
858                  * scenario where you accidentally drop the last superuser.
859                  */
860                 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
861                         !superuser())
862                         ereport(ERROR,
863                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
864                                          errmsg("must be superuser to drop superusers")));
865
866                 /*
867                  * Lock the role, so nobody can add dependencies to her while we drop
868                  * her.  We keep the lock until the end of transaction.
869                  */
870                 LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
871
872                 /* Check for pg_shdepend entries depending on this role */
873                 if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL)
874                         ereport(ERROR,
875                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
876                                          errmsg("role \"%s\" cannot be dropped because some objects depend on it",
877                                                         role),
878                                          errdetail("%s", detail)));
879
880                 /*
881                  * Remove the role from the pg_authid table
882                  */
883                 simple_heap_delete(pg_authid_rel, &tuple->t_self);
884
885                 ReleaseSysCache(tuple);
886
887                 /*
888                  * Remove role from the pg_auth_members table.  We have to remove all
889                  * tuples that show it as either a role or a member.
890                  *
891                  * XXX what about grantor entries?      Maybe we should do one heap scan.
892                  */
893                 ScanKeyInit(&scankey,
894                                         Anum_pg_auth_members_roleid,
895                                         BTEqualStrategyNumber, F_OIDEQ,
896                                         ObjectIdGetDatum(roleid));
897
898                 sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
899                                                                    true, SnapshotNow, 1, &scankey);
900
901                 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
902                 {
903                         simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
904                 }
905
906                 systable_endscan(sscan);
907
908                 ScanKeyInit(&scankey,
909                                         Anum_pg_auth_members_member,
910                                         BTEqualStrategyNumber, F_OIDEQ,
911                                         ObjectIdGetDatum(roleid));
912
913                 sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
914                                                                    true, SnapshotNow, 1, &scankey);
915
916                 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
917                 {
918                         simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
919                 }
920
921                 systable_endscan(sscan);
922
923                 /*
924                  * Advance command counter so that later iterations of this loop will
925                  * see the changes already made.  This is essential if, for example,
926                  * we are trying to drop both a role and one of its direct members ---
927                  * we'll get an error if we try to delete the linking pg_auth_members
928                  * tuple twice.  (We do not need a CCI between the two delete loops
929                  * above, because it's not allowed for a role to directly contain
930                  * itself.)
931                  */
932                 CommandCounterIncrement();
933         }
934
935         /*
936          * Now we can clean up; but keep locks until commit (to avoid possible
937          * deadlock when commit code tries to acquire lock).
938          */
939         heap_close(pg_auth_members_rel, NoLock);
940         heap_close(pg_authid_rel, NoLock);
941
942         /*
943          * Set flag to update flat auth file at commit.
944          */
945         auth_file_update_needed();
946 }
947
948 /*
949  * Rename role
950  */
951 void
952 RenameRole(const char *oldname, const char *newname)
953 {
954         HeapTuple       oldtuple,
955                                 newtuple;
956         TupleDesc       dsc;
957         Relation        rel;
958         Datum           datum;
959         bool            isnull;
960         Datum           repl_val[Natts_pg_authid];
961         char            repl_null[Natts_pg_authid];
962         char            repl_repl[Natts_pg_authid];
963         int                     i;
964         Oid                     roleid;
965
966         /* ExclusiveLock because we need to update the flat auth file */
967         rel = heap_open(AuthIdRelationId, ExclusiveLock);
968         dsc = RelationGetDescr(rel);
969
970         oldtuple = SearchSysCache(AUTHNAME,
971                                                           CStringGetDatum(oldname),
972                                                           0, 0, 0);
973         if (!HeapTupleIsValid(oldtuple))
974                 ereport(ERROR,
975                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
976                                  errmsg("role \"%s\" does not exist", oldname)));
977
978         /*
979          * XXX Client applications probably store the session user somewhere, so
980          * renaming it could cause confusion.  On the other hand, there may not be
981          * an actual problem besides a little confusion, so think about this and
982          * decide.      Same for SET ROLE ... we don't restrict renaming the current
983          * effective userid, though.
984          */
985
986         roleid = HeapTupleGetOid(oldtuple);
987
988         if (roleid == GetSessionUserId())
989                 ereport(ERROR,
990                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
991                                  errmsg("session user may not be renamed")));
992         if (roleid == GetOuterUserId())
993                 ereport(ERROR,
994                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
995                                  errmsg("current user may not be renamed")));
996
997         /* make sure the new name doesn't exist */
998         if (SearchSysCacheExists(AUTHNAME,
999                                                          CStringGetDatum(newname),
1000                                                          0, 0, 0))
1001                 ereport(ERROR,
1002                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
1003                                  errmsg("role \"%s\" already exists", newname)));
1004
1005         if (strcmp(newname, "public") == 0 ||
1006                 strcmp(newname, "none") == 0)
1007                 ereport(ERROR,
1008                                 (errcode(ERRCODE_RESERVED_NAME),
1009                                  errmsg("role name \"%s\" is reserved",
1010                                                 newname)));
1011
1012         /*
1013          * createrole is enough privilege unless you want to mess with a superuser
1014          */
1015         if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
1016         {
1017                 if (!superuser())
1018                         ereport(ERROR,
1019                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1020                                          errmsg("must be superuser to rename superusers")));
1021         }
1022         else
1023         {
1024                 if (!have_createrole_privilege())
1025                         ereport(ERROR,
1026                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1027                                          errmsg("permission denied to rename role")));
1028         }
1029
1030         /* OK, construct the modified tuple */
1031         for (i = 0; i < Natts_pg_authid; i++)
1032                 repl_repl[i] = ' ';
1033
1034         repl_repl[Anum_pg_authid_rolname - 1] = 'r';
1035         repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1036                                                                                                    CStringGetDatum(newname));
1037         repl_null[Anum_pg_authid_rolname - 1] = ' ';
1038
1039         datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1040
1041         if (!isnull && isMD5(DatumGetCString(DirectFunctionCall1(textout, datum))))
1042         {
1043                 /* MD5 uses the username as salt, so just clear it on a rename */
1044                 repl_repl[Anum_pg_authid_rolpassword - 1] = 'r';
1045                 repl_null[Anum_pg_authid_rolpassword - 1] = 'n';
1046
1047                 ereport(NOTICE,
1048                                 (errmsg("MD5 password cleared because of role rename")));
1049         }
1050
1051         newtuple = heap_modifytuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1052         simple_heap_update(rel, &oldtuple->t_self, newtuple);
1053
1054         CatalogUpdateIndexes(rel, newtuple);
1055
1056         ReleaseSysCache(oldtuple);
1057         heap_close(rel, NoLock);
1058
1059         /*
1060          * Set flag to update flat auth file at commit.
1061          */
1062         auth_file_update_needed();
1063 }
1064
1065 /*
1066  * GrantRoleStmt
1067  *
1068  * Grant/Revoke roles to/from roles
1069  */
1070 void
1071 GrantRole(GrantRoleStmt *stmt)
1072 {
1073         Relation        pg_authid_rel;
1074         Oid                     grantor;
1075         List       *grantee_ids;
1076         ListCell   *item;
1077
1078         if (stmt->grantor)
1079                 grantor = get_roleid_checked(stmt->grantor);
1080         else
1081                 grantor = GetUserId();
1082
1083         grantee_ids = roleNamesToIds(stmt->grantee_roles);
1084
1085         /*
1086          * Even though this operation doesn't change pg_authid, we must secure
1087          * exclusive lock on it to protect our update of the flat auth file.
1088          */
1089         pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
1090
1091         /*
1092          * Step through all of the granted roles and add/remove entries for the
1093          * grantees, or, if admin_opt is set, then just add/remove the admin
1094          * option.
1095          *
1096          * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1097          */
1098         foreach(item, stmt->granted_roles)
1099         {
1100                 char       *rolename = strVal(lfirst(item));
1101                 Oid                     roleid = get_roleid_checked(rolename);
1102
1103                 if (stmt->is_grant)
1104                         AddRoleMems(rolename, roleid,
1105                                                 stmt->grantee_roles, grantee_ids,
1106                                                 grantor, stmt->admin_opt);
1107                 else
1108                         DelRoleMems(rolename, roleid,
1109                                                 stmt->grantee_roles, grantee_ids,
1110                                                 stmt->admin_opt);
1111         }
1112
1113         heap_close(pg_authid_rel, NoLock);
1114
1115         /*
1116          * Set flag to update flat auth file at commit.
1117          */
1118         auth_file_update_needed();
1119 }
1120
1121 /*
1122  * roleNamesToIds
1123  *
1124  * Given a list of role names (as String nodes), generate a list of role OIDs
1125  * in the same order.
1126  */
1127 static List *
1128 roleNamesToIds(List *memberNames)
1129 {
1130         List       *result = NIL;
1131         ListCell   *l;
1132
1133         foreach(l, memberNames)
1134         {
1135                 char       *rolename = strVal(lfirst(l));
1136                 Oid                     roleid = get_roleid_checked(rolename);
1137
1138                 result = lappend_oid(result, roleid);
1139         }
1140         return result;
1141 }
1142
1143 /*
1144  * AddRoleMems -- Add given members to the specified role
1145  *
1146  * rolename: name of role to add to (used only for error messages)
1147  * roleid: OID of role to add to
1148  * memberNames: list of names of roles to add (used only for error messages)
1149  * memberIds: OIDs of roles to add
1150  * grantorId: who is granting the membership
1151  * admin_opt: granting admin option?
1152  *
1153  * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1154  * and for calling auth_file_update_needed().
1155  */
1156 static void
1157 AddRoleMems(const char *rolename, Oid roleid,
1158                         List *memberNames, List *memberIds,
1159                         Oid grantorId, bool admin_opt)
1160 {
1161         Relation        pg_authmem_rel;
1162         TupleDesc       pg_authmem_dsc;
1163         ListCell   *nameitem;
1164         ListCell   *iditem;
1165
1166         Assert(list_length(memberNames) == list_length(memberIds));
1167
1168         /* Skip permission check if nothing to do */
1169         if (!memberIds)
1170                 return;
1171
1172         /*
1173          * Check permissions: must have createrole or admin option on the role to
1174          * be changed.  To mess with a superuser role, you gotta be superuser.
1175          */
1176         if (superuser_arg(roleid))
1177         {
1178                 if (!superuser())
1179                         ereport(ERROR,
1180                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1181                                          errmsg("must be superuser to alter superusers")));
1182         }
1183         else
1184         {
1185                 if (!have_createrole_privilege() &&
1186                         !is_admin_of_role(grantorId, roleid))
1187                         ereport(ERROR,
1188                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1189                                          errmsg("must have admin option on role \"%s\"",
1190                                                         rolename)));
1191         }
1192
1193         /* XXX not sure about this check */
1194         if (grantorId != GetUserId() && !superuser())
1195                 ereport(ERROR,
1196                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1197                                  errmsg("must be superuser to set grantor")));
1198
1199         /* We need only regular writer's lock on pg_auth_members */
1200         pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1201         pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1202
1203         forboth(nameitem, memberNames, iditem, memberIds)
1204         {
1205                 const char *membername = strVal(lfirst(nameitem));
1206                 Oid                     memberid = lfirst_oid(iditem);
1207                 HeapTuple       authmem_tuple;
1208                 HeapTuple       tuple;
1209                 Datum           new_record[Natts_pg_auth_members];
1210                 char            new_record_nulls[Natts_pg_auth_members];
1211                 char            new_record_repl[Natts_pg_auth_members];
1212
1213                 /*
1214                  * Refuse creation of membership loops, including the trivial case
1215                  * where a role is made a member of itself.  We do this by checking to
1216                  * see if the target role is already a member of the proposed member
1217                  * role.  We have to ignore possible superuserness, however, else we
1218                  * could never grant membership in a superuser-privileged role.
1219                  */
1220                 if (is_member_of_role_nosuper(roleid, memberid))
1221                         ereport(ERROR,
1222                                         (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1223                                          (errmsg("role \"%s\" is a member of role \"%s\"",
1224                                                          rolename, membername))));
1225
1226                 /*
1227                  * Check if entry for this role/member already exists; if so, give
1228                  * warning unless we are adding admin option.
1229                  */
1230                 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1231                                                                            ObjectIdGetDatum(roleid),
1232                                                                            ObjectIdGetDatum(memberid),
1233                                                                            0, 0);
1234                 if (HeapTupleIsValid(authmem_tuple) &&
1235                         (!admin_opt ||
1236                          ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1237                 {
1238                         ereport(NOTICE,
1239                                         (errmsg("role \"%s\" is already a member of role \"%s\"",
1240                                                         membername, rolename)));
1241                         ReleaseSysCache(authmem_tuple);
1242                         continue;
1243                 }
1244
1245                 /* Build a tuple to insert or update */
1246                 MemSet(new_record, 0, sizeof(new_record));
1247                 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1248                 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1249
1250                 new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1251                 new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1252                 new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1253                 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1254
1255                 if (HeapTupleIsValid(authmem_tuple))
1256                 {
1257                         new_record_repl[Anum_pg_auth_members_grantor - 1] = 'r';
1258                         new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1259                         tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1260                                                                          new_record,
1261                                                                          new_record_nulls, new_record_repl);
1262                         simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1263                         CatalogUpdateIndexes(pg_authmem_rel, tuple);
1264                         ReleaseSysCache(authmem_tuple);
1265                 }
1266                 else
1267                 {
1268                         tuple = heap_formtuple(pg_authmem_dsc,
1269                                                                    new_record, new_record_nulls);
1270                         simple_heap_insert(pg_authmem_rel, tuple);
1271                         CatalogUpdateIndexes(pg_authmem_rel, tuple);
1272                 }
1273
1274                 /* CCI after each change, in case there are duplicates in list */
1275                 CommandCounterIncrement();
1276         }
1277
1278         /*
1279          * Now we can clean up; but keep lock until commit (to avoid possible
1280          * deadlock when commit code tries to acquire lock).
1281          */
1282         heap_close(pg_authmem_rel, NoLock);
1283 }
1284
1285 /*
1286  * DelRoleMems -- Remove given members from the specified role
1287  *
1288  * rolename: name of role to del from (used only for error messages)
1289  * roleid: OID of role to del from
1290  * memberNames: list of names of roles to del (used only for error messages)
1291  * memberIds: OIDs of roles to del
1292  * admin_opt: remove admin option only?
1293  *
1294  * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1295  * and for calling auth_file_update_needed().
1296  */
1297 static void
1298 DelRoleMems(const char *rolename, Oid roleid,
1299                         List *memberNames, List *memberIds,
1300                         bool admin_opt)
1301 {
1302         Relation        pg_authmem_rel;
1303         TupleDesc       pg_authmem_dsc;
1304         ListCell   *nameitem;
1305         ListCell   *iditem;
1306
1307         Assert(list_length(memberNames) == list_length(memberIds));
1308
1309         /* Skip permission check if nothing to do */
1310         if (!memberIds)
1311                 return;
1312
1313         /*
1314          * Check permissions: must have createrole or admin option on the role to
1315          * be changed.  To mess with a superuser role, you gotta be superuser.
1316          */
1317         if (superuser_arg(roleid))
1318         {
1319                 if (!superuser())
1320                         ereport(ERROR,
1321                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1322                                          errmsg("must be superuser to alter superusers")));
1323         }
1324         else
1325         {
1326                 if (!have_createrole_privilege() &&
1327                         !is_admin_of_role(GetUserId(), roleid))
1328                         ereport(ERROR,
1329                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1330                                          errmsg("must have admin option on role \"%s\"",
1331                                                         rolename)));
1332         }
1333
1334         /* We need only regular writer's lock on pg_auth_members */
1335         pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1336         pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1337
1338         forboth(nameitem, memberNames, iditem, memberIds)
1339         {
1340                 const char *membername = strVal(lfirst(nameitem));
1341                 Oid                     memberid = lfirst_oid(iditem);
1342                 HeapTuple       authmem_tuple;
1343
1344                 /*
1345                  * Find entry for this role/member
1346                  */
1347                 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1348                                                                            ObjectIdGetDatum(roleid),
1349                                                                            ObjectIdGetDatum(memberid),
1350                                                                            0, 0);
1351                 if (!HeapTupleIsValid(authmem_tuple))
1352                 {
1353                         ereport(WARNING,
1354                                         (errmsg("role \"%s\" is not a member of role \"%s\"",
1355                                                         membername, rolename)));
1356                         continue;
1357                 }
1358
1359                 if (!admin_opt)
1360                 {
1361                         /* Remove the entry altogether */
1362                         simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
1363                 }
1364                 else
1365                 {
1366                         /* Just turn off the admin option */
1367                         HeapTuple       tuple;
1368                         Datum           new_record[Natts_pg_auth_members];
1369                         char            new_record_nulls[Natts_pg_auth_members];
1370                         char            new_record_repl[Natts_pg_auth_members];
1371
1372                         /* Build a tuple to update with */
1373                         MemSet(new_record, 0, sizeof(new_record));
1374                         MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1375                         MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1376
1377                         new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1378                         new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1379
1380                         tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1381                                                                          new_record,
1382                                                                          new_record_nulls, new_record_repl);
1383                         simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1384                         CatalogUpdateIndexes(pg_authmem_rel, tuple);
1385                 }
1386
1387                 ReleaseSysCache(authmem_tuple);
1388
1389                 /* CCI after each change, in case there are duplicates in list */
1390                 CommandCounterIncrement();
1391         }
1392
1393         /*
1394          * Now we can clean up; but keep lock until commit (to avoid possible
1395          * deadlock when commit code tries to acquire lock).
1396          */
1397         heap_close(pg_authmem_rel, NoLock);
1398 }