mirror of
https://github.com/flatpak/flatpak.git
synced 2026-01-28 09:38:20 -05:00
We rely on broadcast signals for authenticator replies rather than unicast as these are not filtered by the sandbox (due to them being opt-in by the receiver). Actually this already worked fine in the flatpak side as the generated code already subscribes to the signals, this just switches the internal authenticators (test and oci) to using the new way to emit signals.
403 lines
12 KiB
C
403 lines
12 KiB
C
/*
|
|
* Copyright © 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <locale.h>
|
|
|
|
#include "flatpak-auth-private.h"
|
|
#include "flatpak-dbus-generated.h"
|
|
|
|
static GMainLoop *main_loop;
|
|
static guint name_owner_id = 0;
|
|
FlatpakAuthenticator *authenticator;
|
|
|
|
typedef struct {
|
|
FlatpakAuthenticatorRequest *request;
|
|
GSocketService *server;
|
|
char **arg_refs;
|
|
} TokenRequestData;
|
|
|
|
static void
|
|
token_request_data_free (TokenRequestData *data)
|
|
{
|
|
g_clear_object (&data->request);
|
|
g_socket_service_stop (data->server);
|
|
g_clear_object (&data->server);
|
|
g_strfreev (data->arg_refs);
|
|
g_free (data);
|
|
}
|
|
|
|
static TokenRequestData *
|
|
token_request_data_new (FlatpakAuthenticatorRequest *request,
|
|
GSocketService *server,
|
|
const gchar *const *arg_refs)
|
|
{
|
|
TokenRequestData *data = g_new0 (TokenRequestData, 1);
|
|
data->request = g_object_ref (request);
|
|
data->server = g_object_ref (server);
|
|
data->arg_refs = g_strdupv ((char **)arg_refs);
|
|
return data;
|
|
}
|
|
|
|
static char *
|
|
get_required_token (void)
|
|
{
|
|
g_autofree char *required_token_file = NULL;
|
|
g_autofree char *required_token = NULL;
|
|
|
|
required_token_file = g_build_filename (g_get_user_runtime_dir (), "required-token", NULL);
|
|
if (!g_file_get_contents (required_token_file, &required_token, NULL, NULL))
|
|
required_token = g_strdup ("default-token");
|
|
return g_steal_pointer (&required_token);
|
|
}
|
|
|
|
static void
|
|
write_request (char *str)
|
|
{
|
|
g_autofree char *request_file = NULL;
|
|
|
|
request_file = g_build_filename (g_get_user_runtime_dir (), "request", NULL);
|
|
g_file_set_contents (request_file, str, -1, NULL);
|
|
g_free (str);
|
|
}
|
|
|
|
static gboolean
|
|
requires_webflow (void)
|
|
{
|
|
g_autofree char *require_webflow_file = g_build_filename (g_get_user_runtime_dir (), "require-webflow", NULL);
|
|
|
|
return g_file_test (require_webflow_file, G_FILE_TEST_EXISTS);
|
|
}
|
|
|
|
static gboolean
|
|
request_webflow (void)
|
|
{
|
|
g_autofree char *request_webflow_file = g_build_filename (g_get_user_runtime_dir (), "request-webflow", NULL);
|
|
|
|
return g_file_test (request_webflow_file, G_FILE_TEST_EXISTS);
|
|
}
|
|
|
|
static void
|
|
finish_request_ref_tokens (TokenRequestData *data)
|
|
{
|
|
g_autofree char *required_token = NULL;
|
|
GVariantBuilder tokens;
|
|
GVariantBuilder results;
|
|
|
|
g_assert (data->request != NULL);
|
|
|
|
required_token = get_required_token ();
|
|
|
|
g_variant_builder_init (&tokens, G_VARIANT_TYPE ("a{sas}"));
|
|
g_variant_builder_add (&tokens, "{s^as}", required_token, data->arg_refs);
|
|
|
|
g_variant_builder_init (&results, G_VARIANT_TYPE ("a{sv}"));
|
|
g_variant_builder_add (&results, "{sv}", "tokens", g_variant_builder_end (&tokens));
|
|
|
|
g_debug ("emiting response");
|
|
flatpak_authenticator_request_emit_response (data->request,
|
|
FLATPAK_AUTH_RESPONSE_OK,
|
|
g_variant_builder_end (&results));
|
|
}
|
|
|
|
static gboolean
|
|
http_incoming (GSocketService *service,
|
|
GSocketConnection *connection,
|
|
GObject *source_object,
|
|
gpointer user_data)
|
|
{
|
|
TokenRequestData *data = user_data;
|
|
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
|
|
|
|
g_assert (data->request != NULL);
|
|
|
|
/* For the test, just assume any connection is a valid use of the web flow */
|
|
g_debug ("handling incomming http request");
|
|
|
|
g_debug ("emiting webflow done");
|
|
flatpak_authenticator_request_emit_webflow_done (data->request, options);
|
|
|
|
finish_request_ref_tokens (data);
|
|
|
|
token_request_data_free (data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
handle_request_close (FlatpakAuthenticatorRequest *object,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
TokenRequestData *data = user_data;
|
|
|
|
g_debug ("handle_request_close");
|
|
|
|
flatpak_authenticator_request_complete_close (object, invocation);
|
|
|
|
if (requires_webflow ())
|
|
{
|
|
GVariantBuilder results;
|
|
|
|
g_debug ("Webflow was cancelled by client");
|
|
|
|
g_variant_builder_init (&results, G_VARIANT_TYPE ("a{sv}"));
|
|
flatpak_authenticator_request_emit_response (data->request,
|
|
FLATPAK_AUTH_RESPONSE_CANCELLED,
|
|
g_variant_builder_end (&results));
|
|
}
|
|
else
|
|
{
|
|
g_debug ("Ignored webflow cancel by client");
|
|
finish_request_ref_tokens (data); /* Silently succeed anyway */
|
|
}
|
|
|
|
token_request_data_free (data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
handle_request_ref_tokens (FlatpakAuthenticator *authenticator,
|
|
GDBusMethodInvocation *invocation,
|
|
const gchar *arg_handle_token,
|
|
GVariant *arg_authenticator_option,
|
|
const gchar *arg_remote,
|
|
const gchar *arg_remote_uri,
|
|
GVariant *arg_refs,
|
|
GVariant *arg_options,
|
|
const gchar *arg_parent_window)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GSocketService) server = NULL;
|
|
g_autoptr(AutoFlatpakAuthenticatorRequest) request = NULL;
|
|
g_autofree char *uri = NULL;
|
|
g_autofree char *request_path = NULL;
|
|
guint16 port;
|
|
TokenRequestData *data;
|
|
g_autoptr(GPtrArray) refs = NULL;
|
|
gsize n_refs, i;
|
|
g_autofree char *options_s = NULL;
|
|
|
|
g_debug ("handling RequestRefTokens");
|
|
|
|
options_s = g_variant_print (arg_options, FALSE);
|
|
write_request (g_strdup_printf ("remote: %s\n"
|
|
"uri: %s\n"
|
|
"options: %s",
|
|
arg_remote,
|
|
arg_remote_uri,
|
|
options_s));
|
|
|
|
request_path = flatpak_auth_create_request_path (g_dbus_method_invocation_get_sender (invocation),
|
|
arg_handle_token, NULL);
|
|
if (request_path == NULL)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_INVALID_ARGS,
|
|
"Invalid token");
|
|
return TRUE;
|
|
}
|
|
|
|
request = flatpak_authenticator_request_skeleton_new ();
|
|
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (request),
|
|
g_dbus_method_invocation_get_connection (invocation),
|
|
request_path,
|
|
&error))
|
|
{
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return TRUE;
|
|
}
|
|
|
|
server = g_socket_service_new ();
|
|
port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (server), NULL, &error);
|
|
if (port == 0)
|
|
{
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return TRUE;
|
|
}
|
|
|
|
refs = g_ptr_array_new_with_free_func (g_free);
|
|
n_refs = g_variant_n_children (arg_refs);
|
|
for (i = 0; i < n_refs; i++)
|
|
{
|
|
const char *ref, *commit;
|
|
gint32 token_type;
|
|
g_autoptr(GVariant) data = NULL;
|
|
|
|
g_variant_get_child (arg_refs, i, "(&s&si@a{sv})", &ref, &commit, &token_type, &data);
|
|
|
|
g_ptr_array_add (refs, g_strdup (ref));
|
|
}
|
|
g_ptr_array_add (refs, NULL);
|
|
|
|
data = token_request_data_new (request, server, (const char *const*)refs->pdata);
|
|
|
|
g_signal_connect (server, "incoming", (GCallback)http_incoming, data);
|
|
g_signal_connect (request, "handle-close", G_CALLBACK (handle_request_close), data);
|
|
|
|
flatpak_authenticator_complete_request_ref_tokens (authenticator, invocation, request_path);
|
|
|
|
if (request_webflow ())
|
|
{
|
|
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
|
|
uri = g_strdup_printf ("http://localhost:%d", (int)port);
|
|
g_debug ("Requesting webflow %s", uri);
|
|
flatpak_authenticator_request_emit_webflow (request, uri, options);
|
|
}
|
|
else
|
|
{
|
|
finish_request_ref_tokens (data);
|
|
token_request_data_free (data);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_bus_acquired (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
g_debug ("Bus acquired, creating skeleton");
|
|
|
|
g_dbus_connection_set_exit_on_close (connection, FALSE);
|
|
|
|
authenticator = flatpak_authenticator_skeleton_new ();
|
|
flatpak_authenticator_set_version (authenticator, 0);
|
|
|
|
g_signal_connect (authenticator, "handle-request-ref-tokens", G_CALLBACK (handle_request_ref_tokens), NULL);
|
|
|
|
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (authenticator),
|
|
connection,
|
|
FLATPAK_AUTHENTICATOR_OBJECT_PATH,
|
|
&error))
|
|
{
|
|
g_warning ("error: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_name_acquired (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
g_debug ("Name acquired");
|
|
}
|
|
|
|
static void
|
|
on_name_lost (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
g_debug ("Name lost");
|
|
}
|
|
|
|
|
|
static void
|
|
message_handler (const gchar *log_domain,
|
|
GLogLevelFlags log_level,
|
|
const gchar *message,
|
|
gpointer user_data)
|
|
{
|
|
/* Make this look like normal console output */
|
|
if (log_level & G_LOG_LEVEL_DEBUG)
|
|
g_printerr ("F: %s\n", message);
|
|
else
|
|
g_printerr ("%s: %s\n", g_get_prgname (), message);
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char **argv)
|
|
{
|
|
gboolean replace;
|
|
gboolean opt_verbose;
|
|
GOptionContext *context;
|
|
GDBusConnection *session_bus;
|
|
GBusNameOwnerFlags flags;
|
|
g_autoptr(GError) error = NULL;
|
|
const GOptionEntry options[] = {
|
|
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace old daemon.", NULL },
|
|
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Enable debug output.", NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
g_setenv ("GIO_USE_VFS", "local", TRUE);
|
|
|
|
g_set_prgname (argv[0]);
|
|
|
|
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, message_handler, NULL);
|
|
|
|
context = g_option_context_new ("");
|
|
|
|
replace = FALSE;
|
|
opt_verbose = FALSE;
|
|
|
|
g_option_context_set_summary (context, "Flatpak portal");
|
|
g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
|
|
|
|
if (!g_option_context_parse (context, &argc, &argv, &error))
|
|
{
|
|
g_printerr ("%s: %s", g_get_application_name (), error->message);
|
|
g_printerr ("\n");
|
|
g_printerr ("Try \"%s --help\" for more information.",
|
|
g_get_prgname ());
|
|
g_printerr ("\n");
|
|
g_option_context_free (context);
|
|
return 1;
|
|
}
|
|
|
|
if (opt_verbose)
|
|
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL);
|
|
|
|
g_debug ("Started test-authenticator");
|
|
|
|
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
|
if (session_bus == NULL)
|
|
{
|
|
g_printerr ("Can't find bus: %s\n", error->message);
|
|
return 1;
|
|
}
|
|
|
|
flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
|
|
if (replace)
|
|
flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
|
|
|
|
name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
|
"org.flatpak.Authenticator.test",
|
|
flags,
|
|
on_bus_acquired,
|
|
on_name_acquired,
|
|
on_name_lost,
|
|
NULL,
|
|
NULL);
|
|
|
|
main_loop = g_main_loop_new (NULL, FALSE);
|
|
g_main_loop_run (main_loop);
|
|
|
|
return 0;
|
|
}
|