From 479435fb1e3b24379b35e2a7e5df3d80e71fa507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Tue, 22 Jun 2021 09:22:59 +0300 Subject: [PATCH 1/2] [mapplauncherd] Check sailjaild for sandboxing of apps. JB#54498 OMP#JOLLA-178 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check before executing if the arguments don't contain sailjaild and the if the app should be launched in a sandbox. In that case prepend the arguments with sailjail. This is not sufficient alone as the apps are launched via silica-qt5 booster which can't execute sailjail so this must be also taken into account already before arguments reach booster. Signed-off-by: Tomi Leppänen --- src/common/sailjail.c | 40 +++++++++++++++++++++++++++++++++++-- src/common/sailjail.h | 4 ++++ src/launcherlib/appdata.cpp | 21 +++++++++++++++++++ src/launcherlib/appdata.h | 3 +++ src/launcherlib/booster.cpp | 13 +++++++++++- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/common/sailjail.c b/src/common/sailjail.c index cad0674..40b8957 100644 --- a/src/common/sailjail.c +++ b/src/common/sailjail.c @@ -26,6 +26,7 @@ #define SAILJAIL_KEY_ORGANIZATION_NAME "OrganizationName" #define SAILJAIL_KEY_APPLICATION_NAME "ApplicationName" #define SAILJAIL_KEY_PERMISSIONS "Permissions" +#define SAILJAIL_KEY_MODE "Mode" #define NEMO_KEY_APPLICATION_TYPE "X-Nemo-Application-Type" #define NEMO_KEY_SINGLE_INSTANCE "X-Nemo-Single-Instance" #define MAEMO_KEY_FIXED_ARGS "X-Maemo-Fixed-Args" @@ -49,6 +50,10 @@ #define PERMISSIONMGR_SIGNAL_APP_CHANGED "ApplicationChanged" #define PERMISSIONMGR_SIGNAL_APP_REMOVED "ApplicationRemoved" +/* Sailjaild errors */ +#define CODE_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +#define ERROR_INVALID_APPNAME "Invalid application name: " + static DBusConnection * sailjail_connect_bus(void) { @@ -121,7 +126,7 @@ iter_at(DBusMessageIter *iter, int type) static GHashTable * sailjail_application_info(DBusConnection *con, const char *desktop) { - GHashTable *info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); + GHashTable *info = NULL; DBusError err = DBUS_ERROR_INIT; DBusMessage *req = NULL; DBusMessage *rsp = NULL; @@ -140,7 +145,10 @@ sailjail_application_info(DBusConnection *con, const char *desktop) } if (!(rsp = dbus_connection_send_with_reply_and_block(con, req, DBUS_TIMEOUT_INFINITE, &err))) { - error("method call failed: %s: %s", err.name, err.message); + if (strcmp(err.name, CODE_INVALID_ARGS) || + strncmp(err.message, ERROR_INVALID_APPNAME, strlen(ERROR_INVALID_APPNAME))) { + error("method call failed: %s: %s", err.name, err.message); + } goto EXIT; } @@ -160,6 +168,8 @@ sailjail_application_info(DBusConnection *con, const char *desktop) goto EXIT; } + info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); + DBusMessageIter ArrayIter; dbus_message_iter_recurse(&bodyIter, &ArrayIter); while (!iter_at(&ArrayIter, DBUS_TYPE_INVALID)) { @@ -557,3 +567,29 @@ EXIT: return allowed; } + +bool sailjail_sandbox(const char *desktop) +{ + bool sandboxed = false; + DBusConnection *con = NULL; + GHashTable *info = NULL; + const char *mode; + + if (!(con = sailjail_connect_bus())) + goto EXIT; + + if (!(info = sailjail_application_info(con, desktop))) + goto EXIT; + + if ((mode = appinfo_string(info, SAILJAIL_KEY_MODE)) && g_strcmp0(mode, "None")) { + // Mode is either "Normal" or "Compatibility" + sandboxed = true; + } + +EXIT: + if (info) + g_hash_table_destroy(info); + sailjail_disconnect_bus(con); + + return sandboxed; +} diff --git a/src/common/sailjail.h b/src/common/sailjail.h index ae1d095..01ae508 100644 --- a/src/common/sailjail.h +++ b/src/common/sailjail.h @@ -6,8 +6,12 @@ G_BEGIN_DECLS +#define SAILJAIL_PATH "/usr/bin/sailjail" + bool sailjail_verify_launch(const char *desktop, const char **argv); +bool sailjail_sandbox(const char *desktop); + G_END_DECLS #endif // SAILJAIL_H_ diff --git a/src/launcherlib/appdata.cpp b/src/launcherlib/appdata.cpp index 4e9b8eb..a7535c7 100644 --- a/src/launcherlib/appdata.cpp +++ b/src/launcherlib/appdata.cpp @@ -19,6 +19,7 @@ #include "appdata.h" #include "protocol.h" +#include "sailjail.h" #include #include #include @@ -100,6 +101,16 @@ void AppData::setArgv(const char ** newArgv) } } +void AppData::prependArgv(const char * arg) +{ + char **oldArgv = m_argv; + m_argv = (char **)calloc(++m_argc + 1, sizeof *m_argv); + m_argv[0] = strdup(arg); + for (int i = 1; i < m_argc + 1; ++i) + m_argv[i] = oldArgv[i-1]; + free(oldArgv); +} + const char ** AppData::argv() const { return (const char **)m_argv; @@ -237,6 +248,16 @@ void AppData::checkPrivileges() And then, any file in /usr/share/mapplauncherd/privileges.d/ */ + + /* Sailjail does not use this system to gain privileged group. + * It is skipped to avoid unintended consequenses when launching + * other apps via sailjail. + */ + if (m_fileName == SAILJAIL_PATH) { + m_privileges.clear(); + return; + } + static const char *BOOSTER_APP_PRIVILEGES_LIST = "/usr/share/mapplauncherd/privileges"; static const char *BOOSTER_APP_PRIVILEGES_DIR = "/usr/share/mapplauncherd/privileges.d"; m_privileges = getPrivileges(BOOSTER_APP_PRIVILEGES_LIST); diff --git a/src/launcherlib/appdata.h b/src/launcherlib/appdata.h index 6a9ab08..855d668 100644 --- a/src/launcherlib/appdata.h +++ b/src/launcherlib/appdata.h @@ -72,6 +72,9 @@ public: //! Set address of the argument vector void setArgv(const char ** argv); + //! Prepend to argv + void prependArgv(const char *arg); + //! Return address of the argument vector const char ** argv() const; diff --git a/src/launcherlib/booster.cpp b/src/launcherlib/booster.cpp index 98e7436..e9ea568 100644 --- a/src/launcherlib/booster.cpp +++ b/src/launcherlib/booster.cpp @@ -57,6 +57,11 @@ #include "sailjail.h" +static std::string basename(const std::string &str) +{ + return str.substr(str.find_last_of("/") + 1); +} + Booster::Booster() : m_appData(new AppData), m_connection(NULL), @@ -287,6 +292,12 @@ int Booster::run(SocketManager * socketManager) if (boostedApplication() != "default") { if (!sailjail_verify_launch(boostedApplication().c_str(), m_appData->argv())) throw std::runtime_error("Booster: Binary doesn't have launch permissions\n"); + } else if (m_appData->fileName() != SAILJAIL_PATH && + sailjail_sandbox(basename(m_appData->fileName()).c_str())) { + Logger::logDebug("Sandboxing '%s'", m_appData->fileName()); + // Prepend sailjail to arguments + m_appData->prependArgv(SAILJAIL_PATH); + m_appData->setFileName(SAILJAIL_PATH); } return launchProcess(); @@ -701,7 +712,7 @@ void Booster::resetOomAdj() std::string Booster::getFinalName(const std::string &name) { - if (name == "/usr/bin/sailjail") { + if (name == SAILJAIL_PATH) { // This doesn't implement sailjail's parsing logic but instead // has some assumptions about the arguments: // - If there is --, then the application is From 4ec490169e5a822398490b4dd46cffce9e8229a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Tue, 22 Jun 2021 11:11:37 +0300 Subject: [PATCH 2/2] [invoker] Sandbox apps when requested. JB#54498 OMP#JOLLA-178 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forces use of generic booster when it detects that application should be sandboxed but it is not launched via sailjail already and prepends sailjail argument. Skips freeing of calloc'ed array. Signed-off-by: Tomi Leppänen --- src/invoker/CMakeLists.txt | 7 ++++--- src/invoker/invoker.c | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/invoker/CMakeLists.txt b/src/invoker/CMakeLists.txt index 78be60c..8ef4ade 100644 --- a/src/invoker/CMakeLists.txt +++ b/src/invoker/CMakeLists.txt @@ -3,12 +3,13 @@ set(COMMON "${CMAKE_HOME_DIRECTORY}/src/common") # Find dbus include(FindPkgConfig) pkg_check_modules(DBUS dbus-1 REQUIRED) +pkg_check_modules(GLIB glib-2.0 REQUIRED) # Set sources -set(SRC invokelib.c invoker.c ${COMMON}/report.c search.c) +set(SRC invokelib.c invoker.c ${COMMON}/report.c search.c ${COMMON}/sailjail.c) # Set include dirs -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${DBUS_INCLUDE_DIRS} ${COMMON}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${DBUS_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${COMMON}) # Set precompiler flags add_definitions(-DPROG_NAME_INVOKER="invoker") @@ -16,7 +17,7 @@ add_definitions(-DPROG_NAME_INVOKER="invoker") # Set target add_executable(invoker ${SRC}) -target_link_libraries(invoker ${DBUS_LDFLAGS}) +target_link_libraries(invoker ${DBUS_LDFLAGS} ${GLIB_LDFLAGS}) # Add install rule install(TARGETS invoker DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) diff --git a/src/invoker/invoker.c b/src/invoker/invoker.c index 90e6152..c659160 100644 --- a/src/invoker/invoker.c +++ b/src/invoker/invoker.c @@ -48,6 +48,7 @@ #include "protocol.h" #include "invokelib.h" #include "search.h" +#include "sailjail.h" #define BOOSTER_SESSION "silica-session" #define BOOSTER_GENERIC "generic" @@ -627,7 +628,7 @@ static unsigned int get_delay(char *delay_arg, char *param_name, return delay; } -static void notify_app_lauch(const char *desktop_file) +static void notify_app_launch(const char *desktop_file) { DBusConnection *connection; DBusMessage *message; @@ -651,6 +652,14 @@ static void notify_app_lauch(const char *desktop_file) } } +static bool ask_for_sandboxing(const char *app) +{ + char *path = strdup(app); + bool ret_val = sailjail_sandbox(basename(path)); + free(path); + return ret_val; +} + static int wait_for_launched_process_to_exit(int socket_fd) { int exit_status = EXIT_FAILURE; @@ -790,7 +799,7 @@ static int invoke_remote(int socket_fd, const InvokeArgs *args) invoker_send_end(socket_fd); if (args->desktop_file) - notify_app_lauch(args->desktop_file); + notify_app_launch(args->desktop_file); if (args->wait_term) { exit_status = wait_for_launched_process_to_exit(socket_fd), @@ -1080,6 +1089,25 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + // If arguments don't define sailjail and sailjaild says the app must be sandboxed, + // we force sandboxing here + if (strcmp(args.prog_name, SAILJAIL_PATH) && ask_for_sandboxing(args.prog_name)) { + // We must use generic booster here as nothing else would work + // to run sailjail which is not compiled for launching via booster + args.app_type = BOOSTER_GENERIC; + // Prepend sailjail + char **old_argv = args.prog_argv; + args.prog_argc += 2; + args.prog_argv = (char **)calloc(args.prog_argc + 1, sizeof *args.prog_argv); + args.prog_argv[0] = SAILJAIL_PATH; + args.prog_argv[1] = "--"; + for (int i = 2; i < args.prog_argc + 1; ++i) + args.prog_argv[i] = old_argv[i-2]; + // Don't free old_argv because it's probably not dynamically allocated + free(args.prog_name); + args.prog_name = strdup(SAILJAIL_PATH); + } + // Send commands to the launcher daemon info("Invoking execution: '%s'\n", args.prog_name); int ret_val = invoke(&args);