38#include "blocxx/BLOCXX_config.h"
46#ifdef BLOCXX_HAVE_UNISTD_H
60 unsigned const MAX_SYMBOLIC_LINKS = 100;
68 class PartiallyResolvedPath
73 PartiallyResolvedPath(
char const * base_dir);
78 void multi_push_unresolved(
char const * path);
82 void pop_unresolved();
85 bool unresolved_empty()
const;
88 bool unresolved_starts_with_curdir()
const;
91 bool unresolved_starts_with_parent()
const;
95 bool dir_specified()
const;
100 void xfer_component();
107 void reset_resolved();
110 String get_resolved()
const;
113 void lstat_resolved(
struct stat & st)
const;
119 void read_symlink(std::vector<char> & path);
127 mutable std::vector<char> m_resolved;
131 std::vector<char> m_unresolved;
133 bool m_dir_specified;
136 PartiallyResolvedPath::PartiallyResolvedPath(
char const * base_dir)
137 : m_resolved(base_dir, base_dir + std::strlen(base_dir)),
139 m_dir_specified(
false)
143 void PartiallyResolvedPath::multi_push_unresolved(
char const * path)
150 char const * end = path;
155 if (end != path && *(end - 1) ==
'/')
157 m_dir_specified =
true;
159 m_unresolved.push_back(
'/');
160 bool last_separator =
true;
164 bool separator = (c ==
'/');
165 if (!(separator && last_separator))
167 m_unresolved.push_back(c);
169 last_separator = separator;
173 void PartiallyResolvedPath::pop_unresolved()
176 while (!m_unresolved.empty() && m_unresolved.back() !=
'/')
178 m_unresolved.pop_back();
180 while (!m_unresolved.empty() && m_unresolved.back() ==
'/')
182 m_unresolved.pop_back();
186 inline bool PartiallyResolvedPath::unresolved_empty()
const
188 return m_unresolved.empty();
191 bool PartiallyResolvedPath::unresolved_starts_with_curdir()
const
193 std::size_t n = m_unresolved.size();
195 n > 0 && m_unresolved[n - 1] ==
'.' &&
196 (n == 1 || m_unresolved[n - 2] ==
'/')
200 bool PartiallyResolvedPath::unresolved_starts_with_parent()
const
202 std::size_t n = m_unresolved.size();
204 n >= 2 && m_unresolved[n - 1] ==
'.' && m_unresolved[n - 2] ==
'.'
205 && (n == 2 || m_unresolved[n - 3] ==
'/')
209 inline bool PartiallyResolvedPath::dir_specified()
const
211 return m_dir_specified;
214 void PartiallyResolvedPath::xfer_component()
217 std::size_t n = m_resolved.size();
218 BLOCXX_ASSERT(n > 0 && (n == 1 || m_resolved[n - 1] !=
'/'));
221 m_resolved.push_back(
'/');
224 while (!m_unresolved.empty() && (c = m_unresolved.back()) !=
'/')
226 m_unresolved.pop_back();
227 m_resolved.push_back(c);
229 while (!m_unresolved.empty() && m_unresolved.back() ==
'/')
231 m_unresolved.pop_back();
235 void PartiallyResolvedPath::pop_resolved()
237 std::size_t n = m_resolved.size();
244 while (m_resolved.back() !=
'/')
246 m_resolved.pop_back();
249 if (m_resolved.size() > 1)
251 m_resolved.pop_back();
255 inline void PartiallyResolvedPath::reset_resolved()
257 std::vector<char>(1,
'/').swap(m_resolved);
262 std::vector<char> & m_buf;
264 NullTerminate(std::vector<char> & buf)
267 m_buf.push_back(
'\0');
276 inline String PartiallyResolvedPath::get_resolved()
const
278 NullTerminate x(m_resolved);
279 return String(&m_resolved[0]);
282 void wrapped_lstat(
char const * path,
struct stat & st)
286 if(path[1] ==
':' && path[2] == 0)
292 if (
LSTAT(path, &st) < 0)
299 void PartiallyResolvedPath::lstat_resolved(
struct stat & st)
const
301 NullTerminate x(m_resolved);
302 wrapped_lstat(&m_resolved[0], st);
305 void PartiallyResolvedPath::read_symlink(std::vector<char> & path)
308 NullTerminate x(m_resolved);
312 char const * symlink_path = &m_resolved[0];
313 int rv =
READLINK(symlink_path, &buf[0], buf.size());
322 else if (
static_cast<unsigned>(rv) == buf.size())
324 buf.resize(2 * buf.size());
334 char const * strip_leading_slashes(
char const * path)
343 void logFileStatus(
path_results_t const & results,
const uid_t uid)
345 Logger logger(
"blocxx.PathSecurity");
354 bool successful(
false);
358 userName =
"the proper user";
360#if defined(BLOCXX_HPUX) || defined(BLOCXX_AIX)
370 BLOCXX_LOG_ERROR(logger,
Format(
"%1 was insecure. It was not owned by %2%3.", li->first, userName, hpux));
377 " world (or non-root group) writable or did not have the sticky bit set on the directory.", li->first));
386 std::pair<ESecurity, String>
387 path_security(
char const * base_dir,
char const * rel_path, uid_t uid,
bool bdsecure)
391 base_dir[1] ==
'\0' || base_dir[std::strlen(base_dir) - 1] !=
'/');
397 PartiallyResolvedPath prp(base_dir);
399 PartiallyResolvedPath prp(
"");
400 if (rel_path[1] !=
':' && base_dir[1] ==
':')
406 unsigned num_symbolic_links = 0;
410 prp.multi_push_unresolved(rel_path);
412 if( prp.unresolved_empty() )
414 prp.lstat_resolved(st);
417 while (!prp.unresolved_empty())
419 if (prp.unresolved_starts_with_curdir())
421 prp.pop_unresolved();
423 else if (prp.unresolved_starts_with_parent())
425 prp.pop_unresolved();
430 prp.xfer_component();
431 prp.lstat_resolved(st);
432 file_status =
getFileStatus(st, uid, prp.unresolved_empty(), prp.get_resolved());
436 results.
push_back(std::make_pair(prp.get_resolved(), file_status));
439 if (S_ISREG(st.st_mode))
442 if (!prp.unresolved_empty() || prp.dir_specified())
448 else if (S_ISLNK(st.st_mode))
450 if (++num_symbolic_links > MAX_SYMBOLIC_LINKS)
455 std::vector<char> slpath_vec;
456 prp.read_symlink(slpath_vec);
457 char const * slpath = &slpath_vec[0];
458 if (slpath[0] ==
'/')
460 prp.reset_resolved();
461 slpath = strip_leading_slashes(slpath);
467 prp.multi_push_unresolved(slpath);
469 else if (!S_ISDIR(st.st_mode))
471 String msg = prp.get_resolved() +
472 " is not a directory, symbolic link, nor regular file";
479 logFileStatus(results, uid);
480 return std::make_pair(sec, prp.get_resolved());
483 std::pair<ESecurity, String> path_security(
char const * path,
UserId uid)
488 Format(
"%1 is not an absolute path", path).c_str());
490 char const * relpath = strip_leading_slashes(path);
493 char sRootPath[] =
"/";
494 wrapped_lstat(
"/", st);
496 char sRootPath[MAX_PATH];
497 if (::GetWindowsDirectory(sRootPath, MAX_PATH) < 3 )
499 wrapped_lstat(
"/", st);
504 wrapped_lstat(sRootPath, st);
513 logFileStatus(results, uid);
516 return path_security(sRootPath, relpath, uid, (file_status ==
E_FILE_OK));
521std::pair<ESecurity, String>
525 return path_security(abspath.
c_str(), uid);
528std::pair<ESecurity, String>
532 return path_security(base_dir.
c_str(), rel_path.
c_str(), uid,
true);
535std::pair<ESecurity, String>
541std::pair<ESecurity, String>
545 return security(base_dir, rel_path, ::geteuid());
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define BLOCXX_THROW_ERRNO_MSG1(exType, msg, errnum)
Throw an exception using FILE, LINE, errnum and strerror(errnum)
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
#define READLINK(path, buf, size)
#define BLOCXX_FILENAME_SEPARATOR
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability.
void push_back(const T &x)
Append an element to the end of the Array.
V::const_iterator const_iterator
This String class is an abstract data type that represents as NULL terminated string of characters.
const char * c_str() const
BLOCXX_COMMON_API std::pair< ESecurity, String > security(String const &path, UserId uid)
BLOCXX_COMMON_API String getCurrentWorkingDirectory()
Get the process's current working directory.
String getUserName(uid_t uid, bool &ok)
If the username is invalid, or if getUserName() fails for any other reason, 'success' will be set to ...
EFileStatusReturn getFileStatus(struct stat const &x, uid_t uid, bool is_full_path, const String &path)
GetFileStatus() - just to unify the call of file_ok() for Win and xNix.
BLOCXX_COMMON_API bool isPathAbsolute(String const &path)
Array< std::pair< String, EFileStatusReturn > > path_results_t