passenger 5.3.1 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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