diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2019-08-16 14:53:29 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-08-16 14:53:29 +0000 |
commit | ea1dd7a54b7b7bcae7f9ad15a547ed0ee3d4348b (patch) | |
tree | 11a9a25076dd431b76656d79dca575bbb7a04d59 | |
parent | 4a1d30014c12bae57caf423b695378fdc6fea6c9 (diff) | |
download | mongo-ea1dd7a54b7b7bcae7f9ad15a547ed0ee3d4348b.tar.gz |
SERVER-41633 Allow overriding system umask for group/other from process startup
CodeReview: 488750005
-rw-r--r-- | jstests/noPassthrough/umask.js | 39 | ||||
-rw-r--r-- | src/mongo/db/initialize_server_global_state.cpp | 143 | ||||
-rw-r--r-- | src/mongo/db/initialize_server_global_state.idl | 20 |
3 files changed, 163 insertions, 39 deletions
diff --git a/jstests/noPassthrough/umask.js b/jstests/noPassthrough/umask.js index 8d7234b15d3..7ffd93cf829 100644 --- a/jstests/noPassthrough/umask.js +++ b/jstests/noPassthrough/umask.js @@ -40,8 +40,7 @@ if (buildInfo()["modules"].some((mod) => { mongodOptions.auditFormat = "JSON"; } -const checkMask = (topDir, expected, honoringUmask) => { - const maybeNot = honoringUmask ? "" : " not"; +function checkMask(topDir, expected, honoringUmask, customUmask = false) { const processDirectory = (dir) => { jsTestLog(`Checking ${dir}`); ls(dir).forEach((file) => { @@ -54,13 +53,18 @@ const checkMask = (topDir, expected, honoringUmask) => { } const mode = new Number(getFileMode(file)); const modeStr = mode.toString(8); - const msg = `Mode for ${file} is ${modeStr} when${maybeNot} honoring system umask`; + let msg = `Mode for ${file} is ${modeStr} when `; + if (customUmask) { + msg += ' using custom umask'; + } else { + msg += (honoringUmask ? '' : 'not ') + ' honoring system umask'; + } assert.eq(mode.valueOf(), expected, msg); }); }; processDirectory(topDir); -}; +} // First we start up the mongod normally, all the files except mongod.lock should have the mode // 0600 @@ -74,7 +78,32 @@ mongodOptions.setParameter = { }; conn = MongoRunner.runMongod(mongodOptions); MongoRunner.stopMongod(conn); -checkMask(conn.fullOptions.dbpath, permissiveUmask, false); +checkMask(conn.fullOptions.dbpath, permissiveUmask, true); + +// Restart the mongod with custom umask as string, all files should have the mode 0644 +const worldReadableUmask = Number.parseInt("644", 8); +mongodOptions.setParameter = { + processUmask: '022', +}; +conn = MongoRunner.runMongod(mongodOptions); +MongoRunner.stopMongod(conn); +checkMask(conn.fullOptions.dbpath, worldReadableUmask, false, true); + +// Fail to start up with both honorSystemUmask and processUmask set. +mongodOptions.setParameter = { + honorSystemUmask: true, + processUmask: '022', +}; +assert.eq(null, MongoRunner.runMongod(mongodOptions)); + +// Okay to start with both if honorSystemUmask is false. +mongodOptions.setParameter = { + honorSystemUmask: false, + processUmask: '022', +}; +conn = MongoRunner.runMongod(mongodOptions); +MongoRunner.stopMongod(conn); +checkMask(conn.fullOptions.dbpath, worldReadableUmask, false, true); umask(oldUmask.valueOf()); })(); diff --git a/src/mongo/db/initialize_server_global_state.cpp b/src/mongo/db/initialize_server_global_state.cpp index 7c48caa70b6..e09338d5d55 100644 --- a/src/mongo/db/initialize_server_global_state.cpp +++ b/src/mongo/db/initialize_server_global_state.cpp @@ -205,12 +205,9 @@ void forkServerOrDie() { quickExit(EXIT_FAILURE); } -// On POSIX platforms we need to set our umask before opening any log files, so this -// should depend on MungeUmask above, but not on Windows. -MONGO_INITIALIZER_GENERAL( - ServerLogRedirection, - ("GlobalLogManager", "EndStartupOptionHandling", "ForkServer", "MungeUmask"), - ("default")) +MONGO_INITIALIZER_GENERAL(ServerLogRedirection, + ("GlobalLogManager", "EndStartupOptionHandling", "ForkServer"), + ("default")) (InitializerContext*) { using logger::LogManager; using logger::MessageEventDetailsEncoder; @@ -337,27 +334,6 @@ MONGO_INITIALIZER(RegisterShortCircuitExitHandler)(InitializerContext*) { return Status::OK(); } -// On non-windows platforms, drop rwx for group and other unless the -// user has opted into using the system umask. To do so, we first read -// out the current umask (by temporarily setting it to -// no-permissions), and then or the returned umask with the -// restrictions we want to apply and set it back. The overall effect -// is to set the bits for 'other' and 'group', but leave umask bits -// bits for 'user' unaltered. -namespace { - -MONGO_INITIALIZER_WITH_PREREQUISITES(MungeUmask, ("EndStartupOptionHandling")) -(InitializerContext*) { -#ifndef _WIN32 - if (!gHonorSystemUmask) { - umask(umask(S_IRWXU | S_IRWXG | S_IRWXO) | S_IRWXG | S_IRWXO); - } -#endif - - return Status::OK(); -} -} // namespace - bool initializeServerGlobalState(ServiceContext* service, PidFileWrite pidWrite) { #ifndef _WIN32 if (!serverGlobalParams.noUnixSocket && !fs::is_directory(serverGlobalParams.socket)) { @@ -376,4 +352,117 @@ bool initializeServerGlobalState(ServiceContext* service, PidFileWrite pidWrite) return true; } +#ifndef _WIN32 +namespace { +// Handling for `honorSystemUmask` and `processUmask` setParameters. +// Non-Windows platforms only. +// +// If honorSystemUmask is true, processUmask may not be set +// and the umask will be left exactly as set by the OS. +// +// If honorSystemUmask is false, then we will still honor the 'user' +// portion of the current umask, but the group/other bits will be +// set to 1, or whatever value is provided by processUmask if specified. + +// processUmask set parameter may only override group/other bits. +constexpr mode_t kValidUmaskBits = S_IRWXG | S_IRWXO; + +// By default, honorSystemUmask==false masks all group/other bits. +constexpr mode_t kDefaultProcessUmask = S_IRWXG | S_IRWXO; + +bool honorSystemUmask = false; +boost::optional<mode_t> umaskOverride; + +mode_t getUmaskOverride() { + return umaskOverride ? *umaskOverride : kDefaultProcessUmask; +} + +// We need to set our umask before opening any log files. +MONGO_INITIALIZER_GENERAL(MungeUmask, ("EndStartupOptionHandling"), ("ServerLogRedirection")) +(InitializerContext*) { + if (!honorSystemUmask) { + // POSIX does not provide a mechanism for reading the current umask + // without modifying it. + // Do this conservatively by setting a short-lived umask of 0777 + // in order to pull out the user portion of the current umask. + umask((umask(S_IRWXU | S_IRWXG | S_IRWXO) & S_IRWXU) | getUmaskOverride()); + } + + return Status::OK(); +} +} // namespace +#endif + +// --setParameter honorSystemUmask +Status HonorSystemUMaskServerParameter::setFromString(const std::string& value) { +#ifndef _WIN32 + if ((value == "0") || (value == "false")) { + // false may be specified with processUmask + // since it defines precisely how we're not honoring system umask. + honorSystemUmask = false; + return Status::OK(); + } + + if ((value == "1") || (value == "true")) { + if (umaskOverride) { + return {ErrorCodes::BadValue, + "honorSystemUmask and processUmask may not be specified together"}; + } else { + honorSystemUmask = true; + return Status::OK(); + } + } + + return {ErrorCodes::BadValue, "honorSystemUmask must be 'true' or 'false'"}; +#else + return {ErrorCodes::InternalError, "honerSystemUmask is not available on windows"}; +#endif +} + +void HonorSystemUMaskServerParameter::append(OperationContext*, + BSONObjBuilder& b, + const std::string& name) { +#ifndef _WIN32 + b << name << honorSystemUmask; +#endif +} + +// --setParameter processUmask +Status ProcessUMaskServerParameter::setFromString(const std::string& value) { +#ifndef _WIN32 + if (honorSystemUmask) { + return {ErrorCodes::BadValue, + "honorSystemUmask and processUmask may not be specified together"}; + } + + // Convert base from octal + const char* val = value.c_str(); + char* end = nullptr; + + auto mask = std::strtoul(val, &end, 8); + if (end && (end != (val + value.size()))) { + return {ErrorCodes::BadValue, + str::stream() << "'" << value << "' is not a valid octal value"}; + } + + if ((mask & kValidUmaskBits) != mask) { + return {ErrorCodes::BadValue, + str::stream() << "'" << value << "' attempted to set invalid umask bits"}; + } + + umaskOverride = static_cast<mode_t>(mask); + return Status::OK(); +#else + return {ErrorCodes::InternalError, "processUmask is not available on windows"}; +#endif +} + +void ProcessUMaskServerParameter::append(OperationContext*, + BSONObjBuilder& b, + const std::string& name) { +#ifndef _WIN32 + b << name << static_cast<int>(getUmaskOverride()); +#endif +} + } // namespace mongo diff --git a/src/mongo/db/initialize_server_global_state.idl b/src/mongo/db/initialize_server_global_state.idl index 5b697ac1ac3..c25d78fa07f 100644 --- a/src/mongo/db/initialize_server_global_state.idl +++ b/src/mongo/db/initialize_server_global_state.idl @@ -40,12 +40,18 @@ server_parameters: set_at: [ startup, runtime ] honorSystemUmask: - cpp_varname: gHonorSystemUmask - cpp_vartype: bool + description: 'Use the system provided umask, rather than overriding with processUmask config value' + set_at: startup + cpp_class: HonorSystemUMaskServerParameter condition: preprocessor: '!defined(_WIN32)' - default: false - description: 'Max log size in kilobytes' - set_at: [ startup ] - - + + processUmask: + description: > + Override system umask for group/other permissions when honorSystemUmask is false. + User umask is always preserved. + set_at: startup + cpp_class: ProcessUMaskServerParameter + condition: + preprocessor: '!defined(_WIN32)' + |