38 #include "blocxx/BLOCXX_config.h" 46 #ifdef BLOCXX_HAVE_UNISTD_H 58 using namespace FileSystem::Path;
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);
136 PartiallyResolvedPath::PartiallyResolvedPath(
char const * base_dir)
143 void PartiallyResolvedPath::multi_push_unresolved(
char const * path)
150 char const * end = path;
155 if (end != path && *(end - 1) ==
'/')
160 bool last_separator =
true;
164 bool separator = (c ==
'/');
165 if (!(separator && last_separator))
169 last_separator = separator;
173 void PartiallyResolvedPath::pop_unresolved()
186 inline bool PartiallyResolvedPath::unresolved_empty()
const 191 bool PartiallyResolvedPath::unresolved_starts_with_curdir()
const 200 bool PartiallyResolvedPath::unresolved_starts_with_parent()
const 209 inline bool PartiallyResolvedPath::dir_specified()
const 214 void PartiallyResolvedPath::xfer_component()
235 void PartiallyResolvedPath::pop_resolved()
255 inline void PartiallyResolvedPath::reset_resolved()
264 NullTerminate(std::vector<char> & buf)
267 m_buf.push_back(
'\0');
276 inline String PartiallyResolvedPath::get_resolved()
const 282 void wrapped_lstat(
char const * path,
struct stat & st)
285 String tmp_path(path);
286 if(path[1] ==
':' && path[2] == 0)
290 if (
LSTAT(tmp_path.c_str(), &st) < 0)
292 if (
LSTAT(path, &st) < 0)
299 void PartiallyResolvedPath::lstat_resolved(
struct stat & st)
const 305 void PartiallyResolvedPath::read_symlink(std::vector<char> & path)
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");
346 for (path_results_t::const_iterator li = results.begin(); li != results.end(); ++li)
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));
376 BLOCXX_LOG_ERROR(logger, Format(
"%1 was owned by the proper user, but was not a symlink and was either" 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())
445 FileSystemException, prp.get_resolved(), ENOTDIR);
448 else if (S_ISLNK(st.st_mode))
450 if (++num_symbolic_links > MAX_SYMBOLIC_LINKS)
453 FileSystemException, prp.get_resolved(), ELOOP);
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);
512 results.push_back(std::make_pair(String(path), file_status));
513 logFileStatus(results, uid);
516 return path_security(sRootPath, relpath, uid, (file_status ==
E_FILE_OK));
521 std::pair<ESecurity, String>
525 return path_security(abspath.
c_str(), uid);
528 std::pair<ESecurity, String>
532 return path_security(base_dir.
c_str(), rel_path.
c_str(), uid,
true);
535 std::pair<ESecurity, String>
541 std::pair<ESecurity, String>
545 return security(base_dir, rel_path, ::geteuid());
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability.
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
This String class is an abstract data type that represents as NULL terminated string of characters.
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.
const char * c_str() const
std::vector< char > m_resolved
BLOCXX_COMMON_API bool isPathAbsolute(String const &path)
#define BLOCXX_THROW_ERRNO_MSG1(exType, msg, errnum)
Throw an exception using FILE, LINE, errnum and strerror(errnum)
std::vector< char > m_unresolved
#define BLOCXX_FILENAME_SEPARATOR
class BLOCXX_COMMON_API Logger
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
Array< std::pair< String, EFileStatusReturn > > path_results_t
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 ...
std::vector< char > & m_buf
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.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define READLINK(path, buf, size)