1
0
Fork 0

Nasal security: add --allow-nasal-read, warn on non-Nasal-readable

Terrasync, prepare for allowing --download-dir

Add unmangled_fg_scenery
This commit is contained in:
Rebecca N. Palmer 2016-10-09 15:21:09 +01:00
parent c4f584dadf
commit 31cb65e8d9
6 changed files with 76 additions and 24 deletions

View file

@ -1152,7 +1152,7 @@ do_load_xml_to_proptree(const SGPropertyNode * arg)
SGPath validated_path = fgValidatePath(file, false);
if (validated_path.isNull()) {
SG_LOG(SG_IO, SG_ALERT, "loadxml: reading '" << file << "' denied "
"(unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, add them to --fg-aircraft)");
"(unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, pass them to --allow-nasal-read)");
return false;
}

View file

@ -373,8 +373,9 @@ void FGGlobals::append_fg_scenery (const SGPath &path, bool secure)
// out, such that all three dirs are added. Unfortunately there's
// no information as to why the change was made.
fg_scenery.push_back(abspath);
unmangled_fg_scenery.push_back(abspath);
if (secure) {
secure_fg_scenery.push_back(abspath);
extra_read_allowed_paths.push_back(abspath);
}
if (terrainDir.exists()) {
@ -399,10 +400,20 @@ void FGGlobals::append_fg_scenery (const SGPath &path, bool secure)
n->setAttribute(SGPropertyNode::PRESERVE, true);
}
void FGGlobals::append_read_allowed_paths(const SGPath &path)
{
SGPath abspath(path.realpath());
if (!abspath.exists()) {
SG_LOG(SG_GENERAL, SG_WARN, "read-allowed path not found:" << abspath);
return;
}
extra_read_allowed_paths.push_back(abspath);
}
void FGGlobals::clear_fg_scenery()
{
fg_scenery.clear();
secure_fg_scenery.clear();
unmangled_fg_scenery.clear();
fgGetNode("/sim", true)->removeChildren("fg-scenery");
}

View file

