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)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
project(buildat)
|
project(buildat)
|
||||||
|
|
||||||
@ -18,6 +21,7 @@ include_directories("3rdparty/smallsha1")
|
|||||||
# Global options
|
# 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_SERVER TRUE CACHE BOOL "Build server")
|
||||||
set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
|
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} -std=c++0x -Wall")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wno-unused-parameter")
|
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_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)
|
# Always output in color (useful when using head for build output)
|
||||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
|
||||||
# Security / crash protection
|
# Security / crash protection
|
||||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all")
|
#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)
|
if(BUILD_CLIENT)
|
||||||
# Client
|
# Client
|
||||||
set(CLIENT_EXE_NAME buildat_client)
|
|
||||||
set(CLIENT_SRCS
|
set(CLIENT_SRCS
|
||||||
|
src/guard/buildat_guard_interface.cpp
|
||||||
src/client/main.cpp
|
src/client/main.cpp
|
||||||
src/client/state.cpp
|
src/client/state.cpp
|
||||||
src/client/app.cpp
|
src/client/app.cpp
|
||||||
@ -84,7 +120,6 @@ endif(BUILD_CLIENT)
|
|||||||
|
|
||||||
if(BUILD_SERVER)
|
if(BUILD_SERVER)
|
||||||
# Server
|
# Server
|
||||||
set(SERVER_EXE_NAME buildat_server)
|
|
||||||
set(SERVER_SRCS
|
set(SERVER_SRCS
|
||||||
src/server/main.cpp
|
src/server/main.cpp
|
||||||
src/server/state.cpp
|
src/server/state.cpp
|
||||||
@ -110,4 +145,5 @@ if(BUILD_SERVER)
|
|||||||
${LINK_LIBS_ONLY}
|
${LINK_LIBS_ONLY}
|
||||||
)
|
)
|
||||||
endif(BUILD_SERVER)
|
endif(BUILD_SERVER)
|
||||||
|
|
||||||
# vim: set noet ts=4 sw=4:
|
# vim: set noet ts=4 sw=4:
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "client/config.h"
|
#include "client/config.h"
|
||||||
#include "client/state.h"
|
#include "client/state.h"
|
||||||
#include "interface/fs.h"
|
#include "interface/fs.h"
|
||||||
|
#include "guard/buildat_guard_interface.h"
|
||||||
#include <c55/getopt.h>
|
#include <c55/getopt.h>
|
||||||
#include <c55/os.h>
|
#include <c55/os.h>
|
||||||
#include <c55/string_util.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);
|
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_["WindowTitle"] = "Buildat Client";
|
||||||
engineParameters_["LogName"] = "client_Urho3D.log";
|
engineParameters_["LogName"] = "client_Urho3D.log";
|
||||||
engineParameters_["Headless"] = false;
|
engineParameters_["Headless"] = false;
|
||||||
@ -252,6 +263,10 @@ struct CApp: public App, public magic::Application
|
|||||||
throw AppStartupError("Could not initialize Lua environment");
|
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){
|
if(g_client_config.boot_to_menu){
|
||||||
ss_ extname = g_client_config.menu_extension_name;
|
ss_ extname = g_client_config.menu_extension_name;
|
||||||
ss_ script = ss_()+
|
ss_ script = ss_()+
|
||||||
@ -908,7 +923,7 @@ struct CApp: public App, public magic::Application
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// faatal_error(error: string)
|
// fatal_error(error: string)
|
||||||
static int l_fatal_error(lua_State *L)
|
static int l_fatal_error(lua_State *L)
|
||||||
{
|
{
|
||||||
ss_ error = lua_tocppstring(L, 1);
|
ss_ error = lua_tocppstring(L, 1);
|
||||||
|
@ -26,6 +26,14 @@ static bool check_file_writable(const ss_ &path)
|
|||||||
return writable;
|
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 Config::check_paths()
|
||||||
{
|
{
|
||||||
bool fail = false;
|
bool fail = false;
|
||||||
|
@ -14,6 +14,7 @@ namespace client
|
|||||||
bool boot_to_menu = false;
|
bool boot_to_menu = false;
|
||||||
ss_ menu_extension_name = "__menu";
|
ss_ menu_extension_name = "__menu";
|
||||||
|
|
||||||
|
void make_paths_absolute();
|
||||||
bool check_paths();
|
bool check_paths();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "client/config.h"
|
#include "client/config.h"
|
||||||
#include "client/state.h"
|
#include "client/state.h"
|
||||||
#include "client/app.h"
|
#include "client/app.h"
|
||||||
|
#include "guard/buildat_guard_interface.h"
|
||||||
#include <c55/getopt.h>
|
#include <c55/getopt.h>
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||||
@ -33,6 +34,20 @@ void signal_handler_init()
|
|||||||
(void)signal(SIGINT, sigint_handler);
|
(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()
|
void basic_init()
|
||||||
{
|
{
|
||||||
signal_handler_init();
|
signal_handler_init();
|
||||||
@ -42,6 +57,8 @@ void basic_init()
|
|||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
|
||||||
log_set_max_level(LOG_VERBOSE);
|
log_set_max_level(LOG_VERBOSE);
|
||||||
|
|
||||||
|
guard_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -100,6 +117,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.make_paths_absolute();
|
||||||
if(!config.check_paths()){
|
if(!config.check_paths()){
|
||||||
return 1;
|
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