Nasal: remove obsolete io.nas security
This has been superseded by the C++-level fgValidatePath
This commit is contained in:
parent
ece79a7aed
commit
45c3eeb9ad
3 changed files with 0 additions and 217 deletions
|
@ -1,42 +0,0 @@
|
|||
# This file defines which file paths can be opened for reading and/or
|
||||
# writing using Nasal's io.open() and parsexml() functions, as well as
|
||||
# the fgcommands load, save, loadxml, and savexml. The evaluation code
|
||||
# can be found in $FG_ROOT/Nasal/io.nas.
|
||||
#
|
||||
# Empty lines and lines starting with '#' are ignored. All other entries
|
||||
# must be of the form
|
||||
#
|
||||
# {READ|WRITE} {ALLOW|DENY} <path-pattern>
|
||||
#
|
||||
# whereby fields must be separated by exactly one space. The pattern must
|
||||
# not be quoted and may contain spaces. If a pattern starts with $FG_ROOT/
|
||||
# or $FG_HOME/, then these parts are replaced by the respective system
|
||||
# paths. See $FG_ROOT/Nasal/string.nas for the pattern syntax.
|
||||
#
|
||||
# Entries are considered from top down. If no entry matches, then file
|
||||
# access is denied. A local rules file $FG_HOME/Nasal/IOrules using the
|
||||
# same syntax takes precedence over this file. The default rules that
|
||||
# apply if there's no Nasal/IOrules file at all, or there are no rules
|
||||
# defined in it are equivalent to this:
|
||||
#
|
||||
# READ DENY *
|
||||
# WRITE DENY *
|
||||
#
|
||||
# Read and write access to Nasal/IOrules files, however, is *always*
|
||||
# prohibited and not configurable!
|
||||
|
||||
READ ALLOW $FG_ROOT/*
|
||||
READ ALLOW $FG_HOME/*
|
||||
READ ALLOW $FG_AIRCRAFT/*
|
||||
|
||||
WRITE ALLOW /tmp/*.xml
|
||||
WRITE ALLOW $FG_HOME/*.sav
|
||||
WRITE ALLOW $FG_HOME/*.log
|
||||
WRITE ALLOW $FG_HOME/cache/*
|
||||
WRITE ALLOW $FG_HOME/Export/*
|
||||
WRITE ALLOW $FG_HOME/state/*.xml
|
||||
WRITE ALLOW $FG_HOME/aircraft-data/*.xml
|
||||
WRITE ALLOW $FG_HOME/Wildfire/*.xml
|
||||
WRITE ALLOW $FG_HOME/runtime-jetways/*.xml
|
||||
WRITE ALLOW $FG_HOME/Input/Joysticks/*.xml
|
||||
|
147
Nasal/io.nas
147
Nasal/io.nas
|
@ -277,150 +277,3 @@ var writexml = func(path, node, indent = "\t", prefix = "___") {
|
|||
die("writexml(): tree has more than one root node");
|
||||
}
|
||||
|
||||
|
||||
# Redefine io.open() such that files can only be opened under authorized directories.
|
||||
#
|
||||
_setlistener("/sim/signals/nasal-dir-initialized", func {
|
||||
# read IO rules
|
||||
var root = string.normpath(getprop("/sim/fg-root"));
|
||||
var home = string.normpath(getprop("/sim/fg-home"));
|
||||
var config = "Nasal/IOrules";
|
||||
|
||||
var rules_file = nil;
|
||||
var read_rules = [];
|
||||
var write_rules = [];
|
||||
|
||||
var load_rules = func(path) {
|
||||
if (stat(path) == nil)
|
||||
return nil;
|
||||
printlog("info", "using io.open() rules from ", path);
|
||||
read_rules = [];
|
||||
write_rules = [];
|
||||
var file = open(path, "r");
|
||||
for (var no = 1; (var line = readln(file)) != nil; no += 1) {
|
||||
if (!size(line) or line[0] == `#`)
|
||||
continue;
|
||||
|
||||
var f = split(" ", line);
|
||||
if (size(f) < 3 or f[0] != "READ" and f[0] != "WRITE" or f[1] != "DENY" and f[1] != "ALLOW") {
|
||||
printlog("alert", "ERROR: invalid io.open() rule in ", path, ", line ", no, ": ", line);
|
||||
read_rules = write_rules = [];
|
||||
break;
|
||||
}
|
||||
var pattern = f[2];
|
||||
foreach (var p; subvec(f, 3))
|
||||
pattern ~= " " ~ p;
|
||||
var rules = f[0] == "READ" ? read_rules : write_rules;
|
||||
var allow = (f[1] == "ALLOW");
|
||||
|
||||
if (substr(pattern, 0, 13) == "$FG_AIRCRAFT/") {
|
||||
var p = substr(pattern, 13);
|
||||
var sim = props.globals.getNode("/sim");
|
||||
foreach (var c; sim.getChildren("fg-aircraft")) {
|
||||
pattern = string.normpath(c.getValue()) ~ "/" ~ p;
|
||||
append(rules, [pattern, allow]);
|
||||
printlog("info", "IORules: appending ", pattern);
|
||||
}
|
||||
} elsif (substr(pattern, 0, 12) == "$FG_SCENERY/") {
|
||||
var p = substr(pattern, 12);
|
||||
var sim = props.globals.getNode("/sim");
|
||||
foreach (var c; sim.getChildren("fg-scenery")) {
|
||||
pattern = string.normpath(c.getValue()) ~ "/" ~ p;
|
||||
append(rules, [pattern, allow]);
|
||||
printlog("info", "IORules: appending ", pattern);
|
||||
}
|
||||
} else {
|
||||
if (substr(pattern, 0, 9) == "$FG_ROOT/")
|
||||
pattern = root ~ "/" ~ substr(pattern, 9);
|
||||
elsif (substr(pattern, 0, 9) == "$FG_HOME/")
|
||||
pattern = home ~ "/" ~ substr(pattern, 9);
|
||||
|
||||
append(rules, [pattern, allow]);
|
||||
printlog("info", "IORules: appending ", pattern);
|
||||
}
|
||||
}
|
||||
close(file);
|
||||
return path;
|
||||
}
|
||||
|
||||
# catch exceptions so that a die() doesn't ruin everything
|
||||
var rules_file = call(func load_rules(home ~ "/" ~ config)
|
||||
or load_rules(root ~ "/" ~ config), nil, var err = []);
|
||||
if (size(err)) {
|
||||
debug.printerror(err);
|
||||
read_rules = write_rules = [];
|
||||
}
|
||||
|
||||
read_rules = [["*/" ~ config, 0]] ~ read_rules;
|
||||
write_rules = [["*/" ~ config, 0]] ~ write_rules;
|
||||
if (__.log_level <= 3) {
|
||||
print("IOrules/READ: ", debug.string(read_rules));
|
||||
print("IOrules/WRITE: ", debug.string(write_rules));
|
||||
}
|
||||
|
||||
# make safe, local copies
|
||||
var setValue = props._setValue;
|
||||
var getValue = props._getValue;
|
||||
var normpath = string.normpath;
|
||||
var match = string.match;
|
||||
var caller = caller;
|
||||
var die = die;
|
||||
|
||||
# validators
|
||||
var valid = func(path, rules) {
|
||||
var fpath = normpath(path);
|
||||
foreach (var d; rules)
|
||||
if (match(fpath, d[0]))
|
||||
return d[1] ? fpath : nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var read_validator = func(n) setValue(n, [valid(getValue(n, []), read_rules) or ""]);
|
||||
var write_validator = func(n) setValue(n, [valid(getValue(n, []), write_rules) or ""]);
|
||||
|
||||
# validation listeners for load[xml]/save[xml]/parsexml() (see utils.cxx:fgValidatePath)
|
||||
var n = props.globals.getNode("/sim/paths/validate", 1).removeAllChildren();
|
||||
var rval = _setlistener(n.getNode("read", 1)._g, read_validator);
|
||||
var wval = _setlistener(n.getNode("write", 1)._g, write_validator);
|
||||
|
||||
# wrap removelistener
|
||||
globals.removelistener = var remove_listener = (func {
|
||||
var _removelistener = globals.removelistener;
|
||||
func(n) {
|
||||
if (n != rval and n != wval)
|
||||
return _removelistener(n);
|
||||
|
||||
die("removelistener(): removal of protected listener #'" ~ n ~ "' denied (unauthorized access)\n ");
|
||||
}
|
||||
})();
|
||||
|
||||
# wrap io.open()
|
||||
io.open = var io_open = (func {
|
||||
var _open = io.open;
|
||||
func(path, mode = "rb") {
|
||||
var rules = write_rules;
|
||||
if (mode == "r" or mode == "rb" or mode == "br")
|
||||
rules = read_rules;
|
||||
|
||||
if (var vpath = valid(path, rules))
|
||||
return _open(vpath, mode);
|
||||
|
||||
die("io.open(): opening file '" ~ path ~ "' denied (unauthorized access)\n ");
|
||||
}
|
||||
})();
|
||||
|
||||
# wrap closure() to prevent tampering with security related functions
|
||||
var thislistener = caller(0)[1];
|
||||
globals.closure = (func {
|
||||
var _closure = globals.closure;
|
||||
func(fn, level = 0) {
|
||||
var thisfunction = caller(0)[1];
|
||||
if (fn != thislistener and fn != io_open and fn != thisfunction
|
||||
and fn != read_validator and fn != write_validator
|
||||
and fn != remove_listener)
|
||||
return _closure(fn, level);
|
||||
|
||||
die("closure(): query denied (unauthorized access)\n ");
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
|
|
@ -76,32 +76,6 @@ var icmp = func(a, b) cmp(lc(a), lc(b));
|
|||
var imatch = func(a, b) match(lc(a), lc(b));
|
||||
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Functions that are used in the IO security code (io.nas) are defined in a
|
||||
# closure that holds safe copies of system functions. Later manipulation of
|
||||
# append(), pop() etc. doesn't affect them. Of course, any security code
|
||||
# must itself store safe copies of these tamper-proof functions before user
|
||||
# code can redefine them, and the closure() command must be made inaccessible.
|
||||
##
|
||||
|
||||
var match = nil;
|
||||
var normpath = nil;
|
||||
var join = nil;
|
||||
var replace = nil;
|
||||
|
||||
(func {
|
||||
var append = append;
|
||||
var caller = caller;
|
||||
var pop = pop;
|
||||
var setsize = setsize;
|
||||
var size = size;
|
||||
var split = split;
|
||||
var substr = substr;
|
||||
var subvec = subvec;
|
||||
|
||||
|
||||
##
|
||||
# check if string <str> matches shell style pattern <patt>
|
||||
#
|
||||
|
@ -238,8 +212,6 @@ replace = func(str, old, new) {
|
|||
return join(new, split(old, str));
|
||||
}
|
||||
|
||||
})(); # end tamper-proof environment
|
||||
|
||||
|
||||
##
|
||||
# Get a function out of a string template for fast insertion of template
|
||||
|
|
Loading…
Reference in a new issue