aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/user.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/user.c')
-rw-r--r--src/backend/commands/user.c492
1 files changed, 2 insertions, 490 deletions
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index db4f903d144..6136e99519e 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,17 +6,12 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.148 2005/01/27 23:23:56 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.149 2005/02/20 02:21:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
@@ -27,50 +22,17 @@
#include "commands/user.h"
#include "libpq/crypt.h"
#include "miscadmin.h"
-#include "storage/fd.h"
-#include "storage/pmsignal.h"
#include "utils/acl.h"
-#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/flatfiles.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-#define PWD_FILE "pg_pwd"
-#define USER_GROUP_FILE "pg_group"
-
-
extern bool Password_encryption;
-/*
- * The need-to-update-files flags are a pair of SubTransactionIds that show
- * what level of the subtransaction tree requested the update. To register
- * an update, the subtransaction saves its own SubTransactionId in the flag,
- * unless the value was already set to a valid SubTransactionId (which implies
- * that it or a parent level has already requested the same). If it aborts
- * and the value is its SubTransactionId, it resets the flag to
- * InvalidSubTransactionId. If it commits, it changes the value to its
- * parent's SubTransactionId. This way the value is propagated up to the
- * top-level transaction, which will update the files if a valid
- * SubTransactionId is detected.
- */
-static SubTransactionId user_file_update_subid = InvalidSubTransactionId;
-static SubTransactionId group_file_update_subid = InvalidSubTransactionId;
-
-#define user_file_update_needed() \
- do { \
- if (user_file_update_subid == InvalidSubTransactionId) \
- user_file_update_subid = GetCurrentSubTransactionId(); \
- } while (0)
-
-#define group_file_update_needed() \
- do { \
- if (group_file_update_subid == InvalidSubTransactionId) \
- group_file_update_subid = GetCurrentSubTransactionId(); \
- } while (0)
-
static void CheckPgUserAclNotNull(void);
static void UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple,
@@ -80,454 +42,6 @@ static List *IdArrayToList(IdList *oldarray);
/*
- * fputs_quote
- *
- * Outputs string in quotes, with double-quotes duplicated.
- * We could use quote_ident(), but that expects a TEXT argument.
- */
-static void
-fputs_quote(char *str, FILE *fp)
-{
- fputc('"', fp);
- while (*str)
- {
- fputc(*str, fp);
- if (*str == '"')
- fputc('"', fp);
- str++;
- }
- fputc('"', fp);
-}
-
-
-/*
- * group_getfilename --- get full pathname of group file
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- */
-char *
-group_getfilename(void)
-{
- int bufsize;
- char *pfnam;
-
- bufsize = strlen(DataDir) + strlen("/global/") +
- strlen(USER_GROUP_FILE) + 1;
- pfnam = (char *) palloc(bufsize);
- snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);
-
- return pfnam;
-}
-
-
-/*
- * Get full pathname of password file.
- *
- * Note that result string is palloc'd, and should be freed by the caller.
- */
-char *
-user_getfilename(void)
-{
- int bufsize;
- char *pfnam;
-
- bufsize = strlen(DataDir) + strlen("/global/") +
- strlen(PWD_FILE) + 1;
- pfnam = (char *) palloc(bufsize);
- snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);
-
- return pfnam;
-}
-
-
-/*
- * write_group_file: update the flat group file
- */
-static void
-write_group_file(Relation grel)
-{
- char *filename,
- *tempname;
- int bufsize;
- FILE *fp;
- mode_t oumask;
- HeapScanDesc scan;
- HeapTuple tuple;
- TupleDesc dsc = RelationGetDescr(grel);
-
- /*
- * Create a temporary filename to be renamed later. This prevents the
- * backend from clobbering the pg_group file while the postmaster
- * might be reading from it.
- */
- filename = group_getfilename();
- bufsize = strlen(filename) + 12;
- tempname = (char *) palloc(bufsize);
- snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
-
- oumask = umask((mode_t) 077);
- fp = AllocateFile(tempname, "w");
- umask(oumask);
- if (fp == NULL)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m", tempname)));
-
- /*
- * Read pg_group and write the file. Note we use SnapshotSelf to
- * ensure we see all effects of current transaction. (Perhaps could
- * do a CommandCounterIncrement beforehand, instead?)
- */
- scan = heap_beginscan(grel, SnapshotSelf, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Datum datum,
- grolist_datum;
- bool isnull;
- char *groname;
- IdList *grolist_p;
- AclId *aidp;
- int i,
- j,
- num;
- char *usename;
- bool first_user = true;
-
- datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
- /* ignore NULL groupnames --- shouldn't happen */
- if (isnull)
- continue;
- groname = NameStr(*DatumGetName(datum));
-
- /*
- * Check for invalid characters in the group name.
- */
- i = strcspn(groname, "\n");
- if (groname[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid group name \"%s\"", groname)));
- continue;
- }
-
- grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
- /* Ignore NULL group lists */
- if (isnull)
- continue;
-
- /* be sure the IdList is not toasted */
- grolist_p = DatumGetIdListP(grolist_datum);
-
- /* scan grolist */
- num = IDLIST_NUM(grolist_p);
- aidp = IDLIST_DAT(grolist_p);
- for (i = 0; i < num; ++i)
- {
- tuple = SearchSysCache(SHADOWSYSID,
- PointerGetDatum(aidp[i]),
- 0, 0, 0);
- if (HeapTupleIsValid(tuple))
- {
- usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);
-
- /*
- * Check for illegal characters in the user name.
- */
- j = strcspn(usename, "\n");
- if (usename[j] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user name \"%s\"", usename)));
- continue;
- }
-
- /*
- * File format is: "dbname" "user1" "user2" "user3"
- */
- if (first_user)
- {
- fputs_quote(groname, fp);
- fputs("\t", fp);
- }
- else
- fputs(" ", fp);
-
- first_user = false;
- fputs_quote(usename, fp);
-
- ReleaseSysCache(tuple);
- }
- }
- if (!first_user)
- fputs("\n", fp);
- /* if IdList was toasted, free detoasted copy */
- if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))
- pfree(grolist_p);
- }
- heap_endscan(scan);
-
- if (FreeFile(fp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m",
- tempname)));
-
- /*
- * Rename the temp file to its final name, deleting the old pg_pwd. We
- * expect that rename(2) is an atomic action.
- */
- if (rename(tempname, filename))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename file \"%s\" to \"%s\": %m",
- tempname, filename)));
-
- pfree(tempname);
- pfree(filename);
-}
-
-
-/*
- * write_user_file: update the flat password file
- */
-static void
-write_user_file(Relation urel)
-{
- char *filename,
- *tempname;
- int bufsize;
- FILE *fp;
- mode_t oumask;
- HeapScanDesc scan;
- HeapTuple tuple;
- TupleDesc dsc = RelationGetDescr(urel);
-
- /*
- * Create a temporary filename to be renamed later. This prevents the
- * backend from clobbering the pg_pwd file while the postmaster might
- * be reading from it.
- */
- filename = user_getfilename();
- bufsize = strlen(filename) + 12;
- tempname = (char *) palloc(bufsize);
- snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
-
- oumask = umask((mode_t) 077);
- fp = AllocateFile(tempname, "w");
- umask(oumask);
- if (fp == NULL)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m", tempname)));
-
- /*
- * Read pg_shadow and write the file. Note we use SnapshotSelf to
- * ensure we see all effects of current transaction. (Perhaps could
- * do a CommandCounterIncrement beforehand, instead?)
- */
- scan = heap_beginscan(urel, SnapshotSelf, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Datum datum;
- bool isnull;
- char *usename,
- *passwd,
- *valuntil;
- int i;
-
- datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
- /* ignore NULL usernames (shouldn't happen) */
- if (isnull)
- continue;
- usename = NameStr(*DatumGetName(datum));
-
- datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
-
- /*
- * It can be argued that people having a null password shouldn't
- * be allowed to connect under password authentication, because
- * they need to have a password set up first. If you think
- * assuming an empty password in that case is better, change this
- * logic to look something like the code for valuntil.
- */
- if (isnull)
- continue;
-
- passwd = DatumGetCString(DirectFunctionCall1(textout, datum));
-
- datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);
- if (isnull)
- valuntil = pstrdup("");
- else
- valuntil = DatumGetCString(DirectFunctionCall1(abstimeout, datum));
-
- /*
- * Check for illegal characters in the username and password.
- */
- i = strcspn(usename, "\n");
- if (usename[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user name \"%s\"", usename)));
- continue;
- }
- i = strcspn(passwd, "\n");
- if (passwd[i] != '\0')
- {
- ereport(LOG,
- (errmsg("invalid user password \"%s\"", passwd)));
- continue;
- }
-
- /*
- * The extra columns we emit here are not really necessary. To
- * remove them, the parser in backend/libpq/crypt.c would need to
- * be adjusted.
- */
- fputs_quote(usename, fp);
- fputs(" ", fp);
- fputs_quote(passwd, fp);
- fputs(" ", fp);
- fputs_quote(valuntil, fp);
- fputs("\n", fp);
-
- pfree(passwd);
- pfree(valuntil);
- }
- heap_endscan(scan);
-
- if (FreeFile(fp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write to temporary file \"%s\": %m",
- tempname)));
-
- /*
- * Rename the temp file to its final name, deleting the old pg_pwd. We
- * expect that rename(2) is an atomic action.
- */
- if (rename(tempname, filename))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename file \"%s\" to \"%s\": %m",
- tempname, filename)));
-
- pfree(tempname);
- pfree(filename);
-}
-
-
-/*
- * This trigger is fired whenever someone modifies pg_shadow or pg_group
- * via general-purpose INSERT/UPDATE/DELETE commands.
- *
- * XXX should probably have two separate triggers.
- */
-Datum
-update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS)
-{
- user_file_update_needed();
- group_file_update_needed();
-
- return PointerGetDatum(NULL);
-}
-
-
-/*
- * This routine is called during transaction commit or abort.
- *
- * On commit, if we've written pg_shadow or pg_group during the current
- * transaction, update the flat files and signal the postmaster.
- *
- * On abort, just reset the static flags so we don't try to do it on the
- * next successful commit.
- *
- * NB: this should be the last step before actual transaction commit.
- * If any error aborts the transaction after we run this code, the postmaster
- * will still have received and cached the changed data; so minimize the
- * window for such problems.
- */
-void
-AtEOXact_UpdatePasswordFile(bool isCommit)
-{
- Relation urel = NULL;
- Relation grel = NULL;
-
- if (user_file_update_subid == InvalidSubTransactionId &&
- group_file_update_subid == InvalidSubTransactionId)
- return;
-
- if (!isCommit)
- {
- user_file_update_subid = InvalidSubTransactionId;
- group_file_update_subid = InvalidSubTransactionId;
- return;
- }
-
- /*
- * We use ExclusiveLock to ensure that only one backend writes the
- * flat file(s) at a time. That's sufficient because it's okay to
- * allow plain reads of the tables in parallel. There is some chance
- * of a deadlock here (if we were triggered by a user update of
- * pg_shadow or pg_group, which likely won't have gotten a strong
- * enough lock), so get the locks we need before writing anything.
- */
- if (user_file_update_subid != InvalidSubTransactionId)
- urel = heap_openr(ShadowRelationName, ExclusiveLock);
- if (group_file_update_subid != InvalidSubTransactionId)
- grel = heap_openr(GroupRelationName, ExclusiveLock);
-
- /* Okay to write the files */
- if (user_file_update_subid != InvalidSubTransactionId)
- {
- user_file_update_subid = InvalidSubTransactionId;
- write_user_file(urel);
- heap_close(urel, NoLock);
- }
-
- if (group_file_update_subid != InvalidSubTransactionId)
- {
- group_file_update_subid = InvalidSubTransactionId;
- write_group_file(grel);
- heap_close(grel, NoLock);
- }
-
- /*
- * Signal the postmaster to reload its password & group-file cache.
- */
- SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
-}
-
-/*
- * AtEOSubXact_UpdatePasswordFile
- *
- * Called at subtransaction end, this routine resets or updates the
- * need-to-update-files flags.
- */
-void
-AtEOSubXact_UpdatePasswordFile(bool isCommit,
- SubTransactionId mySubid,
- SubTransactionId parentSubid)
-{
- if (isCommit)
- {
- if (user_file_update_subid == mySubid)
- user_file_update_subid = parentSubid;
-
- if (group_file_update_subid == mySubid)
- group_file_update_subid = parentSubid;
- }
- else
- {
- if (user_file_update_subid == mySubid)
- user_file_update_subid = InvalidSubTransactionId;
-
- if (group_file_update_subid == mySubid)
- group_file_update_subid = InvalidSubTransactionId;
- }
-}
-
-/*
* CREATE USER
*/
void
@@ -1060,7 +574,6 @@ AlterUserSet(AlterUserSetStmt *stmt)
}
-
/*
* DROP USER
*/
@@ -1318,7 +831,6 @@ CheckPgUserAclNotNull(void)
}
-
/*
* CREATE GROUP
*/