~ubuntu-branches/ubuntu/lucid/suphp/lucid

« back to all changes in this revision

Viewing changes to src/Application.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Emmanuel Lacour
  • Date: 2009-08-03 15:15:38 UTC
  • mfrom: (8.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20090803151538-pv7yhe6w1mqmceas
Tags: 0.7.1-1
* New upstream release (closes: #528379, #520182) 
* debian/NEWS: add information about AddHandler -> AddType change introduced
  in 0.6.2-2 (closes: #517805)
* debian/conf/suphp.conf, debian/patches/01_debian.dpatch: switch from
  application/x-httpd-php to application/x-httpd-suphp to allow
  simultaneous use of mod_suphp and mod_php (closes: #519005, #514725)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
    suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
 
2
    suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
3
3
 
4
4
    This file is part of suPHP.
5
5
 
33
33
#include "UserInfo.hpp"
34
34
#include "GroupInfo.hpp"
35
35
#include "Util.hpp"
 
36
#include "PathMatcher.hpp"
36
37
 
37
38
#include "Application.hpp"
38
39
 
48
49
    Configuration config;
49
50
    API& api = API_Helper::getSystemAPI();
50
51
    Logger& logger = api.getSystemLogger();
51
 
    
 
52
 
52
53
#ifdef OPT_CONFIGFILE
53
54
    File cfgFile = File(OPT_CONFIGFILE);
54
55
#else
62
63
    // Begin try block - soft exception cannot really be handled before
63
64
    // initialization
64
65
    try {
65
 
        std::string scriptFilename;
66
 
        
67
 
        // If caller is super-user, print info message and exit
68
 
        if (api.getRealProcessUser().isSuperUser()) {
69
 
            this->printAboutMessage();
70
 
            return 0;
71
 
        }
72
 
        config.readFromFile(cfgFile);
73
 
        
74
 
        // Check permissions (real uid, effective uid)
75
 
        this->checkProcessPermissions(config);
76
 
        
77
 
        // Initialize logger
78
 
        // not done before, because we need super-user privileges for
79
 
        // logging anyway
80
 
        logger.init(config);
81
 
 
82
 
        try {
83
 
            scriptFilename = env.getVar("SCRIPT_FILENAME");
84
 
        } catch (KeyNotFoundException& e) {
85
 
            logger.logError("Environment variable SCRIPT_FILENAME not set");
86
 
            this->printAboutMessage();
87
 
            return 1;
88
 
        }
89
 
 
90
 
        this->checkScriptFile(scriptFilename, config, env);
91
 
 
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());
96
 
        }
97
 
 
98
 
        this->changeProcessPermissions(scriptFilename, config, env);
99
 
 
100
 
        interpreter = this->getInterpreter(env, config);
101
 
        targetMode = this->getTargetMode(interpreter);
102
 
        
103
 
        // Prepare environment for new process
104
 
        newEnv = this->prepareEnvironment(env, config, targetMode);
105
 
        
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);
110
 
        }
111
 
        
112
 
        // Log attempt to execute script
113
 
        logger.logInfo("Executing \"" + scriptFilename + "\" as UID "
114
 
                       + Util::intToStr(api.getEffectiveProcessUser().getUid())
115
 
                       + ", GID " 
116
 
                       + Util::intToStr(
117
 
                           api.getEffectiveProcessGroup().getGid()));
118
 
 
119
 
        this->executeScript(scriptFilename, interpreter, targetMode, newEnv, 
120
 
                            config);
121
 
        
122
 
        // Function should never return
123
 
        // So, if we get here, return with error code
124
 
        return 1;
 
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;
125
138
    } catch (SoftException& e) {
126
 
        if (!config.getErrorsToBrowser()) {
127
 
            std::cerr << e;
128
 
            return 2;
129
 
        }
130
 
        std::cout << "Content-Type: text/html\n"
131
 
                  << "Status: 500\n"
132
 
                  << "\n"
133
 
                  << "<html>\n"
134
 
                  << " <head>\n"
135
 
                  << "  <title>500 Internal Server Error</title>\n"
136
 
                  << " </head>\n"
137
 
                  << " <body>\n"
138
 
                  << "  <h1>Internal Server Error</h1>\n"
139
 
                  << "  <p>" << e.getMessage() << "</p>\n"
140
 
                  << "  <hr/>"
141
 
                  << "  <address>suPHP " << PACKAGE_VERSION << "</address>\n"
142
 
                  << " </body>\n"
143
 
                  << "</html>\n";
 
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";
144
157
    }
145
158
}
146
159
 
147
160
 
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;
153
166
}
154
167
 
155
168
 
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!",
162
 
                                __FILE__, __LINE__);
 
