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");
|
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));
|
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>
|
# check if string <str> matches shell style pattern <patt>
|
||||||
#
|
#
|
||||||
|
@ -238,8 +212,6 @@ replace = func(str, old, new) {
|
||||||
return join(new, split(old, str));
|
return join(new, split(old, str));
|
||||||
}
|
}
|
||||||
|
|
||||||
})(); # end tamper-proof environment
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get a function out of a string template for fast insertion of template
|
# Get a function out of a string template for fast insertion of template
|
||||||
|
|
Loading…
Reference in a new issue