mirror of https://github.com/cutefishos/appmotor
[mapplauncerd] Sandboxed application boosters. JB#53844 OMP#JOLLA-43
When booster is executing in sandbox as an applicatiom booster, it needs to verify that command line received from invoker matches Exec line in application desktop file, application launch is allowed, and permissions granted are as was expected at the time of booster launch. Provide booster-generic@.service that can be used for instantiating sandboxed application boosters. D-Bus ipc with sailjaild is modified version of similar code in sailjailclient. The biggest difference is that this version uses private connection via libdbus to avoid leaving stray dbus connections or threads behind when transferring control to application code without use of exec*() functions. Remove cap_sys_ptrace from booster executable as makes it impossible to run the booster within a no-new-privs sandbox. Fix socket passing from booster instance to booster daemon so that it works also when invoker is running in different namespace than booster instance (invoker pid might be unresolvable). Replace ad-hoc booster argument parsing with getopt_long(). Fix issues with argv handling: using const pointers for non-const data, passing data by reference between objects that might have different lifespans and never releasing the dynamically allocated arrays. Fix issues with env passing: duplicating invoker env at booster side as-is can lead to problems like loss of customg session bus socket address that has been set up by firejail. If booster bumps into command read problems, bailout immediately instead of relying on out-of sequence data possibly triggering exit due to unknown commands. As an enabler for sharing code between invoker (written in c) and daemon (written in c++), modify Logger class used by c++ code so that it is just a wrapper for logging functionality used by invoker. Signed-off-by: Simo Piiroinen <simo.piiroinen@jolla.com>pull/1/head
parent
f24871adb2
commit
833b551af1
@ -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