Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/bootstrap/bootstrap.c12
-rw-r--r--src/backend/postmaster/postmaster.c87
-rw-r--r--src/backend/tcop/postgres.c3
-rw-r--r--src/backend/utils/init/globals.c9
-rw-r--r--src/backend/utils/init/miscinit.c115
-rw-r--r--src/backend/utils/misc/guc.c25
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"