flatpak-run: Add parental controls support for filtering apps

Prevent the user from running a flatpak app if that app is filtered by
the parental controls applied to the user.

If flatpak is running as a system user (UID < 1000), ignore failure to
load the app filter. This could happen if a flatpak is run in the
gnome-initial-setup session, before the user’s account is created.

Includes contributions by André Magalhães.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://github.com/flatpak/flatpak/pull/2797
This commit is contained in:
Philip Withnall 2019-06-25 13:51:41 +01:00 committed by Alexander Larsson
parent c16d6f9166
commit ab5c0968e6
4 changed files with 108 additions and 0 deletions

View File

@ -161,6 +161,7 @@ libflatpak_common_la_CFLAGS = \
$(INTERNAL_GPGME_CFLAGS) \
$(JSON_CFLAGS) \
$(LIBSECCOMP_CFLAGS) \
$(MALCONTENT_CFLAGS) \
$(OSTREE_CFLAGS) \
$(SOUP_CFLAGS) \
$(SYSTEMD_CFLAGS) \
@ -175,6 +176,7 @@ libflatpak_common_la_LIBADD = \
$(INTERNAL_GPGME_LIBS) \
$(JSON_LIBS) \
$(LIBSECCOMP_LIBS) \
$(MALCONTENT_LIBS) \
$(OSTREE_LIBS) \
$(SOUP_LIBS) \
$(SYSTEMD_LIBS) \

View File

@ -23,6 +23,7 @@
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <gio/gdesktopappinfo.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h>
@ -36,6 +37,9 @@
#ifdef HAVE_DCONF
#include <dconf/dconf.h>
#endif
#ifdef HAVE_LIBMALCONTENT
#include <libmalcontent/malcontent.h>
#endif
#ifdef ENABLE_SECCOMP
#include <seccomp.h>
@ -3167,6 +3171,92 @@ regenerate_ld_cache (GPtrArray *base_argv_array,
return glnx_steal_fd (&ld_so_fd);
}
/* Check that this user is actually allowed to run this app. When running
* from the gnome-initial-setup session, an app filter might not be available. */
static gboolean
check_parental_controls (const char *app_ref,
FlatpakDeploy *deploy,
GCancellable *cancellable,
GError **error)
{
#ifdef HAVE_LIBMALCONTENT
g_auto(GStrv) app_ref_parts = NULL;
g_autoptr(MctManager) manager = NULL;
g_autoptr(MctAppFilter) app_filter = NULL;
g_autoptr(GAsyncResult) app_filter_result = NULL;
g_autoptr(GDBusConnection) system_bus = NULL;
g_autoptr(GError) local_error = NULL;
g_autoptr(GDesktopAppInfo) app_info = NULL;
gboolean allowed = FALSE;
app_ref_parts = flatpak_decompose_ref (app_ref, error);
if (app_ref_parts == NULL)
return FALSE;
system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
if (system_bus == NULL)
return FALSE;
manager = mct_manager_new (system_bus);
app_filter = mct_manager_get_app_filter (manager, getuid (),
MCT_GET_APP_FILTER_FLAGS_INTERACTIVE,
cancellable, &local_error);
if (g_error_matches (local_error, MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED))
{
g_debug ("Skipping parental controls check for %s since parental "
"controls are disabled globally", app_ref);
return TRUE;
}
else if (local_error != NULL)
{
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
/* Always filter by app ID. Additionally, filter by app info (which runs
* multiple checks, including whether the app ID, executable path and
* content types are allowed) if available. If the flatpak contains
* multiple .desktop files, we use the main one. The app ID check is
* always done, as the binary executed by `flatpak run` isnt necessarily
* extracted from a .desktop file. */
allowed = mct_app_filter_is_flatpak_ref_allowed (app_filter, app_ref);
/* Look up the apps main .desktop file. */
if (deploy != NULL && allowed)
{
g_autoptr(GFile) deploy_dir = NULL;
const char *deploy_path;
g_autofree char *desktop_file_name = NULL;
g_autofree char *desktop_file_path = NULL;
deploy_dir = flatpak_deploy_get_dir (deploy);
deploy_path = flatpak_file_get_path_cached (deploy_dir);
desktop_file_name = g_strconcat (app_ref_parts[1], ".desktop", NULL);
desktop_file_path = g_build_path (G_DIR_SEPARATOR_S,
deploy_path,
"export",
"share",
"applications",
desktop_file_name,
NULL);
app_info = g_desktop_app_info_new_from_filename (desktop_file_path);
}
if (app_info != NULL)
allowed = allowed && mct_app_filter_is_appinfo_allowed (app_filter,
G_APP_INFO (app_info));
if (!allowed)
return flatpak_fail_error (error, FLATPAK_ERROR_PERMISSION_DENIED,
/* Translators: The placeholder is for an app ref. */
_("Running %s is not allowed by the policy set by your administrator"),
app_ref);
#endif /* HAVE_LIBMALCONTENT */
return TRUE;
}
gboolean
flatpak_run_app (const char *app_ref,
FlatpakDeploy *app_deploy,
@ -3225,6 +3315,11 @@ flatpak_run_app (const char *app_ref,
if (app_ref_parts == NULL)
return FALSE;
/* Check the user is allowed to run this flatpak. */
if (!check_parental_controls (app_ref, app_deploy, cancellable, error))
return FALSE;
/* Construct the bwrap context. */
bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ());

View File

@ -222,6 +222,11 @@ PKG_CHECK_MODULES(SYSTEMD, [libsystemd], [have_libsystemd=yes], [have_libsystemd
if test $have_libsystemd = yes; then
AC_DEFINE(HAVE_LIBSYSTEMD, 1, [Define if libsystemd is available])
fi
PKG_CHECK_MODULES([MALCONTENT], [malcontent-0 >= 0.4.0], [have_libmalcontent=yes], [have_libmalcontent=no])
AS_IF([test "$have_libmalcontent" = "yes"],[
AC_DEFINE([HAVE_LIBMALCONTENT], [1], [Define if libmalcontent is available])
])
AM_CONDITIONAL([HAVE_LIBMALCONTENT],[test "$have_libmalcontent" = "yes"])
PKG_CHECK_MODULES(GLIB260, glib-2.0 >= 2.60,
[AC_DEFINE(GLIB_VERSION_MIN_REQUIRED, GLIB_VERSION_2_60, [Ignore massive GTimeVal deprecation warnings in 2.62])],
@ -532,4 +537,5 @@ echo " Privileged group: $PRIVILEGED_GROUP"
echo " Privilege mode: $with_priv_mode"
echo " Use dconf: $have_dconf"
echo " Use libsystemd: $have_libsystemd"
echo " Use libmalcontent: $have_libmalcontent"
echo ""

View File

@ -112,6 +112,11 @@
Flatpak sets the environment variable <envar>FLATPAK_ID</envar> to the application
ID of the running app.
</para>
<para>
If parental controls support is enabled, flatpak will check the
current users parental controls settings, and will refuse to
run an app if it is blacklisted for the current user.
</para>
</refsect1>
<refsect1>