mirror of https://github.com/cutefishos/appmotor
Merge pull request #2 from sailfishos/jb53844_sandboxed_boosters
Sandboxed application boosterspull/1/head
commit
402d9fde2f
@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Generic application launch booster (sandboxed)
|
||||||
|
Requires=dbus.socket booster-silica-session.path lipstick.service
|
||||||
|
After=dbus.service booster-silica-session.path lipstick.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/invoker --type=silica-session -- /usr/bin/sailjail --profile=%i -- /usr/libexec/mapplauncherd/booster-generic --application=%i
|
||||||
|
Restart=always
|
||||||
|
RestartSec=1
|
||||||
|
OOMScoreAdjust=-250
|
||||||
@ -0,0 +1,559 @@
|
|||||||
|
#include "sailjail.h"
|
||||||
|
#include "report.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
|
/* Standard desktop properties */
|
||||||
|
#define DESKTOP_SECTION "Desktop Entry"
|
||||||
|
#define DESKTOP_KEY_NAME "Name"
|
||||||
|
#define DESKTOP_KEY_TYPE "Type"
|
||||||
|
#define DESKTOP_KEY_ICON "Icon"
|
||||||
|
#define DESKTOP_KEY_EXEC "Exec"
|
||||||
|
#define DESKTOP_KEY_NO_DISPLAY "NoDisplay"
|
||||||
|
|
||||||
|
/* Maemo desktop properties */
|
||||||
|
#define MAEMO_SECTION "Desktop Entry"
|
||||||
|
#define MAEMO_KEY_SERVICE "X-Maemo-Service"
|
||||||
|
#define MAEMO_KEY_OBJECT "X-Maemo-Object-Path"
|
||||||
|
#define MAEMO_KEY_METHOD "X-Maemo-Method"
|
||||||
|
|
||||||
|
/* Sailjail desktop properties */
|
||||||
|
#define SAILJAIL_SECTION_PRIMARY "X-Sailjail"
|
||||||
|
#define SAILJAIL_SECTION_SECONDARY "Sailjail"
|
||||||
|
#define SAILJAIL_KEY_ORGANIZATION_NAME "OrganizationName"
|
||||||
|
#define SAILJAIL_KEY_APPLICATION_NAME "ApplicationName"
|
||||||
|
#define SAILJAIL_KEY_PERMISSIONS "Permissions"
|
||||||
|
#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"
|
||||||
|
#define OSSO_KEY_SERVICE "X-Osso-Service"
|
||||||
|
|
||||||
|
/* Sailjaild D-Bus service */
|
||||||
|
#define PERMISSIONMGR_SERVICE "org.sailfishos.sailjaild1"
|
||||||
|
#define PERMISSIONMGR_INTERFACE "org.sailfishos.sailjaild1"
|
||||||
|
#define PERMISSIONMGR_OBJECT "/org/sailfishos/sailjaild1"
|
||||||
|
#define PERMISSIONMGR_METHOD_PROMPT "PromptLaunchPermissions"
|
||||||
|
#define PERMISSIONMGR_METHOD_QUERY "QueryLaunchPermissions"
|
||||||
|
#define PERMISSIONMGR_METHOD_GET_APPLICATIONS "GetApplications"
|
||||||
|
#define PERMISSIONMGR_METHOD_GET_APPINFO "GetAppInfo"
|
||||||
|
#define PERMISSIONMGR_METHOD_GET_LICENSE "GetLicenseAgreed"
|
||||||
|
#define PERMISSIONMGR_METHOD_SET_LICENSE "SetLicenseAgreed"
|
||||||
|
#define PERMISSIONMGR_METHOD_GET_LAUNCHABLE "GetLaunchAllowed"
|
||||||
|
#define PERMISSIONMGR_METHOD_SET_LAUNCHABLE "SetLaunchAllowed"
|
||||||
|
#define PERMISSIONMGR_METHOD_GET_GRANTED "GetGrantedPermissions"
|
||||||
|
#define PERMISSIONMGR_METHOD_SET_GRANTED "SetGrantedPermissions"
|
||||||
|
#define PERMISSIONMGR_SIGNAL_APP_ADDED "ApplicationAdded"
|
||||||
|
#define PERMISSIONMGR_SIGNAL_APP_CHANGED "ApplicationChanged"
|
||||||
|
#define PERMISSIONMGR_SIGNAL_APP_REMOVED "ApplicationRemoved"
|
||||||
|
|
||||||
|
static DBusConnection *
|
||||||
|
sailjail_connect_bus(void)
|
||||||
|
{
|
||||||
|
DBusError err = DBUS_ERROR_INIT;
|
||||||
|
DBusConnection *con = 0;
|
||||||
|
|
||||||
|
if (!(con = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err))) {
|
||||||
|
error("system bus connect failed: %s: %s",
|
||||||
|
err.name, err.message);
|
||||||
|
} else {
|
||||||
|
warning("PRIVATE CONNECTION %p CONNECTED", con);
|
||||||
|
dbus_connection_set_exit_on_disconnect(con, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_error_free(&err);
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sailjail_disconnect_bus(DBusConnection *con)
|
||||||
|
{
|
||||||
|
if (con) {
|
||||||
|
warning("PRIVATE CONNECTION %p DISCONNECTED", con);
|
||||||
|
dbus_connection_close(con);
|
||||||
|
dbus_connection_unref(con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant *
|
||||||
|
appinfo_variant(GHashTable *info, const char *key)
|
||||||
|
{
|
||||||
|
return g_hash_table_lookup(info, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
appinfo_string(GHashTable *info, const char *key)
|
||||||
|
{
|
||||||
|
const char *value = NULL;
|
||||||
|
GVariant *variant = appinfo_variant(info, key);
|
||||||
|
if (variant) {
|
||||||
|
const GVariantType *type = g_variant_get_type(variant);
|
||||||
|
if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING))
|
||||||
|
value = g_variant_get_string(variant, NULL);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char **
|
||||||
|
appinfo_strv(GHashTable *info, const char *key)
|
||||||
|
{
|
||||||
|
const char **value = NULL;
|
||||||
|
GVariant *variant = appinfo_variant(info, key);
|
||||||
|
if (variant) {
|
||||||
|
const GVariantType *type = g_variant_get_type(variant);
|
||||||
|
if (g_variant_type_equal(type, G_VARIANT_TYPE("as")))
|
||||||
|
value = g_variant_get_strv(variant, NULL);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
iter_at(DBusMessageIter *iter, int type)
|
||||||
|
{
|
||||||
|
int have = dbus_message_iter_get_arg_type(iter);
|
||||||
|
if (have != type && type != 0)
|
||||||
|
warning("Expected: %c got: %c", type ?: '?', have ?: '?');
|
||||||
|
return have == 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);
|
||||||
|
DBusError err = DBUS_ERROR_INIT;
|
||||||
|
DBusMessage *req = NULL;
|
||||||
|
DBusMessage *rsp = NULL;
|
||||||
|
|
||||||
|
if (!(req = dbus_message_new_method_call(PERMISSIONMGR_SERVICE,
|
||||||
|
PERMISSIONMGR_OBJECT,
|
||||||
|
PERMISSIONMGR_INTERFACE,
|
||||||
|
PERMISSIONMGR_METHOD_GET_APPINFO))) {
|
||||||
|
error("failed to create dbus method call");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbus_message_append_args(req, DBUS_TYPE_STRING, &desktop, DBUS_TYPE_INVALID)) {
|
||||||
|
error("failed to add method call args");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbus_set_error_from_message(&err, rsp)) {
|
||||||
|
error("error reply received: %s: %s", err.name, err.message);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter bodyIter;
|
||||||
|
if (!dbus_message_iter_init(rsp, &bodyIter)) {
|
||||||
|
error("empty reply received");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iter_at(&bodyIter, DBUS_TYPE_ARRAY)) {
|
||||||
|
error("reply is not an array");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBusMessageIter ArrayIter;
|
||||||
|
dbus_message_iter_recurse(&bodyIter, &ArrayIter);
|
||||||
|
while (!iter_at(&ArrayIter, DBUS_TYPE_INVALID)) {
|
||||||
|
if (!iter_at(&ArrayIter, DBUS_TYPE_DICT_ENTRY)) {
|
||||||
|
error("reply is not an array of dict entries");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
DBusMessageIter dictIter;
|
||||||
|
dbus_message_iter_recurse(&ArrayIter, &dictIter);
|
||||||
|
dbus_message_iter_next(&ArrayIter);
|
||||||
|
if (!iter_at(&dictIter, DBUS_TYPE_STRING)) {
|
||||||
|
error("key is not a string");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
const char *key = NULL;
|
||||||
|
dbus_message_iter_get_basic(&dictIter, &key);
|
||||||
|
dbus_message_iter_next(&dictIter);
|
||||||
|
if (!iter_at(&dictIter, DBUS_TYPE_VARIANT)) {
|
||||||
|
error("values is not a variant");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
DBusMessageIter variantIter;
|
||||||
|
dbus_message_iter_recurse(&dictIter, &variantIter);
|
||||||
|
dbus_message_iter_next(&dictIter);
|
||||||
|
DBusBasicValue value;
|
||||||
|
switch (dbus_message_iter_get_arg_type(&variantIter)) {
|
||||||
|
case DBUS_TYPE_INT32:
|
||||||
|
dbus_message_iter_get_basic(&variantIter, &value);
|
||||||
|
warning("%s = int32:%d", key, value.i32);
|
||||||
|
g_hash_table_insert(info, g_strdup(key), g_variant_new_int32(value.i32));
|
||||||
|
break;
|
||||||
|
case DBUS_TYPE_UINT32:
|
||||||
|
dbus_message_iter_get_basic(&variantIter, &value);
|
||||||
|
warning("%s = uint32:%d", key, value.u32);
|
||||||
|
g_hash_table_insert(info, g_strdup(key), g_variant_new_uint32(value.u32));
|
||||||
|
break;
|
||||||
|
case DBUS_TYPE_BOOLEAN:
|
||||||
|
dbus_message_iter_get_basic(&variantIter, &value);
|
||||||
|
warning("%s = bool:%d", key, value.bool_val);
|
||||||
|
g_hash_table_insert(info, g_strdup(key), g_variant_new_boolean(value.bool_val));
|
||||||
|
break;
|
||||||
|
case DBUS_TYPE_STRING:
|
||||||
|
dbus_message_iter_get_basic(&variantIter, &value);
|
||||||
|
warning("%s = string:'%s'", key, value.str);
|
||||||
|
g_hash_table_insert(info, g_strdup(key), g_variant_new_string(value.str));
|
||||||
|
break;
|
||||||
|
case DBUS_TYPE_ARRAY:
|
||||||
|
if (dbus_message_iter_get_element_type(&variantIter) != DBUS_TYPE_STRING) {
|
||||||
|
error("only arrays of strings are supported");
|
||||||
|
} else {
|
||||||
|
int n = dbus_message_iter_get_element_count(&variantIter);
|
||||||
|
DBusMessageIter valueIter;
|
||||||
|
dbus_message_iter_recurse(&variantIter, &valueIter);
|
||||||
|
char **v = g_malloc0_n(n + 1, sizeof *v);
|
||||||
|
int i = 0;
|
||||||
|
while (i < n) {
|
||||||
|
if (!iter_at(&valueIter, DBUS_TYPE_STRING))
|
||||||
|
break;
|
||||||
|
dbus_message_iter_get_basic(&valueIter, &value);
|
||||||
|
warning("%s[%d] = string:'%s'", key, i, value.str);
|
||||||
|
|
||||||
|
dbus_message_iter_next(&valueIter);
|
||||||
|
v[i++] = g_strdup(value.str);
|
||||||
|
}
|
||||||
|
v[i] = NULL;
|
||||||
|
g_hash_table_insert(info, g_strdup(key), g_variant_new_strv((const gchar *const *)v, i));
|
||||||
|
g_strfreev(v);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("reply contains unhandled variant types");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
if (info && g_hash_table_size(info) < 1) {
|
||||||
|
/* There should always be at least app id key. If we get
|
||||||
|
* empty hash table, either parser is not working or there
|
||||||
|
* are other kinds of problems.
|
||||||
|
*/
|
||||||
|
error("no information about application '%s'", desktop);
|
||||||
|
g_hash_table_destroy(info), info = NULL;
|
||||||
|
}
|
||||||
|
dbus_error_free(&err);
|
||||||
|
if (rsp)
|
||||||
|
dbus_message_unref(rsp);
|
||||||
|
if (req)
|
||||||
|
dbus_message_unref(req);
|
||||||
|
warning("info received = %s", info ? "true" : "false");
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **
|
||||||
|
sailjail_prompt_permissions(DBusConnection *con, const char *desktop)
|
||||||
|
{
|
||||||
|
char **granted = NULL;
|
||||||
|
DBusError err = DBUS_ERROR_INIT;
|
||||||
|
DBusMessage *req = NULL;
|
||||||
|
DBusMessage *rsp = NULL;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (!(req = dbus_message_new_method_call(PERMISSIONMGR_SERVICE,
|
||||||
|
PERMISSIONMGR_OBJECT,
|
||||||
|
PERMISSIONMGR_INTERFACE,
|
||||||
|
PERMISSIONMGR_METHOD_PROMPT))) {
|
||||||
|
error("failed to create dbus method call");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbus_message_append_args(req,
|
||||||
|
DBUS_TYPE_STRING, &desktop,
|
||||||
|
DBUS_TYPE_INVALID)) {
|
||||||
|
error("failed to add method call args");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbus_set_error_from_message(&err, rsp)) {
|
||||||
|
error("error reply received: %s: %s", err.name, err.message);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbus_message_get_args(rsp, &err,
|
||||||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &granted, &len,
|
||||||
|
DBUS_TYPE_INVALID)) {
|
||||||
|
error("parsing reply failed: %s: %s", err.name, err.message);
|
||||||
|
dbus_free_string_array(granted), granted = NULL;
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
dbus_error_free(&err);
|
||||||
|
if (rsp)
|
||||||
|
dbus_message_unref(rsp);
|
||||||
|
if (req)
|
||||||
|
dbus_message_unref(req);
|
||||||
|
|
||||||
|
warning("launch permitted = %s", granted ? "true" : "false");
|
||||||
|
return granted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sailjailclient_get_field_code(const char *arg)
|
||||||
|
{
|
||||||
|
// Non-null string starting with a '%' followed by exactly one character
|
||||||
|
return arg && arg[0] == '%' && arg[1] && !arg[2] ? arg[1] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sailjailclient_is_option(const char *arg)
|
||||||
|
{
|
||||||
|
// Non-null string starting with a hyphen
|
||||||
|
return arg && arg[0] == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sailjailclient_ignore_arg(const char *arg)
|
||||||
|
{
|
||||||
|
return !g_strcmp0(arg, "-prestart");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sailjailclient_match_argv(const char **tpl_argv, const char **app_argv)
|
||||||
|
{
|
||||||
|
bool matching = false;
|
||||||
|
|
||||||
|
/* Rule out template starting with a field code */
|
||||||
|
if (sailjailclient_get_field_code(*tpl_argv)) {
|
||||||
|
error("Exec line starts with field code");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match each arg in template */
|
||||||
|
for (;;) {
|
||||||
|
const char *want = *tpl_argv++;
|
||||||
|
|
||||||
|
/* Allow some slack e.g. regarding "-prestart" options */
|
||||||
|
while (*app_argv && g_strcmp0(*app_argv, want) &&
|
||||||
|
sailjailclient_ignore_arg(*app_argv)) {
|
||||||
|
warning("ignoring argument: %s", *app_argv);
|
||||||
|
++app_argv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!want) {
|
||||||
|
/* Template args exhausted */
|
||||||
|
if (*app_argv) {
|
||||||
|
/* Excess application args */
|
||||||
|
error("argv has unwanted '%s'", *app_argv);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int field_code = sailjailclient_get_field_code(want);
|
||||||
|
|
||||||
|
if (!field_code) {
|
||||||
|
/* Exact match needed */
|
||||||
|
if (g_strcmp0(*app_argv, want)) {
|
||||||
|
/* Application args has something else */
|
||||||
|
error("argv is missing '%s'", want);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
++app_argv;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Field code explanations from "Desktop Entry Specification"
|
||||||
|
*
|
||||||
|
* https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
|
||||||
|
*/
|
||||||
|
int code_args = 0;
|
||||||
|
switch (field_code) {
|
||||||
|
case 'f': /* A single file name (or none) */
|
||||||
|
case 'u': /* A single URL (or none) */
|
||||||
|
code_args = -1;
|
||||||
|
break;
|
||||||
|
case 'c': /* The translated name of the application */
|
||||||
|
case 'k': /* The location of the desktop file */
|
||||||
|
code_args = 1;
|
||||||
|
break;
|
||||||
|
case 'F': /* A list of files */
|
||||||
|
case 'U': /* A list of URLs */
|
||||||
|
code_args = INT_MIN;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
/* The Icon key of the desktop entry expanded as two
|
||||||
|
* arguments, first --icon and then the value of the
|
||||||
|
* Icon key. Should not expand to any arguments if
|
||||||
|
* the Icon key is empty or missing.
|
||||||
|
*/
|
||||||
|
if (!g_strcmp0(*app_argv, "--icon"))
|
||||||
|
++app_argv, code_args = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
case 'v':
|
||||||
|
case 'm':
|
||||||
|
/* Deprecated */
|
||||||
|
error("Exec line has deprecated field code '%s'", want);
|
||||||
|
goto EXIT;
|
||||||
|
default:
|
||||||
|
/* Unknown */
|
||||||
|
error("Exec line has unknown field code '%s'", want);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code_args < 0) {
|
||||||
|
/* Variable number of args */
|
||||||
|
if (sailjailclient_get_field_code(*tpl_argv)) {
|
||||||
|
error("Can't validate '%s %s' combination", want, *tpl_argv);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
for (; code_args < 0; ++code_args) {
|
||||||
|
if (!*app_argv || !g_strcmp0(*app_argv, *tpl_argv))
|
||||||
|
break;
|
||||||
|
if (sailjailclient_is_option(*app_argv)) {
|
||||||
|
error("option '%s' at field code '%s'", *app_argv, want);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
++app_argv;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Specified number of args */
|
||||||
|
for (; code_args > 0; --code_args) {
|
||||||
|
if (!*app_argv) {
|
||||||
|
error("missing args for field code '%s'", want);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
if (sailjailclient_is_option(*app_argv)) {
|
||||||
|
error("option '%s' at field code '%s'", *app_argv, want);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
++app_argv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matching = true;
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
return matching;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sailjailclient_validate_argv(const char *exec, const gchar **app_argv)
|
||||||
|
{
|
||||||
|
bool validated = false;
|
||||||
|
GError *err = NULL;
|
||||||
|
gchar **exec_argv = NULL;
|
||||||
|
|
||||||
|
if (!app_argv || !*app_argv) {
|
||||||
|
error("application argv not defined");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split desktop Exec line into argv */
|
||||||
|
if (!g_shell_parse_argv(exec, NULL, &exec_argv, &err)) {
|
||||||
|
error("Exec line parse failure: %s", err->message);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exec_argv || !*exec_argv) {
|
||||||
|
error("Exec line not defined");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
const char **tpl_argv = (const char **)exec_argv;
|
||||||
|
for (; *tpl_argv; ++tpl_argv) {
|
||||||
|
if (!g_strcmp0(*tpl_argv, app_argv[0]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*tpl_argv) {
|
||||||
|
error("Exec line does not contain '%s'", *app_argv);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sailjailclient_match_argv(tpl_argv, app_argv)) {
|
||||||
|
gchar *args = g_strjoinv(" ", (gchar **)app_argv);
|
||||||
|
error("Application args do not match Exec line template");
|
||||||
|
error("exec: %s", exec);
|
||||||
|
error("args: %s", args);
|
||||||
|
g_free(args);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
validated = true;
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
g_strfreev(exec_argv);
|
||||||
|
g_clear_error(&err);
|
||||||
|
|
||||||
|
return validated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sailjail_verify_launch(const char *desktop, const char **argv)
|
||||||
|
{
|
||||||
|
bool allowed = false;
|
||||||
|
DBusConnection *con = NULL;
|
||||||
|
char **granted = NULL;
|
||||||
|
GHashTable *info = NULL;
|
||||||
|
const char **requested = NULL;
|
||||||
|
const char *exec = NULL;
|
||||||
|
|
||||||
|
if (!(con = sailjail_connect_bus()))
|
||||||
|
goto EXIT;
|
||||||
|
|
||||||
|
if (!(info = sailjail_application_info(con, desktop)))
|
||||||
|
goto EXIT;
|
||||||
|
|
||||||
|
if (!(exec = appinfo_string(info, DESKTOP_KEY_EXEC))) {
|
||||||
|
error("no Exec line defined for application '%s'", desktop);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sailjailclient_validate_argv(exec, argv))
|
||||||
|
goto EXIT;
|
||||||
|
|
||||||
|
if (!(requested = appinfo_strv(info, SAILJAIL_KEY_PERMISSIONS))) {
|
||||||
|
error("no permissions defined for application '%s'", desktop);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("prompting permissions for application '%s'", desktop);
|
||||||
|
if (!(granted = sailjail_prompt_permissions(con, desktop)))
|
||||||
|
goto EXIT;
|
||||||
|
|
||||||
|
for (int i = 0; requested[i]; ++i) {
|
||||||
|
if (!g_strv_contains((const gchar *const *)granted, requested[i])) {
|
||||||
|
error("application '%s' has not been granted '%s' permission",
|
||||||
|
desktop, requested[i]);
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed = true;
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
if (info)
|
||||||
|
g_hash_table_destroy(info);
|
||||||
|
g_strfreev(granted);
|
||||||
|
sailjail_disconnect_bus(con);
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef SAILJAIL_H_
|
||||||
|
# define SAILJAIL_H_
|
||||||
|
|
||||||
|
# include <stdbool.h>
|
||||||
|
# include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
bool sailjail_verify_launch(const char *desktop, const char **argv);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // SAILJAIL_H_
|
||||||
Loading…
Reference in New Issue