From df7e7928b8f0254fde7c7e3efafc5f5dd63661aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Thu, 10 Jun 2021 17:43:48 +0300 Subject: [PATCH 1/5] Correct initialization of appinfo_file_t values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are appinfo_file_t but they were initialized with another enum's values. As the initial values don't matter, just use zeros instead. Signed-off-by: Tomi Leppänen --- daemon/appinfo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/appinfo.c b/daemon/appinfo.c index 7fd2065..1376399 100644 --- a/daemon/appinfo.c +++ b/daemon/appinfo.c @@ -772,9 +772,9 @@ appinfo_parse_desktop(appinfo_t *self) gchar *path2 = NULL; GError *err = NULL; gchar **permissions = NULL; - appinfo_file_t file1_state = APPINFO_STATE_UNSET; - appinfo_file_t file2_state = APPINFO_STATE_UNSET; - appinfo_file_t combined = APPINFO_STATE_UNSET; + appinfo_file_t file1_state = 0; + appinfo_file_t file2_state = 0; + appinfo_file_t combined = 0; path1 = path_from_desktop_name(appinfo_id(self)); path2 = alt_path_from_desktop_name(appinfo_id(self)); From 8d8f4f140dd185e16ed370ed5dafa01d10a5b313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Thu, 10 Jun 2021 16:34:38 +0300 Subject: [PATCH 2/5] Add legacy .config and .cache directories. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add also legacy binary name based .config and .cache directories because apps have stored their data in those directories before. Signed-off-by: Tomi Leppänen --- daemon/sailjailclient.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon/sailjailclient.c b/daemon/sailjailclient.c index da07481..e225eac 100644 --- a/daemon/sailjailclient.c +++ b/daemon/sailjailclient.c @@ -762,6 +762,8 @@ client_launch_application(client_t *self) /* Legacy app binary based data directories are made available. * But only if they already exist. */ client_add_firejail_directory(self, false, "${HOME}/.local/share/%s", binary_name); + client_add_firejail_directory(self, false, "${HOME}/.config/%s", binary_name); + client_add_firejail_directory(self, false, "${HOME}/.cache/%s", binary_name); if( !empty_p(org_name) && !empty_p(app_name) ) { client_add_firejail_directory(self, true, "${HOME}/.cache/%s/%s", org_name, app_name); From 2bf682b3f84a0ef243028b947d544e62f6aad5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Thu, 10 Jun 2021 18:15:23 +0300 Subject: [PATCH 3/5] [daemon] Add mode property. JB#54497 OMP#JOLLA-177 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This tells if the app needs legacy app compatibility or not with provision for adding more values in the future, such as opting out of sandboxing or mandatory sandboxing. Signed-off-by: Tomi Leppänen --- daemon/appinfo.c | 34 ++++++++++++++++++++++++++++++++++ daemon/appinfo.h | 7 +++++++ 2 files changed, 41 insertions(+) diff --git a/daemon/appinfo.c b/daemon/appinfo.c index 1376399..9911017 100644 --- a/daemon/appinfo.c +++ b/daemon/appinfo.c @@ -145,6 +145,8 @@ const gchar *appinfo_get_method (const appinfo_t *self); const gchar *appinfo_get_organization_name(const appinfo_t *self); const gchar *appinfo_get_application_name (const appinfo_t *self); const gchar *appinfo_get_data_directory (const appinfo_t *self); +app_mode_t appinfo_get_mode (const appinfo_t *self); +static const gchar *appinfo_get_mode_string (const appinfo_t *self); void appinfo_set_name (appinfo_t *self, const gchar *name); void appinfo_set_type (appinfo_t *self, const gchar *type); void appinfo_set_icon (appinfo_t *self, const gchar *icon); @@ -156,6 +158,7 @@ void appinfo_set_method (appinfo_t *self, const gch void appinfo_set_organization_name(appinfo_t *self, const gchar *organization_name); void appinfo_set_application_name (appinfo_t *self, const gchar *application_name); void appinfo_set_data_directory (appinfo_t *self, const gchar *data_directory); +void appinfo_set_mode (appinfo_t *self, app_mode_t mode); /* ------------------------------------------------------------------------- * * APPINFO_PERMISSIONS @@ -213,6 +216,7 @@ struct appinfo_t appinfo_state_t anf_state; time_t anf_dt_ctime[APPINFO_DIR_COUNT]; bool anf_dirty; + app_mode_t anf_mode; // desktop properties gchar *anf_dt_name; // DESKTOP_KEY_NAME @@ -250,6 +254,8 @@ appinfo_ctor(appinfo_t *self, applications_t *applications, const gchar *id) self->anf_dt_ctime[APPINFO_DIR_ALT] = -1; self->anf_dirty = false; + self->anf_mode = APP_MODE_NORMAL; + self->anf_dt_name = NULL; self->anf_dt_type = NULL; self->anf_dt_icon = NULL; @@ -366,6 +372,7 @@ appinfo_to_variant(const appinfo_t *self) if( self ) { add_string("Id", appinfo_id(self)); + add_string("Mode", appinfo_get_mode_string(self)); /* Desktop properties */ @@ -548,6 +555,22 @@ appinfo_get_data_directory(const appinfo_t *self) return self->anf_sj_data_directory ?: appinfo_unknown; } +app_mode_t +appinfo_get_mode(const appinfo_t *self) +{ + return self->anf_mode; +} + +static const gchar * +appinfo_get_mode_string(const appinfo_t *self) +{ + static const gchar * const lut[] = { + [APP_MODE_NORMAL] = "Normal", + [APP_MODE_COMPATIBILITY] = "Compatibility", + }; + return lut[self->anf_mode]; +} + /* - - - - - - - - - - - - - - - - - - - * * Setters * - - - - - - - - - - - - - - - - - - - */ @@ -629,6 +652,15 @@ appinfo_set_data_directory(appinfo_t *self, const gchar *data_directory) appinfo_set_dirty(self); } +void +appinfo_set_mode(appinfo_t *self, app_mode_t mode) +{ + if( self->anf_mode != mode ) { + self->anf_mode = mode; + appinfo_set_dirty(self); + } +} + /* ------------------------------------------------------------------------- * * APPINFO_PERMISSIONS * ------------------------------------------------------------------------- */ @@ -851,6 +883,8 @@ appinfo_parse_desktop(appinfo_t *self) group = SAILJAIL_SECTION_SECONDARY; /* else: legacy app => use default profile */ + appinfo_set_mode(self, group ? APP_MODE_NORMAL : APP_MODE_COMPATIBILITY); + stringset_t *set; if( group ) { tmp = keyfile_get_string(ini, group, SAILJAIL_KEY_ORGANIZATION_NAME, 0), diff --git a/daemon/appinfo.h b/daemon/appinfo.h index d9e9c84..23cd891 100644 --- a/daemon/appinfo.h +++ b/daemon/appinfo.h @@ -52,6 +52,11 @@ typedef struct appinfo_t appinfo_t; typedef struct config_t config_t; typedef struct control_t control_t; +typedef enum { + APP_MODE_NORMAL, + APP_MODE_COMPATIBILITY, +} app_mode_t; + /* ========================================================================= * * Prototypes * ========================================================================= */ @@ -95,6 +100,7 @@ const gchar *appinfo_get_method (const appinfo_t *self); const gchar *appinfo_get_organization_name(const appinfo_t *self); const gchar *appinfo_get_application_name (const appinfo_t *self); const gchar *appinfo_get_data_directory (const appinfo_t *self); +app_mode_t appinfo_get_mode (const appinfo_t *self); void appinfo_set_name (appinfo_t *self, const gchar *name); void appinfo_set_type (appinfo_t *self, const gchar *type); void appinfo_set_icon (appinfo_t *self, const gchar *icon); @@ -106,6 +112,7 @@ void appinfo_set_method (appinfo_t *self, const gchar *method) void appinfo_set_organization_name(appinfo_t *self, const gchar *organization_name); void appinfo_set_application_name (appinfo_t *self, const gchar *application_name); void appinfo_set_data_directory (appinfo_t *self, const gchar *data_directory); +void appinfo_set_mode (appinfo_t *self, app_mode_t mode); /* ------------------------------------------------------------------------- * * APPINFO_PERMISSIONS From 9eddd3663effe2d67161a29d10470a38b34759c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Thu, 10 Jun 2021 18:17:51 +0300 Subject: [PATCH 4/5] [client] Allow sandboxing of legacy apps. Fixes JB#54497 OMP#JOLLA-177 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some directories that the apps need and allow defining Exec without absolute path. Signed-off-by: Tomi Leppänen --- daemon/Makefile | 1 + daemon/sailjailclient.c | 42 +++++++++++++++++++++++++++++++++-------- daemon/util.h | 4 ++++ rpm/sailjail.spec | 1 + 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/daemon/Makefile b/daemon/Makefile index 09a2655..3a61661 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -70,6 +70,7 @@ CPPFLAGS += -DVERSION='"${VERSION}"' CPPFLAGS += -DSYSCONFDIR='"${_SYSCONFDIR}"' CPPFLAGS += -DSHAREDSTATEDIR='"${_SHAREDSTATEDIR}"' CPPFLAGS += -DDATADIR='"${_DATADIR}"' +CPPFLAGS += -DBINDIR='"${_BINDIR}"' CPPFLAGS += -D_GNU_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 diff --git a/daemon/sailjailclient.c b/daemon/sailjailclient.c index e225eac..08c938a 100644 --- a/daemon/sailjailclient.c +++ b/daemon/sailjailclient.c @@ -101,6 +101,7 @@ const char *client_sailjail_data_directory (client_t *self) const char **client_sailjail_application_permissions(client_t *self); const char *client_maemo_service (client_t *self); const char *client_maemo_method (client_t *self); +const char *client_mode (client_t *self); /* ------------------------------------------------------------------------- * * CLIENT_PROPERTIES @@ -162,7 +163,7 @@ static int sailjailclient_get_field_code(const char *arg); static bool sailjailclient_is_option (const char *arg); static bool sailjailclient_ignore_arg (const char *arg); static bool sailjailclient_match_argv (const char **tpl_argv, const char **app_argv); -static bool sailjailclient_validate_argv (const char *exec, const gchar **app_argv); +static bool sailjailclient_validate_argv (const char *exec, const gchar **app_argv, bool use_compatibility); static bool sailjailclient_test_elf (const char *filename); static void sailjailclient_print_usage (const char *progname); static bool sailjailclient_binary_check (const char *binary_path); @@ -355,6 +356,12 @@ client_maemo_method(client_t *self) return client_get_appinfo_string(self, MAEMO_KEY_METHOD); } +const char * +client_mode(client_t *self) +{ + return client_get_appinfo_string(self, "Mode"); +} + /* ------------------------------------------------------------------------- * * CLIENT_PROPERTIES * ------------------------------------------------------------------------- */ @@ -732,12 +739,14 @@ client_launch_application(client_t *self) goto EXIT; } + bool use_compatibility = !g_strcmp0(client_mode(self), "Compatibility"); + if( booster_name ) { /* Application booster validates Exec line when it gets * launch requrest from invoker. */ } - else if( !sailjailclient_validate_argv(exec, argv) ) { + else if( !sailjailclient_validate_argv(exec, argv, use_compatibility) ) { log_err("Command line does not match template"); goto EXIT; } @@ -760,10 +769,10 @@ client_launch_application(client_t *self) client_add_firejail_option(self, "--whitelist=%s", desktop1_path); /* Legacy app binary based data directories are made available. - * But only if they already exist. */ - client_add_firejail_directory(self, false, "${HOME}/.local/share/%s", binary_name); - client_add_firejail_directory(self, false, "${HOME}/.config/%s", binary_name); - client_add_firejail_directory(self, false, "${HOME}/.cache/%s", binary_name); + * But they are not created unless legacy app sandboxing is used. */ + client_add_firejail_directory(self, use_compatibility, "${HOME}/.local/share/%s", binary_name); + client_add_firejail_directory(self, use_compatibility, "${HOME}/.config/%s", binary_name); + client_add_firejail_directory(self, use_compatibility, "${HOME}/.cache/%s", binary_name); if( !empty_p(org_name) && !empty_p(app_name) ) { client_add_firejail_directory(self, true, "${HOME}/.cache/%s/%s", org_name, app_name); @@ -1041,7 +1050,7 @@ sailjailclient_match_argv(const char **tpl_argv, const char **app_argv) } static bool -sailjailclient_validate_argv(const char *exec, const gchar **app_argv) +sailjailclient_validate_argv(const char *exec, const gchar **app_argv, bool use_compatibility) { bool validated = false; GError *err = NULL; @@ -1052,6 +1061,11 @@ sailjailclient_validate_argv(const char *exec, const gchar **app_argv) goto EXIT; } + if( use_compatibility && strncmp(app_argv[0], BINDIR "/", sizeof BINDIR) ) { + log_err("Legacy apps must be in: " BINDIR "/"); + goto EXIT; + } + /* Split desktop Exec line into argv */ if( !g_shell_parse_argv(exec, NULL, &exec_argv, &err) ) { log_err("Exec line parse failure: %s", err->message); @@ -1066,11 +1080,17 @@ sailjailclient_validate_argv(const char *exec, const gchar **app_argv) /* Expectation: Exec line might have leading 'wrapper' executables * such as sailjail, invoker, etc -> make an attempt to skip those * by looking for argv[0] for command we are about to launch. + * + * While sandboxing aware apps must use absolute path, legacy apps + * usually don't and they must reside in BINDIR. */ const char **tpl_argv = (const char **)exec_argv; + const char *alternative = app_argv[0] + sizeof (BINDIR "/") - 1; for( ; *tpl_argv; ++tpl_argv ) { if( !g_strcmp0(*tpl_argv, app_argv[0]) ) break; + if( use_compatibility && !g_strcmp0(*tpl_argv, alternative) ) + break; } if( !*tpl_argv ) { @@ -1078,6 +1098,12 @@ sailjailclient_validate_argv(const char *exec, const gchar **app_argv) goto EXIT; } + if( use_compatibility && strncmp(*tpl_argv, BINDIR "/", sizeof BINDIR) ) { + char *temp = (char *)*tpl_argv; + *tpl_argv = g_strconcat(BINDIR "/", temp, NULL); + g_free(temp); + } + if( !sailjailclient_match_argv(tpl_argv, app_argv) ) { gchar *args = g_strjoinv(" ", (gchar **)app_argv); log_err("Application args do not match Exec line template"); @@ -1331,7 +1357,7 @@ sailjailclient_main(int argc, char **argv) if( match_exec ) { if( sailjailclient_validate_argv(match_exec, - client_get_argv(client, NULL)) ) + client_get_argv(client, NULL), false) ) exit_code = EXIT_SUCCESS; goto EXIT; } diff --git a/daemon/util.h b/daemon/util.h index d430e31..f220a47 100644 --- a/daemon/util.h +++ b/daemon/util.h @@ -51,6 +51,10 @@ G_BEGIN_DECLS # define VERSION "0.0.0" # endif +# ifndef BINDIR +# define BINDIR "/usr/bin" +# endif + # ifndef SYSCONFDIR # define SYSCONFDIR "/etc" # endif diff --git a/rpm/sailjail.spec b/rpm/sailjail.spec index 4332145..910cc7a 100644 --- a/rpm/sailjail.spec +++ b/rpm/sailjail.spec @@ -87,6 +87,7 @@ install -D -m755 tools/measure_launch_time.py \ make \ _SYSCONFDIR=%{_sysconfdir}\ _DATADIR=%{_datadir}\ + _BINDIR=%{_bindir}\ _LIBDIR=%{_libdir}\ _USERUNITDIR=%{_userunitdir}\ _UNITDIR=%{_unitdir}\ From ede2eb61ddfb1d5ee6ab2e031abeb9434e2112d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomi=20Lepp=C3=A4nen?= Date: Mon, 14 Jun 2021 17:53:56 +0300 Subject: [PATCH 5/5] [sailjaild] Add opt out option. JB#54498 OMP#JOLLA-178 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add opt out option for sandboxing. This allows app to opt out from default sandboxing. The option should to be validated in the package if the contained app should not be allowed to opt out. This adds "None" mode for sandboxing. This mode is a hint for launcher to skip sailjail but it's still possible to launch an app with sandboxing in none mode. In that case it will start in compatibility mode with default profile. Also add option to enable default profile so that "Compatibility" mode is reported only when default profile is enabled. Signed-off-by: Tomi Leppänen --- daemon/appinfo.c | 24 +++++++++++++++++++++--- daemon/appinfo.h | 1 + daemon/sailjailclient.c | 3 ++- daemon/util.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/daemon/appinfo.c b/daemon/appinfo.c index 9911017..817970c 100644 --- a/daemon/appinfo.c +++ b/daemon/appinfo.c @@ -95,6 +95,7 @@ typedef struct * ========================================================================= */ #define APPINFO_DEFAULT_PROFILE_SECTION "Default Profile" +#define APPINFO_KEY_ENABLED "Enabled" /* ========================================================================= * * Prototypes @@ -567,6 +568,7 @@ appinfo_get_mode_string(const appinfo_t *self) static const gchar * const lut[] = { [APP_MODE_NORMAL] = "Normal", [APP_MODE_COMPATIBILITY] = "Compatibility", + [APP_MODE_NONE] = "None", }; return lut[self->anf_mode]; } @@ -883,10 +885,15 @@ appinfo_parse_desktop(appinfo_t *self) group = SAILJAIL_SECTION_SECONDARY; /* else: legacy app => use default profile */ - appinfo_set_mode(self, group ? APP_MODE_NORMAL : APP_MODE_COMPATIBILITY); + /* Sandboxing=Disabled means that the app opts out of sandboxing and + * launching via sailjail will result in use of compatibility mode. + */ + gchar *sandboxing = NULL; + if( group ) + sandboxing = keyfile_get_string(ini, group, SAILJAIL_KEY_SANDBOXING, 0); stringset_t *set; - if( group ) { + if( group && g_strcmp0(sandboxing, "Disabled") ) { tmp = keyfile_get_string(ini, group, SAILJAIL_KEY_ORGANIZATION_NAME, 0), appinfo_set_organization_name(self, tmp), g_free(tmp); @@ -900,15 +907,26 @@ appinfo_parse_desktop(appinfo_t *self) g_free(tmp); set = keyfile_get_stringset(ini, group, SAILJAIL_KEY_PERMISSIONS); + + appinfo_set_mode(self, APP_MODE_NORMAL); } else { /* Read default profile from configuration */ - set = config_stringset(appinfo_config(self), + const config_t *config = appinfo_config(self); + set = config_stringset(config, APPINFO_DEFAULT_PROFILE_SECTION, SAILJAIL_KEY_PERMISSIONS); + if( !g_strcmp0(sandboxing, "Disabled") || + !config_boolean(config, + APPINFO_DEFAULT_PROFILE_SECTION, + APPINFO_KEY_ENABLED, false) ) + appinfo_set_mode(self, APP_MODE_NONE); + else + appinfo_set_mode(self, APP_MODE_COMPATIBILITY); } appinfo_set_permissions(self, set); stringset_delete(set); + g_free(sandboxing); /* Validate */ if( appinfo_get_name(self) != appinfo_unknown && diff --git a/daemon/appinfo.h b/daemon/appinfo.h index 23cd891..1781d0f 100644 --- a/daemon/appinfo.h +++ b/daemon/appinfo.h @@ -55,6 +55,7 @@ typedef struct control_t control_t; typedef enum { APP_MODE_NORMAL, APP_MODE_COMPATIBILITY, + APP_MODE_NONE, } app_mode_t; /* ========================================================================= * diff --git a/daemon/sailjailclient.c b/daemon/sailjailclient.c index 08c938a..0e2da23 100644 --- a/daemon/sailjailclient.c +++ b/daemon/sailjailclient.c @@ -739,7 +739,8 @@ client_launch_application(client_t *self) goto EXIT; } - bool use_compatibility = !g_strcmp0(client_mode(self), "Compatibility"); + /* Interpret both "Compatibility" and "None" as compatibility mode */ + bool use_compatibility = g_strcmp0(client_mode(self), "Normal"); if( booster_name ) { /* Application booster validates Exec line when it gets diff --git a/daemon/util.h b/daemon/util.h index f220a47..24433a6 100644 --- a/daemon/util.h +++ b/daemon/util.h @@ -122,6 +122,7 @@ G_BEGIN_DECLS # define SAILJAIL_KEY_APPLICATION_NAME "ApplicationName" # define SAILJAIL_KEY_DATA_DIRECTORY "DataDirectory" # define SAILJAIL_KEY_PERMISSIONS "Permissions" +# define SAILJAIL_KEY_SANDBOXING "Sandboxing" # define NEMO_KEY_APPLICATION_TYPE "X-Nemo-Application-Type" # define NEMO_KEY_SINGLE_INSTANCE "X-Nemo-Single-Instance"