Imported Debian patch 0.7.1-1
[hcoop/zz_old/debian/suphp.git] / src / API_Linux.cpp
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 <string>
22 #include <iostream>
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #include "Environment.hpp"
34 #include "UserInfo.hpp"
35 #include "GroupInfo.hpp"
36 #include "API_Linux_Logger.hpp"
37 #include "Util.hpp"
38
39 #include "API_Linux.hpp"
40
41 extern char **environ;
42
43 using namespace suPHP;
44
45 SmartPtr<API_Linux_Logger> suPHP::API_Linux::logger;
46
47 bool suPHP::API_Linux::isSymlink(const std::string path) const
48 throw (SystemException) {
49 struct stat temp;
50 if (lstat(path.c_str(), &temp) == -1) {
51 throw SystemException(std::string("Could not stat \"")
52 + path + "\": "
53 + ::strerror(errno), __FILE__, __LINE__);
54 }
55 if ((temp.st_mode & S_IFLNK) == S_IFLNK) {
56 return true;
57 } else {
58 return false;
59 }
60 }
61 std::string suPHP::API_Linux::readSymlink(const std::string path) const
62 throw (SystemException) {
63 char buf[1024] = {0};
64 if (::readlink(path.c_str(), buf, 1023) == -1) {
65 throw SystemException(std::string("Could not read symlink \"")
66 + path + "\": "
67 + ::strerror(errno), __FILE__, __LINE__);
68 }
69
70 if (buf[0] == '/') {
71 return std::string(buf);
72 } else {
73 if (path.rfind('/') == std::string::npos)
74 return std::string(buf);
75 return path.substr(0, path.rfind('/') + 1) + std::string(buf);
76 }
77 }
78
79 Environment suPHP::API_Linux::getProcessEnvironment() {
80 Environment env;
81 char **entry = ::environ;
82 while (*entry != NULL) {
83 std::string estr = std::string(*entry);
84 int eqpos = estr.find("=");
85 std::string name = estr.substr(0, eqpos);
86 std::string content = estr.substr(eqpos + 1);
87 env.putVar(name, content);
88 entry++;
89 }
90 return env;
91 }
92
93 UserInfo suPHP::API_Linux::getUserInfo(const std::string username)
94 throw (LookupException) {
95 struct passwd *tmpuser = ::getpwnam(username.c_str());
96 if (tmpuser == NULL) {
97 throw LookupException(std::string("Could not lookup username \"")
98 + username + "\"", __FILE__, __LINE__);
99 }
100 return UserInfo(tmpuser->pw_uid);
101 }
102
103 UserInfo suPHP::API_Linux::getUserInfo(const int uid) {
104 return UserInfo(uid);
105 }
106
107 GroupInfo suPHP::API_Linux::getGroupInfo(const std::string groupname)
108 throw (LookupException) {
109 struct group *tmpgroup = ::getgrnam(groupname.c_str());
110 if (tmpgroup == NULL) {
111 throw LookupException(std::string("Could not lookup groupname \"")
112 + groupname + "\"", __FILE__, __LINE__);
113 }
114 return GroupInfo(tmpgroup->gr_gid);
115 }
116
117 GroupInfo suPHP::API_Linux::getGroupInfo(const int gid) {
118 return GroupInfo(gid);
119 }
120
121 UserInfo suPHP::API_Linux::getEffectiveProcessUser() {
122 return UserInfo(::geteuid());
123 }
124
125
126 UserInfo suPHP::API_Linux::getRealProcessUser() {
127 return UserInfo(getuid());
128 }
129
130
131 GroupInfo suPHP::API_Linux::getEffectiveProcessGroup() {
132 return GroupInfo(getegid());
133 }
134
135
136 GroupInfo suPHP::API_Linux::getRealProcessGroup() {
137 return GroupInfo(getgid());
138 }
139
140
141 Logger& suPHP::API_Linux::getSystemLogger() {
142 if (suPHP::API_Linux::logger.get() == NULL) {
143 suPHP::API_Linux::logger.reset(new API_Linux_Logger());
144 }
145 return *(suPHP::API_Linux::logger);
146 }
147
148
149 void suPHP::API_Linux::setProcessUser(const UserInfo& user) const
150 throw (SystemException) {
151 // Reset supplementary groups
152 if (::setgroups(0, NULL) == -1) {
153 throw SystemException(std::string("setgroups() failed: ")
154 + ::strerror(errno), __FILE__, __LINE__);
155 }
156
157 try {
158 if (::initgroups(user.getUsername().c_str(),
159 user.getGroupInfo().getGid())
160 == -1) {
161 throw SystemException(std::string("initgroups() failed: ")
162 + ::strerror(errno), __FILE__, __LINE__);
163 }
164 } catch (LookupException &e) {
165 // Ignore this exception
166 // If we have a UID, which does not exist in /etc/passwd
167 // we simply cannot use supplementary groups
168 }
169
170 if (::setuid(user.getUid()) == -1) {
171 throw SystemException(std::string("setuid() failed: ")
172 + ::strerror(errno), __FILE__, __LINE__);
173 }
174 }
175
176
177 void suPHP::API_Linux::setProcessGroup(const GroupInfo& group) const
178 throw (SystemException) {
179 if (::setgid(group.getGid()) == -1) {
180 throw SystemException(std::string("setgid() failed: ")
181 + ::strerror(errno), __FILE__, __LINE__);
182 }
183 }
184
185 std::string suPHP::API_Linux::UserInfo_getUsername(const UserInfo& uinfo) const
186 throw (LookupException) {
187 struct passwd *tmpuser = ::getpwuid(uinfo.getUid());
188 if (tmpuser == NULL) {
189 throw LookupException(std::string("Could not lookup UID ")
190 + Util::intToStr(uinfo.getUid()),
191 __FILE__, __LINE__);
192 }
193 return std::string(tmpuser->pw_name);
194 }
195
196 GroupInfo suPHP::API_Linux::UserInfo_getGroupInfo(const UserInfo& uinfo) const
197 throw (LookupException) {
198 struct passwd *tmpuser = NULL;
199 tmpuser = getpwuid(uinfo.getUid());
200 if (tmpuser == NULL) {
201 throw LookupException(std::string("Could not lookup UID ")
202 + Util::intToStr(uinfo.getUid()),
203 __FILE__, __LINE__);
204 }
205 return GroupInfo(tmpuser->pw_gid);
206 }
207
208 std::string suPHP::API_Linux::UserInfo_getHomeDirectory(const UserInfo& uinfo) const
209 throw (LookupException) {
210 struct passwd *tmpuser = NULL;
211 tmpuser = getpwuid(uinfo.getUid());
212 if (tmpuser == NULL) {
213 throw LookupException(std::string("Could not lookup UID ")
214 + Util::intToStr(uinfo.getUid()),
215 __FILE__, __LINE__);
216 }
217 return tmpuser->pw_dir;
218 }
219
220 bool suPHP::API_Linux::UserInfo_isSuperUser(const UserInfo& uinfo) const {
221 if (uinfo.getUid() == 0)
222 return true;
223 else
224 return false;
225 }
226
227 std::string suPHP::API_Linux::GroupInfo_getGroupname(const GroupInfo& ginfo)
228 const throw (LookupException) {
229 struct group *tmpgroup = ::getgrgid(ginfo.getGid());
230 if (tmpgroup == NULL) {
231 throw LookupException(std::string("Could not lookup GID ")
232 + Util::intToStr(ginfo.getGid()),
233 __FILE__, __LINE__);
234 }
235 return std::string(tmpgroup->gr_name);
236 }
237
238 bool suPHP::API_Linux::File_exists(const File& file) const {
239 struct stat dummy;
240 if (::lstat(file.getPath().c_str(), &dummy) == 0)
241 return true;
242 else
243 return false;
244 }
245
246 std::string suPHP::API_Linux::File_getRealPath(const File& file) const
247 throw (SystemException) {
248 std::string currentpath = file.getPath();
249 std::string resolvedpath = "";
250 bool failed = true;
251
252 if ((currentpath.size() == 0) || (currentpath.at(0) != '/')) {
253 currentpath = this->getCwd() + std::string("/") + currentpath;
254 }
255
256 // Limit iterations to avoid infinite symlink loops
257 for (int i=0; i<512; i++) {
258 // If nothing is left, we have finished
259 if (currentpath.size() == 0) {
260 resolvedpath = ("/" + resolvedpath);
261 failed = false;
262 break;
263 }
264
265 if (this->isSymlink(currentpath)) {
266 currentpath = this->readSymlink(currentpath);
267 } else {
268 // We know last part is not a symlink, so it is resolved
269 std::string part1 =
270 currentpath.substr(0, currentpath.rfind('/'));
271 std::string part2 =
272 currentpath.substr(currentpath.rfind('/')+1);
273 currentpath = part1;
274 if (resolvedpath.size() == 0)
275 resolvedpath = part2;
276 else
277 resolvedpath = part2 + "/" + resolvedpath;
278 }
279 }
280
281 if (failed) {
282 throw SystemException("Could not resolve path \"" +
283 file.getPath() + "\"", __FILE__, __LINE__);
284 }
285
286 while (resolvedpath.find("/./") != std::string::npos) {
287 int pos = resolvedpath.find("/./");
288 resolvedpath = resolvedpath.substr(0, pos)
289 + resolvedpath.substr(pos + 2);
290 }
291
292 while (resolvedpath.find("/../") != std::string::npos) {
293 int pos = resolvedpath.find("/../");
294 int pos2 = resolvedpath.rfind('/', pos-1);
295 resolvedpath = resolvedpath.substr(0, pos2)
296 + resolvedpath.substr(pos + 3);
297 }
298
299 if (resolvedpath.find("/..", resolvedpath.size() - 3)
300 != std::string::npos) {
301 resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 3);
302 resolvedpath = resolvedpath.substr(0, resolvedpath.rfind('/'));
303 }
304
305 if (resolvedpath.find("/.", resolvedpath.size() - 2)
306 != std::string::npos) {
307 resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 2);
308 }
309
310 if (resolvedpath.size() == 0)
311 resolvedpath = "/";
312
313 return resolvedpath;
314 }
315
316 bool suPHP::API_Linux::File_hasPermissionBit(const File& file, FileMode perm)
317 const throw (SystemException) {
318 struct stat temp;
319 if (lstat(file.getPath().c_str(), &temp) == -1) {
320 throw SystemException(std::string("Could not stat \"")
321 + file.getPath() + "\": "
322 + ::strerror(errno), __FILE__, __LINE__);
323 }
324 switch (perm) {
325 case FILEMODE_USER_READ:
326 if ((temp.st_mode & S_IRUSR) == S_IRUSR)
327 return true;
328 break;
329
330 case FILEMODE_USER_WRITE:
331 if ((temp.st_mode & S_IWUSR) == S_IWUSR)
332 return true;
333 break;
334
335 case FILEMODE_USER_EXEC:
336 if ((temp.st_mode & S_IXUSR) == S_IXUSR)
337 return true;
338 break;
339
340 case FILEMODE_GROUP_READ:
341 if ((temp.st_mode & S_IRGRP) == S_IRGRP)
342 return true;
343 break;
344
345 case FILEMODE_GROUP_WRITE:
346 if ((temp.st_mode & S_IWGRP) == S_IWGRP)
347 return true;
348 break;
349
350 case FILEMODE_GROUP_EXEC:
351 if ((temp.st_mode & S_IXGRP) == S_IXGRP)
352 return true;
353 break;
354
355 case FILEMODE_OTHERS_READ:
356 if ((temp.st_mode & S_IROTH) == S_IROTH)
357 return true;
358 break;
359
360 case FILEMODE_OTHERS_WRITE:
361 if ((temp.st_mode & S_IWOTH) == S_IWOTH)
362 return true;
363 break;
364
365 case FILEMODE_OTHERS_EXEC:
366 if ((temp.st_mode & S_IXOTH) == S_IXOTH)
367 return true;
368 break;
369 }
370
371 return false;
372 }
373
374 UserInfo suPHP::API_Linux::File_getUser(const File& file) const
375 throw (SystemException) {
376 struct stat temp;
377 if (lstat(file.getPath().c_str(), &temp) == -1) {
378 throw SystemException(std::string("Could not stat \"")
379 + file.getPath() + "\": "
380 + ::strerror(errno), __FILE__, __LINE__);
381 }
382 return UserInfo(temp.st_uid);
383 }
384
385 GroupInfo suPHP::API_Linux::File_getGroup(const File& file) const
386 throw (SystemException) {
387 struct stat temp;
388 if (lstat(file.getPath().c_str(), &temp) == -1) {
389 throw SystemException(std::string("Could not stat \"")
390 + file.getPath() + "\": "
391 + ::strerror(errno), __FILE__, __LINE__);
392 }
393 return GroupInfo(temp.st_gid);
394 }
395
396
397 bool suPHP::API_Linux::File_isSymlink(const File& file) const throw (SystemException) {
398 return this->isSymlink(file.getPath());
399 }
400
401
402 void suPHP::API_Linux::execute(std::string program, const CommandLine& cline,
403 const Environment& env) const
404 throw (SystemException) {
405 char **sysCline = NULL;
406 char **sysEnv = NULL;
407 char **p = NULL;
408 char *sysProgram = NULL;
409 std::map<std::string, std::string> map;
410 int i;
411
412
413
414 // Construct commandline
415 sysCline = new char*[cline.size() + 1];
416 for (i=0; i<cline.size(); i++) {
417 std::string arg = cline.getArgument(i);
418 sysCline[i] = new char[arg.size()+1];
419 ::strncpy(sysCline[i], arg.c_str(), arg.size()+1);
420 }
421 sysCline[cline.size()] = NULL;
422
423 // Construct environment
424 map = env.getBackendMap();
425 sysEnv = new char*[map.size() + 1];
426 p = sysEnv;
427 for (std::map<std::string, std::string>::iterator pos = map.begin();
428 pos != map.end();
429 pos++) {
430 std::string var;
431 var = pos->first + "=" + pos->second;
432 *p = new char[var.size()+1];
433 ::strncpy(*p, var.c_str(), var.size()+1);
434 p++;
435 }
436 *p = NULL;
437
438 // Make sure target program name is on heap
439 sysProgram = new char[program.size() + 1];
440 ::strncpy(sysProgram, program.c_str(), program.size()+1);
441 if (execve(sysProgram, sysCline, sysEnv) == -1) {
442 throw SystemException("execve() for program \"" + program
443 + "\" failed: " + ::strerror(errno),
444 __FILE__, __LINE__);
445 }
446
447 // We are still here? This cannot be good..
448 throw SystemException("execve() for program \"" + program
449 + "\" failed because of unknown reason",
450 __FILE__, __LINE__);
451 }
452
453 std::string suPHP::API_Linux::getCwd() const throw (SystemException) {
454 char buf[4096] = {0};
455 if (::getcwd(buf, 4095) == NULL)
456 throw SystemException(std::string("getcwd() failed: ")
457 + ::strerror(errno), __FILE__, __LINE__);
458 return std::string(buf);
459 }
460
461 void suPHP::API_Linux::setCwd(const std::string& dir) const
462 throw (SystemException) {
463 if(::chdir(dir.c_str())) {
464 throw SystemException(std::string("chdir() failed: ")
465 + ::strerror(errno), __FILE__, __LINE__);
466 }
467 }
468
469 void suPHP::API_Linux::setUmask(int mode) const throw (SystemException) {
470 ::umask(mode);
471 }
472
473 void suPHP::API_Linux::chroot(const std::string& dir) const
474 throw (SystemException) {
475 if (::chroot(dir.c_str())) {
476 throw SystemException(std::string("chroot() failed: ")
477 + ::strerror(errno), __FILE__, __LINE__);
478 }
479 }