00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "blocxx/BLOCXX_config.h"
00035 #include "blocxx/Array.hpp"
00036 #include "blocxx/Secure.hpp"
00037 #include "blocxx/FileSystem.hpp"
00038 #include "blocxx/String.hpp"
00039 #include "blocxx/Paths.hpp"
00040 #include "blocxx/Format.hpp"
00041 #include "blocxx/LazyGlobal.hpp"
00042 #ifdef BLOCXX_HAVE_DIRENT_H
00043 #include <dirent.h>
00044 #endif
00045 #include <fcntl.h>
00046 #ifndef BLOCXX_WIN32
00047 #include <grp.h>
00048 #endif
00049 #include <limits.h>
00050 #ifdef BLOCXX_HAVE_PWD_H
00051 #include <pwd.h>
00052 #endif
00053 #ifdef BLOCXX_HAVE_SYS_PARAM_H
00054 #include <sys/param.h>
00055 #endif
00056 #include <sys/types.h>
00057 #include <sys/stat.h>
00058 #ifdef BLOCXX_HAVE_UNISTD_H
00059 #include <unistd.h>
00060 #endif
00061 #include <cstdlib>
00062 #include <cstdio>
00063 #include <cerrno>
00064 #include <vector>
00065 #include <algorithm>
00066
00067 #ifdef AIX
00068 #include "blocxx/StringBuffer.hpp"
00069 #include "blocxx/NonRecursiveMutex.hpp"
00070 #include "blocxx/NonRecursiveMutexLock.hpp"
00071 #include <fstream>
00072 #include <cctype>
00073 #endif
00074
00075 #if defined(BLOCXX_NO_SETRESGID_PROTO) && defined(BLOCXX_HAVE_SETRESGID)
00076 extern "C" { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); }
00077 #endif
00078
00079 #if defined(BLOCXX_NO_SETRESUID_PROTO) && defined(BLOCXX_HAVE_SETRESUID)
00080 extern "C" { int setresuid(uid_t ruid, uid_t euid, uid_t suid); }
00081 #endif
00082
00083 using namespace blocxx;
00084
00085 #define THRBLOCXX_IF(tst, ExceptionClass, msg) \
00086 do \
00087 { \
00088 if (tst) \
00089 { \
00090 BLOCXX_THROW(ExceptionClass, (msg)); \
00091 } \
00092 } while (false)
00093
00094 #define THRBLOCXX_ERRNO_IF(tst, ExceptionClass, msg) \
00095 do \
00096 { \
00097 if (tst) \
00098 { \
00099 BLOCXX_THROW_ERRNO_MSG(ExceptionClass, (msg)); \
00100 } \
00101 } while (false)
00102
00103 #define ABORT_IF(tst, msg) THRBLOCXX_IF((tst), Secure::ProcessAbortException, (msg))
00104
00105 #define ABORT_ERRNO_IF(tst, msg) \
00106 THRBLOCXX_ERRNO_IF((tst), Secure::ProcessAbortException, (msg))
00107
00108 namespace
00109 {
00110 #if !defined(BLOCXX_HAVE_SETEUID) && defined(BLOCXX_HAVE_SETREUID)
00111 int seteuid(uid_t euid)
00112 {
00113 return (setreuid(-1, euid));
00114 }
00115
00116 #endif
00117
00118 #if !defined(BLOCXX_HAVE_SETEGID) && defined(BLOCXX_HAVE_SETRESGID)
00119 int setegid(uid_t egid)
00120 {
00121 return(setresgid(-1, egid, -1));
00122 }
00123 #endif
00124
00125 }
00126
00127 namespace BLOCXX_NAMESPACE
00128 {
00129 namespace Secure
00130 {
00131 BLOCXX_DEFINE_EXCEPTION(ProcessAbort);
00132
00133
00134
00135
00136
00137 void dropPrivilegesPermanently(::uid_t newuid, ::gid_t newgid, EChildGroupAction extendedGroupAction)
00138 {
00139 #ifdef BLOCXX_WIN32
00140 #pragma message(Reminder "TODO: implement it for Win!")
00141 #else
00142
00143
00144
00145
00146 if (newgid == ::gid_t(-1))
00147 {
00148 newgid = ::getgid();
00149 }
00150 ::gid_t oldegid = ::getegid();
00151 ::gid_t oldgid = ::getgid();
00152 if (newuid == ::uid_t(-1))
00153 {
00154 newuid = ::getuid();
00155 }
00156 ::uid_t oldeuid = ::geteuid();
00157 ::uid_t olduid = ::getuid();
00158
00159
00160
00161
00162
00163
00164 if (oldeuid == 0)
00165 {
00166 struct passwd *newuser(NULL);
00167 if (extendedGroupAction == E_SOURCE_EXTENDED_GROUPS)
00168 {
00169 newuser = ::getpwuid(newuid);
00170 }
00171 if (newuser)
00172 {
00173 ::initgroups(newuser->pw_name, newgid);
00174 }
00175 else
00176 {
00177 ::setgroups(1, &newgid);
00178 }
00179 }
00180
00181 if (newgid != oldegid)
00182 {
00183 #if defined(BLOCXX_HAVE_SETRESGID) && !defined(BLOCXX_BROKEN_SETRESGID)
00184 ABORT_ERRNO_IF(::setresgid(newgid, newgid, newgid) == -1, "drop_privileges [1]");
00185 #elif defined(BLOCXX_HAVE_SETREGID) && !defined(BLOCXX_BROKEN_SETREGID)
00186 ABORT_ERRNO_IF(::setregid(newgid, newgid) == -1, "drop_privileges [1]");
00187 #else
00188 ABORT_ERRNO_IF(::setegid(newgid) == -1, "drop_privileges [1]");
00189 ABORT_ERRNO_IF(::setgid(newgid) == -1, "drop_privileges [1.1]");
00190 #endif
00191 }
00192
00193 if (newuid != oldeuid)
00194 {
00195 #if defined(BLOCXX_HAVE_SETRESUID) && !defined(BLOCXX_BROKEN_SETRESUID)
00196 ABORT_ERRNO_IF(::setresuid(newuid, newuid, newuid) == -1, "drop_privileges [2]");
00197 #elif defined(BLOCXX_HAVE_SETREUID) && !defined(BLOCXX_BROKEN_SETREUID)
00198 ABORT_ERRNO_IF(::setreuid(newuid, newuid) == -1, "drop_privileges [2]");
00199 #else
00200 #if !defined(BLOCXX_SETEUID_BREAKS_SETUID)
00201 ABORT_ERRNO_IF(::seteuid(newuid) == -1, "drop_privileges [2]");
00202 #endif
00203 ABORT_ERRNO_IF(::setuid(newuid) == -1, "drop_privileges [2.1]");
00204 #endif
00205 }
00206
00207
00208
00209 ABORT_IF(::getgid() != newgid || ::getegid() != newgid, "drop_privileges [3]");
00210
00211
00212 ABORT_IF(
00213 newuid != 0 && newgid != oldegid &&
00214 #if defined(BLOCXX_HAVE_SETRESGID) && !defined(BLOCXX_BROKEN_SETRESGID)
00215 (::setresgid(oldegid, oldegid, oldegid) != -1 || ::setgid(oldgid) != -1),
00216 #elif defined(BLOCXX_HAVE_SETREGID) && !defined(BLOCXX_BROKEN_SETREGID)
00217 (::setregid(oldegid, oldegid) != -1 || ::setgid(oldgid) != -1),
00218 #else
00219 (::setegid(oldegid) != -1 || ::setgid(oldgid) != -1),
00220 #endif
00221 "drop_privileges [4]"
00222 );
00223
00224
00225 ABORT_IF(::getuid() != newuid || ::geteuid() != newuid, "drop_privileges [5]");
00226
00227
00228 ABORT_IF(
00229 newuid != 0 && newuid != oldeuid &&
00230 #if defined(BLOCXX_HAVE_SETRESUID) && !defined(BLOCXX_BROKEN_SETRESUID)
00231 (::setresuid(oldeuid, oldeuid, oldeuid) != -1 || ::setuid(olduid) != -1),
00232 #elif defined(BLOCXX_HAVE_SETREUID) && !defined(BLOCXX_BROKEN_SETREUID)
00233 (::setreuid(oldeuid, oldeuid) != -1 || ::setuid(olduid) != -1),
00234 #else
00235 (::seteuid(oldeuid) != -1 || ::setuid(olduid) != -1),
00236 #endif
00237 "drop_privileges [6]"
00238 );
00239 #endif
00240 }
00241
00242 namespace
00243 {
00244 #ifdef AIX
00245 NonRecursiveMutex envMutex;
00246 String odmdir;
00247
00248 char const default_odmdir[] = "ODMDIR=/etc/objrepos";
00249
00250 String check_line(String const & line)
00251 {
00252 StringBuffer sb;
00253 char const * s;
00254 char c;
00255 for (s = line.c_str(); (c = *s) && !std::isspace(c); ++s)
00256 {
00257 switch (c)
00258 {
00259 case '\\':
00260 if (s[1] == '\0')
00261 {
00262
00263 return default_odmdir;
00264 }
00265 c = *++s;
00266 break;
00267 case '$':
00268 case '`':
00269 case '"':
00270 case '\'':
00271
00272 return default_odmdir;
00273 default:
00274 ;
00275 }
00276 sb += c;
00277 }
00278 if (c == '\0')
00279 {
00280 return sb.releaseString();
00281 }
00282 while (std::isspace(*s))
00283 {
00284 ++s;
00285 }
00286 if (*s == '#')
00287 {
00288 return sb.releaseString();
00289 }
00290
00291 return default_odmdir;
00292 }
00293
00294 String setODMDIR()
00295 {
00296 String retval(default_odmdir);
00297 std::ifstream is("/etc/environment");
00298 while (is)
00299 {
00300 String s = String::getLine(is).trim();
00301 if (s.startsWith("ODMDIR="))
00302 {
00303 retval = check_line(s);
00304 }
00305 }
00306 return retval;
00307 }
00308
00309 void addPlatformSpecificEnvVars(StringArray & environ)
00310 {
00311 NonRecursiveMutexLock lock(envMutex);
00312 if (odmdir.empty())
00313 {
00314 odmdir = setODMDIR();
00315 }
00316 environ.push_back(odmdir);
00317 }
00318
00319 #else
00320
00321 void addPlatformSpecificEnvVars(StringArray &absEnvironment)
00322 {
00323 #ifdef BLOCXX_WIN32
00324 char* const lpInheritedEnvironment = GetEnvironmentStrings();
00325 char* lpInheritedEnvIterator = lpInheritedEnvironment;
00326 if (lpInheritedEnvironment && *lpInheritedEnvironment && lpInheritedEnvironment[1])
00327 {
00328 for ( ; *lpInheritedEnvIterator; lpInheritedEnvIterator++)
00329 {
00330 absEnvironment.push_back( String( lpInheritedEnvIterator ) );
00331 lpInheritedEnvIterator += lstrlen(lpInheritedEnvIterator);
00332 }
00333 FreeEnvironmentStrings( (LPTCH)lpInheritedEnvironment );
00334 }
00335 #endif
00336 }
00337
00338 #endif
00339
00340 struct MinimalEnvironmentConstructor
00341 {
00342 static StringArray* create(int dummy)
00343 {
00344 AutoPtr<StringArray> retval(new StringArray);
00345 retval->push_back("IFS= \t\n");
00346 retval->push_back("PATH=" _PATH_STDPATH);
00347 char * tzstr = ::getenv("TZ");
00348 if (tzstr)
00349 {
00350 retval->push_back(String("TZ=") + tzstr);
00351 }
00352 addPlatformSpecificEnvVars(*retval);
00353 return retval.release();
00354 }
00355 };
00356
00357 LazyGlobal<StringArray, int, MinimalEnvironmentConstructor> g_minimalEnvironment = BLOCXX_LAZY_GLOBAL_INIT(0);
00358 }
00359
00360 StringArray minimalEnvironment()
00361 {
00362 return g_minimalEnvironment;
00363 }
00364
00365 void runAs(char const * username, EChildGroupAction extendedGroupAction)
00366 {
00367 #ifdef BLOCXX_WIN32
00368 #pragma message(Reminder "TODO: implement it for Win!")
00369 #else
00370 ABORT_IF(!username, "null user name");
00371 ABORT_IF(*username == '\0', "empty user name");
00372 ABORT_IF(::getuid() != 0 || ::geteuid() != 0, "non-root user calling runAs");
00373 errno = 0;
00374 struct passwd * pwent = ::getpwnam(username);
00375
00376 ABORT_ERRNO_IF(!pwent && errno != 0, Format("getpwnam(\"%1\") failed", username).c_str());
00377 ABORT_IF(!pwent, Format("user name (%1) not found", username).c_str());
00378 int rc = ::chdir("/");
00379 ABORT_ERRNO_IF(rc != 0, "chdir failed");
00380 Secure::dropPrivilegesPermanently(pwent->pw_uid, pwent->pw_gid, extendedGroupAction);
00381 #endif
00382 }
00383
00384 }
00385 }