173
        api.getUserInfo(config.getWebserverUser())) {
 
174
        throw SecurityException("Calling user is not webserver user!",
 
175
                                __FILE__, __LINE__);
163
176
    }
164
 
    
 
177
 
165
178
    if (!api.getEffectiveProcessUser().isSuperUser()) {
166
 
        throw SecurityException(
167
 
            "Do not have root privileges. Executable not set-uid root?",
168
 
            __FILE__, __LINE__);
 
179
        throw SecurityException(
 
180
            "Do not have root privileges. Executable not set-uid root?",
 
181
            __FILE__, __LINE__);
169
182
    }
170
183
}
171
184
 
172
185
 
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());
180
194
 
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__);
186
200
    }
187
 
    
188
 
    // Get full path to script file
189
 
 
190
 
    File realScriptFile = File(scriptFile.getRealPath());
191
 
    File directory = realScriptFile.getParentDirectory();
192
 
    
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()
 
204
            + " does not exist";
 
205
        logger.logWarning(error);
 
206
        throw SoftException(error, __FILE__, __LINE__);
200
207
    }
201
208
 
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",
205
 
                                __FILE__, __LINE__);
206
 
    if (config.getCheckVHostDocroot()
207
 
        && realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT")) 
208
 
        != 0) {
209
 
        
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__);
215
 
    }
216
 
 
217
 
    // Check script and directory permissions
 
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
218
236
    if (!realScriptFile.hasUserReadBit()) {
219
 
        std::string error = "File \"" + realScriptFile.getPath()
220
 
            + "\" not readable";
221
 
        logger.logWarning(error);
222
 
        throw SoftException(error, __FILE__, __LINE__);
223
 
        
 
237
        std::string error = "File \"" + realScriptFile.getPath()
 
238
            + "\" not readable";
 
239
        logger.logWarning(error);
 
240
        throw SoftException(error, __FILE__, __LINE__);
 
241
 
224
242
    }
225
243
 
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__);
232
 
    }
233
 
    
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__);
240
250
    }
241
251
 
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__);
248
 
    }
249
 
    
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__);
256
258
    }
257
259
 
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__);
265
 
    }
266
 
}
267
 
 
268
 
 
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__);
 
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
273
327
    throw (SystemException, SoftException, SecurityException) {
274
 
    UserInfo targetUser;
275
 
    GroupInfo targetGroup;
276
328
 
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();
280
333
 
291
344
 
292
345
    // Check UID/GID of script
293
346
    if (scriptFile.getUser().getUid() < config.getMinUid()) {
294
 
        std::string error = "UID of script \"" + scriptFilename
295
 
            + "\" is smaller than min_uid";
296
 
        logger.logWarning(error);
297
 
        throw SoftException(error, __FILE__, __LINE__);
 
347
        std::string error = "UID of script \"" + scriptFilename
 
348
            + "\" is smaller than min_uid";
 
349
        logger.logWarning(error);
 
350
        throw SoftException(error, __FILE__, __LINE__);
298
351
    }
299
352
    if (scriptFile.getGroup().getGid() < config.getMinGid()) {
300
 
        std::string error = "GID of script \"" + scriptFilename
301
 
            + "\" is smaller than min_gid";
302
 
        logger.logWarning(error);
303
 
        throw SoftException(error, __FILE__, __LINE__);
 
353
        std::string error = "GID of script \"" + scriptFilename
 
354
            + "\" is smaller than min_gid";
 
355
        logger.logWarning(error);
 
356
        throw SoftException(error, __FILE__, __LINE__);
304
357
    }
305
 
    
 
358
 
306
359
    // Paranoid and force mode
307
360
 
308
361
#if (defined(OPT_USERGROUP_PARANOID) || defined(OPT_USERGROUP_FORCE))
309
362
    std::string targetUsername, targetGroupname;
310
363
    try {
311
 
        targetUsername = environment.getVar("SUPHP_USER");
312
 
        targetGroupname = environment.getVar("SUPHP_GROUP");
 
364
        targetUsername = environment.getVar("SUPHP_USER");
 
365
        targetGroupname = environment.getVar("SUPHP_GROUP");
313
366
    } catch (KeyNotFoundException& e) {
314
 
        throw SecurityException(
315
 
            "Environment variable SUPHP_USER or SUPHP_GROUP not set", 
316
 
            __FILE__, __LINE__);
 
367
        throw SecurityException(
 
368
            "Environment variable SUPHP_USER or SUPHP_GROUP not set",
 
369
            __FILE__, __LINE__);
317
370
    }
318
 
    
 
371
 
319
372
    if (targetUsername[0] == '#' && targetUsername.find_first_not_of(
320
 
            "0123456789", 1) == std::string::npos) {
321
 
        targetUser = api.getUserInfo(Util::strToInt(targetUsername.substr(1)));
 
373
            "0123456789", 1) == std::string::npos) {
 
374
        targetUser = api.getUserInfo(Util::strToInt(targetUsername.substr(1)));
322
375
    } else {
323
 
        targetUser = api.getUserInfo(targetUsername);
 
376
        targetUser = api.getUserInfo(targetUsername);
324
377
    }
