blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Quest Software, Inc. All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Quest Software, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 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 } // end anonymous namespace 00126 00127 namespace BLOCXX_NAMESPACE 00128 { 00129 namespace Secure 00130 { 00131 BLOCXX_DEFINE_EXCEPTION(ProcessAbort); 00132 00133 // Original source: Item 1.3, _Secure Programming Cookbook for C and C++_, by 00134 // John Viega and Matt Messier. 00135 // Original C code reformatted and modified for C++. 00136 // Some inspiration provided by uidswap.c from openssh-portable 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 // Note: If any manipulation of privileges cannot be completed 00143 // successfully, it is safest to assume that the process is in an 00144 // unknown state and not allow it to continue (abort). 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 // If root privileges are to be dropped, be sure to pare down the 00160 // ancillary groups for the process before doing anything else because 00161 // the setgroups() system call requires root privileges. Drop ancillary 00162 // groups regardless of whether privileges are being dropped temporarily 00163 // or permanently. 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 // verify that the changes were successful 00208 // make sure gid drop was successful 00209 ABORT_IF(::getgid() != newgid || ::getegid() != newgid, "drop_privileges [3]"); 00210 00211 // make sure gid restoration fails 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 // make sure uid drop was successful 00225 ABORT_IF(::getuid() != newuid || ::geteuid() != newuid, "drop_privileges [5]"); 00226 00227 // make sure uid restoration fails 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 // Unexpected format 00263 return default_odmdir; 00264 } 00265 c = *++s; 00266 break; 00267 case '$': 00268 case '`': 00269 case '"': 00270 case '\'': 00271 // Unexpected format 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 // Unexpected format 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 } // end unnamed namespace 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 // return value from getpwnam is a static, so don't free it. 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 } // namespace Secure 00385 } // namespace BLOCXX_NAMESPACE