summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLauri Aarnio <Lauri.Aarnio@iki.fi>2009-01-29 17:01:40 +0200
committerLauri Leukkunen <lle@rahina.org>2009-02-10 08:38:10 +0200
commit905e4b3d3ad88ef66587707ed975184afaff9dcc (patch)
treecd8fcb3c6b7d90a5c246cf251fff894fc1d4eb07
parent3af026302c6095daf1be51a72372e7b91c6fe789 (diff)
Exec algorithm improvements: Policy-based mapping of script interpreters
- this commit adds support for exec policy based mapping of script interpreter: A script interpreter can be selected based on location of the script. For example, perl scripts from tools_root may map /usr/bin/perl to tools_root, while perl scripts from the target_root can map /usr/bin/perl to target_root. - This commit does not contain changes to the mapping rules => everything still works just as before (related changes to the "devel" mode will be added by another commit)
-rw-r--r--include/mapping.h3
-rw-r--r--include/sb2.h4
-rw-r--r--lua_scripts/argvenvp.lua145
-rw-r--r--lua_scripts/main.lua2
-rw-r--r--luaif/argvenvp.c129
-rw-r--r--preload/sb_exec.c35
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.
*/