62
63
// Begin try block - soft exception cannot really be handled before
65
std::string scriptFilename;
67
// If caller is super-user, print info message and exit
68
if (api.getRealProcessUser().isSuperUser()) {
69
this->printAboutMessage();
72
config.readFromFile(cfgFile);
74
// Check permissions (real uid, effective uid)
75
this->checkProcessPermissions(config);
78
// not done before, because we need super-user privileges for
83
scriptFilename = env.getVar("SCRIPT_FILENAME");
84
} catch (KeyNotFoundException& e) {
85
logger.logError("Environment variable SCRIPT_FILENAME not set");
86
this->printAboutMessage();
90
this->checkScriptFile(scriptFilename, config, env);
92
// Root privileges are needed for chroot()
93
// so do this before changing process permissions
94
if (config.getChrootPath().length() > 0) {
95
api.chroot(config.getChrootPath());
98
this->changeProcessPermissions(scriptFilename, config, env);
100
interpreter = this->getInterpreter(env, config);
101
targetMode = this->getTargetMode(interpreter);
103
// Prepare environment for new process
104
newEnv = this->prepareEnvironment(env, config, targetMode);
106
// Set PATH_TRANSLATED to SCRIPT_FILENAME, otherwise
107
// the PHP interpreter will not be able to find the script
108
if (targetMode == TARGETMODE_PHP && newEnv.hasVar("PATH_TRANSLATED")) {
109
newEnv.setVar("PATH_TRANSLATED", scriptFilename);
112
// Log attempt to execute script
113
logger.logInfo("Executing \"" + scriptFilename + "\" as UID "
114
+ Util::intToStr(api.getEffectiveProcessUser().getUid())
117
api.getEffectiveProcessGroup().getGid()));
119
this->executeScript(scriptFilename, interpreter, targetMode, newEnv,
122
// Function should never return
123
// So, if we get here, return with error code
66
std::string scriptFilename;
68
GroupInfo targetGroup;
70
// If caller is super-user, print info message and exit
71
if (api.getRealProcessUser().isSuperUser()) {
72
this->printAboutMessage();
75
config.readFromFile(cfgFile);
77
// Check permissions (real uid, effective uid)
78
this->checkProcessPermissions(config);
81
// not done before, because we need super-user privileges for
86
scriptFilename = env.getVar("SCRIPT_FILENAME");
87
} catch (KeyNotFoundException& e) {
88
logger.logError("Environment variable SCRIPT_FILENAME not set");
89
this->printAboutMessage();
94
// Do checks that do not need target user info
95
this->checkScriptFileStage1(scriptFilename, config, env);
97
// Find out target user
98
this->checkProcessPermissions(scriptFilename, config, env, targetUser, targetGroup);
100
// Now do checks that might require user info
101
this->checkScriptFileStage2(scriptFilename, config, env, targetUser, targetGroup);
103
// Root privileges are needed for chroot()
104
// so do this before changing process permissions
105
if (config.getChrootPath().length() > 0) {
106
PathMatcher pathMatcher = PathMatcher(targetUser, targetGroup);
107
std::string chrootPath = pathMatcher.resolveVariables(config.getChrootPath());
108
api.chroot(chrootPath);
111
this->changeProcessPermissions(config, targetUser, targetGroup);
113
interpreter = this->getInterpreter(env, config);
114
targetMode = this->getTargetMode(interpreter);
116
// Prepare environment for new process
117
newEnv = this->prepareEnvironment(env, config, targetMode);
119
// Set PATH_TRANSLATED to SCRIPT_FILENAME, otherwise
120
// the PHP interpreter will not be able to find the script
121
if (targetMode == TARGETMODE_PHP && newEnv.hasVar("PATH_TRANSLATED")) {
122
newEnv.setVar("PATH_TRANSLATED", scriptFilename);
125
// Log attempt to execute script
126
logger.logInfo("Executing \"" + scriptFilename + "\" as UID "
127
+ Util::intToStr(api.getEffectiveProcessUser().getUid())
130
api.getEffectiveProcessGroup().getGid()));
132
this->executeScript(scriptFilename, interpreter, targetMode, newEnv,
135
// Function should never return
136
// So, if we get here, return with error code
125
138
} catch (SoftException& e) {
126
if (!config.getErrorsToBrowser()) {
130
std::cout << "Content-Type: text/html\n"
135
<< " <title>500 Internal Server Error</title>\n"
138
<< " <h1>Internal Server Error</h1>\n"
139
<< " <p>" << e.getMessage() << "</p>\n"
141
<< " <address>suPHP " << PACKAGE_VERSION << "</address>\n"
139
if (!config.getErrorsToBrowser()) {
143
std::cout << "Content-Type: text/html\n"
148
<< " <title>500 Internal Server Error</title>\n"
151
<< " <h1>Internal Server Error</h1>\n"
152
<< " <p>" << e.getMessage() << "</p>\n"
154
<< " <address>suPHP " << PACKAGE_VERSION << "</address>\n"
148
161
void suPHP::Application::printAboutMessage() {
149
162
std::cerr << "suPHP version " << PACKAGE_VERSION << "\n";
150
std::cerr << "(c) 2002-2005 Sebastian Marsching\n";
163
std::cerr << "(c) 2002-2007 Sebastian Marsching\n";
151
164
std::cerr << std::endl;
152
165
std::cerr << "suPHP has to be called by mod_suphp to work." << std::endl;
156
void suPHP::Application::checkProcessPermissions(Configuration& config)
169
void suPHP::Application::checkProcessPermissions(Configuration& config)
157
170
throw (SecurityException, LookupException) {
158
171
API& api = API_Helper::getSystemAPI();
159
172
if (api.getRealProcessUser() !=
160
api.getUserInfo(config.getWebserverUser())) {
161
throw SecurityException("Calling user is not webserver user!",
173
api.getUserInfo(config.getWebserverUser())) {
174
throw SecurityException("Calling user is not webserver user!",
165
178
if (!api.getEffectiveProcessUser().isSuperUser()) {
166
throw SecurityException(
167
"Do not have root privileges. Executable not set-uid root?",
179
throw SecurityException(
180
"Do not have root privileges. Executable not set-uid root?",
173
void suPHP::Application::checkScriptFile(
174
const std::string& scriptFilename,
186
void suPHP::Application::checkScriptFileStage1(
187
const std::string& scriptFilename,
175
188
const Configuration& config,
176
189
const Environment& environment) const
177
190
throw (SystemException, SoftException) {
178
191
Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
179
192
File scriptFile = File(scriptFilename);
193
File realScriptFile = File(scriptFile.getRealPath());
181
195
// Check wheter file exists
182
196
if (!scriptFile.exists()) {
183
std::string error = "File " + scriptFile.getPath() + " does not exist";
184
logger.logWarning(error);
185
throw SoftException(error, __FILE__, __LINE__);
197
std::string error = "File " + scriptFile.getPath() + " does not exist";
198
logger.logWarning(error);
199
throw SoftException(error, __FILE__, __LINE__);
188
// Get full path to script file
190
File realScriptFile = File(scriptFile.getRealPath());
191
File directory = realScriptFile.getParentDirectory();
193
// Check wheter script is in docroot
194
if (realScriptFile.getPath().find(config.getDocroot()) != 0) {
195
std::string error = "Script \"" + scriptFile.getPath()
196
+ "\" resolving to \"" + realScriptFile.getPath()
197
+ "\" not within configured docroot";
198
logger.logWarning(error);
199
throw SoftException(error, __FILE__, __LINE__);
201
if (!realScriptFile.exists()) {
202
std::string error = "File " + realScriptFile.getPath()
203
+ " referenced by symlink " +scriptFile.getPath()
205
logger.logWarning(error);
206
throw SoftException(error, __FILE__, __LINE__);
202
209
// If enabled, check whether script is in the vhost's docroot
203
210
if (!environment.hasVar("DOCUMENT_ROOT"))
204
throw SoftException("Environment variable DOCUMENT_ROOT not set",
206
if (config.getCheckVHostDocroot()
207
&& realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT"))
210
std::string error = "File \"" + realScriptFile.getPath()
211
+ "\" is not in document root of Vhost \""
212
+ environment.getVar("DOCUMENT_ROOT") + "\"";
213
logger.logWarning(error);
214
throw SoftException(error, __FILE__, __LINE__);
217
// Check script and directory permissions
211
throw SoftException("Environment variable DOCUMENT_ROOT not set",
213
if (config.getCheckVHostDocroot()
214
&& realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT"))
217
std::string error = "File \"" + realScriptFile.getPath()
218
+ "\" is not in document root of Vhost \""
219
+ environment.getVar("DOCUMENT_ROOT") + "\"";
220
logger.logWarning(error);
221
throw SoftException(error, __FILE__, __LINE__);
223
if (config.getCheckVHostDocroot()
224
&& scriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT"))
227
std::string error = "File \"" + scriptFile.getPath()
228
+ "\" is not in document root of Vhost \""
229
+ environment.getVar("DOCUMENT_ROOT") + "\"";
230
logger.logWarning(error);
231
throw SoftException(error, __FILE__, __LINE__);
234
// Check script permissions
235
// Directories will be checked later
218
236
if (!realScriptFile.hasUserReadBit()) {
219
std::string error = "File \"" + realScriptFile.getPath()
221
logger.logWarning(error);
222
throw SoftException(error, __FILE__, __LINE__);
237
std::string error = "File \"" + realScriptFile.getPath()
239
logger.logWarning(error);
240
throw SoftException(error, __FILE__, __LINE__);
226
244
if (!config.getAllowFileGroupWriteable()
227
&& realScriptFile.hasGroupWriteBit()) {
228
std::string error = "File \"" + realScriptFile.getPath()
229
+ "\" is writeable by group";
230
logger.logWarning(error);
231
throw SoftException(error, __FILE__, __LINE__);
234
if (!config.getAllowDirectoryGroupWriteable()
235
&& directory.hasGroupWriteBit()) {
236
std::string error = "Directory \"" + directory.getPath()
237
+ "\" is writeable by group";
238
logger.logWarning(error);
239
throw SoftException(error, __FILE__, __LINE__);
245
&& realScriptFile.hasGroupWriteBit()) {
246
std::string error = "File \"" + realScriptFile.getPath()
247
+ "\" is writeable by group";
248
logger.logWarning(error);
249
throw SoftException(error, __FILE__, __LINE__);
242
252
if (!config.getAllowFileOthersWriteable()
243
&& realScriptFile.hasOthersWriteBit()) {
244
std::string error = "File \"" + realScriptFile.getPath()
245
+ "\" is writeable by others";
246
logger.logWarning(error);
247
throw SoftException(error, __FILE__, __LINE__);
250
if (!config.getAllowDirectoryOthersWriteable()
251
&& directory.hasOthersWriteBit()) {
252
std::string error = "Directory \"" + directory.getPath()
253
+ "\" is writeable by others";
254
logger.logWarning(error);
255
throw SoftException(error, __FILE__, __LINE__);
253
&& realScriptFile.hasOthersWriteBit()) {
254
std::string error = "File \"" + realScriptFile.getPath()
255
+ "\" is writeable by others";
256
logger.logWarning(error);
257
throw SoftException(error, __FILE__, __LINE__);
258
260
// Check UID/GID of symlink is matching target
259
261
if (scriptFile.getUser() != realScriptFile.getUser()
260
|| scriptFile.getGroup() != realScriptFile.getGroup()) {
261
std::string error = "UID or GID of symlink \"" + scriptFile.getPath()
262
+ "\" is not matching its target";
263
logger.logWarning(error);
264
throw SoftException(error, __FILE__, __LINE__);
269
void suPHP::Application::changeProcessPermissions(
270
const std::string& scriptFilename,
271
const Configuration& config,
272
const Environment& environment) const
262
|| scriptFile.getGroup() != realScriptFile.getGroup()) {
263
std::string error = "UID or GID of symlink \"" + scriptFile.getPath()
264
+ "\" is not matching its target";
265
logger.logWarning(error);
266
throw SoftException(error, __FILE__, __LINE__);
270
void suPHP::Application::checkScriptFileStage2(
271
const std::string& scriptFilename,
272
const Configuration& config,
273
const Environment& environment,
274
const UserInfo& targetUser,
275
const GroupInfo& targetGroup) const
276
throw (SystemException, SoftException) {
277
Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
278
File scriptFile = File(scriptFilename);
279
PathMatcher pathMatcher = PathMatcher(targetUser, targetGroup);
281
// Get full path to script file
282
File realScriptFile = File(scriptFile.getRealPath());
284
// Check wheter script is in one of the defined docroots
285
bool file_in_docroot = false;
286
const std::vector<std::string> docroots = config.getDocroots();
287
for (std::vector<std::string>::const_iterator i = docroots.begin(); i != docroots.end(); i++) {
288
std::string docroot = *i;
289
if (pathMatcher.matches(docroot, realScriptFile.getPath())) {
290
file_in_docroot = true;
294
if (!file_in_docroot) {
295
std::string error = "Script \"" + scriptFile.getPath()
296
+ "\" resolving to \"" + realScriptFile.getPath()
297
+ "\" not within configured docroot";
298
logger.logWarning(error);
299
throw SoftException(error, __FILE__, __LINE__);
301
file_in_docroot = false;
302
for (std::vector<std::string>::const_iterator i = docroots.begin(); i != docroots.end(); i++) {
303
std::string docroot = *i;
304
if (pathMatcher.matches(docroot, scriptFile.getPath())) {
305
file_in_docroot = true;
309
if (!file_in_docroot) {
310
std::string error = "Script \"" + scriptFile.getPath()
311
+ "\" not within configured docroot";
312
logger.logWarning(error);
313
throw SoftException(error, __FILE__, __LINE__);
316
// Check directory ownership and permissions
317
checkParentDirectories(realScriptFile, targetUser, config);
318
checkParentDirectories(scriptFile, targetUser, config);
321
void suPHP::Application::checkProcessPermissions(
322
const std::string& scriptFilename,
323
const Configuration& config,
324
const Environment& environment,
325
UserInfo& targetUser,
326
GroupInfo& targetGroup) const
273
327
throw (SystemException, SoftException, SecurityException) {
275
GroupInfo targetGroup;
277
File scriptFile = File(File(scriptFilename).getRealPath());
329
File scriptFile = File(scriptFilename);
330
File realScriptFile = File(scriptFile.getRealPath());
278
331
API& api = API_Helper::getSystemAPI();
279
332
Logger& logger = api.getSystemLogger();
444
503
TargetMode suPHP::Application::getTargetMode(const std::string& interpreter)
445
504
throw (SecurityException) {
446
505
if (interpreter.substr(0, 4) == "php:")
447
return TARGETMODE_PHP;
506
return TARGETMODE_PHP;
448
507
else if (interpreter == "execute:!self")
449
return TARGETMODE_SELFEXECUTE;
508
return TARGETMODE_SELFEXECUTE;
451
throw SecurityException("Unknown Interpreter: " + interpreter,
510
throw SecurityException("Unknown Interpreter: " + interpreter,
456
515
void suPHP::Application::executeScript(const std::string& scriptFilename,
457
const std::string& interpreter,
459
const Environment& env,
460
const Configuration& config) const
516
const std::string& interpreter,
518
const Environment& env,
519
const Configuration& config) const
461
520
throw (SoftException) {
463
// Change working directory to script path
464
API_Helper::getSystemAPI().setCwd(
465
File(scriptFilename).getParentDirectory().getPath());
466
if (mode == TARGETMODE_PHP) {
467
std::string interpreterPath = interpreter.substr(4);
469
cline.putArgument(interpreterPath);
470
API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
471
} else if (mode == TARGETMODE_SELFEXECUTE) {
473
cline.putArgument(scriptFilename);
474
API_Helper::getSystemAPI().execute(scriptFilename, cline, env);
522
// Change working directory to script path
523
API_Helper::getSystemAPI().setCwd(
524
File(scriptFilename).getParentDirectory().getPath());
525
if (mode == TARGETMODE_PHP) {
526
std::string interpreterPath = interpreter.substr(4);
528
cline.putArgument(interpreterPath);
529
API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
530
} else if (mode == TARGETMODE_SELFEXECUTE) {
532
cline.putArgument(scriptFilename);
533
API_Helper::getSystemAPI().execute(scriptFilename, cline, env);
476
535
} catch (SystemException& e) {
477
throw SoftException("Could not execute script \"" + scriptFilename
478
+ "\"", e, __FILE__, __LINE__);
536
throw SoftException("Could not execute script \"" + scriptFilename
537
+ "\"", e, __FILE__, __LINE__);
542
void suPHP::Application::checkParentDirectories(const File& file,
543
const UserInfo& owner,
544
const Configuration& config) const throw (SoftException) {
545
File directory = file;
546
Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
548
directory = directory.getParentDirectory();
550
UserInfo directoryOwner = directory.getUser();
551
if (directoryOwner != owner && !directoryOwner.isSuperUser()) {
552
std::string error = "Directory " + directory.getPath()
553
+ " is not owned by " + owner.getUsername();
554
logger.logWarning(error);
555
throw SoftException(error, __FILE__, __LINE__);
558
if (!directory.isSymlink()
559
&& !config.getAllowDirectoryGroupWriteable()
560
&& directory.hasGroupWriteBit()) {
561
std::string error = "Directory \"" + directory.getPath()
562
+ "\" is writeable by group";
563
logger.logWarning(error);
564
throw SoftException(error, __FILE__, __LINE__);
567
if (!directory.isSymlink()
568
&& !config.getAllowDirectoryOthersWriteable()
569
&& directory.hasOthersWriteBit()) {
570
std::string error = "Directory \"" + directory.getPath()
571
+ "\" is writeable by others";
572
logger.logWarning(error);
573
throw SoftException(error, __FILE__, __LINE__);
575
} while (directory.getPath() != "/");
483
579
int main(int argc, char **argv) {
485
API& api = API_Helper::getSystemAPI();
489
for (int i=0; i<argc; i++) {
490
cmdline.putArgument(argv[i]);
492
env = api.getProcessEnvironment();
493
return app.run(cmdline, env);
581
API& api = API_Helper::getSystemAPI();
585
for (int i=0; i<argc; i++) {
586
cmdline.putArgument(argv[i]);
588
env = api.getProcessEnvironment();
589
return app.run(cmdline, env);
494
590
} catch (Exception& e) {