Import FlightGear's fgValidatePath() as SGPath::validate()

This will allow us to perform access control validation in SimGear. The
current implementation of SGPath::validate() is 99% Rebecca Palmer's
work (see fgValidatePath() in FlightGear commit
6a30e7086ea2f1a060dd77dab6e7e8a15b43e82d); only the coding style has
been slightly modernized here since we can now use, for instance,
C++11's range-based for loop.
This commit is contained in:
Florent Rougon 2022-08-22 13:15:48 +02:00
parent 1ef4a7eb9e
commit e002a481f4
2 changed files with 94 additions and 2 deletions

View File

@ -53,6 +53,10 @@
using std::string;
using simgear::strutils::starts_with;
// For SGPath::validate()
static string_list read_allowed_paths;
static string_list write_allowed_paths;
/**
* define directory path separators
*/
@ -279,6 +283,62 @@ void SGPath::set_cached(bool cached)
_cached = false;
}
// ***************************************************************************
// * Access permissions for Nasal code *
// ***************************************************************************
// Static member function
void SGPath::clearListOfAllowedPaths(bool write)
{
string_list& allowed_paths(write ? write_allowed_paths : read_allowed_paths);
allowed_paths.clear();
}
// Static member function
void SGPath::addAllowedPathPattern(const string& pattern, bool write)
{
string_list& allowed_paths(write ? write_allowed_paths : read_allowed_paths);
allowed_paths.push_back(pattern);
}
// Static member function
const string_list& SGPath::getListOfAllowedPaths(bool write)
{
return write ? write_allowed_paths : read_allowed_paths;
}
SGPath SGPath::validate(bool write) const
{
// Normalize the path (prevents ../../.. or symlink trickery)
const string normed_path = realpath().utf8Str();
const string_list& allowed_paths{
write ? write_allowed_paths : read_allowed_paths};
string::size_type star_pos;
// Check against each allowed pattern
for (const auto& path: allowed_paths) {
star_pos = path.find('*');
if (star_pos == string::npos) {
if (!(path.compare(normed_path))) {
return fromUtf8(normed_path);
}
} else {
if ((path.size()-1 <= normed_path.size()) /* long enough to be a potential match */
&& !(path.substr(0, star_pos)
.compare(normed_path.substr(0, star_pos))) /* before-star parts match */
&& !(path.substr(star_pos+1, path.size()-star_pos-1)
.compare(normed_path.substr(star_pos+1+normed_path.size()-path.size(),
path.size()-star_pos-1))) /* after-star parts match */) {
return fromUtf8(normed_path);
}
}
}
// No match found
return SGPath();
}
// append another piece to the existing path
void SGPath::append( const string& p ) {
if ( path.empty() ) {
@ -993,7 +1053,7 @@ SGPath SGPath::realpath() const
char* buf = ::realpath(path.c_str(), NULL);
#endif
if (!buf) // File does not exist: return the realpath it would have if created now
// (needed for fgValidatePath security)
// (needed for SGPath::validate() security)
{
if (path.empty()) {
return simgear::Dir::current().path();

View File

@ -104,7 +104,39 @@ public:
* retrieved each time it is queried. Caching is enabled by default
*/
void set_cached(bool cached);
/**
* Clear a list of allowed paths patterns for access by Nasal and fgcommands.
* @param write True for write operations, false for read operations
*
* There are two lists of patterns: one for read operations and the other
* for write operations. The 'write' argument tells which list to act on.
* These lists are used by validate(), which determines whether access is
* allowed for a given path.
*/
static void clearListOfAllowedPaths(bool write);
/**
* Add a path pattern to the specified access control list.
* @param write True for write operations, false for read operations
*/
static void addAllowedPathPattern(const std::string& pattern, bool write);
/**
* Get a const reference to the specified access control list.
* @param write True for write operations, false for read operations
*/
static const string_list& getListOfAllowedPaths(bool write);
/**
* File access control, used by Nasal and fgcommands.
* @param write True for write operations, false for read operations
* @return The validated path on success, or empty if access is denied
*
* Warning: because this always (not just on Windows) treats both \ and /
* as path separators, and accepts relative paths (check-to-use race if
* the current directory changes), always use the returned path---not the
* original one.
*/
SGPath validate(bool write) const;
/**
* Append another piece to the existing path. Inserts a path
* separator between the existing component and the new component.