325
378
 
326
379
    if (targetGroupname[0] == '#' && targetGroupname.find_first_not_of(
327
 
            "0123456789", 1) == std::string::npos) {
328
 
        targetGroup = api.getGroupInfo(
329
 
            Util::strToInt(targetGroupname.substr(1)));
 
380
            "0123456789", 1) == std::string::npos) {
 
381
        targetGroup = api.getGroupInfo(
 
382
            Util::strToInt(targetGroupname.substr(1)));
330
383
    } else {
331
 
        targetGroup = api.getGroupInfo(targetGroupname);
 
384
        targetGroup = api.getGroupInfo(targetGroupname);
332
385
    }
333
386
#endif // OPT_USERGROUP_PARANOID || OPT_USERGROUP_FORCE
334
387
 
338
391
    targetUser = scriptFile.getUser();
339
392
    targetGroup = scriptFile.getGroup();
340
393
#endif // OPT_USERGROUP_OWNER
341
 
    
 
394
 
342
395
    // Paranoid mode only
343
396
 
344
397
#ifdef OPT_USERGROUP_PARANOID
345
398
    if (targetUser != scriptFile.getUser()) {
346
 
        std::string error ="Mismatch between target UID ("
347
 
            + Util::intToStr(targetUser.getUid()) + ") and UID (" 
348
 
            + Util::intToStr(scriptFile.getUser().getUid()) + ") of file \"" 
349
 
            + scriptFile.getPath() + "\"";
350
 
        logger.logWarning(error);
351
 
        throw SoftException(error, __FILE__, __LINE__);
 
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__);
352
405
    }
353
406
 
354
407
    if (targetGroup != scriptFile.getGroup()) {
355
 
        std::string error ="Mismatch between target GID ("
356
 
            + Util::intToStr(targetGroup.getGid()) + ") and GID (" 
357
 
            + Util::intToStr(scriptFile.getGroup().getGid()) + ") of file \"" 
358
 
            + scriptFile.getPath() + "\"";
359
 
        logger.logWarning(error);
360
 
        throw SoftException(error, __FILE__, __LINE__);
 
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__);
361
414
    }
362
 
#endif // OPT_USERGROUP_PARANOID    
 
415
#endif // OPT_USERGROUP_PARANOID
 
416
}
363
417
 
364
 
    // Common code used for all modes
 
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();
365
424
 
366
425
    // Set new group first, because we still need super-user privileges
367
426
    // for this
368
427
    api.setProcessGroup(targetGroup);
369
 
    
 
428
 
370
429
    // Then set new user
371
430
    api.setProcessUser(targetUser);
372
431
 
379
438
    throw (KeyNotFoundException) {
380
439
    // Create environment for new process from old environment
381
440
    Environment env = sourceEnv;
382
 
    
 
441
 
383
442
    // Delete unwanted environment variables
384
443
    if (env.hasVar("LD_PRELOAD"))
385
 
        env.deleteVar("LD_PRELOAD");
 
444
        env.deleteVar("LD_PRELOAD");
386
445
    if (env.hasVar("LD_LIBRARY_PATH"))
387
 
        env.deleteVar("LD_LIBRARY_PATH");
 
446
        env.deleteVar("LD_LIBRARY_PATH");
388
447
    if (env.hasVar("SUPHP_USER"))
389
 
        env.deleteVar("SUPHP_USER");
 
448
        env.deleteVar("SUPHP_USER");
390
449
    if (env.hasVar("SUPHP_GROUP"))
391
 
        env.deleteVar("SUPHP_GROUP");
 
450
        env.deleteVar("SUPHP_GROUP");
392
451
    if (env.hasVar("SUPHP_HANDLER"))
393
 
        env.deleteVar("SUPHP_HANDLER");
 
452
        env.deleteVar("SUPHP_HANDLER");
394
453
    if (env.hasVar("SUPHP_AUTH_USER"))
395
 
        env.deleteVar("SUPHP_AUTH_USER");
 
454
        env.deleteVar("SUPHP_AUTH_USER");
396
455
    if (env.hasVar("SUPHP_AUTH_PW"))
397
 
        env.deleteVar("SUPHP_AUTH_PW");
 
456
        env.deleteVar("SUPHP_AUTH_PW");
398
457
    if (env.hasVar("SUPHP_PHP_CONFIG"))
399
 
        env.deleteVar("SUPHP_PHP_CONFIG");
400
 
    
 
458
        env.deleteVar("SUPHP_PHP_CONFIG");
 
459
 
401
460
    // Reset PATH
402
461
    env.putVar("PATH", config.getEnvPath());
403
462
 
404
463
    // If we are in PHP mode, set PHP specific variables
405
464
    if (mode == TARGETMODE_PHP) {
406
 
        if (sourceEnv.hasVar("SUPHP_PHP_CONFIG"))
407
 
            env.putVar("PHPRC", sourceEnv.getVar("SUPHP_PHP_CONFIG"));
408
 
        if (sourceEnv.hasVar("SUPHP_AUTH_USER")
409
 
            && sourceEnv.hasVar("SUPHP_AUTH_PW")) {
410
 
            env.putVar("PHP_AUTH_USER", sourceEnv.getVar("SUPHP_AUTH_USER"));
411
 
            env.putVar("PHP_AUTH_PW", sourceEnv.getVar("SUPHP_AUTH_PW"));
412
 
        }
 
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
        }
413
472
 
414
 
        // PHP may need this, when compiled with security features
415
 
        if (!env.hasVar("REDIRECT_STATUS")) {
416
 
            env.putVar("REDIRECT_STATUS", "200");
417
 
        }
 
473
        // PHP may need this, when compiled with security features
 
474
        if (!env.hasVar("REDIRECT_STATUS")) {
 
475
            env.putVar("REDIRECT_STATUS", "200");
 
476
        }
418
477
    }
