void *sbox_find_next_symbol(int log_enabled, const char *fn_name) { char *msg; void *fn_ptr; if(log_enabled) SB_LOG(SB_LOGLEVEL_DEBUG, "%s: %s", __func__, fn_name); fn_ptr = dlsym(RTLD_NEXT, fn_name); if ((msg = dlerror()) != NULL) { fprintf(stderr, "%s: dlsym(%s): %s\n", PACKAGE_NAME, fn_name, msg); if(log_enabled) SB_LOG(SB_LOGLEVEL_ERROR, "ERROR: %s: dlsym(%s): %s", PACKAGE_NAME, fn_name, msg); assert(0); } if (log_enabled && SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE)) { Dl_info dli; if (dladdr(fn_ptr, &dli)) { SB_LOG(SB_LOGLEVEL_NOISE, "%s: %s at 0x%p, in file '%s'", __func__, fn_name, fn_ptr, dli.dli_fname); } else { SB_LOG(SB_LOGLEVEL_NOISE, "%s: %s at 0x%p", __func__, fn_name, fn_ptr); } } return(fn_ptr); }
/* compare vectors of strings and log if there are any changes. * TO BE IMPROVED: a more clever algorithm would be nice, something * that would log only the modified parts... now this displays * everything if something was changed, which easily creates * a *lot* of noise if SB_LOGLEVEL_NOISE is enabled. */ static void compare_and_log_strvec_changes(const char *vecname, char *const *orig_strv, char *const *new_strv) { char *const *ptr2old = orig_strv; char *const *ptr2new = new_strv; int strv_modified = 0; while (*ptr2old && *ptr2new) { if (strcmp(*ptr2old, *ptr2new)) { strv_modified = 1; break; } ptr2old++, ptr2new++; } if (!strv_modified && (*ptr2old || *ptr2new)) { strv_modified = 1; } if (strv_modified) { SB_LOG(SB_LOGLEVEL_DEBUG, "%s[] was modified", vecname); if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE)) { int i; for (i = 0, ptr2new = new_strv; *ptr2new; i++, ptr2new++) { SB_LOG(SB_LOGLEVEL_NOISE, "[%d]='%s'", i, *ptr2new); } } } else { SB_LOG(SB_LOGLEVEL_NOISE, "%s[] was not modified", vecname); } }
/* "sb.debug_messages_enabled", to be called from lua code * returns true if SB_LOG messages have been enabled for the debug levels * (debug,noise,noise2...) */ int lua_sb_debug_messages_enabled(lua_State *l) { if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { lua_pushboolean(l, 1); } else { lua_pushboolean(l, 0); } return 1; }
/* get access to sb2 context, create the structure * if it didn't exist; in that case, the structure is only * cleared (most notably, the Lua system is not initialized * by this routine!) * * Remember to call release_sb2context() after the * pointer is not needed anymore. */ struct sb2context *get_sb2context(void) { struct sb2context *ptr = NULL; if (!sb2_global_vars_initialized__) sb2_initialize_global_variables(); if (!SB_LOG_INITIALIZED()) sblog_init(); SB_LOG(SB_LOGLEVEL_NOISE, "get_sb2context()"); if (pthread_detection_done == 0) check_pthread_library(); if (pthread_library_is_available) { if (pthread_once_fnptr) (*pthread_once_fnptr)(&sb2context_key_once, alloc_sb2context_key); if (pthread_getspecific_fnptr) ptr = (*pthread_getspecific_fnptr)(sb2context_key); if (!ptr) ptr = alloc_sb2context(); if (!ptr) { SB_LOG(SB_LOGLEVEL_ERROR, "Something's wrong with" " the pthreads support"); fprintf(stderr, "FATAL: sb2 preload library:" " Something's wrong with" " the pthreads support.\n"); exit(1); } } else { /* no pthreads, single-thread application */ ptr = my_sb2context; if (!ptr) ptr = alloc_sb2context(); if (!ptr) { SB_LOG(SB_LOGLEVEL_ERROR, "Failed to get Lua instance" " (and the pthreads support is " " disabled!)"); fprintf(stderr, "FATAL: sb2 preload library:" " Failed to get Lua instance" " (and the pthreads support is disabled!)\n"); exit(1); } } if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { increment_sb2if_usage_counter(ptr); } return(ptr); }
static void increment_sb2if_usage_counter(volatile struct sb2context *ptr) { if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { /* Well, to make this bullet-proof the sb2if structure * should be locked, but since this code is now used only for * producing debugging information and the pointer is marked * "volatile", the results are good enough. No need to slow * down anything with additional locks - this function is * called frequently. */ if (ptr->sb2context_in_use > 0) SB_LOG(SB_LOGLEVEL_DEBUG, "Lua instance already in use! (%d)", ptr->sb2context_in_use); (ptr->sb2context_in_use)++; } }
void release_sb2context(struct sb2context *sb2if) { if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { int i; volatile struct sb2context *ptr = sb2if; SB_LOG(SB_LOGLEVEL_NOISE, "release_sb2context()"); if (!ptr) { SB_LOG(SB_LOGLEVEL_DEBUG, "release_sb2context(): ptr is NULL "); return; } i = ptr->sb2context_in_use; if (i > 1) SB_LOG(SB_LOGLEVEL_DEBUG, "Lua instance usage counter was %d", i); (ptr->sb2context_in_use)--; } }
void release_lua(struct lua_instance *luaif) { if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { int i; volatile struct lua_instance *ptr = luaif; SB_LOG(SB_LOGLEVEL_NOISE, "release_lua()"); if (!ptr) { SB_LOG(SB_LOGLEVEL_DEBUG, "release_lua(): ptr is NULL "); return; } i = ptr->lua_instance_in_use; if (i > 1) SB_LOG(SB_LOGLEVEL_DEBUG, "Lua instance usage counter was %d", i); (ptr->lua_instance_in_use)--; } }
static void revert_to_user_version_of_env_var( const char *realvarname, const char *wrappedname) { char *cp = getenv(wrappedname); if (cp) { SB_LOG(SB_LOGLEVEL_DEBUG, "Revert env.var:%s=%s (%s)", realvarname, cp, wrappedname); setenv(realvarname, cp, 1/*overwrite*/); } else { int r; SB_LOG(SB_LOGLEVEL_DEBUG, "Revert env.var:Clear %s", realvarname); r = unsetenv(realvarname); if(r < 0) { int e = errno; SB_LOG(SB_LOGLEVEL_ERROR, "unsetenv(%s) failed, errno=%d", realvarname, e); } } if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) dump_environ_to_log("revert_to_user_version_of_env_var: env. is now:"); }
/* 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. */ if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("sb_execve_map_script_interpreter M1", luaif->lua); } SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_map_script_interpreter: call lua, gettop=%d", lua_gettop(luaif->lua)); lua_call(luaif->lua, 8, 8); SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_map_script_interpreter: return from lua, gettop=%d", lua_gettop(luaif->lua)); if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("sb_execve_map_script_interpreter M2", luaif->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); /* remove return values from the stack, leave rule & policy. */ lua_pop(luaif->lua, 6); break; case 1: SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_map_script_interpreter: argv&envp were not modified"); /* remove return values from the stack, leave rule & policy. */ lua_pop(luaif->lua, 6); break; case 2: SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_map_script_interpreter: use sbox_map_path_for_exec"); /* remove all return values from the stack. */ lua_pop(luaif->lua, 8); if (mapped_interpreter) free(mapped_interpreter); mapped_interpreter = NULL; { mapping_results_t mapping_result; clear_mapping_results_struct(&mapping_result); sbox_map_path_for_exec("script_interp", interpreter, &mapping_result); if (mapping_result.mres_result_buf) { mapped_interpreter = strdup(mapping_result.mres_result_buf); } free_mapping_results(&mapping_result); } 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"); /* remove return values from the stack, leave rule & policy. */ lua_pop(luaif->lua, 6); 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); /* remove return values from the stack, leave rule & policy. */ lua_pop(luaif->lua, 6); break; } if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("sb_execve_map_script_interpreter E2", luaif->lua); } SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_map_script_interpreter: at exit, gettop=%d", lua_gettop(luaif->lua)); release_lua(luaif); return mapped_interpreter; }
/* Exec Postprocessing: * Called with "rule" and "exec_policy" already in lua's stack. */ int sb_execve_postprocess(char *exec_type, char **mapped_file, char **filename, const char *binary_name, char ***argv, char ***envp) { struct lua_instance *luaif; int res, new_argc; int replace_environment = 0; luaif = get_lua(); if (!luaif) return(0); if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("sb_execve_postprocess entry", luaif->lua); } if (!argv || !envp) { SB_LOG(SB_LOGLEVEL_ERROR, "ERROR: sb_argvenvp: (argv || envp) == NULL"); release_lua(luaif); return -1; } SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_postprocess: gettop=%d", lua_gettop(luaif->lua)); lua_getfield(luaif->lua, LUA_GLOBALSINDEX, "sb_execve_postprocess"); /* stack now contains "rule", "exec_policy" and "sb_execve_postprocess". * move "sb_execve_postprocess" to the bottom : */ lua_insert(luaif->lua, -3); lua_pushstring(luaif->lua, exec_type); lua_pushstring(luaif->lua, *mapped_file); lua_pushstring(luaif->lua, *filename); lua_pushstring(luaif->lua, binary_name); strvec_to_lua_table(luaif, *argv); strvec_to_lua_table(luaif, *envp); /* args: rule, exec_policy, exec_type, mapped_file, filename, * binaryname, argv, envp * returns: res, mapped_file, filename, argc, argv, envc, envp */ lua_call(luaif->lua, 8, 7); res = lua_tointeger(luaif->lua, -7); switch (res) { case 0: /* exec arguments were modified, replace contents of * argv vector */ SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_postprocess: Updated argv&envp"); free(*mapped_file); *mapped_file = strdup(lua_tostring(luaif->lua, -6)); free(*filename); *filename = strdup(lua_tostring(luaif->lua, -5)); strvec_free(*argv); new_argc = lua_tointeger(luaif->lua, -4); lua_string_table_to_strvec(luaif, -3, argv, new_argc); replace_environment = 1; break; case 1: SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_postprocess: argv was not modified"); /* always update environment when we are going to exec */ replace_environment = 1; break; case -1: SB_LOG(SB_LOGLEVEL_DEBUG, "sb_execve_postprocess: exec denied"); break; default: SB_LOG(SB_LOGLEVEL_ERROR, "sb_execve_postprocess: Unsupported result %d", res); break; } if (replace_environment) { int new_envc; new_envc = lua_tointeger(luaif->lua, -2); strvec_free(*envp); lua_string_table_to_strvec(luaif, -1, envp, new_envc); } /* remove sb_execve_postprocess return values from the stack. */ lua_pop(luaif->lua, 7); SB_LOG(SB_LOGLEVEL_NOISE, "sb_execve_postprocess: at exit, gettop=%d", lua_gettop(luaif->lua)); release_lua(luaif); return res; }
/* note: this expects that the lua stack already contains the mapping rule, * needed by sbox_translate_path (lua code). * at exit the rule is still there. */ char *call_lua_function_sbox_translate_path( const path_mapping_context_t *ctx, int result_log_level, const char *abs_clean_virtual_path, int *flagsp, char **exec_policy_name_ptr) { struct sb2context *sb2ctx = ctx->pmc_sb2ctx; int flags; char *host_path = NULL; SB_LOG(SB_LOGLEVEL_NOISE, "calling sbox_translate_path for %s(%s), fn_class=0x%X", ctx->pmc_func_name, abs_clean_virtual_path, ctx->pmc_fn_class); if (!sb2ctx->lua) sb2context_initialize_lua(sb2ctx); SB_LOG(SB_LOGLEVEL_NOISE, "call_lua_function_sbox_translate_path: gettop=%d", lua_gettop(sb2ctx->lua)); if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("call_lua_function_sbox_translate_path entry", sb2ctx->lua); } lua_getfield(sb2ctx->lua, LUA_GLOBALSINDEX, "sbox_translate_path"); /* stack now contains the rule object and string "sbox_translate_path", * move the string to the bottom: */ lua_insert(sb2ctx->lua, -2); /* add other parameters */ lua_pushstring(sb2ctx->lua, ctx->pmc_binary_name); lua_pushstring(sb2ctx->lua, ctx->pmc_func_name); lua_pushstring(sb2ctx->lua, abs_clean_virtual_path); lua_pushnumber(sb2ctx->lua, ctx->pmc_fn_class); /* 5 arguments, returns rule,policy,path,flags */ lua_call(sb2ctx->lua, 5, 4); host_path = (char *)lua_tostring(sb2ctx->lua, -2); if (host_path && (*host_path != '/')) { SB_LOG(SB_LOGLEVEL_ERROR, "Mapping failed: Result is not absolute ('%s'->'%s')", abs_clean_virtual_path, host_path); host_path = NULL; } else if (host_path) { host_path = strdup(host_path); } flags = lua_tointeger(sb2ctx->lua, -1); check_mapping_flags(flags, "sbox_translate_path"); if (flagsp) *flagsp = flags; if (exec_policy_name_ptr) { char *exec_policy_name; if (*exec_policy_name_ptr) { free(*exec_policy_name_ptr); *exec_policy_name_ptr = NULL; } exec_policy_name = (char *)lua_tostring(sb2ctx->lua, -3); if (exec_policy_name) { *exec_policy_name_ptr = strdup(exec_policy_name); } } lua_pop(sb2ctx->lua, 3); /* leave the rule to the stack */ if (host_path) { char *new_host_path = clean_and_log_fs_mapping_result(ctx, abs_clean_virtual_path, result_log_level, host_path, flags); free(host_path); host_path = new_host_path; } if (!host_path) { SB_LOG(SB_LOGLEVEL_ERROR, "No result from sbox_translate_path for: %s '%s'", ctx->pmc_func_name, abs_clean_virtual_path); } SB_LOG(SB_LOGLEVEL_NOISE, "call_lua_function_sbox_translate_path: at exit, gettop=%d", lua_gettop(sb2ctx->lua)); if(SB_LOG_IS_ACTIVE(SB_LOGLEVEL_NOISE3)) { dump_lua_stack("call_lua_function_sbox_translate_path exit", sb2ctx->lua); } return(host_path); }
int do_exec(int *result_errno_ptr, const char *exec_fn_name, const char *orig_file, char *const *orig_argv, char *const *orig_envp) { char *new_file = NULL; char **new_argv = NULL; char **new_envp = NULL; int result; PROCESSCLOCK(clk1) START_PROCESSCLOCK(SB_LOGLEVEL_INFO, &clk1, "do_exec"); if (getenv("SBOX_DISABLE_MAPPING")) { /* just run it, don't worry, be happy! */ } else { int r; char **my_envp_copy = NULL; /* used only for debug log */ char *tmp, *binaryname; enum binary_type type; tmp = strdup(orig_file); binaryname = strdup(basename(tmp)); /* basename may modify *tmp */ free(tmp); if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { char *buf = strvec_to_string(orig_argv); SB_LOG(SB_LOGLEVEL_DEBUG, "EXEC/Orig.args: %s : %s", orig_file, buf); free(buf); /* create a copy of intended environment for logging, * before preprocessing */ my_envp_copy = prepare_envp_for_do_exec(orig_file, binaryname, orig_envp); } new_envp = prepare_envp_for_do_exec(orig_file, binaryname, orig_envp); r = prepare_exec(exec_fn_name, NULL/*exec_policy_name: not yet known*/, orig_file, 0, orig_argv, orig_envp, &type, &new_file, &new_argv, &new_envp); if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { /* find out and log if preprocessing did something */ compare_and_log_strvec_changes("argv", orig_argv, new_argv); compare_and_log_strvec_changes("envp", my_envp_copy, new_envp); } if (r < 0) { SB_LOG(SB_LOGLEVEL_DEBUG, "EXEC denied by prepare_exec(), %s", orig_file); *result_errno_ptr = errno; STOP_AND_REPORT_PROCESSCLOCK(SB_LOGLEVEL_INFO, &clk1, "Exec denied"); return(r); /* exec denied */ } if (check_envp_has_ld_preload_and_ld_library_path( new_envp ? new_envp : orig_envp) == 0) { SB_LOG(SB_LOGLEVEL_ERROR, "exec(%s) failed, internal configuration error: " "LD_LIBRARY_PATH and/or LD_PRELOAD were not set " "by exec mapping logic", orig_file); *result_errno_ptr = EINVAL; STOP_AND_REPORT_PROCESSCLOCK(SB_LOGLEVEL_INFO, &clk1, "Config error"); return(-1); } } errno = *result_errno_ptr; /* restore to orig.value */ STOP_AND_REPORT_PROCESSCLOCK(SB_LOGLEVEL_INFO, &clk1, orig_file); result = sb_next_execve( (new_file ? new_file : orig_file), (new_argv ? new_argv : orig_argv), (new_envp ? new_envp : orig_envp)); *result_errno_ptr = errno; SB_LOG(SB_LOGLEVEL_DEBUG, "EXEC failed (%s), errno=%d", orig_file, *result_errno_ptr); return(result); }