@ -107,7 +107,9 @@ private:
// Roots of FlightGear scenery tree
PathList fg_scenery;
PathList secure_fg_scenery;
PathList unmangled_fg_scenery;
// Paths Nasal is allowed to read
PathList extra_read_allowed_paths;
std::string browser;
@ -218,7 +220,8 @@ public:
void set_fg_home (const SGPath &home);
const PathList &get_fg_scenery () const { return fg_scenery; }
const PathList &get_secure_fg_scenery () const { return secure_fg_scenery; }
const PathList &get_unmangled_fg_scenery () const { return unmangled_fg_scenery; }
const PathList &get_extra_read_allowed_paths () const { return extra_read_allowed_paths; }
/**
* Add a scenery directory
*
@ -232,6 +235,16 @@ public:
void append_fg_scenery (const PathList &scenery, bool secure = false);
void clear_fg_scenery();
/**
* Allow Nasal to read a path
*
* To avoid can-read-any-file security holes, do NOT call this on paths
* obtained from the property tree or other Nasal-writable places
*
* Only works during initial load (before fgInitAllowedPaths)
*/
void append_read_allowed_paths (const SGPath &path);
/**
* specify a path we'll prepend to the aircraft paths list if non-empty.

View file

@ -865,6 +865,28 @@ fgOptTerrasyncDir( const char *arg )
return FG_OPTIONS_OK;
}
static int
fgOptDownloadDir( const char *arg )
{
SGPath p = SGPath::fromLocal8Bit(arg);
//globals->append_read_allowed_paths(p);
fgSetString("/sim/paths/download-dir", p.utf8Str());
return FG_OPTIONS_OK;
}
static int
fgOptAllowNasalRead( const char *arg )
{
PathList paths = SGPath::pathsFromLocal8Bit(arg);
if(paths.size() == 0) {
SG_LOG(SG_GENERAL, SG_WARN, "--allow-nasal-read requires a list of directories to allow");
}
for( PathList::const_iterator it = paths.begin(); it != paths.end(); ++it ) {
globals->append_read_allowed_paths(*it);
}
return FG_OPTIONS_OK;
}
static int
fgOptFov( const char *arg )
{
@ -1629,7 +1651,8 @@ struct OptionDesc {
{"disable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", false, "", 0 },
{"enable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", true, "", 0 },
{"terrasync-dir", true, OPTION_FUNC, "", false, "", fgOptTerrasyncDir },
{"download-dir", true, OPTION_STRING, "/sim/paths/download-dir", false, "", 0 },
{"download-dir", true, OPTION_FUNC, "", false, "", fgOptDownloadDir },
{"allow-nasal-read", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptAllowNasalRead },
{"geometry", true, OPTION_FUNC, "", false, "", fgOptGeometry },
{"bpp", true, OPTION_FUNC, "", false, "", fgOptBpp },
{"units-feet", false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 },

View file

@ -92,36 +92,31 @@ void fgInitAllowedPaths()
}
read_allowed_paths.clear();
write_allowed_paths.clear();
std::string fg_root = globals->get_fg_root().realpath().utf8Str();
std::string fg_home = globals->get_fg_home().realpath().utf8Str();
read_allowed_paths.push_back(fg_root + "/*");
read_allowed_paths.push_back(fg_home + "/*");
read_allowed_paths.push_back(fg_root);
read_allowed_paths.push_back(fg_home);
const PathList& aircraft_paths = globals->get_aircraft_paths();
const PathList& scenery_paths = globals->get_secure_fg_scenery();
const PathList& other_read_paths = globals->get_extra_read_allowed_paths();
// not plain fg_scenery, to avoid making
// /sim/terrasync/scenery-dir a security hole
PathList read_paths = aircraft_paths;
read_paths.insert(read_paths.end(), scenery_paths.begin(), scenery_paths.end());
read_paths.insert(read_paths.end(), other_read_paths.begin(), other_read_paths.end());
read_paths.push_back(globals->get_fg_root());
read_paths.push_back(globals->get_fg_home());
for( PathList::const_iterator it = read_paths.begin(); it != read_paths.end(); ++it )
for( PathList::const_iterator it = read_paths.begin(); it != read_paths.end(); ++it )
{
// if we get the initialization order wrong, better to have an
// obvious error than a can-read-everything security hole...
if (it->isNull() || fg_root.empty() || fg_home.empty()) {
if (it->isNull()) {
flightgear::fatalMessageBox("Nasal initialization error",
"Empty string in FG_ROOT, FG_HOME, FG_AIRCRAFT or FG_SCENERY",
"Empty string in FG_ROOT, FG_HOME, FG_AIRCRAFT, FG_SCENERY or --allow-nasal-read",
"or fgInitAllowedPaths() called too early");
exit(-1);
}
read_allowed_paths.push_back(it->realpath().utf8Str() + "/*");
read_allowed_paths.push_back(it->realpath().utf8Str());
}
std::string fg_home = globals->get_fg_home().realpath().utf8Str();
write_allowed_paths.push_back(fg_home + "/*.sav");
write_allowed_paths.push_back(fg_home + "/*.log");
write_allowed_paths.push_back(fg_home + "/cache/*");
@ -142,10 +137,20 @@ void fgInitAllowedPaths()
fgValidatePath(homePath + "/aircraft-data/yes..xml",true).isNull() ||
fgValidatePath(homePath + "/.\\yes.bmp",false).isNull()) {
flightgear::fatalMessageBox("Nasal initialization error",
"The FG_HOME directory must not be inside any of the FG_ROOT, FG_AIRCRAFT or FG_SCENERY directories",
"The FG_HOME directory must not be inside any of the FG_ROOT, FG_AIRCRAFT, FG_SCENERY or --allow-nasal-read directories",
"(check that you have not accidentally included an extra :, as an empty part means the current directory)");
exit(-1);
}
// Warn the user if they have an unreadable Terrasync directory
// (can't securely make it readable because Nasal can change /sim/terrasync/scenery-dir)
if(fgValidatePath(SGPath::fromUtf8(fgGetString("/sim/terrasync/scenery-dir")),false).isNull()) {
SG_LOG(SG_GENERAL, SG_WARN, "You have a non-standard Terrasync directory "
<< "set only by /sim/terrasync/scenery-dir. For security reasons, "
<< "Nasal scripts are not allowed to read such directories, which "
<< "may break some features (e.g. animated jetways). To fix this, "
<< "use the launcher's settings, or --terrasync-dir in .fgfsrc");
}
}
/**

View file

@ -582,7 +582,7 @@ static naRef f_directory(naContext c, naRef me, int argc, naRef* args)
SG_LOG(SG_NASAL, SG_ALERT, "directory(): listing '" <<
naStr_data(args[0]) << "' denied (unauthorized directory - authorization"
" no longer follows symlinks; to authorize reading additional "
"directories, add them to --fg-aircraft)");
"directories, pass them to --allow-nasal-read)");
// to avoid breaking dialogs, pretend it doesn't exist rather than erroring out
return naNil();
}
@ -689,7 +689,7 @@ static naRef f_open(naContext c, naRef me, int argc, naRef* args)
SG_LOG(SG_NASAL, SG_ALERT, "open(): reading/writing '" <<
naStr_data(file) << "' denied (unauthorized directory - authorization"
" no longer follows symlinks; to authorize reading additional "
"directories, add them to --fg-aircraft)");
"directories, pass them to --allow-nasal-read)");
naRuntimeError(c, "open(): access denied (unauthorized directory)");
return naNil();
}
@ -731,7 +731,7 @@ static naRef f_parsexml(naContext c, naRef me, int argc, naRef* args)
SG_LOG(SG_NASAL, SG_ALERT, "parsexml(): reading '" <<
naStr_data(args[0]) << "' denied (unauthorized directory - authorization"
" no longer follows symlinks; to authorize reading additional "
"directories, add them to --fg-aircraft)");
"directories, pass them to --allow-nasal-read)");
naRuntimeError(c, "parsexml(): access denied (unauthorized directory)");
return naNil();
}