mirror of https://github.com/cutefishos/appmotor
				
				
				
			Remove sailjail.c
							parent
							
								
									6040742b60
								
							
						
					
					
						commit
						bb3384e7fb
					
				@ -1,624 +0,0 @@
 | 
				
			|||||||
#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 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"
 | 
					 | 
				
			||||||
#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"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Sailjaild errors */
 | 
					 | 
				
			||||||
#define CODE_INVALID_ARGS     "org.freedesktop.DBus.Error.InvalidArgs"
 | 
					 | 
				
			||||||
#define ERROR_INVALID_APPNAME "Invalid application name: "
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Directories */
 | 
					 | 
				
			||||||
#define BINDIR "/usr/bin"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const gchar *
 | 
					 | 
				
			||||||
path_basename(const gchar *path)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    const gchar *base = NULL;
 | 
					 | 
				
			||||||
    if( path ) {
 | 
					 | 
				
			||||||
        gchar *end = strrchr(path, '/');
 | 
					 | 
				
			||||||
        base = end ? end + 1 : path;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return base;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool
 | 
					 | 
				
			||||||
path_dirname_eq(const char *path, const char *target)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gchar *dir_path = g_path_get_dirname(path);
 | 
					 | 
				
			||||||
    bool result = !strcmp(dir_path, target);
 | 
					 | 
				
			||||||
    g_free(dir_path);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 = NULL;
 | 
					 | 
				
			||||||
    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))) {
 | 
					 | 
				
			||||||
        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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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)) {
 | 
					 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
            debug("%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);
 | 
					 | 
				
			||||||
            debug("%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);
 | 
					 | 
				
			||||||
            debug("%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);
 | 
					 | 
				
			||||||
            debug("%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);
 | 
					 | 
				
			||||||
                    debug("%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);
 | 
					 | 
				
			||||||
    debug("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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    info("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;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* 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.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * App may also be defined without absolute path, in which case it
 | 
					 | 
				
			||||||
     * must reside in BINDIR and it must not have any 'wrapper'
 | 
					 | 
				
			||||||
     * executables. Thus check the path of app_argv[0] and compare Exec
 | 
					 | 
				
			||||||
     * line to the binary name.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    const char **tpl_argv = (const char **)exec_argv;
 | 
					 | 
				
			||||||
    if (!path_dirname_eq(app_argv[0], BINDIR) ||
 | 
					 | 
				
			||||||
            g_strcmp0(*tpl_argv, path_basename(app_argv[0]))) {
 | 
					 | 
				
			||||||
        /* App is not specified without path as the first argument,
 | 
					 | 
				
			||||||
         * => there might be 'wrappers' and we match to full path.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Argument zero has been checked already */
 | 
					 | 
				
			||||||
    if (!sailjailclient_match_argv(tpl_argv + 1, app_argv + 1)) {
 | 
					 | 
				
			||||||
        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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,17 +0,0 @@
 | 
				
			|||||||
#ifndef SAILJAIL_H_
 | 
					 | 
				
			||||||
# define SAILJAIL_H_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# include <stdbool.h>
 | 
					 | 
				
			||||||
# include <glib.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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_
 | 
					 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue