Add our own polkit listener implementation

This lets us respect the fancy output setting, and
it lets us do some other things that make it better
integrated.

Closes: #2379
Approved by: alexlarsson
This commit is contained in:
Matthias Clasen 2018-12-02 17:31:37 -05:00 committed by Atomic Bot
parent 6d95383050
commit b8cad064eb
6 changed files with 530 additions and 5 deletions

View File

@ -20,6 +20,8 @@ app/%-dbus-generated.h: app/%-dbus-generated.c
flatpak_SOURCES = \
app/flatpak-main.c \
app/flatpak-polkit-agent-text-listener.h \
app/flatpak-polkit-agent-text-listener.c \
app/flatpak-builtins.h \
app/flatpak-builtins-utils.h \
app/flatpak-builtins-utils.c \

View File

@ -32,8 +32,7 @@
#ifdef USE_SYSTEM_HELPER
#include <polkit/polkit.h>
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
#include <polkitagent/polkitagent.h>
#include "flatpak-polkit-agent-text-listener.h"
#endif
#include "flatpak-builtins.h"
@ -638,7 +637,7 @@ main (int argc,
#ifdef USE_SYSTEM_HELPER
/* Install a polkit agent as fallback, in case we're running on a console */
listener = polkit_agent_text_listener_new (NULL, &error);
listener = flatpak_polkit_agent_text_listener_new (NULL, &error);
if (listener == NULL)
{
g_debug ("Failed to create polkit agent listener: %s", error->message);
@ -653,7 +652,8 @@ main (int argc,
subject = polkit_unix_process_new_for_owner (getpid (), 0, -1);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "fallback", g_variant_new_boolean (TRUE));
if (g_strcmp0 (g_getenv ("FLATPAK_FORCE_TEXT_AUTH"), "1") != 0)
g_variant_builder_add (&opt_builder, "{sv}", "fallback", g_variant_new_boolean (TRUE));
options = g_variant_ref_sink (g_variant_builder_end (&opt_builder));
agent = polkit_agent_listener_register_with_options (listener,

View File

@ -0,0 +1,495 @@
/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This library 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 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, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <termios.h>
#include <unistd.h>
#include "flatpak-polkit-agent-text-listener.h"
#include "flatpak-utils-private.h"
struct _FlatpakPolkitAgentTextListener
{
PolkitAgentListener parent_instance;
GSimpleAsyncResult *simple;
PolkitAgentSession *active_session;
gulong cancel_id;
GCancellable *cancellable;
FILE *tty;
};
typedef struct
{
PolkitAgentListenerClass parent_class;
} FlatpakPolkitAgentTextListenerClass;
static void flatpak_polkit_agent_text_listener_initiate_authentication (PolkitAgentListener *_listener,
const gchar *action_id,
const gchar *message,
const gchar *icon_name,
PolkitDetails *details,
const gchar *cookie,
GList *identities,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
static gboolean flatpak_polkit_agent_text_listener_initiate_authentication_finish (PolkitAgentListener *_listener,
GAsyncResult *res,
GError **error);
static void initable_iface_init (GInitableIface *initable_iface);
G_DEFINE_TYPE_WITH_CODE (FlatpakPolkitAgentTextListener, flatpak_polkit_agent_text_listener, POLKIT_AGENT_TYPE_LISTENER,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
static void
flatpak_polkit_agent_text_listener_init (FlatpakPolkitAgentTextListener *listener)
{
}
static void
flatpak_polkit_agent_text_listener_finalize (GObject *object)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (object);
if (listener->tty != NULL)
fclose (listener->tty);
if (listener->active_session != NULL)
g_object_unref (listener->active_session);
if (G_OBJECT_CLASS (flatpak_polkit_agent_text_listener_parent_class)->finalize != NULL)
G_OBJECT_CLASS (flatpak_polkit_agent_text_listener_parent_class)->finalize (object);
}
static void
flatpak_polkit_agent_text_listener_class_init (FlatpakPolkitAgentTextListenerClass *klass)
{
GObjectClass *gobject_class;
PolkitAgentListenerClass *listener_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = flatpak_polkit_agent_text_listener_finalize;
listener_class = POLKIT_AGENT_LISTENER_CLASS (klass);
listener_class->initiate_authentication = flatpak_polkit_agent_text_listener_initiate_authentication;
listener_class->initiate_authentication_finish = flatpak_polkit_agent_text_listener_initiate_authentication_finish;
}
PolkitAgentListener *
flatpak_polkit_agent_text_listener_new (GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return POLKIT_AGENT_LISTENER (g_initable_new (FLATPAK_POLKIT_AGENT_TYPE_TEXT_LISTENER,
cancellable,
error,
NULL));
}
static gboolean
initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (initable);
gboolean ret;
const gchar *tty_name;
ret = FALSE;
tty_name = ctermid (NULL);
if (tty_name == NULL)
{
g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED,
"Cannot determine pathname for current controlling terminal for the process: %s",
strerror (errno));
goto out;
}
listener->tty = fopen (tty_name, "r+");
if (listener->tty == NULL)
{
g_set_error (error, POLKIT_ERROR, POLKIT_ERROR_FAILED,
"Error opening current controlling terminal for the process (`%s'): %s",
tty_name, strerror (errno));
goto out;
}
ret = TRUE;
out:
return ret;
}
static void
initable_iface_init (GInitableIface *initable_iface)
{
initable_iface->init = initable_init;
}
static void
on_completed (PolkitAgentSession *session,
gboolean gained_authorization,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (user_data);
if (flatpak_fancy_output ())
fprintf (listener->tty, FLATPAK_ANSI_RED);
if (gained_authorization)
fprintf (listener->tty, "==== AUTHENTICATION COMPLETE ====\n");
else
fprintf (listener->tty, "==== AUTHENTICATION FAILED ====\n");
if (flatpak_fancy_output ())
{
sleep (1); /* show the message for a bit */
fprintf (listener->tty, FLATPAK_ANSI_COLOR_RESET FLATPAK_ANSI_ALT_SCREEN_OFF);
}
fflush (listener->tty);
g_simple_async_result_complete_in_idle (listener->simple);
g_object_unref (listener->simple);
g_object_unref (listener->active_session);
g_cancellable_disconnect (listener->cancellable, listener->cancel_id);
g_object_unref (listener->cancellable);
listener->simple = NULL;
listener->active_session = NULL;
listener->cancel_id = 0;
}
static void
on_request (PolkitAgentSession *session,
const gchar *request,
gboolean echo_on,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (user_data);
struct termios ts, ots;
GString *str;
fprintf (listener->tty, "%s", request);
fflush (listener->tty);
setbuf (listener->tty, NULL);
tcgetattr (fileno (listener->tty), &ts);
ots = ts;
ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
tcsetattr (fileno (listener->tty), TCSAFLUSH, &ts);
str = g_string_new (NULL);
while (TRUE)
{
gint c;
c = getc (listener->tty);
if (c == '\n')
{
/* ok, done */
break;
}
else if (c == EOF)
{
tcsetattr (fileno (listener->tty), TCSAFLUSH, &ots);
g_error ("Got unexpected EOF while reading from controlling terminal.");
abort ();
break;
}
else
{
g_string_append_c (str, c);
}
}
tcsetattr (fileno (listener->tty), TCSAFLUSH, &ots);
putc ('\n', listener->tty);
polkit_agent_session_response (session, str->str);
memset (str->str, '\0', str->len);
g_string_free (str, TRUE);
}
static void
on_show_error (PolkitAgentSession *session,
const gchar *text,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (user_data);
fprintf (listener->tty, "Error: %s\n", text);
fflush (listener->tty);
}
static void
on_show_info (PolkitAgentSession *session,
const gchar *text,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (user_data);
fprintf (listener->tty, "Info: %s\n", text);
fflush (listener->tty);
}
static void
on_cancelled (GCancellable *cancellable,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (user_data);
fprintf (listener->tty, "Cancelled\n");
fflush (listener->tty);
polkit_agent_session_cancel (listener->active_session);
}
static gchar *
identity_to_human_readable_string (PolkitIdentity *identity)
{
gchar *ret;
g_return_val_if_fail (POLKIT_IS_IDENTITY (identity), NULL);
ret = NULL;
if (POLKIT_IS_UNIX_USER (identity))
{
struct passwd pw;
struct passwd *ppw;
char buf[2048];
int res;
res = getpwuid_r (polkit_unix_user_get_uid (POLKIT_UNIX_USER (identity)),
&pw,
buf,
sizeof buf,
&ppw);
if (res != 0)
{
g_warning ("Error calling getpwuid_r: %s", strerror (res));
}
else
{
if (ppw->pw_gecos == NULL || strlen (ppw->pw_gecos) == 0 || strcmp (ppw->pw_gecos, ppw->pw_name) == 0)
{
ret = g_strdup_printf ("%s", ppw->pw_name);
}
else
{
ret = g_strdup_printf ("%s (%s)", ppw->pw_gecos, ppw->pw_name);
}
}
}
if (ret == NULL)
ret = polkit_identity_to_string (identity);
return ret;
}
static PolkitIdentity *
choose_identity (FlatpakPolkitAgentTextListener *listener,
GList *identities)
{
GList *l;
guint n;
guint num_identities;
GString *str;
PolkitIdentity *ret;
guint num;
gchar *endp;
ret = NULL;
fprintf (listener->tty, "Multiple identities can be used for authentication:\n");
for (l = identities, n = 0; l != NULL; l = l->next, n++)
{
PolkitIdentity *identity = POLKIT_IDENTITY (l->data);
gchar *s;
s = identity_to_human_readable_string (identity);
fprintf (listener->tty, " %d. %s\n", n + 1, s);
g_free (s);
}
num_identities = n;
fprintf (listener->tty, "Choose identity to authenticate as (1-%d): ", num_identities);
fflush (listener->tty);
str = g_string_new (NULL);
while (TRUE)
{
gint c;
c = getc (listener->tty);
if (c == '\n')
{
/* ok, done */
break;
}
else if (c == EOF)
{
g_error ("Got unexpected EOF while reading from controlling terminal.");
abort ();
break;
}
else
{
g_string_append_c (str, c);
}
}
num = strtol (str->str, &endp, 10);
if (str->len == 0 || *endp != '\0' || (num < 1 || num > num_identities))
{
fprintf (listener->tty, "Invalid response `%s'.\n", str->str);
goto out;
}
ret = g_list_nth_data (identities, num-1);
out:
g_string_free (str, TRUE);
return ret;
}
static void
flatpak_polkit_agent_text_listener_initiate_authentication (PolkitAgentListener *_listener,
const gchar *action_id,
const gchar *message,
const gchar *icon_name,
PolkitDetails *details,
const gchar *cookie,
GList *identities,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
FlatpakPolkitAgentTextListener *listener = FLATPAK_POLKIT_AGENT_TEXT_LISTENER (_listener);
GSimpleAsyncResult *simple;
PolkitIdentity *identity;
simple = g_simple_async_result_new (G_OBJECT (listener),
callback,
user_data,
flatpak_polkit_agent_text_listener_initiate_authentication);
if (listener->active_session != NULL)
{
g_simple_async_result_set_error (simple, POLKIT_ERROR, POLKIT_ERROR_FAILED,
"An authentication session is already underway.");
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
goto out;
}
g_assert (g_list_length (identities) >= 1);
if (flatpak_fancy_output ())
{
fprintf (listener->tty, FLATPAK_ANSI_ALT_SCREEN_ON FLATPAK_ANSI_RED);
}
fprintf (listener->tty, "==== AUTHENTICATING FOR %s ====\n", action_id);
if (flatpak_fancy_output ())
fprintf (listener->tty, FLATPAK_ANSI_COLOR_RESET);
fprintf (listener->tty, "%s\n", message);
/* handle multiple identies by asking which one to use */
if (g_list_length (identities) > 1)
{
identity = choose_identity (listener, identities);
if (identity == NULL)
{
fprintf (listener->tty, "\x1B[1;31m");
fprintf (listener->tty, "==== AUTHENTICATION CANCELED ====\n");
fprintf (listener->tty, "\x1B[0m");
fflush (listener->tty);
g_simple_async_result_set_error (simple,
POLKIT_ERROR,
POLKIT_ERROR_FAILED,
"Authentication was canceled.");
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
goto out;
}
}
else
{
gchar *s;
identity = identities->data;
s = identity_to_human_readable_string (identity);
fprintf (listener->tty,
"Authenticating as: %s\n",
s);
g_free (s);
}
listener->active_session = polkit_agent_session_new (identity, cookie);
g_signal_connect (listener->active_session,
"completed",
G_CALLBACK (on_completed),
listener);
g_signal_connect (listener->active_session,
"request",
G_CALLBACK (on_request),
listener);
g_signal_connect (listener->active_session,
"show-info",
G_CALLBACK (on_show_info),
listener);
g_signal_connect (listener->active_session,
"show-error",
G_CALLBACK (on_show_error),
listener);
listener->simple = simple;
listener->cancellable = g_object_ref (cancellable);
listener->cancel_id = g_cancellable_connect (cancellable,
G_CALLBACK (on_cancelled),
listener,
NULL);
polkit_agent_session_initiate (listener->active_session);
out:
;
}
static gboolean
flatpak_polkit_agent_text_listener_initiate_authentication_finish (PolkitAgentListener *_listener,
GAsyncResult *res,
GError **error)
{
g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) ==
flatpak_polkit_agent_text_listener_initiate_authentication);
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return FALSE;
return TRUE;
}

