diff --git a/CMakeLists.txt b/CMakeLists.txt index 863f682..d81e626 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,6 @@ +# http://www.apache.org/licenses/LICENSE-2.0 +# Copyright 2014 Perttu Ahola + 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: diff --git a/src/client/app.cpp b/src/client/app.cpp index 3e03509..1852697 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -5,6 +5,7 @@ #include "client/config.h" #include "client/state.h" #include "interface/fs.h" +#include "guard/buildat_guard_interface.h" #include #include #include @@ -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); diff --git a/src/client/config.cpp b/src/client/config.cpp index e4cd20c..e9b2d86 100644 --- a/src/client/config.cpp +++ b/src/client/config.cpp @@ -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; diff --git a/src/client/config.h b/src/client/config.h index 202ec71..da1ef12 100644 --- a/src/client/config.h +++ b/src/client/config.h @@ -14,6 +14,7 @@ namespace client bool boot_to_menu = false; ss_ menu_extension_name = "__menu"; + void make_paths_absolute(); bool check_paths(); }; } diff --git a/src/client/main.cpp b/src/client/main.cpp index abf6c7f..a2f1108 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -5,6 +5,7 @@ #include "client/config.h" #include "client/state.h" #include "client/app.h" +#include "guard/buildat_guard_interface.h" #include #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; } diff --git a/src/guard/buildat_guard_interface.cpp b/src/guard/buildat_guard_interface.cpp new file mode 100644 index 0000000..cdf92f6 --- /dev/null +++ b/src/guard/buildat_guard_interface.cpp @@ -0,0 +1,64 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#ifndef _WIN32 + #include "core/log.h" + #include + #define __GNU_SOURCE // RTLD_NEXT + #ifndef __USE_GNU + #define __USE_GNU // RTLD_NEXT + #endif + #include + #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 diff --git a/src/guard/buildat_guard_interface.h b/src/guard/buildat_guard_interface.h new file mode 100644 index 0000000..3b591f7 --- /dev/null +++ b/src/guard/buildat_guard_interface.h @@ -0,0 +1,15 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola + +// 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); +} diff --git a/src/guard/guard_launcher.sh b/src/guard/guard_launcher.sh new file mode 100644 index 0000000..abb40d7 --- /dev/null +++ b/src/guard/guard_launcher.sh @@ -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" $@ diff --git a/src/guard/lib.c b/src/guard/lib.c new file mode 100644 index 0000000..1dd9415 --- /dev/null +++ b/src/guard/lib.c @@ -0,0 +1,111 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#include +#include +#define __GNU_SOURCE // RTLD_NEXT +#ifndef __USE_GNU + #define __USE_GNU // RTLD_NEXT +#endif +#include + +// 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