diff options
-rw-r--r-- | include/mapping.h | 3 | ||||
-rw-r--r-- | include/sb2.h | 4 | ||||
-rw-r--r-- | lua_scripts/argvenvp.lua | 145 | ||||
-rw-r--r-- | lua_scripts/main.lua | 2 | ||||
-rw-r--r-- | luaif/argvenvp.c | 129 | ||||
-rw-r--r-- | preload/sb_exec.c | 35 |
6 files changed, 275 insertions, 43 deletions
diff --git a/include/mapping.h b/include/mapping.h index 89fa113..ee8a883 100644 --- a/include/mapping.h +++ b/include/mapping.h @@ -25,6 +25,9 @@ char *scratchbox_path_for_exec(const char *func_name, const char *path, extern int sb_execve_preprocess(char **file, char ***argv, char ***envp); extern char *emumode_map(const char *path); extern void sb_push_string_to_lua_stack(char *str); +extern char *sb_execve_map_script_interpreter(const char *interpreter, + const char *interp_arg, const char *mapped_script_filename, + const char *orig_script_filename, char ***argv, char ***envp); extern int sb_execve_postprocess(char *exec_type, char **mapped_file, char **filename, const char *binary_name, char ***argv, char ***envp); diff --git a/include/sb2.h b/include/sb2.h index 8ea463d..d9808ac 100644 --- a/include/sb2.h +++ b/include/sb2.h @@ -44,10 +44,12 @@ struct lua_instance { * - Added special handler for /proc => sb.procfs_mapping_request() was * added to luaif.c (and mapping.lua needs it) * - sbox_get_mapping_requirements() now returns four values + * * Differences between "61" and "60,2008-12-07" + * - added execve_map_script_interpreter() * * NOTE: the corresponding identifier for Lua is in lua_scripts/main.lua */ -#define SB2_LUA_C_INTERFACE_VERSION "60,2008-12-07" +#define SB2_LUA_C_INTERFACE_VERSION "61" extern struct lua_instance *get_lua(void); extern void release_lua(struct lua_instance *ptr); diff --git a/lua_scripts/argvenvp.lua b/lua_scripts/argvenvp.lua index e647e78..ac65000 100644 --- a/lua_scripts/argvenvp.lua +++ b/lua_scripts/argvenvp.lua @@ -205,6 +205,115 @@ function setenv_native_app_ld_library_path(exec_policy, envp) end -- ------------------------------------ +-- returns args_ok, rule, exec_policy +function check_rule_and_policy(rule, exec_policy, filename, mapped_file) + + -- First, if either rule or the exec policy is a string, something + -- has failed during the previous steps or mapping has been disabled; + -- => fail + if ((type(rule) == "string") or (type(exec_policy) == "string")) then + local rs = "" + local eps = "" + if (type(rule) == "string") then + rs = rule + end + if (type(exec_policy) == "string") then + eps = exec_policy + end + + sb.log("debug", "check_rule_and_policy: "..rs..";"..eps); + return false, rule, exec_policy + end + + -- if exec_policy was not provided by the caller (i.e. not + -- provided by the mapping rule), look up the policy from + -- exec_policy_chains array. + if (exec_policy == nil) then + local res + + sb.log("debug", "trying exec_policy_chains..") + res, exec_policy = sb_find_exec_policy(binaryname, mapped_file) + if res == 0 then + -- there is no default policy for this mode + sb.log("notice", + "sb_execve_postprocess: No exec_policy for "..filename) + return false, rule, exec_policy + end + end + + -- Exec policy is OK. + + return true, rule, exec_policy +end + +-- ------------------------------------ +-- Script interpreter mapping. + +-- This is called from C: +-- returns: rule, policy, result, mapped_interpreter, #argv, argv, #envp, envp +-- "result" is one of: +-- 0: argv / envp were modified; mapped_interpreter was set +-- 1: argv / envp were not modified; mapped_interpreter was set +-- 2: argv / envp were not modified; caller should call ordinary path +-- mapping to find the interpreter +-- -1: deny exec. +function sb_execve_map_script_interpreter(rule, exec_policy, interpreter, + interp_arg, mapped_script_filename, orig_script_filename, argv, envp) + + local args_ok + args_ok, rule, exec_policy = check_rule_and_policy(rule, + exec_policy, orig_script_filename, mapped_script_filename) + + if args_ok == false then + -- no exec policy. Deny exec, we can't find the interpreter + sb.log("error", "Unable to map script interpreter."); + return rule, exec_policy, -1, interpreter, #argv, argv, #envp, envp + end + + -- exec policy is OK. + + if (exec_policy.script_log_level ~= nil) then + sb.log(exec_policy.script_log_level, + exec_policy.script_log_message) + end + + if (exec_policy.script_deny_exec == true) then + return rule, exec_policy, -1, interpreter, #argv, argv, #envp, envp + end + + if (exec_policy.name == nil) then + sb.log("debug", "Applying nameless exec_policy to script") + else + sb.log("debug", string.format( + "Applying exec_policy '%s' to script", + exec_policy.name)) + end + + if (exec_policy.script_interpreter_rule ~= nil) then + local exec_pol_2, mapped_interpreter, ro_flag + + exec_pol_2, mapped_interpreter, ro_flag = sbox_execute_rule( + interpreter, "map_script_interpreter", + interpreter, interpreter, + exec_policy.script_interpreter_rule) + + if exec_policy.script_set_argv0_to_mapped_interpreter then + argv[1] = mapped_interpreter + return rule, exec_policy, 0, + mapped_interpreter, #argv, argv, #envp, envp + else + return rule, exec_policy, 1, + mapped_interpreter, #argv, argv, #envp, envp + end + end + + -- The default case: + -- exec policy says nothing about the script interpreters. + -- use ordinary path mapping to find it + return rule, exec_policy, 2, interpreter, #argv, argv, #envp, envp +end + +-- ------------------------------------ -- Exec postprocessing. -- function sb_execve_postprocess is called to decide HOW the executable -- should be started (see description of the algorithm in sb_exec.c) @@ -533,40 +642,16 @@ end function sb_execve_postprocess(rule, exec_policy, exec_type, mapped_file, filename, binaryname, argv, envp) - -- First, if either rule or the exec policy is a string, something - -- has failed during the previous steps or mapping has been disabled; - -- in both cases postprocessing is not needed, exec must be allowed. - if ((type(rule) == "string") or (type(exec_policy) == "string")) then - local rs = "" - local eps = "" - if (type(rule) == "string") then - rs = rule - end - if (type(exec_policy) == "string") then - eps = exec_policy - end + local args_ok + args_ok, rule, exec_policy = check_rule_and_policy(rule, + exec_policy, filename, mapped_file) - sb.log("debug", "sb_execve_postprocess: "..rs..";"..eps.. - " (going to exec with orig.args)") + if args_ok == false then + -- postprocessing is not needed / can't be done, but + -- exec must be allowed. return 1, mapped_file, filename, #argv, argv, #envp, envp end - -- if exec_policy was not provided by the caller (i.e. not - -- provided by the mapping rule), look up the policy from - -- exec_policy_chains array. - if (exec_policy == nil) then - local res - - sb.log("debug", "trying exec_policy_chains..") - res, exec_policy = sb_find_exec_policy(binaryname, mapped_file) - if res == 0 then - -- there is no default policy for this mode - sb.log("notice", - "sb_execve_postprocess: No exec_policy for "..filename) - return 1, mapped_file, filename, #argv, argv, #envp, envp - end - end - -- Exec policy found. if (exec_policy.log_level ~= nil) then diff --git a/lua_scripts/main.lua b/lua_scripts/main.lua index ef0b8a7..a9ca58a 100644 --- a/lua_scripts/main.lua +++ b/lua_scripts/main.lua @@ -15,7 +15,7 @@ debug_messages_enabled = sb.debug_messages_enabled() -- -- NOTE: the corresponding identifier for C is in include/sb2.h, -- see that file for description about differences -sb2_lua_c_interface_version = "60,2008-12-07" +sb2_lua_c_interface_version = "61" function do_file(filename) if (debug_messages_enabled) then diff --git a/luaif/argvenvp.c b/luaif/argvenvp.c index c4eda50..1ac11fc 100644 --- a/luaif/argvenvp.c +++ b/luaif/argvenvp.c @@ -24,7 +24,9 @@ static void strvec_to_lua_table(struct lua_instance *luaif, char **args) int i; lua_newtable(luaif->lua); - for (p = args, i = 1; *p; p++, i++) { + SB_LOG(SB_LOGLEVEL_NOISE2, "strvec_to_lua_table: "); + for (p = args, i = 1; p && *p; p++, i++) { + SB_LOG(SB_LOGLEVEL_NOISE2, "set element %d to '%s'", i, *p); lua_pushnumber(luaif->lua, i); lua_pushstring(luaif->lua, *p); lua_settable(luaif->lua, -3); @@ -219,6 +221,131 @@ int sb_execve_postprocess(char *exec_type, return res; } +/* Map script interpreter: + * Called with "rule" and "exec_policy" already in lua's stack, + * leaves (possibly modified) "rule" and "exec_policy" to lua's stack. +*/ +char *sb_execve_map_script_interpreter( + const char *interpreter, + const char *interp_arg, + const char *mapped_script_filename, + const char *orig_script_filename, + char ***argv, + char ***envp) +{ + struct lua_instance *luaif; + char *mapped_interpreter; + int new_argc, new_envc; + int res; + + luaif = get_lua(); + if (!luaif) return(0); + + if (!argv || !envp) { + SB_LOG(SB_LOGLEVEL_ERROR, + "ERROR: sb_execve_map_script_interpreter: " + "(argv || envp) == NULL"); + release_lua(luaif); + return NULL; + } + + SB_LOG(SB_LOGLEVEL_NOISE, + "sb_execve_map_script_interpreter: gettop=%d" + " interpreter=%s interp_arg=%s " + "mapped_script_filename=%s orig_script_filename=%s", + lua_gettop(luaif->lua), interpreter, interp_arg, + mapped_script_filename, orig_script_filename); + + lua_getfield(luaif->lua, LUA_GLOBALSINDEX, + "sb_execve_map_script_interpreter"); + + /* stack now contains "rule", "exec_policy" and + * "sb_execve_map_script_interpreter". + * move "sb_execve_map_script_interpreter" to the bottom : */ + lua_insert(luaif->lua, -3); + + lua_pushstring(luaif->lua, interpreter); + if (interp_arg) lua_pushstring(luaif->lua, interp_arg); + else lua_pushnil(luaif->lua); + lua_pushstring(luaif->lua, mapped_script_filename); + lua_pushstring(luaif->lua, orig_script_filename); + strvec_to_lua_table(luaif, *argv); + strvec_to_lua_table(luaif, *envp); + + /* args: rule, exec_policy, interpreter, interp_arg, + * mapped_script_filename, orig_script_filename, + * argv, envp + * returns: rule, policy, result, mapped_interpreter, #argv, argv, + * #envp, envp + * "result" is one of: + * 0: argv / envp were modified; mapped_interpreter was set + * 1: argv / envp were not modified; mapped_interpreter was set + * -1: deny exec. + */ + SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_map_script_interpreter: call lua"); + lua_call(luaif->lua, 8, 8); + SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_map_script_interpreter: return from lua"); + + mapped_interpreter = (char *)lua_tostring(luaif->lua, -5); + if (mapped_interpreter) mapped_interpreter = strdup(mapped_interpreter); + + res = lua_tointeger(luaif->lua, -6); + switch (res) { + + case 0: + /* exec arguments were modified, replace contents of + * argv and envp vectors */ + SB_LOG(SB_LOGLEVEL_DEBUG, + "sb_execve_map_script_interpreter: Updated argv&envp"); + + strvec_free(*argv); + new_argc = lua_tointeger(luaif->lua, -4); + lua_string_table_to_strvec(luaif, -3, argv, new_argc); + + new_envc = lua_tointeger(luaif->lua, -2); + strvec_free(*envp); + lua_string_table_to_strvec(luaif, -1, envp, new_envc); + break; + + case 1: + SB_LOG(SB_LOGLEVEL_DEBUG, + "sb_execve_map_script_interpreter: argv&envp were not modified"); + break; + + case 2: + if (mapped_interpreter) free(mapped_interpreter); + mapped_interpreter = NULL; + mapped_interpreter = scratchbox_path("script_interp", + interpreter, NULL/*RO-flag addr.*/, + 0/*dont_resolve_final_symlink*/); + SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_map_script_interpreter: " + "interpreter=%s mapped_interpreter=%s", + interpreter, mapped_interpreter); + break; + + case -1: + SB_LOG(SB_LOGLEVEL_DEBUG, + "sb_execve_map_script_interpreter: exec denied"); + if (mapped_interpreter) free(mapped_interpreter); + mapped_interpreter = NULL; + break; + + default: + SB_LOG(SB_LOGLEVEL_ERROR, + "sb_execve_map_script_interpreter: Unsupported result %d", res); + break; + } + + /* remove return values from the stack, leave rule & policy. */ + lua_pop(luaif->lua, 6); + + SB_LOG(SB_LOGLEVEL_NOISE, + "sb_execve_map_script_interpreter: at exit, gettop=%d", + lua_gettop(luaif->lua)); + release_lua(luaif); + return mapped_interpreter; +} + /* * Tries to find exec_policy for given real_binary_name and return * field 'field_name' as string. Returned value should be released diff --git a/preload/sb_exec.c b/preload/sb_exec.c index 3dbacb2..d3d7b8a 100644 --- a/preload/sb_exec.c +++ b/preload/sb_exec.c @@ -41,8 +41,12 @@ * following: * * 4a. For scripts (files starting with #!), script interpreter name - * will be read from the file and it will be processed again by - * prepare_exec() + * will be read from the file, mapped by sb_execve_map_script_interpreter() + * (a lua function in argvenvp.lua), and once the interpreter + * location has been found, the parameters will be processed again by + * prepare_exec(). + * This solution supports location- and exec policy based mapping + * of the script interpreter. * * 4b. For native binaries, an additional call to an exec postprocessing * function will be made, because the type of the file is not enough @@ -483,6 +487,7 @@ static int prepare_hashbang( char **new_argv; char hashbang[SBOX_MAXPATH]; /* only 60 needed on linux, just be safe */ char interpreter[SBOX_MAXPATH]; + char *interp_arg = NULL; if ((fd = open_nomap(*mapped_file, O_RDONLY)) < 0) { /* unexpected error, just run it */ @@ -518,11 +523,12 @@ static int prepare_hashbang( strcpy(interpreter, ptr); new_argv[n++] = strdup(interpreter); } else { - new_argv[n++] = strdup(&hashbang[j]); /* this was the one and only * allowed argument for the * interpreter */ + interp_arg = strdup(&hashbang[j]); + new_argv[n++] = interp_arg; break; } } @@ -531,19 +537,28 @@ static int prepare_hashbang( if (ch == '\n' || ch == 0) break; } - mapped_interpreter = scratchbox_path("execve", interpreter, - NULL/*RO-flag addr.*/, 0/*dont_resolve_final_symlink*/); - SB_LOG(SB_LOGLEVEL_DEBUG, "prepare_hashbang(): interpreter=%s," - "mapped_interpreter=%s", interpreter, - mapped_interpreter); new_argv[n++] = strdup(orig_file); /* the unmapped script path */ - for (i = 1; (*argvp)[i] != NULL && i < argc; ) { new_argv[n++] = (*argvp)[i++]; } - new_argv[n] = NULL; + /* rule & policy are in the stack */ + mapped_interpreter = sb_execve_map_script_interpreter( + interpreter, interp_arg, *mapped_file, orig_file, + &new_argv, envpp); + + if (!mapped_interpreter) { + SB_LOG(SB_LOGLEVEL_ERROR, + "failed to map script interpreter=%s", interpreter); + return(-1); + } + + SB_LOG(SB_LOGLEVEL_DEBUG, "prepare_hashbang(): interpreter=%s," + "mapped_interpreter=%s", interpreter, + mapped_interpreter); + /* rule & policy are in still stack */ + /* feed this through prepare_exec to let it deal with * cpu transparency etc. */ |