diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/bootstrap/bootstrap.c | 12 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 87 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 3 | ||||
-rw-r--r-- | src/backend/utils/init/globals.c | 9 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 115 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 25 |
6 files changed, 160 insertions, 91 deletions
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 644084d1c3b..59cd4b17f32 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -349,13 +349,15 @@ AuxiliaryProcessMain(int argc, char *argv[]) proc_exit(1); } - /* Validate we have been given a reasonable-looking DataDir */ - Assert(DataDir); - ValidatePgVersion(DataDir); - - /* Change into DataDir (if under postmaster, should be done already) */ + /* + * Validate we have been given a reasonable-looking DataDir and change + * into it (if under postmaster, should be done already). + */ if (!IsUnderPostmaster) + { + checkDataDir(); ChangeToDataDir(); + } /* If standalone, create lockfile for data directory */ if (!IsUnderPostmaster) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 10afecffb37..ccf63e93097 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -391,7 +391,7 @@ static DNSServiceRef bonjour_sdref = NULL; static void CloseServerPorts(int status, Datum arg); static void unlink_external_pid_file(int status, Datum arg); static void getInstallationPaths(const char *argv0); -static void checkDataDir(void); +static void checkControlFile(void); static Port *ConnCreate(int serverFd); static void ConnFree(Port *port); static void reset_shared(int port); @@ -588,7 +588,12 @@ PostmasterMain(int argc, char *argv[]) IsPostmasterEnvironment = true; /* - * for security, no dir or file created can be group or other accessible + * We should not be creating any files or directories before we check the + * data directory (see checkDataDir()), but just in case set the umask to + * the most restrictive (owner-only) permissions. + * + * checkDataDir() will reset the umask based on the data directory + * permissions. */ umask(PG_MODE_MASK_OWNER); @@ -877,6 +882,9 @@ PostmasterMain(int argc, char *argv[]) /* Verify that DataDir looks reasonable */ checkDataDir(); + /* Check that pg_control exists */ + checkControlFile(); + /* And switch working directory into it */ ChangeToDataDir(); @@ -1469,82 +1477,17 @@ getInstallationPaths(const char *argv0) */ } - /* - * Validate the proposed data directory + * Check that pg_control exists in the correct location in the data directory. + * + * No attempt is made to validate the contents of pg_control here. This is + * just a sanity check to see if we are looking at a real data directory. */ static void -checkDataDir(void) +checkControlFile(void) { char path[MAXPGPATH]; FILE *fp; - struct stat stat_buf; - - Assert(DataDir); - - if (stat(DataDir, &stat_buf) != 0) - { - if (errno == ENOENT) - ereport(FATAL, - (errcode_for_file_access(), - errmsg("data directory \"%s\" does not exist", - DataDir))); - else - ereport(FATAL, - (errcode_for_file_access(), - errmsg("could not read permissions of directory \"%s\": %m", - DataDir))); - } - - /* eventual chdir would fail anyway, but let's test ... */ - if (!S_ISDIR(stat_buf.st_mode)) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("specified data directory \"%s\" is not a directory", - DataDir))); - - /* - * Check that the directory belongs to my userid; if not, reject. - * - * This check is an essential part of the interlock that prevents two - * postmasters from starting in the same directory (see CreateLockFile()). - * Do not remove or weaken it. - * - * XXX can we safely enable this check on Windows? - */ -#if !defined(WIN32) && !defined(__CYGWIN__) - if (stat_buf.st_uid != geteuid()) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("data directory \"%s\" has wrong ownership", - DataDir), - errhint("The server must be started by the user that owns the data directory."))); -#endif - - /* - * Check if the directory has group or world access. If so, reject. - * - * It would be possible to allow weaker constraints (for example, allow - * group access) but we cannot make a general assumption that that is - * okay; for example there are platforms where nearly all users - * customarily belong to the same group. Perhaps this test should be - * configurable. - * - * XXX temporarily suppress check when on Windows, because there may not - * be proper support for Unix-y file permissions. Need to think of a - * reasonable check to apply on Windows. - */ -#if !defined(WIN32) && !defined(__CYGWIN__) - if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) - ereport(FATAL, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("data directory \"%s\" has group or world access", - DataDir), - errdetail("Permissions should be u=rwx (0700)."))); -#endif - - /* Look for PG_VERSION before looking for pg_control */ - ValidatePgVersion(DataDir); snprintf(path, sizeof(path), "%s/global/pg_control", DataDir); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 7bdecc32ec0..5095a4f6867 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3731,8 +3731,7 @@ PostgresMain(int argc, char *argv[], * Validate we have been given a reasonable-looking DataDir (if under * postmaster, assume postmaster did this already). */ - Assert(DataDir); - ValidatePgVersion(DataDir); + checkDataDir(); /* Change into DataDir (if under postmaster, was done already) */ ChangeToDataDir(); diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index c1f0441b081..0a3163398f3 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -16,8 +16,11 @@ * *------------------------------------------------------------------------- */ +#include <sys/stat.h> + #include "postgres.h" +#include "common/file_perm.h" #include "libpq/libpq-be.h" #include "libpq/pqcomm.h" #include "miscadmin.h" @@ -59,6 +62,12 @@ struct Latch *MyLatch; */ char *DataDir = NULL; +/* + * Mode of the data directory. The default is 0700 but may it be changed in + * checkDataDir() to 0750 if the data directory actually has that mode. + */ +int data_directory_mode = PG_DIR_MODE_OWNER; + char OutputFileName[MAXPGPATH]; /* debugging output file */ char my_exec_path[MAXPGPATH]; /* full path to my executable */ diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index f8f08f3f88b..03b28c3604a 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -89,6 +89,100 @@ SetDatabasePath(const char *path) } /* + * Validate the proposed data directory. + * + * Also initialize file and directory create modes and mode mask. + */ +void +checkDataDir(void) +{ + struct stat stat_buf; + + Assert(DataDir); + + if (stat(DataDir, &stat_buf) != 0) + { + if (errno == ENOENT) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("data directory \"%s\" does not exist", + DataDir))); + else + ereport(FATAL, + (errcode_for_file_access(), + errmsg("could not read permissions of directory \"%s\": %m", + DataDir))); + } + + /* eventual chdir would fail anyway, but let's test ... */ + if (!S_ISDIR(stat_buf.st_mode)) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("specified data directory \"%s\" is not a directory", + DataDir))); + + /* + * Check that the directory belongs to my userid; if not, reject. + * + * This check is an essential part of the interlock that prevents two + * postmasters from starting in the same directory (see CreateLockFile()). + * Do not remove or weaken it. + * + * XXX can we safely enable this check on Windows? + */ +#if !defined(WIN32) && !defined(__CYGWIN__) + if (stat_buf.st_uid != geteuid()) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("data directory \"%s\" has wrong ownership", + DataDir), + errhint("The server must be started by the user that owns the data directory."))); +#endif + + /* + * Check if the directory has correct permissions. If not, reject. + * + * Only two possible modes are allowed, 0700 and 0750. The latter mode + * indicates that group read/execute should be allowed on all newly + * created files and directories. + * + * XXX temporarily suppress check when on Windows, because there may not + * be proper support for Unix-y file permissions. Need to think of a + * reasonable check to apply on Windows. + */ +#if !defined(WIN32) && !defined(__CYGWIN__) + if (stat_buf.st_mode & PG_MODE_MASK_GROUP) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("data directory \"%s\" has invalid permissions", + DataDir), + errdetail("Permissions should be u=rwx (0700) or u=rwx,g=rx (0750)."))); +#endif + + /* + * Reset creation modes and mask based on the mode of the data directory. + * + * The mask was set earlier in startup to disallow group permissions on + * newly created files and directories. However, if group read/execute + * are present on the data directory then modify the create modes and mask + * to allow group read/execute on newly created files and directories and + * set the data_directory_mode GUC. + * + * Suppress when on Windows, because there may not be proper support for + * Unix-y file permissions. + */ +#if !defined(WIN32) && !defined(__CYGWIN__) + SetDataDirectoryCreatePerm(stat_buf.st_mode); + + umask(pg_mode_mask); + data_directory_mode = pg_dir_create_mode; +#endif + + /* Check for PG_VERSION */ + ValidatePgVersion(DataDir); +} + +/* * Set data directory, but make sure it's an absolute path. Use this, * never set DataDir directly. */ @@ -829,7 +923,7 @@ CreateLockFile(const char *filename, bool amPostmaster, /* * Try to create the lock file --- O_EXCL makes this atomic. * - * Think not to make the file protection weaker than 0600. See + * Think not to make the file protection weaker than 0600/0640. See * comments below. */ fd = open(filename, O_RDWR | O_CREAT | O_EXCL, pg_file_create_mode); @@ -899,17 +993,14 @@ CreateLockFile(const char *filename, bool amPostmaster, * implies that the existing process has a different userid than we * do, which means it cannot be a competing postmaster. A postmaster * cannot successfully attach to a data directory owned by a userid - * other than its own. (This is now checked directly in - * checkDataDir(), but has been true for a long time because of the - * restriction that the data directory isn't group- or - * world-accessible.) Also, since we create the lockfiles mode 600, - * we'd have failed above if the lockfile belonged to another userid - * --- which means that whatever process kill() is reporting about - * isn't the one that made the lockfile. (NOTE: this last - * consideration is the only one that keeps us from blowing away a - * Unix socket file belonging to an instance of Postgres being run by - * someone else, at least on machines where /tmp hasn't got a - * stickybit.) + * other than its own, as enforced in checkDataDir(). Also, since we + * create the lockfiles mode 0600/0640, we'd have failed above if the + * lockfile belonged to another userid --- which means that whatever + * process kill() is reporting about isn't the one that made the + * lockfile. (NOTE: this last consideration is the only one that + * keeps us from blowing away a Unix socket file belonging to an + * instance of Postgres being run by someone else, at least on + * machines where /tmp hasn't got a stickybit.) */ if (other_pid != my_pid && other_pid != my_p_pid && other_pid != my_gp_pid) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 71c2b4eff16..8441837e0ff 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -194,6 +194,7 @@ static void assign_application_name(const char *newval, void *extra); static bool check_cluster_name(char **newval, void **extra, GucSource source); static const char *show_unix_socket_permissions(void); static const char *show_log_file_mode(void); +static const char *show_data_directory_mode(void); /* Private functions in guc-file.l that need to be called from guc.c */ static ConfigVariable *ProcessConfigFileInternal(GucContext context, @@ -2044,6 +2045,21 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, show_log_file_mode }, + + { + {"data_directory_mode", PGC_INTERNAL, PRESET_OPTIONS, + gettext_noop("Mode of the data directory."), + gettext_noop("The parameter value is a numeric mode specification " + "in the form accepted by the chmod and umask system " + "calls. (To use the customary octal format the number " + "must start with a 0 (zero).)"), + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &data_directory_mode, + 0700, 0000, 0777, + NULL, NULL, show_data_directory_mode + }, + { {"work_mem", PGC_USERSET, RESOURCES_MEM, gettext_noop("Sets the maximum memory to be used for query workspaces."), @@ -10744,4 +10760,13 @@ show_log_file_mode(void) return buf; } +static const char * +show_data_directory_mode(void) +{ + static char buf[12]; + + snprintf(buf, sizeof(buf), "%04o", data_directory_mode); + return buf; +} + #include "guc-file.c" |