passenger 5.3.1 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +19 -0
  3. data/build/cxx_tests.rb +3 -1
  4. data/build/support/cxx_dependency_map.rb +120 -27
  5. data/dev/configkit-schemas/index.json +15 -3
  6. data/src/agent/Core/AdminPanelConnector.h +5 -2
  7. data/src/agent/Core/ApplicationPool/Group/StateInspection.cpp +2 -0
  8. data/src/agent/Core/Config.h +2 -1
  9. data/src/agent/Core/Controller/Config.h +6 -1
  10. data/src/agent/Core/Controller/InitRequest.cpp +6 -1
  11. data/src/agent/Core/CoreMain.cpp +26 -60
  12. data/src/agent/Core/SpawningKit/DirectSpawner.h +18 -6
  13. data/src/agent/Core/SpawningKit/ErrorRenderer.h +8 -8
  14. data/src/agent/Core/SpawningKit/Handshake/Perform.h +217 -61
  15. data/src/agent/Core/SpawningKit/Handshake/Prepare.h +57 -8
  16. data/src/agent/Core/SpawningKit/Handshake/Session.h +34 -1
  17. data/src/agent/Core/SpawningKit/Handshake/WorkDir.h +20 -4
  18. data/src/agent/Core/SpawningKit/SmartSpawner.h +90 -27
  19. data/src/agent/ExecHelper/ExecHelperMain.cpp +3 -0
  20. data/src/agent/Shared/ApiAccountUtils.h +2 -2
  21. data/src/agent/SpawnEnvSetupper/SpawnEnvSetupperMain.cpp +14 -4
  22. data/src/agent/Watchdog/Config.h +2 -1
  23. data/src/agent/Watchdog/WatchdogMain.cpp +38 -0
  24. data/src/apache2_module/Hooks.cpp +1 -0
  25. data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1 -1
  26. data/src/cxx_supportlib/ConfigKit/README.md +1 -1
  27. data/src/cxx_supportlib/Constants.h +6 -1
  28. data/src/cxx_supportlib/FileTools/FileManip.cpp +34 -2
  29. data/src/cxx_supportlib/FileTools/FileManip.h +58 -1
  30. data/src/cxx_supportlib/FileTools/PathManip.cpp +3 -2
  31. data/src/cxx_supportlib/FileTools/PathSecurityCheck.cpp +99 -0
  32. data/src/cxx_supportlib/FileTools/PathSecurityCheck.h +69 -0
  33. data/src/cxx_supportlib/Utils.cpp +37 -6
  34. data/src/cxx_supportlib/Utils.h +6 -0
  35. data/src/cxx_supportlib/Utils/AsyncSignalSafeUtils.h +14 -0
  36. data/src/cxx_supportlib/Utils/IOUtils.cpp +10 -18
  37. data/src/cxx_supportlib/Utils/IOUtils.h +10 -9
  38. data/src/cxx_supportlib/Utils/JsonUtils.h +12 -8
  39. data/src/cxx_supportlib/Utils/SystemMetricsCollector.h +4 -4
  40. data/src/cxx_supportlib/Utils/SystemTime.h +1 -1
  41. data/src/cxx_supportlib/WebSocketCommandReverseServer.h +3 -3
  42. data/src/cxx_supportlib/oxt/system_calls.cpp +25 -1
  43. data/src/cxx_supportlib/oxt/system_calls.hpp +3 -1
  44. data/src/helper-scripts/meteor-loader.rb +115 -28
  45. data/src/helper-scripts/rack-preloader.rb +1 -1
  46. data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +4 -4
  47. data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +4 -4
  48. data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +0 -10
  49. data/src/nginx_module/LocationConfig/AutoGeneratedHeaderSerialization.c +0 -42
  50. data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +0 -6
  51. data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +0 -8
  52. data/src/nginx_module/MainConfig/AutoGeneratedCreateFunction.c +10 -0
  53. data/src/nginx_module/MainConfig/AutoGeneratedManifestGeneration.c +22 -0
  54. data/src/nginx_module/MainConfig/AutoGeneratedStruct.h +8 -0
  55. data/src/nginx_module/ngx_http_passenger_module.c +6 -5
  56. data/src/ruby_supportlib/phusion_passenger.rb +1 -1
  57. data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +0 -1
  58. data/src/ruby_supportlib/phusion_passenger/common_library.rb +3 -0
  59. data/src/ruby_supportlib/phusion_passenger/config/installation_utils.rb +3 -3
  60. data/src/ruby_supportlib/phusion_passenger/constants.rb +5 -0
  61. data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +4 -2
  62. data/src/ruby_supportlib/phusion_passenger/platform_info.rb +3 -3
  63. data/src/ruby_supportlib/phusion_passenger/request_handler.rb +1 -1
  64. data/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller.rb +1 -1
  65. metadata +4 -2
