2 suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
4 This file is part of suPHP.
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.
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.
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
26 #include <sys/types.h>
33 #include "Environment.hpp"
34 #include "UserInfo.hpp"
35 #include "GroupInfo.hpp"
36 #include "API_Linux_Logger.hpp"
39 #include "API_Linux.hpp"
41 extern char **environ
;
43 using namespace suPHP
;
45 SmartPtr
<API_Linux_Logger
> suPHP::API_Linux::logger
;
47 bool suPHP::API_Linux::isSymlink(const std::string path
) const
48 throw (SystemException
) {
50 if (lstat(path
.c_str(), &temp
) == -1) {
51 throw SystemException(std::string("Could not stat \"")
53 + ::strerror(errno
), __FILE__
, __LINE__
);
55 if ((temp
.st_mode
& S_IFLNK
) == S_IFLNK
) {
61 std::string
suPHP::API_Linux::readSymlink(const std::string path
) const
62 throw (SystemException
) {
64 if (::readlink(path
.c_str(), buf
, 1023) == -1) {
65 throw SystemException(std::string("Could not read symlink \"")
67 + ::strerror(errno
), __FILE__
, __LINE__
);
71 return std::string(buf
);
73 if (path
.rfind('/') == std::string::npos
)
74 return std::string(buf
);
75 return path
.substr(0, path
.rfind('/') + 1) + std::string(buf
);
79 Environment
suPHP::API_Linux::getProcessEnvironment() {
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
);
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__
);
100 return UserInfo(tmpuser
->pw_uid
);
103 UserInfo
suPHP::API_Linux::getUserInfo(const int uid
) {
104 return UserInfo(uid
);
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__
);
114 return GroupInfo(tmpgroup
->gr_gid
);
117 GroupInfo
suPHP::API_Linux::getGroupInfo(const int gid
) {
118 return GroupInfo(gid
);
121 UserInfo
suPHP::API_Linux::getEffectiveProcessUser() {
122 return UserInfo(::geteuid());
126 UserInfo
suPHP::API_Linux::getRealProcessUser() {
127 return UserInfo(getuid());
131 GroupInfo
suPHP::API_Linux::getEffectiveProcessGroup() {
132 return GroupInfo(getegid());
136 GroupInfo
suPHP::API_Linux::getRealProcessGroup() {
137 return GroupInfo(getgid());
141 Logger
& suPHP::API_Linux::getSystemLogger() {
142 if (suPHP::API_Linux::logger
.get() == NULL
) {
143 suPHP::API_Linux::logger
.reset(new API_Linux_Logger());
145 return *(suPHP::API_Linux::logger
);
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__
);
158 if (::initgroups(user
.getUsername().c_str(),
159 user
.getGroupInfo().getGid())
161 throw SystemException(std::string("initgroups() failed: ")
162 + ::strerror(errno
), __FILE__
, __LINE__
);
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
170 if (::setuid(user
.getUid()) == -1) {
171 throw SystemException(std::string("setuid() failed: ")
172 + ::strerror(errno
), __FILE__
, __LINE__
);
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__
);
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()),
193 return std::string(tmpuser
->pw_name
);
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()),
205 return GroupInfo(tmpuser
->pw_gid
);
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()),
217 return tmpuser
->pw_dir
;
220 bool suPHP::API_Linux::UserInfo_isSuperUser(const UserInfo
& uinfo
) const {
221 if (uinfo
.getUid() == 0)
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()),
235 return std::string(tmpgroup
->gr_name
);
238 bool suPHP::API_Linux::File_exists(const File
& file
) const {
240 if (::lstat(file
.getPath().c_str(), &dummy
) == 0)
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
= "";
252 if ((currentpath
.size() == 0) || (currentpath
.at(0) != '/')) {
253 currentpath
= this->getCwd() + std::string("/") + currentpath
;
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
);
265 if (this->isSymlink(currentpath
)) {
266 currentpath
= this->readSymlink(currentpath
);
268 // We know last part is not a symlink, so it is resolved
270 currentpath
.substr(0, currentpath
.rfind('/'));
272 currentpath
.substr(currentpath
.rfind('/')+1);
274 if (resolvedpath
.size() == 0)
275 resolvedpath
= part2
;
277 resolvedpath
= part2
+ "/" + resolvedpath
;
282 throw SystemException("Could not resolve path \"" +
283 file
.getPath() + "\"", __FILE__
, __LINE__
);
286 while (resolvedpath
.find("/./") != std::string::npos
) {
287 int pos
= resolvedpath
.find("/./");
288 resolvedpath
= resolvedpath
.substr(0, pos
)
289 + resolvedpath
.substr(pos
+ 2);
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);
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('/'));
305 if (resolvedpath
.find("/.", resolvedpath
.size() - 2)
306 != std::string::npos
) {
307 resolvedpath
= resolvedpath
.substr(0, resolvedpath
.size() - 2);
310 if (resolvedpath
.size() == 0)
316 bool suPHP::API_Linux::File_hasPermissionBit(const File
& file
, FileMode perm
)
317 const throw (SystemException
) {
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__
);
325 case FILEMODE_USER_READ
:
326 if ((temp
.st_mode
& S_IRUSR
) == S_IRUSR
)
330 case FILEMODE_USER_WRITE
:
331 if ((temp
.st_mode
& S_IWUSR
) == S_IWUSR
)
335 case FILEMODE_USER_EXEC
:
336 if ((temp
.st_mode
& S_IXUSR
) == S_IXUSR
)
340 case FILEMODE_GROUP_READ
:
341 if ((temp
.st_mode
& S_IRGRP
) == S_IRGRP
)
345 case FILEMODE_GROUP_WRITE
:
346 if ((temp
.st_mode
& S_IWGRP
) == S_IWGRP
)
350 case FILEMODE_GROUP_EXEC
:
351 if ((temp
.st_mode
& S_IXGRP
) == S_IXGRP
)
355 case FILEMODE_OTHERS_READ
:
356 if ((temp
.st_mode
& S_IROTH
) == S_IROTH
)
360 case FILEMODE_OTHERS_WRITE
:
361 if ((temp
.st_mode
& S_IWOTH
) == S_IWOTH
)
365 case FILEMODE_OTHERS_EXEC
:
366 if ((temp
.st_mode
& S_IXOTH
) == S_IXOTH
)
374 UserInfo
suPHP::API_Linux::File_getUser(const File
& file
) const
375 throw (SystemException
) {
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__
);
382 return UserInfo(temp
.st_uid
);
385 GroupInfo
suPHP::API_Linux::File_getGroup(const File
& file
) const
386 throw (SystemException
) {
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__
);
393 return GroupInfo(temp
.st_gid
);
397 bool suPHP::API_Linux::File_isSymlink(const File
& file
) const throw (SystemException
) {
398 return this->isSymlink(file
.getPath());
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
;
408 char *sysProgram
= NULL
;
409 std::map
<std::string
, std::string
> map
;
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);
421 sysCline
[cline
.size()] = NULL
;
423 // Construct environment
424 map
= env
.getBackendMap();
425 sysEnv
= new char*[map
.size() + 1];
427 for (std::map
<std::string
, std::string
>::iterator pos
= map
.begin();
431 var
= pos
->first
+ "=" + pos
->second
;
432 *p
= new char[var
.size()+1];
433 ::strncpy(*p
, var
.c_str(), var
.size()+1);
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
),
447 // We are still here? This cannot be good..
448 throw SystemException("execve() for program \"" + program
449 + "\" failed because of unknown reason",
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
);
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__
);
469 void suPHP::API_Linux::setUmask(int mode
) const throw (SystemException
) {
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__
);