diff options
Diffstat (limited to 'src/backend/commands/user.c')
-rw-r--r-- | src/backend/commands/user.c | 492 |
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 */ |