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/appinfo.c b/daemon/appinfo.c index 7fd2065..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 @@ -145,6 +146,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 +159,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 +217,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 +255,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 +373,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 +556,23 @@ 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", + [APP_MODE_NONE] = "None", + }; + return lut[self->anf_mode]; +} + /* - - - - - - - - - - - - - - - - - - - * * Setters * - - - - - - - - - - - - - - - - - - - */ @@ -629,6 +654,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 * ------------------------------------------------------------------------- */ @@ -772,9 +806,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)); @@ -851,8 +885,15 @@ appinfo_parse_desktop(appinfo_t *self) group = SAILJAIL_SECTION_SECONDARY; /* else: legacy app => use default profile */ + /* 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); @@ -866,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 d9e9c84..1781d0f 100644 --- a/daemon/appinfo.h +++ b/daemon/appinfo.h @@ -52,6 +52,12 @@ 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_NONE, +} app_mode_t; + /* ========================================================================= * * Prototypes * ========================================================================= */ @@ -95,6 +101,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 +113,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 diff --git a/daemon/sailjailclient.c b/daemon/sailjailclient.c index da07481..0e2da23 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,15 @@ client_launch_application(client_t *self) goto EXIT; } + /* 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 * 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,8 +770,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); + * 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); @@ -1039,7 +1051,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; @@ -1050,6 +1062,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); @@ -1064,11 +1081,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 ) { @@ -1076,6 +1099,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"); @@ -1329,7 +1358,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..24433a6 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 @@ -118,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" diff --git a/rpm/sailjail.spec b/rpm/sailjail.spec index ec7bfa1..b0d5f58 100644 --- a/rpm/sailjail.spec +++ b/rpm/sailjail.spec @@ -88,6 +88,7 @@ install -D -m755 tools/measure_launch_time.py \ make \ _SYSCONFDIR=%{_sysconfdir}\ _DATADIR=%{_datadir}\ + _BINDIR=%{_bindir}\ _LIBDIR=%{_libdir}\ _USERUNITDIR=%{_userunitdir}\ _UNITDIR=%{_unitdir}\