419
 
    
 
478
 
420
479
    return env;
421
480
}
422
481
 
425
484
    const Environment& env, const Configuration& config)
426
485
    throw (SecurityException) {
427
486
    if (!env.hasVar("SUPHP_HANDLER"))
428
 
        throw SecurityException("Environment variable SUPHP_HANDLER not set",
429
 
                                __FILE__, __LINE__);
 
487
        throw SecurityException("Environment variable SUPHP_HANDLER not set",
 
488
                                __FILE__, __LINE__);
430
489
    std::string handler = env.getVar("SUPHP_HANDLER");
431
 
    
 
490
 
432
491
    std::string interpreter = "";
433
492
    try {
434
 
        interpreter = config.getInterpreter(handler);
 
493
        interpreter = config.getInterpreter(handler);
435
494
    } catch (KeyNotFoundException& e) {
436
 
        throw SecurityException ("Handler not found in configuration", e,
437
 
                                 __FILE__, __LINE__);
 
495
        throw SecurityException ("Handler not found in configuration", e,
 
496
                                 __FILE__, __LINE__);
438
497
    }
439
 
    
 
498
 
440
499
    return interpreter;
441
500
}
442
501
 
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;
450
509
    else
451
 
        throw SecurityException("Unknown Interpreter: " + interpreter,
452
 
                                __FILE__, __LINE__);
 
510
        throw SecurityException("Unknown Interpreter: " + interpreter,
 
511
                                __FILE__, __LINE__);
453
512
}
454
513
 
455
514
 
456
515
void suPHP::Application::executeScript(const std::string& scriptFilename,
457
 
                                       const std::string& interpreter,
458
 
                                       TargetMode mode,
459
 
                                       const Environment& env,
460
 
                                       const Configuration& config) const
 
516
                                       const std::string& interpreter,
 
517
                                       TargetMode mode,
 
518
                                       const Environment& env,
 
519
                                       const Configuration& config) const
461
520
    throw (SoftException) {
462
521
    try {
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);
468
 
            CommandLine cline;
469
 
            cline.putArgument(interpreterPath);
470
 
            API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
471
 
        } else if (mode == TARGETMODE_SELFEXECUTE) {
472
 
            CommandLine cline;
473
 
            cline.putArgument(scriptFilename);
474
 
            API_Helper::getSystemAPI().execute(scriptFilename, cline, env);
475
 
        }
 
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
        }
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__);
479
538
    }
480
539
}
481
540
 
482
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
 
483
579
int main(int argc, char **argv) {
484
580
    try {
485
 
        API& api = API_Helper::getSystemAPI();
486
 
        CommandLine cmdline;
487
 
        Environment env;
488
 
        Application app;
489
 
        for (int i=0; i<argc; i++) {
490
 
            cmdline.putArgument(argv[i]);
491
 
        }
492
 
        env = api.getProcessEnvironment();
493
 
        return app.run(cmdline, env);
 
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);
494
590
    } catch (Exception& e) {
495
 
        std::cerr << e;
496
 
        return 1;
 
591
        std::cerr << e;
 
592
        return 1;
497
593
    }
498
594
}