Add an LD_PRELOAD library "buildat_guard" which takes care of limiting Urho3D::ResourceCache's file system access while it's lacking the functionalty by itself
This commit is contained in:
parent
19a1da6033
commit
4b25272cc9
@ -1,3 +1,6 @@
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(buildat)
|
||||
|
||||
@ -18,6 +21,7 @@ include_directories("3rdparty/smallsha1")
|
||||
# Global options
|
||||
#
|
||||
|
||||
set(BUILD_GUARD TRUE CACHE BOOL "Build LD_PRELOAD guard (used for the client)")
|
||||
set(BUILD_SERVER TRUE CACHE BOOL "Build server")
|
||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
||||
|
||||
@ -50,17 +54,49 @@ define_dependency_libs("Urho3D")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wno-unused-parameter")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x -g -O0 -Wall")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") # Executables
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") # Shared libs
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") # Static libs
|
||||
|
||||
# Always output in color (useful when using head for build output)
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
|
||||
# Security / crash protection
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all")
|
||||
|
||||
set(CLIENT_EXE_NAME buildat_client)
|
||||
set(SERVER_EXE_NAME buildat_server)
|
||||
|
||||
if(BUILD_GUARD)
|
||||
# LD_PRELOAD wrapper
|
||||
set(PRELOAD_LIB_NAME buildat_guard)
|
||||
set(PRELOAD_SRCS
|
||||
src/guard/lib.c
|
||||
)
|
||||
add_library(${PRELOAD_LIB_NAME} SHARED ${PRELOAD_SRCS})
|
||||
|
||||
# Create wrapper script in place of client
|
||||
set(GUARD_SCRIPT_NAME ${CLIENT_EXE_NAME})
|
||||
set(GUARD_SCRIPT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${GUARD_SCRIPT_NAME})
|
||||
add_custom_command(
|
||||
OUTPUT ${GUARD_SCRIPT_PATH}
|
||||
COMMAND cp ${CMAKE_SOURCE_DIR}/src/guard/guard_launcher.sh
|
||||
${GUARD_SCRIPT_PATH}
|
||||
COMMAND chmod +x ${GUARD_SCRIPT_PATH}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/src/guard/guard_launcher.sh
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(generate_guard_script ALL
|
||||
DEPENDS ${GUARD_SCRIPT_PATH}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/src/guard/guard_launcher.sh)
|
||||
|
||||
# Rename client to different name
|
||||
set(CLIENT_EXE_NAME "${CLIENT_EXE_NAME}.bin")
|
||||
endif(BUILD_GUARD)
|
||||
|
||||
if(BUILD_CLIENT)
|
||||
# Client
|
||||
set(CLIENT_EXE_NAME buildat_client)
|
||||
set(CLIENT_SRCS
|
||||
src/guard/buildat_guard_interface.cpp
|
||||
src/client/main.cpp
|
||||
src/client/state.cpp
|
||||
src/client/app.cpp
|
||||
@ -84,7 +120,6 @@ endif(BUILD_CLIENT)
|
||||
|
||||
if(BUILD_SERVER)
|
||||
# Server
|
||||
set(SERVER_EXE_NAME buildat_server)
|
||||
set(SERVER_SRCS
|
||||
src/server/main.cpp
|
||||
src/server/state.cpp
|
||||
@ -110,4 +145,5 @@ if(BUILD_SERVER)
|
||||
${LINK_LIBS_ONLY}
|
||||
)
|
||||
endif(BUILD_SERVER)
|
||||
|
||||
# vim: set noet ts=4 sw=4:
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "client/config.h"
|
||||
#include "client/state.h"
|
||||
#include "interface/fs.h"
|
||||
#include "guard/buildat_guard_interface.h"
|
||||
#include <c55/getopt.h>
|
||||
#include <c55/os.h>
|
||||
#include <c55/string_util.h>
|
||||
@ -76,6 +77,16 @@ struct CApp: public App, public magic::Application
|
||||
resource_paths_s += fs->get_absolute_path(path);
|
||||
}
|
||||
|
||||
// Set allowed paths in buildat guard wrapper (ignored if not available)
|
||||
buildat_guard_clear_paths();
|
||||
for(const ss_ &path : resource_paths){
|
||||
buildat_guard_add_valid_base_path(
|
||||
fs->get_absolute_path(path).c_str());
|
||||
}
|
||||
buildat_guard_add_valid_base_path(
|
||||
fs->get_absolute_path(g_client_config.cache_path).c_str());
|
||||
|
||||
// Set Urho3D engine parameters
|
||||
engineParameters_["WindowTitle"] = "Buildat Client";
|
||||
engineParameters_["LogName"] = "client_Urho3D.log";
|
||||
engineParameters_["Headless"] = false;
|
||||
@ -252,6 +263,10 @@ struct CApp: public App, public magic::Application
|
||||
throw AppStartupError("Could not initialize Lua environment");
|
||||
}
|
||||
|
||||
// Enable guard now. Everything from here should not access any weird
|
||||
// files.
|
||||
buildat_guard_enable(true);
|
||||
|
||||
if(g_client_config.boot_to_menu){
|
||||
ss_ extname = g_client_config.menu_extension_name;
|
||||
ss_ script = ss_()+
|
||||
@ -908,7 +923,7 @@ struct CApp: public App, public magic::Application
|
||||
return 2;
|
||||
}
|
||||
|
||||
// faatal_error(error: string)
|
||||
// fatal_error(error: string)
|
||||
static int l_fatal_error(lua_State *L)
|
||||
{
|
||||
ss_ error = lua_tocppstring(L, 1);
|
||||
|
@ -26,6 +26,14 @@ static bool check_file_writable(const ss_ &path)
|
||||
return writable;
|
||||
}
|
||||
|
||||
void Config::make_paths_absolute()
|
||||
{
|
||||
auto *fs = interface::getGlobalFilesystem();
|
||||
share_path = fs->get_absolute_path(share_path);
|
||||
cache_path = fs->get_absolute_path(cache_path);
|
||||
urho3d_path = fs->get_absolute_path(urho3d_path);
|
||||
}
|
||||
|
||||
bool Config::check_paths()
|
||||
{
|
||||
bool fail = false;
|
||||
|
@ -14,6 +14,7 @@ namespace client
|
||||
bool boot_to_menu = false;
|
||||
ss_ menu_extension_name = "__menu";
|
||||
|
||||
void make_paths_absolute();
|
||||
bool check_paths();
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "client/config.h"
|
||||
#include "client/state.h"
|
||||
#include "client/app.h"
|
||||
#include "guard/buildat_guard_interface.h"
|
||||
#include <c55/getopt.h>
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
@ -33,6 +34,20 @@ void signal_handler_init()
|
||||
(void)signal(SIGINT, sigint_handler);
|
||||
}
|
||||
|
||||
void guard_init()
|
||||
{
|
||||
if(buildat_guard_init() != 0){
|
||||
log_w(MODULE, "The buildat_guard interface could not be accessed."
|
||||
" You should LD_PRELOAD it.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard is used only when Urho3D::ResourceCache is accessed by unsafe code
|
||||
buildat_guard_enable(false);
|
||||
|
||||
//buildat_guard_add_valid_base_path("/usr/share/");
|
||||
}
|
||||
|
||||
void basic_init()
|
||||
{
|
||||
signal_handler_init();
|
||||
@ -42,6 +57,8 @@ void basic_init()
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
log_set_max_level(LOG_VERBOSE);
|
||||
|
||||
guard_init();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -100,6 +117,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
config.make_paths_absolute();
|
||||
if(!config.check_paths()){
|
||||
return 1;
|
||||
}
|
||||
|
64
src/guard/buildat_guard_interface.cpp
Normal file
64
src/guard/buildat_guard_interface.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#ifndef _WIN32
|
||||
#include "core/log.h"
|
||||
#include <stdio.h>
|
||||
#define __GNU_SOURCE // RTLD_NEXT
|
||||
#ifndef __USE_GNU
|
||||
#define __USE_GNU // RTLD_NEXT
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#define MODULE "guard"
|
||||
extern "C"
|
||||
{
|
||||
void (*r_buildat_guard_add_valid_base_path)(const char *path) = NULL;
|
||||
void (*r_buildat_guard_enable)(int enable) = NULL;
|
||||
void (*r_buildat_guard_clear_paths)(void) = NULL;
|
||||
|
||||
int buildat_guard_init()
|
||||
{
|
||||
r_buildat_guard_add_valid_base_path = (void (*)(const char*))
|
||||
dlsym(RTLD_NEXT, "buildat_guard_add_valid_base_path");
|
||||
r_buildat_guard_enable = (void (*)(int))
|
||||
dlsym(RTLD_NEXT, "buildat_guard_enable");
|
||||
r_buildat_guard_clear_paths = (void (*)(void))
|
||||
dlsym(RTLD_NEXT, "buildat_guard_clear_paths");
|
||||
int succesful = (r_buildat_guard_add_valid_base_path != NULL);
|
||||
log_v(MODULE, "buildat_guard_init(): %s", succesful?"succesful":"failed");
|
||||
return !succesful;
|
||||
}
|
||||
|
||||
void buildat_guard_add_valid_base_path(const char *path)
|
||||
{
|
||||
if(!r_buildat_guard_add_valid_base_path)
|
||||
return;
|
||||
log_v(MODULE, "buildat_guard_add_valid_base_path(\"%s\")", path);
|
||||
(*r_buildat_guard_add_valid_base_path)(path);
|
||||
}
|
||||
|
||||
void buildat_guard_enable(int enable)
|
||||
{
|
||||
if(!r_buildat_guard_enable)
|
||||
return;
|
||||
log_v(MODULE, "buildat_guard_enable(%s)", enable?"true":"false");
|
||||
(*r_buildat_guard_enable)(enable);
|
||||
}
|
||||
|
||||
void buildat_guard_clear_paths(void)
|
||||
{
|
||||
if(!r_buildat_guard_clear_paths)
|
||||
return;
|
||||
log_v(MODULE, "buildat_guard_clear_paths()");
|
||||
(*r_buildat_guard_clear_paths)();
|
||||
}
|
||||
}
|
||||
#else // #ifndef _WIN32
|
||||
extern "C"
|
||||
{
|
||||
// Not supported on platform
|
||||
int buildat_guard_init(){ return 0; }
|
||||
void buildat_guard_add_valid_base_path(const char *path){}
|
||||
void buildat_guard_enable(int enable){}
|
||||
void buildat_guard_clear_paths(void){}
|
||||
}
|
||||
#endif // #ifndef _WIN32
|
15
src/guard/buildat_guard_interface.h
Normal file
15
src/guard/buildat_guard_interface.h
Normal file
@ -0,0 +1,15 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
// Remember to add / to end of base path if it is a directory
|
||||
extern "C" {
|
||||
// Returns zero if succesful.
|
||||
// Returns non-zero on error.
|
||||
// Returns non-zero if not supported on platform.
|
||||
extern int buildat_guard_init();
|
||||
// If initialized and supported, wraps to the buildat_guard library.
|
||||
// If not initialized or not supported, does nothing.
|
||||
extern void buildat_guard_add_valid_base_path(const char *path);
|
||||
extern void buildat_guard_enable(int enable);
|
||||
extern void buildat_guard_clear_paths(void);
|
||||
}
|
4
src/guard/guard_launcher.sh
Normal file
4
src/guard/guard_launcher.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
echo "guard_launcher.sh: If you wish to debug buildat_client, run $DIR/buildat_client.bin directly"
|
||||
LD_PRELOAD="$DIR/../lib/libbuildat_guard.so" "$DIR/buildat_client.bin" $@
|
111
src/guard/lib.c
Normal file
111
src/guard/lib.c
Normal file
@ -0,0 +1,111 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#define __GNU_SOURCE // RTLD_NEXT
|
||||
#ifndef __USE_GNU
|
||||
#define __USE_GNU // RTLD_NEXT
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
|
||||
// API
|
||||
|
||||
// Only absolute paths allowed.
|
||||
// Remember to add / to end of base path if it is a directory
|
||||
void buildat_guard_add_valid_base_path(const char *path);
|
||||
|
||||
void buildat_guard_enable(int enable);
|
||||
|
||||
void buildat_guard_clear_paths();
|
||||
|
||||
// The API shall be made available to the main program by LD_PRELOADing this
|
||||
// library.
|
||||
|
||||
// Implementation
|
||||
|
||||
static int guard_enabled = 1;
|
||||
|
||||
#define MAX_allowed_base_paths 50
|
||||
#define MAX_allowed_base_path_size 1000
|
||||
static char allowed_base_paths[MAX_allowed_base_paths][MAX_allowed_base_path_size] = {0};
|
||||
static size_t allowed_base_paths_next_i = 0;
|
||||
|
||||
void buildat_guard_add_valid_base_path(const char *path)
|
||||
{
|
||||
// Don't add more paths than what fit the static storage
|
||||
if(allowed_base_paths_next_i >= MAX_allowed_base_paths){
|
||||
fprintf(stderr, "### guard: Will not add base path: %s (storage full)\n", path);
|
||||
return;
|
||||
}
|
||||
// Require absolute path
|
||||
if(path[0] != '/'){
|
||||
fprintf(stderr, "### guard: Will not add base path: %s (not absolute path)\n", path);
|
||||
return;
|
||||
}
|
||||
// Limit length to that of static storage
|
||||
if(strlen(path) > MAX_allowed_base_path_size - 1){
|
||||
fprintf(stderr, "### guard: Will not add base path: %s (path too long)\n", path);
|
||||
return;
|
||||
}
|
||||
// Path ok; Copy it and return
|
||||
strncpy(allowed_base_paths[allowed_base_paths_next_i], path, MAX_allowed_base_path_size-1);
|
||||
allowed_base_paths[allowed_base_paths_next_i][MAX_allowed_base_path_size-1] = '\0';
|
||||
allowed_base_paths_next_i++;
|
||||
}
|
||||
|
||||
void buildat_guard_enable(int enable)
|
||||
{
|
||||
guard_enabled = enable;
|
||||
}
|
||||
|
||||
void buildat_guard_clear_paths()
|
||||
{
|
||||
allowed_base_paths_next_i = 0;
|
||||
}
|
||||
|
||||
// Returns non-zero value if path is allowed
|
||||
static int path_allowed(const char *path)
|
||||
{
|
||||
size_t i;
|
||||
const size_t path_len = strlen(path);
|
||||
for(i=0; i<allowed_base_paths_next_i && i<MAX_allowed_base_paths; i++){
|
||||
const char *base = allowed_base_paths[i];
|
||||
const size_t base_len = strlen(base);
|
||||
// Must start with base path
|
||||
if(path_len < base_len)
|
||||
continue;
|
||||
if(memcmp(path, base, base_len) != 0)
|
||||
continue;
|
||||
// The rest of it must not contain '..'
|
||||
if(strstr(path + base_len, "..") != NULL)
|
||||
continue;
|
||||
// Path is ok
|
||||
fprintf(stderr, "### guard: Path %s allowed within base %s\n", path, base);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "### guard: Path not allowed: %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE* (*r_fopen)(const char *path, const char *mode) = NULL;
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
static void ensure_initialized()
|
||||
{
|
||||
if(initialized)
|
||||
return;
|
||||
initialized = 1;
|
||||
r_fopen = dlsym(RTLD_NEXT, "fopen");
|
||||
fprintf(stderr, "### guard: Initialized.\n");
|
||||
}
|
||||
|
||||
// Urho3D's File uses fopen() on Linux and _wfopen() on Windows
|
||||
FILE* fopen(const char *path, const char *mode)
|
||||
{
|
||||
if(guard_enabled && !path_allowed(path)){
|
||||
return NULL;
|
||||
}
|
||||
ensure_initialized();
|
||||
return (*r_fopen)(path, mode);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user