1 /*-------------------------------------------------------------------------
4 * Commands for manipulating users and groups.
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.92 2002/03/02 21:39:23 momjian Exp $
11 *-------------------------------------------------------------------------
15 #include <sys/types.h>
20 #include "access/heapam.h"
21 #include "catalog/catname.h"
22 #include "catalog/pg_database.h"
23 #include "catalog/pg_shadow.h"
24 #include "catalog/pg_group.h"
25 #include "catalog/indexing.h"
26 #include "commands/user.h"
27 #include "libpq/crypt.h"
28 #include "miscadmin.h"
29 #include "storage/pmsignal.h"
30 #include "utils/array.h"
31 #include "utils/builtins.h"
32 #include "utils/fmgroids.h"
33 #include "utils/guc.h"
34 #include "utils/lsyscache.h"
35 #include "utils/syscache.h"
38 extern bool Password_encryption;
40 static void CheckPgUserAclNotNull(void);
42 /*---------------------------------------------------------------------
43 * write_password_file / update_pg_pwd
45 * copy the modified contents of pg_shadow to a file used by the postmaster
46 * for user authentication. The file is stored as $PGDATA/global/pg_pwd.
48 * This function set is both a trigger function for direct updates to pg_shadow
49 * as well as being called directly from create/alter/drop user.
51 * We raise an error to force transaction rollback if we detect an illegal
52 * username or password --- illegal being defined as values that would
53 * mess up the pg_pwd parser.
54 *---------------------------------------------------------------------
57 write_password_file(Relation rel)
66 TupleDesc dsc = RelationGetDescr(rel);
69 * Create a temporary filename to be renamed later. This prevents the
70 * backend from clobbering the pg_pwd file while the postmaster might
73 filename = crypt_getpwdfilename();
74 bufsize = strlen(filename) + 12;
75 tempname = (char *) palloc(bufsize);
77 snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
78 oumask = umask((mode_t) 077);
79 fp = AllocateFile(tempname, "w");
82 elog(ERROR, "write_password_file: unable to write %s: %m", tempname);
85 scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
86 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
99 datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
101 continue; /* ignore NULL usernames */
102 str_n = DatumGetCString(DirectFunctionCall1(nameout, datum_n));
104 datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
107 * It can be argued that people having a null password shouldn't
108 * be allowed to connect under password authentication, because
109 * they need to have a password set up first. If you think
110 * assuming an empty password in that case is better, change this
111 * logic to look something like the code for valuntil.
118 str_p = DatumGetCString(DirectFunctionCall1(textout, datum_p));
120 datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
122 str_v = pstrdup("\\N");
124 str_v = DatumGetCString(DirectFunctionCall1(nabstimeout, datum_v));
127 * Check for illegal characters in the username and password.
129 i = strcspn(str_n, CRYPT_PWD_FILE_SEPSTR "\n");
130 if (str_n[i] != '\0')
131 elog(ERROR, "Invalid user name '%s'", str_n);
132 i = strcspn(str_p, CRYPT_PWD_FILE_SEPSTR "\n");
133 if (str_p[i] != '\0')
134 elog(ERROR, "Invalid user password '%s'", str_p);
137 * The extra columns we emit here are not really necessary. To
138 * remove them, the parser in backend/libpq/crypt.c would need to
143 CRYPT_PWD_FILE_SEPSTR
145 CRYPT_PWD_FILE_SEPSTR
147 CRYPT_PWD_FILE_SEPSTR
149 CRYPT_PWD_FILE_SEPSTR
151 CRYPT_PWD_FILE_SEPSTR
153 CRYPT_PWD_FILE_SEPSTR
155 CRYPT_PWD_FILE_SEPSTR
169 elog(ERROR, "%s: %m", tempname);
173 * Rename the temp file to its final name, deleting the old pg_pwd. We
174 * expect that rename(2) is an atomic action.
176 if (rename(tempname, filename))
177 elog(ERROR, "rename %s to %s: %m", tempname, filename);
179 pfree((void *) tempname);
180 pfree((void *) filename);
183 * Signal the postmaster to reload its password-file cache.
185 SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
190 /* This is the wrapper for triggers. */
192 update_pg_pwd(PG_FUNCTION_ARGS)
195 * ExclusiveLock ensures no one modifies pg_shadow while we read it,
196 * and that only one backend rewrites the flat file at a time. It's
197 * OK to allow normal reads of pg_shadow in parallel, however.
199 Relation rel = heap_openr(ShadowRelationName, ExclusiveLock);
201 write_password_file(rel);
202 /* OK to release lock, since we did not modify the relation */
203 heap_close(rel, ExclusiveLock);
204 return PointerGetDatum(NULL);
213 CreateUser(CreateUserStmt *stmt)
215 Relation pg_shadow_rel;
216 TupleDesc pg_shadow_dsc;
219 Datum new_record[Natts_pg_shadow];
220 char new_record_nulls[Natts_pg_shadow];
221 bool user_exists = false,
222 sysid_exists = false,
227 char *password = NULL; /* PostgreSQL user password */
228 bool encrypt_password = Password_encryption; /* encrypt password? */
229 char encrypted_password[MD5_PASSWD_LEN + 1];
230 int sysid = 0; /* PgSQL system id (valid if havesysid) */
231 bool createdb = false; /* Can the user create databases? */
232 bool createuser = false; /* Can this user create users? */
233 List *groupElts = NIL; /* The groups the user is a member of */
234 char *validUntil = NULL; /* The time the login is valid
236 DefElem *dpassword = NULL;
237 DefElem *dsysid = NULL;
238 DefElem *dcreatedb = NULL;
239 DefElem *dcreateuser = NULL;
240 DefElem *dgroupElts = NULL;
241 DefElem *dvalidUntil = NULL;
243 /* Extract options from the statement node tree */
244 foreach(option, stmt->options)
246 DefElem *defel = (DefElem *) lfirst(option);
248 if (strcmp(defel->defname, "password") == 0 ||
249 strcmp(defel->defname, "encryptedPassword") == 0 ||
250 strcmp(defel->defname, "unencryptedPassword") == 0)
253 elog(ERROR, "CREATE USER: conflicting options");
255 if (strcmp(defel->defname, "encryptedPassword") == 0)
256 encrypt_password = true;
257 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
258 encrypt_password = false;
260 else if (strcmp(defel->defname, "sysid") == 0)
263 elog(ERROR, "CREATE USER: conflicting options");
266 else if (strcmp(defel->defname, "createdb") == 0)
269 elog(ERROR, "CREATE USER: conflicting options");
272 else if (strcmp(defel->defname, "createuser") == 0)
275 elog(ERROR, "CREATE USER: conflicting options");
278 else if (strcmp(defel->defname, "groupElts") == 0)
281 elog(ERROR, "CREATE USER: conflicting options");
284 else if (strcmp(defel->defname, "validUntil") == 0)
287 elog(ERROR, "CREATE USER: conflicting options");
291 elog(ERROR, "CREATE USER: option \"%s\" not recognized",
296 createdb = intVal(dcreatedb->arg) != 0;
298 createuser = intVal(dcreateuser->arg) != 0;
301 sysid = intVal(dsysid->arg);
303 elog(ERROR, "user id must be positive");
307 validUntil = strVal(dvalidUntil->arg);
309 password = strVal(dpassword->arg);
311 groupElts = (List *) dgroupElts->arg;
313 /* Check some permissions first */
315 CheckPgUserAclNotNull();
318 elog(ERROR, "CREATE USER: permission denied");
321 * Scan the pg_shadow relation to be certain the user or id doesn't
322 * already exist. Note we secure exclusive lock, because we also need
323 * to be sure of what the next usesysid should be, and we need to
324 * protect our update of the flat password file.
326 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
327 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
329 scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
330 max_id = 99; /* start auto-assigned ids at 100 */
331 while (!user_exists && !sysid_exists &&
332 HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
337 datum = heap_getattr(tuple, Anum_pg_shadow_usename,
338 pg_shadow_dsc, &null);
340 user_exists = (strcmp((char *) DatumGetName(datum), stmt->user) == 0);
342 datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
343 pg_shadow_dsc, &null);
345 if (havesysid) /* customized id wanted */
346 sysid_exists = (DatumGetInt32(datum) == sysid);
350 if (DatumGetInt32(datum) > max_id)
351 max_id = DatumGetInt32(datum);
357 elog(ERROR, "CREATE USER: user name \"%s\" already exists",
360 elog(ERROR, "CREATE USER: sysid %d is already assigned", sysid);
362 /* If no sysid given, use max existing id + 1 */
367 * Build a tuple to insert
369 new_record[Anum_pg_shadow_usename - 1] =
370 DirectFunctionCall1(namein, CStringGetDatum(stmt->user));
371 new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid);
373 AssertState(BoolIsValid(createdb));
374 new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb);
375 new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
376 AssertState(BoolIsValid(createuser));
377 new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser);
378 /* superuser gets catupd right by default */
379 new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);
383 if (!encrypt_password || isMD5(password))
384 new_record[Anum_pg_shadow_passwd - 1] =
385 DirectFunctionCall1(textin, CStringGetDatum(password));
388 if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
390 elog(ERROR, "CREATE USER: password encryption failed");
391 new_record[Anum_pg_shadow_passwd - 1] =
392 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
396 new_record[Anum_pg_shadow_valuntil - 1] =
397 DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
399 new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
400 new_record_nulls[Anum_pg_shadow_usesysid - 1] = ' ';
402 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
403 new_record_nulls[Anum_pg_shadow_usetrace - 1] = ' ';
404 new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
405 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
407 new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
408 new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
410 new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n';
412 tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
415 * Insert new record in the pg_shadow table
417 heap_insert(pg_shadow_rel, tuple);
422 if (RelationGetForm(pg_shadow_rel)->relhasindex)
424 Relation idescs[Num_pg_shadow_indices];
426 CatalogOpenIndices(Num_pg_shadow_indices,
427 Name_pg_shadow_indices, idescs);
428 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
430 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
434 * Add the user to the groups specified. We'll just call the below
435 * AlterGroup for this.
437 foreach(item, groupElts)
441 ags.name = strVal(lfirst(item)); /* the group name to add
444 ags.listUsers = makeList1(makeInteger(sysid));
445 AlterGroup(&ags, "CREATE USER");
449 * Write the updated pg_shadow data to the flat password file.
451 write_password_file(pg_shadow_rel);
454 * Now we can clean up; but keep lock until commit.
456 heap_close(pg_shadow_rel, NoLock);
465 AlterUser(AlterUserStmt *stmt)
467 Datum new_record[Natts_pg_shadow];
468 char new_record_nulls[Natts_pg_shadow];
469 Relation pg_shadow_rel;
470 TupleDesc pg_shadow_dsc;
475 char *password = NULL; /* PostgreSQL user password */
476 bool encrypt_password = Password_encryption; /* encrypt password? */
477 char encrypted_password[MD5_PASSWD_LEN + 1];
478 int createdb = -1; /* Can the user create databases? */
479 int createuser = -1; /* Can this user create users? */
480 char *validUntil = NULL; /* The time the login is valid
482 DefElem *dpassword = NULL;
483 DefElem *dcreatedb = NULL;
484 DefElem *dcreateuser = NULL;
485 DefElem *dvalidUntil = NULL;
487 /* Extract options from the statement node tree */
488 foreach(option, stmt->options)
490 DefElem *defel = (DefElem *) lfirst(option);
492 if (strcmp(defel->defname, "password") == 0 ||
493 strcmp(defel->defname, "encryptedPassword") == 0 ||
494 strcmp(defel->defname, "unencryptedPassword") == 0)
497 elog(ERROR, "ALTER USER: conflicting options");
499 if (strcmp(defel->defname, "encryptedPassword") == 0)
500 encrypt_password = true;
501 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
502 encrypt_password = false;
504 else if (strcmp(defel->defname, "createdb") == 0)
507 elog(ERROR, "ALTER USER: conflicting options");
510 else if (strcmp(defel->defname, "createuser") == 0)
513 elog(ERROR, "ALTER USER: conflicting options");
516 else if (strcmp(defel->defname, "validUntil") == 0)
519 elog(ERROR, "ALTER USER: conflicting options");
523 elog(ERROR, "ALTER USER: option \"%s\" not recognized",
528 createdb = intVal(dcreatedb->arg);
530 createuser = intVal(dcreateuser->arg);
532 validUntil = strVal(dvalidUntil->arg);
534 password = strVal(dpassword->arg);
537 CheckPgUserAclNotNull();
539 /* must be superuser or just want to change your own password */
545 strcmp(GetUserName(GetUserId()), stmt->user) == 0))
546 elog(ERROR, "ALTER USER: permission denied");
548 /* changes to the flat password file cannot be rolled back */
549 if (IsTransactionBlock() && password)
550 elog(INFO, "ALTER USER: password changes cannot be rolled back");
553 * Scan the pg_shadow relation to be certain the user exists. Note we
554 * secure exclusive lock to protect our update of the flat password
557 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
558 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
560 tuple = SearchSysCache(SHADOWNAME,
561 PointerGetDatum(stmt->user),
563 if (!HeapTupleIsValid(tuple))
564 elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
567 * Build a tuple to update, perusing the information just obtained
569 new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
570 CStringGetDatum(stmt->user));
571 new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
573 /* sysid - leave as is */
574 new_record[Anum_pg_shadow_usesysid - 1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
575 new_record_nulls[Anum_pg_shadow_usesysid - 1] = null ? 'n' : ' ';
581 new_record[Anum_pg_shadow_usecreatedb - 1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
582 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = null ? 'n' : ' ';
586 new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0);
587 new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
590 /* trace - leave as is */
591 new_record[Anum_pg_shadow_usetrace - 1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
592 new_record_nulls[Anum_pg_shadow_usetrace - 1] = null ? 'n' : ' ';
595 * createuser (superuser) and catupd
597 * XXX It's rather unclear how to handle catupd. It's probably best to
598 * keep it equal to the superuser status, otherwise you could end up
599 * with a situation where no existing superuser can alter the
600 * catalogs, including pg_shadow!
605 new_record[Anum_pg_shadow_usesuper - 1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
606 new_record_nulls[Anum_pg_shadow_usesuper - 1] = null ? 'n' : ' ';
608 new_record[Anum_pg_shadow_usecatupd - 1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
609 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = null ? 'n' : ' ';
613 new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0);
614 new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
616 new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser > 0);
617 new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
623 if (!encrypt_password || isMD5(password))
624 new_record[Anum_pg_shadow_passwd - 1] =
625 DirectFunctionCall1(textin, CStringGetDatum(password));
628 if (!EncryptMD5(password, stmt->user, strlen(stmt->user),
630 elog(ERROR, "CREATE USER: password encryption failed");
631 new_record[Anum_pg_shadow_passwd - 1] =
632 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
634 new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
639 new_record[Anum_pg_shadow_passwd - 1] =
640 heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
641 new_record_nulls[Anum_pg_shadow_passwd - 1] = null ? 'n' : ' ';
647 new_record[Anum_pg_shadow_valuntil - 1] =
648 DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
649 new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
654 new_record[Anum_pg_shadow_valuntil - 1] =
655 heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
656 new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
659 /* leave useconfig as is */
660 new_record[Anum_pg_shadow_useconfig - 1] =
661 heap_getattr(tuple, Anum_pg_shadow_useconfig, pg_shadow_dsc, &null);
662 new_record_nulls[Anum_pg_shadow_useconfig - 1] = null ? 'n' : ' ';
664 new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
665 simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
668 if (RelationGetForm(pg_shadow_rel)->relhasindex)
670 Relation idescs[Num_pg_shadow_indices];
672 CatalogOpenIndices(Num_pg_shadow_indices,
673 Name_pg_shadow_indices, idescs);
674 CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel,
676 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
679 ReleaseSysCache(tuple);
680 heap_freetuple(new_tuple);
683 * Write the updated pg_shadow data to the flat password file.
685 write_password_file(pg_shadow_rel);
688 * Now we can clean up.
690 heap_close(pg_shadow_rel, NoLock);
699 AlterUserSet(AlterUserSetStmt *stmt)
705 Datum repl_val[Natts_pg_shadow];
706 char repl_null[Natts_pg_shadow];
707 char repl_repl[Natts_pg_shadow];
710 valuestr = (stmt->value
711 ? ((A_Const *) lfirst(stmt->value))->val.val.str
714 rel = heap_openr(ShadowRelationName, RowExclusiveLock);
715 oldtuple = SearchSysCache(SHADOWNAME,
716 PointerGetDatum(stmt->user),
718 if (!HeapTupleIsValid(oldtuple))
719 elog(ERROR, "user \"%s\" does not exist", stmt->user);
722 || ((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetUserId()))
723 elog(ERROR, "permission denied");
725 for (i = 0; i < Natts_pg_shadow; i++)
728 repl_repl[Anum_pg_shadow_useconfig-1] = 'r';
729 if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
731 repl_null[Anum_pg_shadow_useconfig-1] = 'n';
738 repl_null[Anum_pg_shadow_useconfig-1] = ' ';
740 datum = SysCacheGetAttr(SHADOWNAME, oldtuple,
741 Anum_pg_shadow_useconfig, &isnull);
744 a = GUCArrayAdd(isnull
746 : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
747 stmt->variable, valuestr);
749 a = GUCArrayDelete(isnull
751 : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
754 repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
757 newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
758 simple_heap_update(rel, &oldtuple->t_self, newtuple);
761 Relation idescs[Num_pg_shadow_indices];
763 CatalogOpenIndices(Num_pg_shadow_indices, Name_pg_shadow_indices, idescs);
764 CatalogIndexInsert(idescs, Num_pg_shadow_indices, rel, newtuple);
765 CatalogCloseIndices(Num_pg_shadow_indices, idescs);
768 ReleaseSysCache(oldtuple);
769 heap_close(rel, RowExclusiveLock);
778 DropUser(DropUserStmt *stmt)
780 Relation pg_shadow_rel;
781 TupleDesc pg_shadow_dsc;
785 elog(ERROR, "DROP USER: permission denied");
787 if (IsTransactionBlock())
788 elog(INFO, "DROP USER cannot be rolled back completely");
791 * Scan the pg_shadow relation to find the usesysid of the user to be
792 * deleted. Note we secure exclusive lock, because we need to protect
793 * our update of the flat password file.
795 pg_shadow_rel = heap_openr(ShadowRelationName, ExclusiveLock);
796 pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
798 foreach(item, stmt->users)
809 const char *user = strVal(lfirst(item));
811 tuple = SearchSysCache(SHADOWNAME,
812 PointerGetDatum(user),
814 if (!HeapTupleIsValid(tuple))
815 elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
816 (length(stmt->users) > 1) ? " (no users removed)" : "");
818 usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
820 if (usesysid == GetUserId())
821 elog(ERROR, "current user cannot be dropped");
822 if (usesysid == GetSessionUserId())
823 elog(ERROR, "session user cannot be dropped");
826 * Check if user still owns a database. If so, error out.
828 * (It used to be that this function would drop the database
829 * automatically. This is not only very dangerous for people that
830 * don't read the manual, it doesn't seem to be the behaviour one
831 * would expect either.) -- petere 2000/01/14)
833 pg_rel = heap_openr(DatabaseRelationName, AccessShareLock);
834 pg_dsc = RelationGetDescr(pg_rel);
836 ScanKeyEntryInitialize(&scankey, 0x0,
837 Anum_pg_database_datdba, F_INT4EQ,
838 Int32GetDatum(usesysid));
840 scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
842 if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
846 datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
849 dbname = DatumGetCString(DirectFunctionCall1(nameout, datum));
850 elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
852 (length(stmt->users) > 1) ? " (no users removed)" : "");
856 heap_close(pg_rel, AccessShareLock);
859 * Somehow we'd have to check for tables, views, etc. owned by the
860 * user as well, but those could be spread out over all sorts of
861 * databases which we don't have access to (easily).
865 * Remove the user from the pg_shadow table
867 simple_heap_delete(pg_shadow_rel, &tuple->t_self);
869 ReleaseSysCache(tuple);
872 * Remove user from groups
874 * try calling alter group drop user for every group
876 pg_rel = heap_openr(GroupRelationName, ExclusiveLock);
877 pg_dsc = RelationGetDescr(pg_rel);
878 scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
879 while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
883 /* the group name from which to try to drop the user: */
884 datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
885 ags.name = DatumGetCString(DirectFunctionCall1(nameout, datum));
887 ags.listUsers = makeList1(makeInteger(usesysid));
888 AlterGroup(&ags, "DROP USER");
891 heap_close(pg_rel, ExclusiveLock);
894 * Advance command counter so that later iterations of this loop
895 * will see the changes already made. This is essential if, for
896 * example, we are trying to drop two users who are members of the
897 * same group --- the AlterGroup for the second user had better
898 * see the tuple updated from the first one.
900 CommandCounterIncrement();
904 * Write the updated pg_shadow data to the flat password file.
906 write_password_file(pg_shadow_rel);
909 * Now we can clean up.
911 heap_close(pg_shadow_rel, NoLock);
917 * CheckPgUserAclNotNull
919 * check to see if there is an ACL on pg_shadow
922 CheckPgUserAclNotNull()
926 htup = SearchSysCache(RELNAME,
927 PointerGetDatum(ShadowRelationName),
929 if (!HeapTupleIsValid(htup))
930 elog(ERROR, "CheckPgUserAclNotNull: \"%s\" not found",
933 if (heap_attisnull(htup, Anum_pg_class_relacl))
935 "To use passwords, you have to revoke permissions on %s "
936 "so normal users cannot read the passwords. "
937 "Try 'REVOKE ALL ON \"%s\" FROM PUBLIC'.",
938 ShadowRelationName, ShadowRelationName);
940 ReleaseSysCache(htup);
949 CreateGroup(CreateGroupStmt *stmt)
951 Relation pg_group_rel;
954 TupleDesc pg_group_dsc;
955 bool group_exists = false,
956 sysid_exists = false,
959 Datum new_record[Natts_pg_group];
960 char new_record_nulls[Natts_pg_group];
964 ArrayType *userarray;
966 List *userElts = NIL;
967 DefElem *dsysid = NULL;
968 DefElem *duserElts = NULL;
970 foreach(option, stmt->options)
972 DefElem *defel = (DefElem *) lfirst(option);
974 if (strcmp(defel->defname, "sysid") == 0)
977 elog(ERROR, "CREATE GROUP: conflicting options");
980 else if (strcmp(defel->defname, "userElts") == 0)
983 elog(ERROR, "CREATE GROUP: conflicting options");
987 elog(ERROR, "CREATE GROUP: option \"%s\" not recognized",
993 sysid = intVal(dsysid->arg);
995 elog(ERROR, "group id must be positive");
1000 userElts = (List *) duserElts->arg;
1003 * Make sure the user can do this.
1006 elog(ERROR, "CREATE GROUP: permission denied");
1008 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1009 pg_group_dsc = RelationGetDescr(pg_group_rel);
1011 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1012 max_id = 99; /* start auto-assigned ids at 100 */
1013 while (!group_exists && !sysid_exists &&
1014 HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1019 datum = heap_getattr(tuple, Anum_pg_group_groname,
1020 pg_group_dsc, &null);
1022 group_exists = (strcmp((char *) DatumGetName(datum), stmt->name) == 0);
1024 datum = heap_getattr(tuple, Anum_pg_group_grosysid,
1025 pg_group_dsc, &null);
1027 if (havesysid) /* customized id wanted */
1028 sysid_exists = (DatumGetInt32(datum) == sysid);
1032 if (DatumGetInt32(datum) > max_id)
1033 max_id = DatumGetInt32(datum);
1039 elog(ERROR, "CREATE GROUP: group name \"%s\" already exists",
1042 elog(ERROR, "CREATE GROUP: group sysid %d is already assigned",
1046 * Translate the given user names to ids
1048 foreach(item, userElts)
1050 const char *groupuser = strVal(lfirst(item));
1053 v = makeInteger(get_usesysid(groupuser));
1054 if (!member(v, newlist))
1055 newlist = lappend(newlist, v);
1058 /* build an array to insert */
1063 userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1064 userarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1065 userarray->flags = 0;
1066 ARR_NDIM(userarray) = 1; /* one dimensional array */
1067 ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
1068 ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
1069 /* fill the array */
1071 foreach(item, newlist)
1072 ((int *) ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
1078 * Form a tuple to insert
1083 new_record[Anum_pg_group_groname - 1] =
1084 DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1085 new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(sysid);
1086 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(userarray);
1088 new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1089 new_record_nulls[Anum_pg_group_grosysid - 1] = ' ';
1090 new_record_nulls[Anum_pg_group_grolist - 1] = userarray ? ' ' : 'n';
1092 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1095 * Insert a new record in the pg_group_table
1097 heap_insert(pg_group_rel, tuple);
1102 if (RelationGetForm(pg_group_rel)->relhasindex)
1104 Relation idescs[Num_pg_group_indices];
1106 CatalogOpenIndices(Num_pg_group_indices,
1107 Name_pg_group_indices, idescs);
1108 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1110 CatalogCloseIndices(Num_pg_group_indices, idescs);
1113 heap_close(pg_group_rel, NoLock);
1122 AlterGroup(AlterGroupStmt *stmt, const char *tag)
1124 Relation pg_group_rel;
1125 TupleDesc pg_group_dsc;
1126 HeapTuple group_tuple;
1129 * Make sure the user can do this.
1132 elog(ERROR, "%s: permission denied", tag);
1134 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1135 pg_group_dsc = RelationGetDescr(pg_group_rel);
1138 * Fetch existing tuple for group.
1140 group_tuple = SearchSysCache(GRONAME,
1141 PointerGetDatum(stmt->name),
1143 if (!HeapTupleIsValid(group_tuple))
1144 elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
1147 * Now decide what to do.
1149 AssertState(stmt->action == +1 || stmt->action == -1);
1151 if (stmt->action == +1) /* add users, might also be invoked by
1154 Datum new_record[Natts_pg_group];
1155 char new_record_nulls[Natts_pg_group];
1156 ArrayType *newarray,
1158 List *newlist = NIL,
1162 Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
1165 oldarray = (ArrayType *) datum;
1166 Assert(null || ARR_NDIM(oldarray) == 1);
1167 /* first add the old array to the hitherto empty list */
1169 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
1177 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
1178 sizeof(int), 0, &valueNull));
1179 v = makeInteger(arrval);
1180 /* filter out duplicates */
1181 if (!member(v, newlist))
1182 newlist = lappend(newlist, v);
1186 * now convert the to be added usernames to sysids and add them to
1189 foreach(item, stmt->listUsers)
1193 if (strcmp(tag, "ALTER GROUP") == 0)
1195 /* Get the uid of the proposed user to add. */
1196 v = makeInteger(get_usesysid(strVal(lfirst(item))));
1198 else if (strcmp(tag, "CREATE USER") == 0)
1201 * in this case we already know the uid and it wouldn't be
1202 * in the cache anyway yet
1208 elog(ERROR, "AlterGroup: unknown tag %s", tag);
1209 v = NULL; /* keep compiler quiet */
1212 if (!member(v, newlist))
1213 newlist = lappend(newlist, v);
1217 * we silently assume here that this error will only come
1218 * up in a ALTER GROUP statement
1220 elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"",
1221 tag, strVal(lfirst(item)), stmt->name);
1224 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1225 newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1226 newarray->flags = 0;
1227 ARR_NDIM(newarray) = 1; /* one dimensional array */
1228 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
1229 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
1230 /* fill the array */
1232 foreach(item, newlist)
1233 ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
1236 * Form a tuple with the new array and write it back.
1238 new_record[Anum_pg_group_groname - 1] =
1239 DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1240 new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1241 new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
1242 new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
1243 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
1244 new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
1246 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1247 simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
1249 /* Update indexes */
1250 if (RelationGetForm(pg_group_rel)->relhasindex)
1252 Relation idescs[Num_pg_group_indices];
1254 CatalogOpenIndices(Num_pg_group_indices,
1255 Name_pg_group_indices, idescs);
1256 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1258 CatalogCloseIndices(Num_pg_group_indices, idescs);
1260 } /* endif alter group add user */
1262 else if (stmt->action == -1) /* drop users from group */
1266 bool is_dropuser = strcmp(tag, "DROP USER") == 0;
1268 datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
1272 elog(NOTICE, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
1277 Datum new_record[Natts_pg_group];
1278 char new_record_nulls[Natts_pg_group];
1279 ArrayType *oldarray,
1281 List *newlist = NIL,
1285 oldarray = (ArrayType *) datum;
1286 Assert(ARR_NDIM(oldarray) == 1);
1287 /* first add the old array to the hitherto empty list */
1288 for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
1296 arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
1297 sizeof(int), 0, &valueNull));
1298 v = makeInteger(arrval);
1299 /* filter out duplicates */
1300 if (!member(v, newlist))
1301 newlist = lappend(newlist, v);
1305 * now convert the to be dropped usernames to sysids and
1306 * remove them from the list
1308 foreach(item, stmt->listUsers)
1314 /* Get the uid of the proposed user to drop. */
1315 v = makeInteger(get_usesysid(strVal(lfirst(item))));
1319 /* for dropuser we already know the uid */
1322 if (member(v, newlist))
1323 newlist = LispRemove(v, newlist);
1324 else if (!is_dropuser)
1325 elog(NOTICE, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
1328 newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
1329 newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
1330 newarray->flags = 0;
1331 ARR_NDIM(newarray) = 1; /* one dimensional array */
1332 ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
1333 ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
1334 /* fill the array */
1336 foreach(item, newlist)
1337 ((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
1340 * Insert the new tuple with the updated user list
1342 new_record[Anum_pg_group_groname - 1] =
1343 DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
1344 new_record_nulls[Anum_pg_group_groname - 1] = ' ';
1345 new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
1346 new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
1347 new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
1348 new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
1350 tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
1351 simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
1353 /* Update indexes */
1354 if (RelationGetForm(pg_group_rel)->relhasindex)
1356 Relation idescs[Num_pg_group_indices];
1358 CatalogOpenIndices(Num_pg_group_indices,
1359 Name_pg_group_indices, idescs);
1360 CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
1362 CatalogCloseIndices(Num_pg_group_indices, idescs);
1365 } /* endif group not null */
1366 } /* endif alter group drop user */
1368 ReleaseSysCache(group_tuple);
1370 heap_close(pg_group_rel, NoLock);
1379 DropGroup(DropGroupStmt *stmt)
1381 Relation pg_group_rel;
1384 TupleDesc pg_group_dsc;
1385 bool gro_exists = false;
1388 * Make sure the user can do this.
1391 elog(ERROR, "DROP GROUP: permission denied");
1394 * Scan the pg_group table and delete all matching groups.
1396 pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
1397 pg_group_dsc = RelationGetDescr(pg_group_rel);
1398 scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
1400 while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
1405 datum = heap_getattr(tuple, Anum_pg_group_groname,
1406 pg_group_dsc, &null);
1407 if (!null && strcmp((char *) DatumGetName(datum), stmt->name) == 0)
1410 simple_heap_delete(pg_group_rel, &tuple->t_self);
1420 elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
1422 heap_close(pg_group_rel, NoLock);