/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Alexander Larsson
*/
#include "config.h"
#include "flatpak-run-sockets-private.h"
/* Setup for simple sockets that only need one function goes in this file.
* Setup for more complicated sockets should go in its own file. */
#include "flatpak-run-cups-private.h"
#include "flatpak-run-pulseaudio-private.h"
#include "flatpak-run-x11-private.h"
#include "flatpak-utils-private.h"
/**
* flatpak_run_add_wayland_args:
*
* Returns: %TRUE if a Wayland socket was found.
*/
static gboolean
flatpak_run_add_wayland_args (FlatpakBwrap *bwrap)
{
const char *wayland_display;
g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
g_autofree char *wayland_socket = NULL;
g_autofree char *sandbox_wayland_socket = NULL;
gboolean res = FALSE;
struct stat statbuf;
wayland_display = g_getenv ("WAYLAND_DISPLAY");
if (!wayland_display)
wayland_display = "wayland-0";
if (wayland_display[0] == '/')
wayland_socket = g_strdup (wayland_display);
else
wayland_socket = g_build_filename (user_runtime_dir, wayland_display, NULL);
if (!g_str_has_prefix (wayland_display, "wayland-") ||
strchr (wayland_display, '/') != NULL)
{
wayland_display = "wayland-0";
flatpak_bwrap_set_env (bwrap, "WAYLAND_DISPLAY", wayland_display, TRUE);
}
sandbox_wayland_socket = g_strdup_printf ("/run/flatpak/%s", wayland_display);
if (stat (wayland_socket, &statbuf) == 0 &&
(statbuf.st_mode & S_IFMT) == S_IFSOCK)
{
res = TRUE;
flatpak_bwrap_add_args (bwrap,
"--ro-bind", wayland_socket, sandbox_wayland_socket,
NULL);
flatpak_bwrap_add_runtime_dir_member (bwrap, wayland_display);
}
return res;
}
static void
flatpak_run_add_gssproxy_args (FlatpakBwrap *bwrap)
{
/* We only expose the gssproxy user service. The gssproxy system service is
* not intended to be exposed to sandboxed environments.
*/
g_autofree char *gssproxy_host_dir = g_build_filename (g_get_user_runtime_dir (), "gssproxy", NULL);
const char *gssproxy_sandboxed_dir = "/run/flatpak/gssproxy/";
if (g_file_test (gssproxy_host_dir, G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap, "--ro-bind", gssproxy_host_dir, gssproxy_sandboxed_dir, NULL);
}
static void
flatpak_run_add_resolved_args (FlatpakBwrap *bwrap)
{
const char *resolved_socket = "/run/systemd/resolve/io.systemd.Resolve";
if (g_file_test (resolved_socket, G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap, "--bind", resolved_socket, resolved_socket, NULL);
}
static void
flatpak_run_add_journal_args (FlatpakBwrap *bwrap)
{
g_autofree char *journal_socket_socket = g_strdup ("/run/systemd/journal/socket");
g_autofree char *journal_stdout_socket = g_strdup ("/run/systemd/journal/stdout");
if (g_file_test (journal_socket_socket, G_FILE_TEST_EXISTS))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", journal_socket_socket, journal_socket_socket,
NULL);
}
if (g_file_test (journal_stdout_socket, G_FILE_TEST_EXISTS))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", journal_stdout_socket, journal_stdout_socket,
NULL);
}
}
static void
flatpak_run_add_pcsc_args (FlatpakBwrap *bwrap)
{
const char * pcsc_socket;
const char * sandbox_pcsc_socket = "/run/pcscd/pcscd.comm";
pcsc_socket = g_getenv ("PCSCLITE_CSOCK_NAME");
if (pcsc_socket)
{
if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS))
{
flatpak_bwrap_unset_env (bwrap, "PCSCLITE_CSOCK_NAME");
return;
}
}
else
{
pcsc_socket = "/run/pcscd/pcscd.comm";
if (!g_file_test (pcsc_socket, G_FILE_TEST_EXISTS))
return;
}
flatpak_bwrap_add_args (bwrap,
"--ro-bind", pcsc_socket, sandbox_pcsc_socket,
NULL);
flatpak_bwrap_set_env (bwrap, "PCSCLITE_CSOCK_NAME", sandbox_pcsc_socket, TRUE);
}
static void
flatpak_run_add_gpg_agent_args (FlatpakBwrap *bwrap)
{
const char * agent_socket;
g_autofree char * sandbox_agent_socket = NULL;
g_autoptr(GError) gpgconf_error = NULL;
g_autoptr(GSubprocess) process = NULL;
GInputStream *base_stream = NULL;
g_autoptr(GDataInputStream) data_stream = NULL;
process = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
&gpgconf_error,
"gpgconf", "--list-dir", "agent-socket", NULL);
if (gpgconf_error)
{
g_info ("GPG-Agent directories: %s", gpgconf_error->message);
return;
}
base_stream = g_subprocess_get_stdout_pipe (process);
data_stream = g_data_input_stream_new (base_stream);
agent_socket = g_data_input_stream_read_line (data_stream,
NULL, NULL,
&gpgconf_error);
if (!agent_socket || gpgconf_error)
{
g_info ("GPG-Agent directories: %s", gpgconf_error->message);
return;
}
sandbox_agent_socket = g_strdup_printf ("/run/user/%d/gnupg/S.gpg-agent", getuid ());
flatpak_bwrap_add_args (bwrap,
"--ro-bind-try", agent_socket, sandbox_agent_socket,
NULL);
}
static void
flatpak_run_add_ssh_args (FlatpakBwrap *bwrap)
{
static const char sandbox_auth_socket[] = "/run/flatpak/ssh-auth";
const char * auth_socket;
auth_socket = g_getenv ("SSH_AUTH_SOCK");
if (!auth_socket)
return; /* ssh agent not present */
if (!g_file_test (auth_socket, G_FILE_TEST_EXISTS))
{
/* Let's clean it up, so that the application will not try to connect */
flatpak_bwrap_unset_env (bwrap, "SSH_AUTH_SOCK");
return;
}
flatpak_bwrap_add_args (bwrap,
"--ro-bind", auth_socket, sandbox_auth_socket,
NULL);
flatpak_bwrap_set_env (bwrap, "SSH_AUTH_SOCK", sandbox_auth_socket, TRUE);
}
/*
* Expose sockets that are available for `flatpak build`, apply_extra, and
* `flatpak run`, except for D-Bus which is handled separately due to its
* use of a proxy.
*/
void
flatpak_run_add_socket_args_environment (FlatpakBwrap *bwrap,
FlatpakContextShares shares,
FlatpakContextSockets sockets)
{
gboolean has_wayland = FALSE;
gboolean allow_x11;
if (sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND)
{
g_info ("Allowing wayland access");
has_wayland = flatpak_run_add_wayland_args (bwrap);
}
if ((sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0)
allow_x11 = !has_wayland;
else
allow_x11 = (sockets & FLATPAK_CONTEXT_SOCKET_X11) != 0;
flatpak_run_add_x11_args (bwrap, allow_x11, shares);
if (sockets & FLATPAK_CONTEXT_SOCKET_SSH_AUTH)
{
flatpak_run_add_ssh_args (bwrap);
}
if (sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO)
{
g_info ("Allowing pulseaudio access");
flatpak_run_add_pulseaudio_args (bwrap, shares);
}
if (sockets & FLATPAK_CONTEXT_SOCKET_PCSC)
{
flatpak_run_add_pcsc_args (bwrap);
}
if (sockets & FLATPAK_CONTEXT_SOCKET_CUPS)
{
flatpak_run_add_cups_args (bwrap);
}
if (sockets & FLATPAK_CONTEXT_SOCKET_GPG_AGENT)
{
flatpak_run_add_gpg_agent_args (bwrap);
}
}
/*
* Expose sockets that are available for `flatpak run` only.
*/
void
flatpak_run_add_socket_args_late (FlatpakBwrap *bwrap,
FlatpakContextShares shares)
{
if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0)
{
flatpak_run_add_gssproxy_args (bwrap);
flatpak_run_add_resolved_args (bwrap);
}
flatpak_run_add_journal_args (bwrap);
}