@@ -201,6 +201,9 @@ switchGroup(uid_t uid, const struct passwd *userInfo, gid_t gid) {
201
201
  if (ngroups <= NGROUPS_MAX) {
202
202
  setgroupsCalled = true;
203
203
  gidset.reset(new gid_t[ngroups]);
204
+ for (int i = 0; i < ngroups; i++) {
205
+ gidset[i] = groups[i];
206
+ }
204
207
  if (setgroups(ngroups, gidset.get()) == -1) {
205
208
  int e = errno;
206
209
  fprintf(stderr, "ERROR: setgroups(%d, ...) failed: %s (errno=%d)\n",
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2015-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2015-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -262,7 +262,7 @@ struct ApiAccount {
262
262
  if (doc.isMember("password")) {
263
263
  password = doc["password"].asString();
264
264
  } else {
265
- password = strip(readAll(doc["password_file"].asString()));
265
+ password = strip(unsafeReadFile(doc["password_file"].asString()));
266
266
  }
267
267
  readonly = doc["level"].asString() == "readonly";
268
268
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2012-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2012-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -63,8 +63,8 @@
63
63
  #include <FileTools/FileManip.h>
64
64
  #include <FileTools/PathManip.h>
65
65
  #include <Utils.h>
66
- #include <Utils/IOUtils.h>
67
66
  #include <Utils/StrIntUtils.h>
67
+ #include <Core/SpawningKit/Handshake/WorkDir.h>
68
68
  #include <Core/SpawningKit/Exceptions.h>
69
69
 
70
70
  using namespace std;
@@ -100,7 +100,7 @@ static Json::Value
100
100
  readArgsJson(const string &workDir) {
101
101
  Json::Reader reader;
102
102
  Json::Value result;
103
- string contents = readAll(workDir + "/args.json");
103
+ string contents = unsafeReadFile(workDir + "/args.json");
104
104
  if (reader.parse(contents, result)) {
105
105
  return result;
106
106
  } else {
@@ -359,7 +359,7 @@ lookupUserGroup(const Context &context, uid_t *uid, struct passwd **userInfo,
359
359
  }
360
360
  }
361
361
 
362
- void
362
+ static void
363
363
  chownNewWorkDirFiles(const Context &context, uid_t uid, gid_t gid) {
364
364
  chown((context.workDir + "/response/steps/subprocess_before_first_exec/state").c_str(),
365
365
  uid, gid);
@@ -377,6 +377,11 @@ chownNewWorkDirFiles(const Context &context, uid_t uid, gid_t gid) {
377
377
  uid, gid);
378
378
  }
379
379
 
380
+ static void
381
+ finalizeWorkDir(const Context &context, uid_t uid, gid_t gid) {
382
+ SpawningKit::HandshakeWorkDir::finalize(context.workDir, uid, gid);
383
+ }
384
+
380
385
  static void
381
386
  enterLveJail(const Context &context, const struct passwd *userInfo) {
382
387
  string lveInitErr;
@@ -873,6 +878,8 @@ spawnEnvSetupperMain(int argc, char *argv[]) {
873
878
  lookupUserGroup(context, &uid, &userInfo, &gid);
874
879
  shell = userInfo->pw_shell;
875
880
  } else {
881
+ uid = geteuid();
882
+ gid = getegid();
876
883
  shell = lookupCurrentUserShell();
877
884
  }
878
885
  if (setUlimits(context.args)) {
@@ -880,6 +887,7 @@ spawnEnvSetupperMain(int argc, char *argv[]) {
880
887
  }
881
888
  if (shouldTrySwitchUser) {
882
889
  chownNewWorkDirFiles(context, uid, gid);
890
+ finalizeWorkDir(context, uid, gid);
883
891
 
884
892
  enterLveJail(context, userInfo);
885
893
  switchGroup(context, uid, userInfo, gid);
@@ -888,6 +896,8 @@ spawnEnvSetupperMain(int argc, char *argv[]) {
888
896
  switchUser(context, uid, userInfo);
889
897
  dumpEnvvars(context.workDir);
890
898
  dumpUserInfo(context.workDir);
899
+ } else {
900
+ finalizeWorkDir(context, uid, gid);
891
901
  }
892
902
  } else if (executedThroughShell(context)) {
893
903
  recordJourneyStepEnd(context, SpawningKit::SUBPROCESS_OS_SHELL,
@@ -132,6 +132,7 @@ using namespace std;
132
132
  * integration_mode string - default("standalone")
133
133
  * log_level string - default("notice")
134
134
  * log_target any - default({"stderr": true})
135
+ * max_instances_per_app unsigned integer - read_only
135
136
  * max_pool_size unsigned integer - default(6)
136
137
  * multi_app boolean - default(false),read_only
137
138
  * passenger_root string required read_only
@@ -145,7 +146,7 @@ using namespace std;
145
146
  * security_update_checker_interval unsigned integer - default(86400)
146
147
  * security_update_checker_proxy_url string - -
147
148
  * security_update_checker_url string - default("https://securitycheck.phusionpassenger.com/v1/check.json")
148
- * server_software string - default("Phusion_Passenger/5.3.1")
149
+ * server_software string - default("Phusion_Passenger/5.3.2")
149
150
  * setsid boolean - default(false)
150
151
  * show_version_in_header boolean - default(true)
151
152
  * single_app_mode_app_root string - default,read_only
@@ -71,6 +71,7 @@
71
71
  #include <Constants.h>
72
72
  #include <InstanceDirectory.h>
73
73
  #include <FileDescriptor.h>
74
+ #include <FileTools/PathSecurityCheck.h>
74
75
  #include <RandomGenerator.h>
75
76
  #include <BackgroundEventLoop.h>
76
77
  #include <LoggingKit/LoggingKit.h>
@@ -977,6 +978,39 @@ lookupDefaultUidGid(uid_t &uid, gid_t &gid) {
977
978
  }
978
979
  }
979
980
 
981
+ static void
982
+ warnIfInstanceDirVulnerable(const string &root) {
983
+ TRACE_POINT();
984
+
985
+ if (geteuid() != 0) {
986
+ return; // Passenger is not root, so no escalation.
987
+ }
988
+
989
+ vector<string> errors, checkErrors;
990
+ if (isPathProbablySecureForRootUse(root, errors, checkErrors)) {
991
+ if (!checkErrors.empty()) {
992
+ string message = "WARNING: unable to perform privilege escalation vulnerability detection:\n";
993
+ foreach (string line, checkErrors) {
994
+ message.append("\n - " + line);
995
+ }
996
+ P_WARN(message);
997
+ }
998
+ } else {
999
+ string message = "WARNING: potential privilege escalation vulnerability detected. " \
1000
+ PROGRAM_NAME " is running as root, and part(s) of the " SHORT_PROGRAM_NAME
1001
+ " instance directory (" + root + ") can be changed by non-root user(s):\n";
1002
+ foreach (string line, errors) {
1003
+ message.append("\n - " + line);
1004
+ }
1005
+ foreach (string line, checkErrors) {
1006
+ message.append("\n - " + line);
1007
+ }
1008
+ message.append("\n\nPlease either fix up the permissions for the insecure paths, or use" \
1009
+ " a different location for the instance dir that can only be modified by root.");
1010
+ P_WARN(message);
1011
+ }
1012
+ }
1013
+
980
1014
  static void
981
1015
  initializeWorkingObjects(const WorkingObjectsPtr &wo, InstanceDirToucherPtr &instanceDirToucher,
982
1016
  uid_t uidBeforeLoweringPrivilege)
@@ -1005,6 +1039,10 @@ initializeWorkingObjects(const WorkingObjectsPtr &wo, InstanceDirToucherPtr &ins
1005
1039
  if (watchdogConfig->get("integration_mode").asString() == "standalone") {
1006
1040
  instanceOptions.properties["standalone_engine"] = watchdogConfig->get("standalone_engine").asString();
1007
1041
  }
1042
+
1043
+ // check if path is safe
1044
+ warnIfInstanceDirVulnerable(watchdogConfig->get("instance_registry_dir").asString());
1045
+
1008
1046
  wo->instanceDir = boost::make_shared<InstanceDirectory>(instanceOptions,
1009
1047
  watchdogConfig->get("instance_registry_dir").asString());
1010
1048
  wo->extraConfigToPassToSubAgents["instance_dir"] = wo->instanceDir->getPath();
@@ -1326,6 +1326,7 @@ public:
1326
1326
  config["show_version_in_header"] = serverConfig.showVersionInHeader;
1327
1327
  config["max_pool_size"] = serverConfig.maxPoolSize;
1328
1328
  config["pool_idle_time"] = serverConfig.poolIdleTime;
1329
+ config["max_instances_per_app"] = serverConfig.maxInstancesPerApp;
1329
1330
  config["response_buffer_high_watermark"] = serverConfig.responseBufferHighWatermark;
1330
1331
  config["stat_throttle_rate"] = serverConfig.statThrottleRate;
1331
1332
  config["turbocaching"] = serverConfig.turbocaching;
@@ -4,7 +4,7 @@
4
4
 
5
5
  At the time of writing (25 Feb 2017), ConfigKit was just introduced, so these practices and patterns aren't yet used everywhere, but the long-term plan is to adopt these practices/patterns throughout the entire codebase.
6
6
 
7
- <!-- MarkdownTOC depth=3 autolink="true" bracket="round" -->
7
+ <!-- MarkdownTOC levels="1,2,3,4" autolink="true" bracket="round" -->
8
8
 
9
9
  - [The "component" pattern](#the-component-pattern)
10
10
  - [Components are composable](#components-are-composable)
@@ -4,7 +4,7 @@ ConfigKit is a configuration management system that lets you define configuratio
4
4
 
5
5
  **Table of contents:**
6
6
 
7
- <!-- MarkdownTOC depth=3 autolink="true" bracket="round" -->
7
+ <!-- MarkdownTOC levels="1,2,3,4" autolink="true" bracket="round" -->
8
8
 
9
9
  - [Motivations](#motivations)
10
10
  - [Configuration flow from high-level to low-level with a minimum of repeated code](#configuration-flow-from-high-level-to-low-level-with-a-minimum-of-repeated-code)
@@ -81,7 +81,7 @@
81
81
  #define PASSENGER_API_VERSION_MAJOR 0
82
82
  #define PASSENGER_API_VERSION_MINOR 3
83
83
  #define PASSENGER_DEFAULT_USER "nobody"
84
- #define PASSENGER_VERSION "5.3.1"
84
+ #define PASSENGER_VERSION "5.3.2"
85
85
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
86
86
  #define PROCESS_SHUTDOWN_TIMEOUT 60
87
87
  #define PROCESS_SHUTDOWN_TIMEOUT_DISPLAY "1 minute"
@@ -98,6 +98,11 @@
98
98
  #define SERVER_KIT_MAX_SERVER_ENDPOINTS 4
99
99
  #define SERVER_TOKEN_NAME "Phusion_Passenger"
100
100
  #define SHORT_PROGRAM_NAME "Passenger"
101
+ #define SPAWNINGKIT_MAX_ERROR_CATEGORY_SIZE 32
102
+ #define SPAWNINGKIT_MAX_JOURNEY_STEP_FILE_SIZE 32
103
+ #define SPAWNINGKIT_MAX_PROPERTIES_JSON_SIZE 32768
104
+ #define SPAWNINGKIT_MAX_SUBPROCESS_ENVDUMP_SIZE 131072
105
+ #define SPAWNINGKIT_MAX_SUBPROCESS_ERROR_MESSAGE_SIZE 131072
101
106
  #define SUPPORT_URL "https://www.phusionpassenger.com/support"
102
107
  #define USER_NAMESPACE_DIRNAME ".passenger"
103
108
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2010-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -34,6 +34,7 @@
34
34
 
35
35
  #include <cstdio>
36
36
  #include <cerrno>
37
+ #include <limits>
37
38
 
38
39
  #include <FileTools/FileManip.h>
39
40
  #include <FileDescriptor.h>
@@ -44,6 +45,7 @@
44
45
  #include <Utils.h> // parseModeString
45
46
  #include <Utils/CachedFileStat.hpp>
46
47
  #include <Utils/IOUtils.h>
48
+ #include <Utils/ScopeGuard.h>
47
49
 
48
50
  namespace Passenger {
49
51
 
@@ -181,6 +183,36 @@ createFile(const string &filename, const StaticString &contents, mode_t permissi
181
183
  }
182
184
  }
183
185
 
186
+ string
187
+ unsafeReadFile(const string &path) {
188
+ int fd = open(path.c_str(), O_RDONLY);
189
+ if (fd != -1) {
190
+ FdGuard guard(fd, __FILE__, __LINE__);
191
+ return readAll(fd, std::numeric_limits<size_t>::max()).first;
192
+ } else {
193
+ int e = errno;
194
+ throw FileSystemException("Cannot open '" + path + "' for reading",
195
+ e, path);
196
+ }
197
+ }
198
+
199
+ pair<string, bool>
200
+ safeReadFile(int dirfd, const string &basename, size_t maxSize) {
201
+ if (basename.find('/') != string::npos) {
202
+ throw ArgumentException("basename may not contain slashes");
203
+ }
204
+
205
+ int fd = openat(dirfd, basename.c_str(), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
206
+ if (fd != -1) {
207
+ FdGuard guard(fd, __FILE__, __LINE__);
208
+ return readAll(fd, maxSize);
209
+ } else {
210
+ int e = errno;
211
+ throw FileSystemException("Cannot open '" + basename + "' for reading",
212
+ e, basename);
213
+ }
214
+ }
215
+
184
216
  void
185
217
  makeDirTree(const string &path, const StaticString &mode, uid_t owner, gid_t group) {
186
218
  struct stat buf;
@@ -240,7 +272,7 @@ makeDirTree(const string &path, const StaticString &mode, uid_t owner, gid_t gro
240
272
  group = (gid_t) -1; // Don't let chown change file group.
241
273
  }
242
274
  do {
243
- ret = chown(current.c_str(), owner, group);
275
+ ret = lchown(current.c_str(), owner, group);
244
276
  } while (ret == -1 && errno == EINTR);
245
277
  if (ret == -1) {
246
278
  char message[1024];
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2017 Phusion Holding B.V.
3
+ * Copyright (c) 2010-2018 Phusion Holding B.V.
4
4
  *
5
5
  * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
6
  * trademarks of Phusion Holding B.V.
@@ -31,6 +31,7 @@
31
31
  #include <unistd.h>
32
32
  #include <cstddef>
33
33
  #include <string>
34
+ #include <utility>
34
35
  #include <StaticString.h>
35
36
 
36
37
  namespace boost {
@@ -131,6 +132,62 @@ void createFile(const string &filename, const StaticString &contents,
131
132
  bool overwrite = true,
132
133
  const char *callerFile = NULL, unsigned int callerLine = 0);
133
134
 
135
+ /**
136
+ * Read all data from the given file until EOF.
137
+ * This function is "unsafe" in the sense that it lacks the security
138
+ * checks implemented by `safeReadFile()`. Read the docs for that function
139
+ * for more information.
140
+ *
141
+ * @throws SystemException
142
+ */
143
+ string unsafeReadFile(const string &path);
144
+
145
+ /**
146
+ * Read all data from the given file until EOF.
147
+ *
148
+ * - `dirfd` is a file descriptor of the directory that contains the file
149
+ * you want read from.
150
+ * - `basename` is the basename of the file you want to read from. `basename`
151
+ * may thus not contain slashes.
152
+ * - `maxSize` is the maximum number of bytes you want to read.
153
+ *
154
+ * Returns a pair `(contents, eof)`.
155
+ *
156
+ * - `contents` is the read file contents, which is at most `maxSize` bytes.
157
+ * - `eof` indicates whether the entire file has been read. If false, then it
158
+ * means the amount of data is larger than `maxSize`.
159
+ *
160
+ * This function is "safe" in following sense:
161
+ *
162
+ * - It mitigates symbolic link attacks. `open(path)` may not be safe for
163
+ * processes running as root, because if a user controls any parts of the
164
+ * path then the user can swap one of the parent directories, or the file
165
+ * itself, with a symlink. This causes us to read an arbitrary file.
166
+ *
167
+ * This function mitigates this attack by requiring a `dirfd` and by opening
168
+ * the file with O_NOFOLLOW. The caller must ensure that `dirfd` is created
169
+ * at a time when it knows that no user controls any parts of the path to
170
+ * that directory.
171
+ *
172
+ * However, this mitigation does mean that safeReadFile() *cannot be used to
173
+ * read from a symlink*!
174
+ *
175
+ * - It mitigates DoS attacks through non-regular files which may block the
176
+ * reader, like FIFOs or block devices. For example if the path refers to a
177
+ * FIFO which a user created, and the user never opens the FIFO on the other
178
+ * side, then the open can block indefinitely.
179
+ *
180
+ * This function mitigates this attack by opening the file with O_NONBLOCK.
181
+ *
182
+ * - It mitigates DoS attacks by creating a very large file. Since we slurp
183
+ * the entire file into memory, it is a good idea if we impose a limit
184
+ * on how much data we read.
185
+ *
186
+ * @throws ArgumentException
187
+ * @throws SystemException
188
+ */
189
+ pair<string, bool> safeReadFile(int dirfd, const string &basename, size_t maxSize);
190
+
134
191
  /**
135
192
  * Create the directory at the given path, creating intermediate directories
136
193
  * if necessary. The created directories' permissions are exactly as specified
@@ -84,11 +84,12 @@ absolutizePath(const StaticString &path, const StaticString &workingDir) {
84
84
  vector<string> components;
85
85
  if (!startsWith(path, "/")) {
86
86
  if (workingDir.empty()) {
87
- char buffer[PATH_MAX];
88
- if (getcwd(buffer, sizeof(buffer)) == NULL) {
87
+ char buffer[PATH_MAX + 1];
88
+ if (getcwd(buffer, PATH_MAX) == NULL) {
89
89
  int e = errno;
90
90
  throw SystemException("Unable to query current working directory", e);
91
91
  }
92
+ buffer[PATH_MAX] = '\0';
92
93
  split(buffer + 1, '/', components);
93
94
  } else {
94
95
  string absoluteWorkingDir = absolutizePath(workingDir);
@@ -0,0 +1,99 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2018 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+ #include <sys/stat.h>
28
+ #include <unistd.h>
29
+
30
+ #include <cerrno>
31
+
32
+ #include <FileTools/PathSecurityCheck.h>
33
+ #include <FileTools/PathManip.h>
34
+ #include <Utils.h>
35
+ #include <Utils/StrIntUtils.h>
36
+
37
+ namespace Passenger {
38
+
39
+
40
+ static bool
41
+ isSinglePathProbablySecureForRootUse(const string &path,
42
+ vector<string> &errors, vector<string> &checkErrors)
43
+ {
44
+ struct stat s;
45
+ int ret;
46
+
47
+ do {
48
+ ret = stat(path.c_str(), &s);
49
+ } while (ret == -1 && errno == EAGAIN);
50
+ if (ret == -1) {
51
+ int e = errno;
52
+ checkErrors.push_back("Security check skipped on " + path
53
+ + ": stat() failed: " + strerror(e) + " (errno="
54
+ + toString(e) + ")");
55
+ return true;
56
+ }
57
+
58
+ if (s.st_uid != 0) {
59
+ errors.push_back(path + " is not secure: it can be modified by user "
60
+ + getUserName(s.st_uid));
61
+ return false;
62
+ }
63
+
64
+ if (s.st_mode & S_ISVTX) {
65
+ return true;
66
+ }
67
+
68
+ if (s.st_mode & S_IWGRP) {
69
+ errors.push_back(path + " is not secure: it can be modified by group "
70
+ + getGroupName(s.st_gid));
71
+ return false;
72
+ }
73
+
74
+ if (s.st_mode & S_IWOTH) {
75
+ errors.push_back(path + " is not secure: it can be modified by anybody");
76
+ return false;
77
+ }
78
+
79
+ return true;
80
+ }
81
+
82
+ bool
83
+ isPathProbablySecureForRootUse(const StaticString &path, vector<string> &errors,
84
+ vector<string> &checkErrors)
85
+ {
86
+ string fullPath = absolutizePath(path);
87
+ bool result = true;
88
+
89
+ while (!fullPath.empty() && fullPath != "/") {
90
+ result = isSinglePathProbablySecureForRootUse(fullPath, errors, checkErrors)
91
+ && result;
92
+ fullPath = extractDirName(fullPath);
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+
99
+ } // namespace Passenger