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