View File

@ -0,0 +1,24 @@
#ifndef __FLATPAK_POLKIT_AGENT_TEXT_LISTENER_H
#define __FLATPAK_POLKIT_AGENT_TEXT_LISTENER_H
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
#include <polkit/polkit.h>
#include <polkitagent/polkitagent.h>
G_BEGIN_DECLS
typedef struct _FlatpakPolkitAgentTextListener FlatpakPolkitAgentTextListener;
#define FLATPAK_POLKIT_AGENT_TYPE_TEXT_LISTENER (flatpak_polkit_agent_text_listener_get_type())
#define FLATPAK_POLKIT_AGENT_TEXT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FLATPAK_POLKIT_AGENT_TYPE_TEXT_LISTENER, FlatpakPolkitAgentTextListener))
#define FLATPAK_POLKIT_AGENT_IS_TEXT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FLATPAK_POLKIT_AGENT_TYPE_TEXT_LISTENER))
GType flatpak_polkit_agent_text_listener_get_type (void) G_GNUC_CONST;
PolkitAgentListener *flatpak_polkit_agent_text_listener_new (GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __FLATPAK_POLKIT_AGENT_TEXT_LISTENER_H */

View File

@ -42,7 +42,8 @@ typedef enum {
FLATPAK_HOST_COMMAND_FLAGS_WATCH_BUS = 1 << 1,
} FlatpakHostCommandFlags;
#define FLATPAK_ANSI_ALT_SCREEN_ON "\x1B[?1049h"
#define FLATPAK_ANSI_ALT_SCREEN_OFF "\x1B[?1049l"
#define FLATPAK_ANSI_BOLD_ON "\x1b[1m"
#define FLATPAK_ANSI_BOLD_OFF "\x1b[22m"
#define FLATPAK_ANSI_RED "\x1b[31m"

View File

@ -592,6 +592,9 @@ flatpak_get_gtk_theme (void)
gboolean
flatpak_fancy_output (void)
{
if (g_strcmp0 (g_getenv ("FLATPAK_FANCY_OUTPUT"), "0") == 0)
return FALSE;
return isatty (STDOUT_FILENO);
}