diff --git a/Nasal/IOrules b/Nasal/IOrules new file mode 100644 index 000000000..634b8213b --- /dev/null +++ b/Nasal/IOrules @@ -0,0 +1,31 @@ +# Permissions for Nasal's io.open() +# +# This file defines which file paths can be opened for reading and/or +# writing using Nasal's io.open() command. The respective 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 when there's no Nasal/IOrules file at all 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/* + +WRITE ALLOW $FG_HOME/Export/* diff --git a/Nasal/io.nas b/Nasal/io.nas index 5c97aca49..965c4004e 100644 --- a/Nasal/io.nas +++ b/Nasal/io.nas @@ -148,16 +148,50 @@ _setlistener("/sim/signals/nasal-dir-initialized", func { var _open = open; var root = string.fixpath(getprop("/sim/fg-root")); var home = string.fixpath(getprop("/sim/fg-home")); + var config = "Nasal/IOrules"; - var read_rules = [ # [pattern, allow(1)/deny(0)] - [root ~ "/*", 1], - [home ~ "/*", 1], - ]; + var read_rules = []; + var write_rules = []; - var write_rules = [ - [home ~ "/Scenery/*.stg", 1], - [home ~ "/Export/*", 1], - ]; + var load_rules = func(path) { + if(stat(path) == nil) + return 0; + printlog("info", "using io.open() rules from ", path); + read_rules = []; + write_rules = []; + var file = open(path, "r"); + var no = 0; + while ((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") and (f[1] != "DENY" and f[1] != "ALLOW")) { + printlog("alert", "ERROR: invalid io.open() rule in ", path, ", line ", no, ": ", line); + read_rules = write_rules = []; + break; # don't use die() or return, as io.open() has yet to be redefined + } + var pattern = f[2]; + foreach (var p; subvec(f, 3)) + pattern ~= " " ~ p; + 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(f[0] == "READ" ? read_rules : write_rules, [pattern, f[1] == "ALLOW"]); + } + close(file); + return 1; + } + + load_rules(home ~ "/" ~ config) or load_rules(root ~ "/" ~ config); + read_rules = [["*/" ~ config, 0]] ~ read_rules; + write_rules = [["*/" ~ config, 0]] ~ write_rules; + if(getprop("/sim/logging/priority") == "info") { + print("READ: ", debug.string(read_rules)); + print("WRITE: ", debug.string(write_rules)); + } open = func(path, mode = "rb") { var rules = write_rules;