From c4b79348848c24a50766643c4dc28851708454eb Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 28 Mar 2017 14:50:17 +0200 Subject: [PATCH] client-info: Replace desktop ID detection For newer (>= 0.6.10) versions of Flatpak, the way to export the desktop ID has changed from requiring cgroups to not requiring it. See https://github.com/flatpak/flatpak/releases/tag/0.6.10 This changes the private gclue_client_info_get_xdg_id() API in that a NULL xdg_id should be considered to be disqualifying: " Like parse_app_info_from_fileinfo(), returns NULL on failure, "" if not sandboxed, and a desktop ID otherwise " This ensures that sandboxed applications can't change their own desktop ID through any means. https://bugs.freedesktop.org/show_bug.cgi?id=97776 --- src/gclue-client-info.c | 121 ++++++++++++++++++++++++++++++--------------- src/gclue-service-client.c | 30 +++++++++-- 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/src/gclue-client-info.c b/src/gclue-client-info.c index 1a47669..59f7b3c 100644 --- a/src/gclue-client-info.c +++ b/src/gclue-client-info.c @@ -21,6 +21,10 @@ */ #include +#include +#include +#include +#include #include "gclue-client-info.h" @@ -181,52 +185,85 @@ on_name_vanished (GDBusConnection *connection, 0); } -/* Based on got_credentials_cb() from xdg-app source code */ -static char * -get_xdg_id (guint32 pid) +/* Based on xdp_get_app_id_from_pid() from xdg-desktop-portal * + * Returns NULL on failure, keyfile with name "" if not sandboxed, and full app-info otherwise */ +static GKeyFile * +parse_app_info_from_fileinfo (int pid) { - char *xdg_id = NULL; - g_autofree char *path = NULL; - g_autofree char *content = NULL; - gchar **lines; - int i; - - path = g_strdup_printf ("/proc/%u/cgroup", pid); - - if (!g_file_get_contents (path, &content, NULL, NULL)) - return NULL; - lines = g_strsplit (content, "\n", -1); - - for (i = 0; lines[i] != NULL; i++) { - const char *unit = lines[i] + strlen ("1:name=systemd:"); - g_autofree char *scope = NULL; - const char *name; - char *dash; - - if (!g_str_has_prefix (lines[i], "1:name=systemd:")) - continue; - - scope = g_path_get_basename (unit); - if ((!g_str_has_prefix (scope, "xdg-app-") && - !g_str_has_prefix (scope, "flatpak-")) || - !g_str_has_suffix (scope, ".scope")) - break; - - /* strlen("flatpak-") == strlen("xdg-app-") - * so all is good here */ - name = scope + strlen("xdg-app-"); - dash = strchr (name, '-'); + g_autofree char *root_path = NULL; + g_autofree char *path = NULL; + g_autofree char *content = NULL; + g_autofree char *app_id = NULL; + int root_fd = -1; + int info_fd = -1; + struct stat stat_buf; + g_autoptr(GError) local_error = NULL; + g_autoptr(GMappedFile) mapped = NULL; + g_autoptr(GKeyFile) metadata = NULL; + + root_path = g_strdup_printf ("/proc/%u/root", pid); + root_fd = openat (AT_FDCWD, root_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (root_fd == -1) + { + /* Not able to open the root dir shouldn't happen. Probably the app died and + we're failing due to /proc/$pid not existing. In that case fail instead + of treating this as privileged. */ + return NULL; + } + + metadata = g_key_file_new (); + + info_fd = openat (root_fd, ".flatpak-info", O_RDONLY | O_CLOEXEC | O_NOCTTY); + close (root_fd); + if (info_fd == -1) + { + if (errno == ENOENT) + { + /* No file => on the host */ + g_key_file_set_string (metadata, "Application", "name", ""); + return g_steal_pointer (&metadata); + } - if (dash == NULL) - break; + /* Some weird error => failure */ + return NULL; + } + + if (fstat (info_fd, &stat_buf) != 0 || !S_ISREG (stat_buf.st_mode)) + { + /* Some weird fd => failure */ + close (info_fd); + return NULL; + } + + mapped = g_mapped_file_new_from_fd (info_fd, FALSE, &local_error); + if (mapped == NULL) + { + close (info_fd); + return NULL; + } + + if (!g_key_file_load_from_data (metadata, + g_mapped_file_get_contents (mapped), + g_mapped_file_get_length (mapped), + G_KEY_FILE_NONE, &local_error)) + { + close (info_fd); + return NULL; + } + + return g_steal_pointer (&metadata); +} - *dash = 0; - xdg_id = g_strdup (name); - } +static char * +get_xdg_id (guint32 pid) +{ + g_autoptr(GKeyFile) app_info = NULL; - g_strfreev (lines); + app_info = parse_app_info_from_fileinfo (pid); + if (app_info == NULL) + return NULL; - return xdg_id; + return g_key_file_get_string (app_info, "Application", "name", NULL); } static void @@ -437,6 +474,8 @@ gclue_client_info_check_bus_name (GClueClientInfo *info, return (strcmp (bus_name, info->priv->bus_name) == 0); } +/* Like parse_app_info_from_fileinfo(), returns NULL on failure, + * "" if not sandboxed, and a desktop ID otherwise */ const char * gclue_client_info_get_xdg_id (GClueClientInfo *info) { diff --git a/src/gclue-service-client.c b/src/gclue-service-client.c index eecf090..e8a591a 100644 --- a/src/gclue-service-client.c +++ b/src/gclue-service-client.c @@ -420,18 +420,38 @@ gclue_service_client_handle_start (GClueDBusClient *client, } desktop_id = gclue_client_info_get_xdg_id (priv->client_info); - if (desktop_id == NULL) - /* Non-xdg app */ - desktop_id = gclue_dbus_client_get_desktop_id (client); - if (desktop_id == NULL) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, - "'DesktopId' property must be set"); + "Failed to read Flatpak application information"); return TRUE; } + if (*desktop_id == '\0') { + /* Non-flatpak app */ + desktop_id = gclue_dbus_client_get_desktop_id (client); + + if (desktop_id == NULL) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "'DesktopId' property must be set"); + return TRUE; + } + } else { + const char *property; + + property = gclue_dbus_client_get_desktop_id (client); + if (property != NULL && g_strcmp0 (property, desktop_id) != 0) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "'DesktopId' property does not match Flatpak information"); + return TRUE; + } + } + config = gclue_config_get_singleton (); uid = gclue_client_info_get_user_id (priv->client_info); app_perm = gclue_config_get_app_perm (config, -- 2.12.0