commit a7c0c2b72ac2f2f5ce64624512c3758ec3422a02 Author: Florent Rougon Date: Sat Aug 26 17:49:30 2017 +0200 Call fgInitAllowedPaths() earlier + fix for CVE-2017-13709 This is, for convenience, the combination of two commits: https://sourceforge.net/p/flightgear/flightgear/ci/c7a2aef59979af3e9ff22daabb37bdaadb91cd75/ and https://sourceforge.net/p/flightgear/flightgear/ci/2a5e3d06b2c0d9f831063afe7e7260bca456d679/ Indeed, the second commit wouldn't work correctly without the first one: fgInitAllowedPaths() must be called before FGLogger::init() gets to run in order for fgValidatePath() to be usable there for the security check. diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 8205d3f0e..cccb61c62 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -1145,6 +1145,11 @@ void fgStartNewReset() flightgear::Options::sharedInstance()->processOptions(); + // Rebuild the lists of allowed paths for cases where a path comes from an + // untrusted source, such as the global property tree (this uses $FG_HOME + // and other paths set by Options::processOptions()). + fgInitAllowedPaths(); + const auto& resMgr = simgear::EmbeddedResourceManager::instance(); // The language was (re)set in processOptions() const string locale = globals->get_locale()->getPreferredLanguage(); diff --git a/src/Main/logger.cxx b/src/Main/logger.cxx index 6c18162c3..00b6833ca 100644 --- a/src/Main/logger.cxx +++ b/src/Main/logger.cxx @@ -9,12 +9,17 @@ #include "logger.hxx" -#include +#include #include +#include #include +#include +#include #include "fg_props.hxx" +#include "globals.hxx" +#include "util.hxx" using std::string; using std::endl; @@ -59,6 +64,25 @@ FGLogger::init () child->setStringValue("filename", filename.c_str()); } + // Security: the path comes from the global Property Tree; it *must* be + // validated before we overwrite the file. + const SGPath authorizedPath = fgValidatePath(SGPath::fromUtf8(filename), + /* write */ true); + + if (authorizedPath.isNull()) { + const string propertyPath = child->getChild("filename") + ->getPath(/* simplify */ true); + const string msg = + "The FGLogger logging system, via the '" + propertyPath + "' property, " + "was asked to write to '" + filename + "', however this path is not " + "authorized for writing anymore for security reasons. " + + "Please choose another location, for instance in the $FG_HOME/Export " + "folder (" + (globals->get_fg_home() / "Export").utf8Str() + ")."; + + SG_LOG(SG_GENERAL, SG_ALERT, msg); + exit(EXIT_FAILURE); + } + string delimiter = child->getStringValue("delimiter"); if (delimiter.empty()) { delimiter = ","; @@ -68,7 +92,8 @@ FGLogger::init () log.interval_ms = child->getLongValue("interval-ms"); log.last_time_ms = globals->get_sim_time_sec() * 1000; log.delimiter = delimiter.c_str()[0]; - log.output = new std::ofstream(filename.c_str()); + // Security: use the return value of fgValidatePath() + log.output = new sg_ofstream(authorizedPath, std::ios_base::out); if (!log.output) { SG_LOG(SG_GENERAL, SG_ALERT, "Cannot write log to " << filename); continue; diff --git a/src/Main/logger.hxx b/src/Main/logger.hxx index 3d2146a83..de6209756 100644 --- a/src/Main/logger.hxx +++ b/src/Main/logger.hxx @@ -6,10 +6,10 @@ #ifndef __LOGGER_HXX #define __LOGGER_HXX 1 -#include #include #include +#include #include #include @@ -39,7 +39,7 @@ private: Log (); virtual ~Log (); std::vector nodes; - std::ostream * output; + sg_ofstream * output; long interval_ms; double last_time_ms; char delimiter; diff --git a/src/Main/main.cxx b/src/Main/main.cxx index c72db1c3a..3264d8d8f 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -541,6 +541,11 @@ int fgMainInit( int argc, char **argv ) return EXIT_SUCCESS; } + // Set the lists of allowed paths for cases where a path comes from an + // untrusted source, such as the global property tree (this uses $FG_HOME + // and other paths set by Options::processOptions()). + fgInitAllowedPaths(); + const auto& resMgr = simgear::EmbeddedResourceManager::createInstance(); initFlightGearEmbeddedResources(); // The language was set in processOptions() diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 89a1e0f96..9e37a2e93 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -911,10 +911,6 @@ void FGNasalSys::init() .member("simulatedTime", &TimerObj::isSimTime, &f_timerObj_setSimTime) .member("isRunning", &TimerObj::isRunning); - - // Set allowed paths for Nasal I/O - fgInitAllowedPaths(); - // Now load the various source files in the Nasal directory simgear::Dir nasalDir(SGPath(globals->get_fg_root(), "Nasal")); loadScriptDirectory(nasalDir);