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 <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 | } |