623e7ab4 |
1 | /* |
2 | suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com> |
3 | |
4 | This file is part of suPHP. |
5 | |
6 | suPHP is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU General Public License as published by |
8 | the Free Software Foundation; either version 2 of the License, or |
9 | (at your option) any later version. |
10 | |
11 | suPHP is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with suPHP; if not, write to the Free Software |
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | */ |
20 | |
21 | #include <iostream> |
22 | |
23 | #include "config.h" |
24 | |
25 | #include "CommandLine.hpp" |
26 | #include "Environment.hpp" |
27 | #include "Exception.hpp" |
28 | #include "File.hpp" |
29 | #include "Configuration.hpp" |
30 | #include "API.hpp" |
31 | #include "API_Helper.hpp" |
32 | #include "Logger.hpp" |
33 | #include "UserInfo.hpp" |
34 | #include "GroupInfo.hpp" |
35 | #include "Util.hpp" |
36 | #include "PathMatcher.hpp" |
37 | |
38 | #include "Application.hpp" |
39 | |
40 | using namespace suPHP; |
41 | |
42 | |
43 | suPHP::Application::Application() { |
44 | /* do nothing */ |
45 | } |
46 | |
47 | |
48 | int suPHP::Application::run(CommandLine& cmdline, Environment& env) { |
49 | Configuration config; |
50 | API& api = API_Helper::getSystemAPI(); |
51 | Logger& logger = api.getSystemLogger(); |
52 | |
53 | #ifdef OPT_CONFIGFILE |
54 | File cfgFile = File(OPT_CONFIGFILE); |
55 | #else |
56 | File cfgFile = File("/etc/suphp.conf"); |
57 | #endif |
58 | |
59 | std::string interpreter; |
60 | TargetMode targetMode; |
61 | Environment newEnv; |
62 | |
63 | // Begin try block - soft exception cannot really be handled before |
64 | // initialization |
65 | try { |
66 | std::string scriptFilename; |
67 | UserInfo targetUser; |
68 | GroupInfo targetGroup; |
69 | |
70 | // If caller is super-user, print info message and exit |
71 | if (api.getRealProcessUser().isSuperUser()) { |
72 | this->printAboutMessage(); |
73 | return 0; |
74 | } |
75 | config.readFromFile(cfgFile); |
76 | |
77 | // Check permissions (real uid, effective uid) |
78 | this->checkProcessPermissions(config); |
79 | |
80 | // Initialize logger |
81 | // not done before, because we need super-user privileges for |
82 | // logging anyway |
83 | logger.init(config); |
84 | |
85 | try { |
86 | scriptFilename = env.getVar("SCRIPT_FILENAME"); |
87 | } catch (KeyNotFoundException& e) { |
88 | logger.logError("Environment variable SCRIPT_FILENAME not set"); |
89 | this->printAboutMessage(); |
90 | return 1; |
91 | } |
92 | |
93 | |
94 | // Do checks that do not need target user info |
95 | this->checkScriptFileStage1(scriptFilename, config, env); |
96 | |
97 | // Find out target user |
98 | this->checkProcessPermissions(scriptFilename, config, env, targetUser, targetGroup); |
99 | |
100 | // Now do checks that might require user info |
101 | this->checkScriptFileStage2(scriptFilename, config, env, targetUser, targetGroup); |
102 | |
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); |
109 | } |
110 | |
111 | this->changeProcessPermissions(config, targetUser, targetGroup); |
112 | |
113 | interpreter = this->getInterpreter(env, config); |
114 | targetMode = this->getTargetMode(interpreter); |
115 | |
116 | // Prepare environment for new process |
117 | newEnv = this->prepareEnvironment(env, config, targetMode); |
118 | |
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); |
123 | } |
124 | |
125 | // Log attempt to execute script |
126 | logger.logInfo("Executing \"" + scriptFilename + "\" as UID " |
127 | + Util::intToStr(api.getEffectiveProcessUser().getUid()) |
128 | + ", GID " |
129 | + Util::intToStr( |
130 | api.getEffectiveProcessGroup().getGid())); |
131 | |
132 | this->executeScript(scriptFilename, interpreter, targetMode, newEnv, |
133 | config); |
134 | |
135 | // Function should never return |
136 | // So, if we get here, return with error code |
137 | return 1; |
138 | } catch (SoftException& e) { |
139 | if (!config.getErrorsToBrowser()) { |
140 | std::cerr << e; |
141 | return 2; |
142 | } |
143 | std::cout << "Content-Type: text/html\n" |
144 | << "Status: 500\n" |
145 | << "\n" |
146 | << "<html>\n" |
147 | << " <head>\n" |
148 | << " <title>500 Internal Server Error</title>\n" |
149 | << " </head>\n" |
150 | << " <body>\n" |
151 | << " <h1>Internal Server Error</h1>\n" |
152 | << " <p>" << e.getMessage() << "</p>\n" |
153 | << " <hr/>" |
154 | << " <address>suPHP " << PACKAGE_VERSION << "</address>\n" |
155 | << " </body>\n" |
156 | << "</html>\n"; |
157 | } |
158 | } |
159 | |
160 | |
161 | void suPHP::Application::printAboutMessage() { |
162 | std::cerr << "suPHP version " << PACKAGE_VERSION << "\n"; |
163 | std::cerr << "(c) 2002-2007 Sebastian Marsching\n"; |
164 | std::cerr << std::endl; |
165 | std::cerr << "suPHP has to be called by mod_suphp to work." << std::endl; |
166 | } |
167 | |
168 | |
169 | void suPHP::Application::checkProcessPermissions(Configuration& config) |
170 | throw (SecurityException, LookupException) { |
171 | API& api = API_Helper::getSystemAPI(); |
172 | if (api.getRealProcessUser() != |
173 | api.getUserInfo(config.getWebserverUser())) { |
174 | throw SecurityException("Calling user is not webserver user!", |
175 | __FILE__, __LINE__); |
176 | } |
177 | |
178 | if (!api.getEffectiveProcessUser().isSuperUser()) { |
179 | throw SecurityException( |
180 | "Do not have root privileges. Executable not set-uid root?", |
181 | __FILE__, __LINE__); |
182 | } |
183 | } |
184 | |
185 | |
186 | void suPHP::Application::checkScriptFileStage1( |
187 | const std::string& scriptFilename, |
188 | const Configuration& config, |
189 | const Environment& environment) const |
190 | throw (SystemException, SoftException) { |
191 | Logger& logger = API_Helper::getSystemAPI().getSystemLogger(); |
192 | File scriptFile = File(scriptFilename); |
193 | File realScriptFile = File(scriptFile.getRealPath()); |
194 | |
195 | // Check wheter file exists |
196 | if (!scriptFile.exists()) { |
197 | std::string error = "File " + scriptFile.getPath() + " does not exist"; |
198 | logger.logWarning(error); |
199 | throw SoftException(error, __FILE__, __LINE__); |
200 | } |
201 | if (!realScriptFile.exists()) { |
202 | std::string error = "File " + realScriptFile.getPath() |
203 | + " referenced by symlink " +scriptFile.getPath() |
204 | + " does not exist"; |
205 | logger.logWarning(error); |
206 | throw SoftException(error, __FILE__, __LINE__); |
207 | } |
208 | |
209 | // If enabled, check whether script is in the vhost's docroot |
210 | if (!environment.hasVar("DOCUMENT_ROOT")) |
211 | throw SoftException("Environment variable DOCUMENT_ROOT not set", |
212 | __FILE__, __LINE__); |
213 | if (config.getCheckVHostDocroot() |
214 | && realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT")) |
215 | != 0) { |
216 | |
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__); |
222 | } |
223 | if (config.getCheckVHostDocroot() |
224 | && scriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT")) |
225 | != 0) { |
226 | |
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__); |
232 | } |
233 | |
234 | // Check script permissions |
235 | // Directories will be checked later |
236 | if (!realScriptFile.hasUserReadBit()) { |
237 | std::string error = "File \"" + realScriptFile.getPath() |
238 | + "\" not readable"; |
239 | logger.logWarning(error); |
240 | throw SoftException(error, __FILE__, __LINE__); |
241 | |
242 | } |
243 | |
244 | if (!config.getAllowFileGroupWriteable() |
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__); |
250 | } |
251 | |
252 | if (!config.getAllowFileOthersWriteable() |
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 | } |
259 | |
260 | // Check UID/GID of symlink is matching target |
261 | if (scriptFile.getUser() != realScriptFile.getUser() |
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__); |
267 | } |
268 | } |
269 | |
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); |
280 | |
281 | // Get full path to script file |
282 | File realScriptFile = File(scriptFile.getRealPath()); |
283 | |
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; |
291 | break; |
292 | } |
293 | } |
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__); |
300 | } |
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; |
306 | break; |
307 | } |
308 | } |
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__); |
314 | } |
315 | |
316 | // Check directory ownership and permissions |
317 | checkParentDirectories(realScriptFile, targetUser, config); |
318 | checkParentDirectories(scriptFile, targetUser, config); |
319 | } |
320 | |
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 |
327 | throw (SystemException, SoftException, SecurityException) { |
328 | |
329 | File scriptFile = File(scriptFilename); |
330 | File realScriptFile = File(scriptFile.getRealPath()); |
331 | API& api = API_Helper::getSystemAPI(); |
332 | Logger& logger = api.getSystemLogger(); |
333 | |
334 | // Make sure that exactly one mode is set |
335 | |
336 | #if !defined(OPT_USERGROUP_OWNER) && !defined(OPT_USERGROUP_FORCE) && !defined(OPT_USERGROUP_PARANOID) |
337 | #error "No uid/gid change model specified" |
338 | #endif |
339 | #if (defined(OPT_USERGROUP_OWNER) && defined(OPT_USERGROUP_FORCE)) || (defined(OPT_USERGROUP_FORCE) && defined(OPT_USERGROUP_PARANOID)) || (defined(OPT_USERGROUP_OWNER) && defined(OPT_USERGROUP_PARANOID)) |
340 | #error "More than one uid/gid change model specified" |
341 | #endif |
342 | |
343 | // Common code (for all security modes) |
344 | |
345 | // Check UID/GID of script |
346 | if (scriptFile.getUser().getUid() < config.getMinUid()) { |
347 | std::string error = "UID of script \"" + scriptFilename |
348 | + "\" is smaller than min_uid"; |
349 | logger.logWarning(error); |
350 | throw SoftException(error, __FILE__, __LINE__); |
351 | } |
352 | if (scriptFile.getGroup().getGid() < config.getMinGid()) { |
353 | std::string error = "GID of script \"" + scriptFilename |
354 | + "\" is smaller than min_gid"; |
355 | logger.logWarning(error); |
356 | throw SoftException(error, __FILE__, __LINE__); |
357 | } |
358 | |
359 | // Paranoid and force mode |
360 | |
361 | #if (defined(OPT_USERGROUP_PARANOID) || defined(OPT_USERGROUP_FORCE)) |
362 | std::string targetUsername, targetGroupname; |
363 | try { |
364 | targetUsername = environment.getVar("SUPHP_USER"); |
365 | targetGroupname = environment.getVar("SUPHP_GROUP"); |
366 | } catch (KeyNotFoundException& e) { |
367 | throw SecurityException( |
368 | "Environment variable SUPHP_USER or SUPHP_GROUP not set", |
369 | __FILE__, __LINE__); |
370 | } |
371 | |
372 | if (targetUsername[0] == '#' && targetUsername.find_first_not_of( |
373 | "0123456789", 1) == std::string::npos) { |
374 | targetUser = api.getUserInfo(Util::strToInt(targetUsername.substr(1))); |
375 | } else { |
376 | targetUser = api.getUserInfo(targetUsername); |
377 | } |
378 | |
379 | if (targetGroupname[0] == '#' && targetGroupname.find_first_not_of( |
380 | "0123456789", 1) == std::string::npos) { |
381 | targetGroup = api.getGroupInfo( |
382 | Util::strToInt(targetGroupname.substr(1))); |
383 | } else { |
384 | targetGroup = api.getGroupInfo(targetGroupname); |
385 | } |
386 | #endif // OPT_USERGROUP_PARANOID || OPT_USERGROUP_FORCE |
387 | |
388 | // Owner mode only |
389 | |
390 | #ifdef OPT_USERGROUP_OWNER |
391 | targetUser = scriptFile.getUser(); |
392 | targetGroup = scriptFile.getGroup(); |
393 | #endif // OPT_USERGROUP_OWNER |
394 | |
395 | // Paranoid mode only |
396 | |
397 | #ifdef OPT_USERGROUP_PARANOID |
398 | if (targetUser != scriptFile.getUser()) { |
399 | std::string error ="Mismatch between target UID (" |
400 | + Util::intToStr(targetUser.getUid()) + ") and UID (" |
401 | + Util::intToStr(scriptFile.getUser().getUid()) + ") of file \"" |
402 | + scriptFile.getPath() + "\""; |
403 | logger.logWarning(error); |
404 | throw SoftException(error, __FILE__, __LINE__); |
405 | } |
406 | |
407 | if (targetGroup != scriptFile.getGroup()) { |
408 | std::string error ="Mismatch between target GID (" |
409 | + Util::intToStr(targetGroup.getGid()) + ") and GID (" |
410 | + Util::intToStr(scriptFile.getGroup().getGid()) + ") of file \"" |
411 | + scriptFile.getPath() + "\""; |
412 | logger.logWarning(error); |
413 | throw SoftException(error, __FILE__, __LINE__); |
414 | } |
415 | #endif // OPT_USERGROUP_PARANOID |
416 | } |
417 | |
418 | void suPHP::Application::changeProcessPermissions( |
419 | const Configuration& config, |
420 | const UserInfo& targetUser, |
421 | const GroupInfo& targetGroup) const |
422 | throw (SystemException, SoftException, SecurityException) { |
423 | API& api = API_Helper::getSystemAPI(); |
424 | |
425 | // Set new group first, because we still need super-user privileges |
426 | // for this |
427 | api.setProcessGroup(targetGroup); |
428 | |
429 | // Then set new user |
430 | api.setProcessUser(targetUser); |
431 | |
432 | api.setUmask(config.getUmask()); |
433 | } |
434 | |
435 | |
436 | Environment suPHP::Application::prepareEnvironment( |
437 | const Environment& sourceEnv, const Configuration& config, TargetMode mode) |
438 | throw (KeyNotFoundException) { |
439 | // Create environment for new process from old environment |
440 | Environment env = sourceEnv; |
441 | |
442 | // Delete unwanted environment variables |
443 | if (env.hasVar("LD_PRELOAD")) |
444 | env.deleteVar("LD_PRELOAD"); |
445 | if (env.hasVar("LD_LIBRARY_PATH")) |
446 | env.deleteVar("LD_LIBRARY_PATH"); |
447 | if (env.hasVar("SUPHP_USER")) |
448 | env.deleteVar("SUPHP_USER"); |
449 | if (env.hasVar("SUPHP_GROUP")) |
450 | env.deleteVar("SUPHP_GROUP"); |
451 | if (env.hasVar("SUPHP_HANDLER")) |
452 | env.deleteVar("SUPHP_HANDLER"); |
453 | if (env.hasVar("SUPHP_AUTH_USER")) |
454 | env.deleteVar("SUPHP_AUTH_USER"); |
455 | if (env.hasVar("SUPHP_AUTH_PW")) |
456 | env.deleteVar("SUPHP_AUTH_PW"); |
457 | if (env.hasVar("SUPHP_PHP_CONFIG")) |
458 | env.deleteVar("SUPHP_PHP_CONFIG"); |
459 | |
460 | // Reset PATH |
461 | env.putVar("PATH", config.getEnvPath()); |
462 | |
463 | // If we are in PHP mode, set PHP specific variables |
464 | if (mode == TARGETMODE_PHP) { |
465 | if (sourceEnv.hasVar("SUPHP_PHP_CONFIG")) |
466 | env.putVar("PHPRC", sourceEnv.getVar("SUPHP_PHP_CONFIG")); |
467 | if (sourceEnv.hasVar("SUPHP_AUTH_USER") |
468 | && sourceEnv.hasVar("SUPHP_AUTH_PW")) { |
469 | env.putVar("PHP_AUTH_USER", sourceEnv.getVar("SUPHP_AUTH_USER")); |
470 | env.putVar("PHP_AUTH_PW", sourceEnv.getVar("SUPHP_AUTH_PW")); |
471 | } |
472 | |
473 | // PHP may need this, when compiled with security features |
474 | if (!env.hasVar("REDIRECT_STATUS")) { |
475 | env.putVar("REDIRECT_STATUS", "200"); |
476 | } |
477 | } |
478 | |
479 | return env; |
480 | } |
481 | |
482 | |
483 | std::string suPHP::Application::getInterpreter( |
484 | const Environment& env, const Configuration& config) |
485 | throw (SecurityException) { |
486 | if (!env.hasVar("SUPHP_HANDLER")) |
487 | throw SecurityException("Environment variable SUPHP_HANDLER not set", |
488 | __FILE__, __LINE__); |
489 | std::string handler = env.getVar("SUPHP_HANDLER"); |
490 | |
491 | std::string interpreter = ""; |
492 | try { |
493 | interpreter = config.getInterpreter(handler); |
494 | } catch (KeyNotFoundException& e) { |
495 | throw SecurityException ("Handler not found in configuration", e, |
496 | __FILE__, __LINE__); |
497 | } |
498 | |
499 | return interpreter; |
500 | } |
501 | |
502 | |
503 | TargetMode suPHP::Application::getTargetMode(const std::string& interpreter) |
504 | throw (SecurityException) { |
505 | if (interpreter.substr(0, 4) == "php:") |
506 | return TARGETMODE_PHP; |
507 | else if (interpreter == "execute:!self") |
508 | return TARGETMODE_SELFEXECUTE; |
509 | else |
510 | throw SecurityException("Unknown Interpreter: " + interpreter, |
511 | __FILE__, __LINE__); |
512 | } |
513 | |
514 | |
515 | void suPHP::Application::executeScript(const std::string& scriptFilename, |
516 | const std::string& interpreter, |
517 | TargetMode mode, |
518 | const Environment& env, |
519 | const Configuration& config) const |
520 | throw (SoftException) { |
521 | try { |
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); |
527 | CommandLine cline; |
528 | cline.putArgument(interpreterPath); |
529 | API_Helper::getSystemAPI().execute(interpreterPath, cline, env); |
530 | } else if (mode == TARGETMODE_SELFEXECUTE) { |
531 | CommandLine cline; |
532 | cline.putArgument(scriptFilename); |
533 | API_Helper::getSystemAPI().execute(scriptFilename, cline, env); |
534 | } |
535 | } catch (SystemException& e) { |
536 | throw SoftException("Could not execute script \"" + scriptFilename |
537 | + "\"", e, __FILE__, __LINE__); |
538 | } |
539 | } |
540 | |
541 | |
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(); |
547 | do { |
548 | directory = directory.getParentDirectory(); |
549 | |
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__); |
556 | } |
557 | |
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__); |
565 | } |
566 | |
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__); |
574 | } |
575 | } while (directory.getPath() != "/"); |
576 | } |
577 | |
578 | |
579 | int main(int argc, char **argv) { |
580 | try { |
581 | API& api = API_Helper::getSystemAPI(); |
582 | CommandLine cmdline; |
583 | Environment env; |
584 | Application app; |
585 | for (int i=0; i<argc; i++) { |
586 | cmdline.putArgument(argv[i]); |
587 | } |
588 | env = api.getProcessEnvironment(); |
589 | return app.run(cmdline, env); |
590 | } catch (Exception& e) { |
591 | std::cerr << e; |
592 | return 1; |
593 | } |
594 | } |