blocxx
Secure.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Quest Software, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Quest Software, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
34#include "blocxx/BLOCXX_config.h"
35#include "blocxx/Array.hpp"
36#include "blocxx/Secure.hpp"
37#include "blocxx/FileSystem.hpp"
38#include "blocxx/String.hpp"
39#include "blocxx/Paths.hpp"
40#include "blocxx/Format.hpp"
41#include "blocxx/LazyGlobal.hpp"
42#ifdef BLOCXX_HAVE_DIRENT_H
43#include <dirent.h>
44#endif
45#include <fcntl.h>
46#ifndef BLOCXX_WIN32
47#include <grp.h>
48#endif
49#include <limits.h>
50#ifdef BLOCXX_HAVE_PWD_H
51#include <pwd.h>
52#endif
53#ifdef BLOCXX_HAVE_SYS_PARAM_H
54#include <sys/param.h>
55#endif
56#include <sys/types.h>
57#include <sys/stat.h>
58#ifdef BLOCXX_HAVE_UNISTD_H
59#include <unistd.h>
60#endif
61#include <cstdlib>
62#include <cstdio>
63#include <cerrno>
64#include <vector>
65#include <algorithm>
66
67#ifdef AIX
71#include <fstream>
72#include <cctype>
73#endif
74
75#if defined(BLOCXX_NO_SETRESGID_PROTO) && defined(BLOCXX_HAVE_SETRESGID)
76extern "C" { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); }
77#endif
78
79#if defined(BLOCXX_NO_SETRESUID_PROTO) && defined(BLOCXX_HAVE_SETRESUID)
80extern "C" { int setresuid(uid_t ruid, uid_t euid, uid_t suid); }
81#endif
82
83using namespace blocxx;
84
85#define THRBLOCXX_IF(tst, ExceptionClass, msg) \
86 do \
87 { \
88 if (tst) \
89 { \
90 BLOCXX_THROW(ExceptionClass, (msg)); \
91 } \
92 } while (false)
93
94#define THRBLOCXX_ERRNO_IF(tst, ExceptionClass, msg) \
95 do \
96 { \
97 if (tst) \
98 { \
99 BLOCXX_THROW_ERRNO_MSG(ExceptionClass, (msg)); \
100 } \
101 } while (false)
102
103#define ABORT_IF(tst, msg) THRBLOCXX_IF((tst), Secure::ProcessAbortException, (msg))
104
105#define ABORT_ERRNO_IF(tst, msg) \
106 THRBLOCXX_ERRNO_IF((tst), Secure::ProcessAbortException, (msg))
107
108namespace
109{
110#if !defined(BLOCXX_HAVE_SETEUID) && defined(BLOCXX_HAVE_SETREUID)
111int seteuid(uid_t euid)
112{
113 return (setreuid(-1, euid));
114}
115
116#endif
117
118#if !defined(BLOCXX_HAVE_SETEGID) && defined(BLOCXX_HAVE_SETRESGID)
119int setegid(uid_t egid)
120{
121 return(setresgid(-1, egid, -1));
122}
123#endif
124
125} // end anonymous namespace
126
127namespace BLOCXX_NAMESPACE
128{
129namespace Secure
130{
132
133 // Original source: Item 1.3, _Secure Programming Cookbook for C and C++_, by
134 // John Viega and Matt Messier.
135 // Original C code reformatted and modified for C++.
136 // Some inspiration provided by uidswap.c from openssh-portable
137 void dropPrivilegesPermanently(::uid_t newuid, ::gid_t newgid, EChildGroupAction extendedGroupAction)
138 {
139#ifdef BLOCXX_WIN32
140#pragma message(Reminder "TODO: implement it for Win!")
141#else
142 // Note: If any manipulation of privileges cannot be completed
143 // successfully, it is safest to assume that the process is in an
144 // unknown state and not allow it to continue (abort).
145
146 if (newgid == ::gid_t(-1))
147 {
148 newgid = ::getgid();
149 }
150 ::gid_t oldegid = ::getegid();
151 ::gid_t oldgid = ::getgid();
152 if (newuid == ::uid_t(-1))
153 {
154 newuid = ::getuid();
155 }
156 ::uid_t oldeuid = ::geteuid();
157 ::uid_t olduid = ::getuid();
158
159 // If root privileges are to be dropped, be sure to pare down the
160 // ancillary groups for the process before doing anything else because
161 // the setgroups() system call requires root privileges. Drop ancillary
162 // groups regardless of whether privileges are being dropped temporarily
163 // or permanently.
164 if (oldeuid == 0)
165 {
166 struct passwd *newuser(NULL);
167 if (extendedGroupAction == E_SOURCE_EXTENDED_GROUPS)
168 {
169 newuser = ::getpwuid(newuid);
170 }
171 if (newuser)
172 {
173 ::initgroups(newuser->pw_name, newgid);
174 }
175 else
176 {
177 ::setgroups(1, &newgid);
178 }
179 }
180
181 if (newgid != oldegid)
182 {
183#if defined(BLOCXX_HAVE_SETRESGID) && !defined(BLOCXX_BROKEN_SETRESGID)
184 ABORT_ERRNO_IF(::setresgid(newgid, newgid, newgid) == -1, "drop_privileges [1]");
185#elif defined(BLOCXX_HAVE_SETREGID) && !defined(BLOCXX_BROKEN_SETREGID)
186 ABORT_ERRNO_IF(::setregid(newgid, newgid) == -1, "drop_privileges [1]");
187#else
188 ABORT_ERRNO_IF(::setegid(newgid) == -1, "drop_privileges [1]");
189 ABORT_ERRNO_IF(::setgid(newgid) == -1, "drop_privileges [1.1]");
190#endif
191 }
192
193 if (newuid != oldeuid)
194 {
195#if defined(BLOCXX_HAVE_SETRESUID) && !defined(BLOCXX_BROKEN_SETRESUID)
196 ABORT_ERRNO_IF(::setresuid(newuid, newuid, newuid) == -1, "drop_privileges [2]");
197#elif defined(BLOCXX_HAVE_SETREUID) && !defined(BLOCXX_BROKEN_SETREUID)
198 ABORT_ERRNO_IF(::setreuid(newuid, newuid) == -1, "drop_privileges [2]");
199#else
200#if !defined(BLOCXX_SETEUID_BREAKS_SETUID)
201 ABORT_ERRNO_IF(::seteuid(newuid) == -1, "drop_privileges [2]");
202#endif
203 ABORT_ERRNO_IF(::setuid(newuid) == -1, "drop_privileges [2.1]");
204#endif
205 }
206
207 // verify that the changes were successful
208 // make sure gid drop was successful
209 ABORT_IF(::getgid() != newgid || ::getegid() != newgid, "drop_privileges [3]");
210
211 // make sure gid restoration fails
212 ABORT_IF(
213 newuid != 0 && newgid != oldegid &&
214#if defined(BLOCXX_HAVE_SETRESGID) && !defined(BLOCXX_BROKEN_SETRESGID)
215 (::setresgid(oldegid, oldegid, oldegid) != -1 || ::setgid(oldgid) != -1),
216#elif defined(BLOCXX_HAVE_SETREGID) && !defined(BLOCXX_BROKEN_SETREGID)
217 (::setregid(oldegid, oldegid) != -1 || ::setgid(oldgid) != -1),
218#else
219 (::setegid(oldegid) != -1 || ::setgid(oldgid) != -1),
220#endif
221 "drop_privileges [4]"
222 );
223
224 // make sure uid drop was successful
225 ABORT_IF(::getuid() != newuid || ::geteuid() != newuid, "drop_privileges [5]");
226
227 // make sure uid restoration fails
228 ABORT_IF(
229 newuid != 0 && newuid != oldeuid &&
230#if defined(BLOCXX_HAVE_SETRESUID) && !defined(BLOCXX_BROKEN_SETRESUID)
231 (::setresuid(oldeuid, oldeuid, oldeuid) != -1 || ::setuid(olduid) != -1),
232#elif defined(BLOCXX_HAVE_SETREUID) && !defined(BLOCXX_BROKEN_SETREUID)
233 (::setreuid(oldeuid, oldeuid) != -1 || ::setuid(olduid) != -1),
234#else
235 (::seteuid(oldeuid) != -1 || ::setuid(olduid) != -1),
236#endif
237 "drop_privileges [6]"
238 );
239#endif
240 }
241
242namespace
243{
244#ifdef AIX
245 NonRecursiveMutex envMutex;
246 String odmdir;
247
248 char const default_odmdir[] = "ODMDIR=/etc/objrepos";
249
250 String check_line(String const & line)
251 {
252 StringBuffer sb;
253 char const * s;
254 char c;
255 for (s = line.c_str(); (c = *s) && !std::isspace(c); ++s)
256 {
257 switch (c)
258 {
259 case '\\':
260 if (s[1] == '\0')
261 {
262 // Unexpected format
263 return default_odmdir;
264 }
265 c = *++s;
266 break;
267 case '$':
268 case '`':
269 case '"':
270 case '\'':
271 // Unexpected format
272 return default_odmdir;
273 default:
274 ;
275 }
276 sb += c;
277 }
278 if (c == '\0')
279 {
280 return sb.releaseString();
281 }
282 while (std::isspace(*s))
283 {
284 ++s;
285 }
286 if (*s == '#')
287 {
288 return sb.releaseString();
289 }
290 // Unexpected format
291 return default_odmdir;
292 }
293
294 String setODMDIR()
295 {
296 String retval(default_odmdir);
297 std::ifstream is("/etc/environment");
298 while (is)
299 {
300 String s = String::getLine(is).trim();
301 if (s.startsWith("ODMDIR="))
302 {
303 retval = check_line(s);
304 }
305 }
306 return retval;
307 }
308
309 void addPlatformSpecificEnvVars(StringArray & environ)
310 {
311 NonRecursiveMutexLock lock(envMutex);
312 if (odmdir.empty())
313 {
314 odmdir = setODMDIR();
315 }
316 environ.push_back(odmdir);
317 }
318
319#else
320
321 void addPlatformSpecificEnvVars(StringArray &absEnvironment)
322 {
323#ifdef BLOCXX_WIN32
324 char* const lpInheritedEnvironment = GetEnvironmentStrings();
325 char* lpInheritedEnvIterator = lpInheritedEnvironment;
326 if (lpInheritedEnvironment && *lpInheritedEnvironment && lpInheritedEnvironment[1])
327 {
328 for ( ; *lpInheritedEnvIterator; lpInheritedEnvIterator++)
329 {
330 absEnvironment.push_back( String( lpInheritedEnvIterator ) );
331 lpInheritedEnvIterator += lstrlen(lpInheritedEnvIterator);
332 }
333 FreeEnvironmentStrings( (LPTCH)lpInheritedEnvironment );
334 }
335#endif
336 }
337
338#endif
339
340 struct MinimalEnvironmentConstructor
341 {
342 static StringArray* create(int dummy)
343 {
344 AutoPtr<StringArray> retval(new StringArray);
345 retval->push_back("IFS= \t\n");
346 retval->push_back("PATH=" _PATH_STDPATH);
347 char * tzstr = ::getenv("TZ");
348 if (tzstr)
349 {
350 retval->push_back(String("TZ=") + tzstr);
351 }
352 addPlatformSpecificEnvVars(*retval);
353 return retval.release();
354 }
355 };
356
357 LazyGlobal<StringArray, int, MinimalEnvironmentConstructor> g_minimalEnvironment = BLOCXX_LAZY_GLOBAL_INIT(0);
358} // end unnamed namespace
359
361 {
362 return g_minimalEnvironment;
363 }
364
365 void runAs(char const * username, EChildGroupAction extendedGroupAction)
366 {
367#ifdef BLOCXX_WIN32
368#pragma message(Reminder "TODO: implement it for Win!")
369#else
370 ABORT_IF(!username, "null user name");
371 ABORT_IF(*username == '\0', "empty user name");
372 ABORT_IF(::getuid() != 0 || ::geteuid() != 0, "non-root user calling runAs");
373 errno = 0;
374 struct passwd * pwent = ::getpwnam(username);
375 // return value from getpwnam is a static, so don't free it.
376 ABORT_ERRNO_IF(!pwent && errno != 0, Format("getpwnam(\"%1\") failed", username).c_str());
377 ABORT_IF(!pwent, Format("user name (%1) not found", username).c_str());
378 int rc = ::chdir("/");
379 ABORT_ERRNO_IF(rc != 0, "chdir failed");
380 Secure::dropPrivilegesPermanently(pwent->pw_uid, pwent->pw_gid, extendedGroupAction);
381#endif
382 }
383
384} // namespace Secure
385} // namespace BLOCXX_NAMESPACE
BLOCXX_IMPORT char ** environ
#define BLOCXX_DEFINE_EXCEPTION(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_LAZY_GLOBAL_INIT(...)
Statically initialize a LazyGlobal instance.
#define _PATH_STDPATH
Definition Paths.hpp:61
#define ABORT_IF(tst, msg)
Definition Secure.cpp:103
#define ABORT_ERRNO_IF(tst, msg)
Definition Secure.cpp:105
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
static String getLine(std::istream &istr)
Reads from in input stream until a newline is encountered.
Definition String.cpp:1505
String & trim()
Strip all leading and trailing space characters (as defined by the C function isspace()) from this St...
Definition String.cpp:790
StringArray minimalEnvironment()
Definition Secure.cpp:360
void dropPrivilegesPermanently(::uid_t newuid, ::gid_t newgid, EChildGroupAction extendedGroupAction)
Definition Secure.cpp:137
void runAs(char const *username, EChildGroupAction extendedGroupAction)
Look up user ID and group ID for username in password file, chdir to "/", then drop privileges and ru...
Definition Secure.cpp:365
Taken from RFC 1321.
Array< String > StringArray
Definition CommonFwd.hpp:73