PolicyKit: Branch 'master' - 2 commits

David Zeuthen david at kemper.freedesktop.org
Thu Aug 12 13:57:17 PDT 2010


 docs/polkit/polkit-1-docs.xml                         |    1 
 docs/polkit/polkit-1-sections.txt                     |   19 
 docs/polkit/polkit-1.types                            |    1 
 src/polkit/polkitsubject.c                            |   22 
 src/polkitagent/Makefile.am                           |   15 
 src/polkitagent/polkitagent.h                         |    2 
 src/polkitagent/polkitagentenumtypes.c.template       |   39 +
 src/polkitagent/polkitagentenumtypes.h.template       |   24 
 src/polkitagent/polkitagenthelper-pam.c               |   19 
 src/polkitagent/polkitagenthelperprivate.h            |    2 
 src/polkitagent/polkitagentlistener.c                 |  244 ++++++-
 src/polkitagent/polkitagentlistener.h                 |   60 +
 src/polkitagent/polkitagentsession.c                  |   28 
 src/polkitagent/polkitagenttextlistener.c             |  565 ++++++++++++++++++
 src/polkitagent/polkitagenttextlistener.h             |   45 +
 src/polkitagent/polkitagenttypes.h                    |    3 
 src/polkitbackend/polkitbackendinteractiveauthority.c |  395 ++++++++----
 src/programs/Makefile.am                              |    1 
 src/programs/pkcheck.c                                |    2 
 src/programs/pkexec.c                                 |   46 +
 20 files changed, 1324 insertions(+), 209 deletions(-)

New commits:
commit 42177383585e1e01cd6150f891176afcd4538a82
Author: David Zeuthen <davidz at redhat.com>
Date:   Thu Aug 12 16:51:51 2010 -0400

    Add textual authentication agent and use it in pkexec(1)
    
    This makes pkexec(1) work when e.g. logging in via ssh(1) or the linux
    console but also when using `su -'. Example:
    
     [davidz at x61 ~]$ su - bateman
     Password:
     [bateman at x61 ~]$ pkexec bash
     ==== AUTHENTICATING FOR org.freedesktop.policykit.exec ===
     Authentication is needed to run `/bin/bash' as the super user
     Authenticating as: root
     Password:
     ==== AUTHENTICATION COMPLETE ===
     [root at x61 ~]#
    
    Summary of changes
    
     - Added a PolkitAgentTextListener class
    
     - Add new polkit_agent_listener_register() (and _unregister()) API
    
     - Deprecate polkit_agent_register_listener API
    
     - Allow registering authentication agents for PolkitUnixProcess subjects
       and prefer such agents to ones governing the session
    
     - Make PolkitAgentSession use the thread-default GMainContext - otherwise
       it won't work in spawned threads
    
     - (finally) use PolkitAgentTextListener in pkexec(1) if authorization
       via authentication is possible but no authentication agent was
       found
    
    Signed-off-by: David Zeuthen <davidz at redhat.com>

diff --git a/docs/polkit/polkit-1-docs.xml b/docs/polkit/polkit-1-docs.xml
index 19c228a..06510ca 100644
--- a/docs/polkit/polkit-1-docs.xml
+++ b/docs/polkit/polkit-1-docs.xml
@@ -100,6 +100,7 @@
   <part id="ref-authentication-agent-api">
     <title>Authentication Agent API Reference</title>
     <xi:include href="xml/polkitagentlistener.xml"/>
+    <xi:include href="xml/polkitagenttextlistener.xml"/>
     <xi:include href="xml/polkitagentsession.xml"/>
   </part>
 
diff --git a/docs/polkit/polkit-1-sections.txt b/docs/polkit/polkit-1-sections.txt
index ef72acb..85b5d59 100644
--- a/docs/polkit/polkit-1-sections.txt
+++ b/docs/polkit/polkit-1-sections.txt
@@ -369,7 +369,9 @@ PolkitAgentListener
 PolkitAgentListenerClass
 polkit_agent_listener_initiate_authentication
 polkit_agent_listener_initiate_authentication_finish
-polkit_agent_register_listener
+PolkitAgentRegisterFlags
+polkit_agent_listener_register
+polkit_agent_listener_unregister
 <SUBSECTION Standard>
 POLKIT_AGENT_LISTENER
 POLKIT_AGENT_IS_LISTENER
@@ -381,6 +383,21 @@ POLKIT_AGENT_LISTENER_GET_CLASS
 </SECTION>
 
 <SECTION>
+<FILE>polkitagenttextlistener</FILE>
+<TITLE>PolkitAgentTextListener</TITLE>
+PolkitAgentTextListener
+polkit_agent_text_listener_new
+<SUBSECTION Standard>
+POLKIT_AGENT_TEXT_LISTENER
+POLKIT_AGENT_IS_TEXT_LISTENER
+POLKIT_AGENT_TYPE_TEXT_LISTENER
+polkit_agent_text_listener_get_type
+POLKIT_AGENT_TEXT_LISTENER_CLASS
+POLKIT_AGENT_IS_TEXT_LISTENER_CLASS
+POLKIT_AGENT_TEXT_LISTENER_GET_CLASS
+</SECTION>
+
+<SECTION>
 <FILE>polkittemporaryauthorization</FILE>
 <TITLE>PolkitTemporaryAuthorization</TITLE>
 PolkitTemporaryAuthorization
diff --git a/docs/polkit/polkit-1.types b/docs/polkit/polkit-1.types
index 2cab12a..e50812b 100644
--- a/docs/polkit/polkit-1.types
+++ b/docs/polkit/polkit-1.types
@@ -26,3 +26,4 @@ polkit_backend_local_authorization_store_get_type
 
 polkit_agent_session_get_type
 polkit_agent_listener_get_type
+polkit_agent_text_listener_get_type
diff --git a/src/polkitagent/Makefile.am b/src/polkitagent/Makefile.am
index 8776599..733da72 100644
--- a/src/polkitagent/Makefile.am
+++ b/src/polkitagent/Makefile.am
@@ -18,8 +18,21 @@ INCLUDES =                                                      \
 
 BUILT_SOURCES = 						\
 	marshal.stamp						\
+	polkitagentenumtypes.c		polkitagentenumtypes.h	\
 	$(NULL)
 
+enum_headers = polkitagentlistener.h
+
+polkitagentenumtypes.h: $(enum_headers) polkitagentenumtypes.h.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	 cd $(srcdir) && glib-mkenums --template polkitagentenumtypes.h.template $(enum_headers)) > \
+	   polkitagentenumtypes.h.tmp && mv polkitagentenumtypes.h.tmp polkitagentenumtypes.h
+
+polkitagentenumtypes.c: $(enum_headers) polkitagentenumtypes.c.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	 cd $(srcdir) && glib-mkenums --template polkitagentenumtypes.c.template $(enum_headers)) > \
+	   polkitagentenumtypes.c.tmp && mv polkitagentenumtypes.c.tmp polkitagentenumtypes.c
+
 marshal.stamp : Makefile.am $(srcdir)/polkitagentmarshal.list
 	glib-genmarshal --prefix=_polkit_agent_marshal $(srcdir)/polkitagentmarshal.list --header > polkitagentmarshal.h.tmp && mv polkitagentmarshal.h.tmp polkitagentmarshal.h
 	(echo "#include \"polkitagentmarshal.h\""; glib-genmarshal --prefix=_polkit_agent_marshal $(srcdir)/polkitagentmarshal.list --body) > polkitagentmarshal.c.tmp && mv polkitagentmarshal.c.tmp polkitagentmarshal.c
@@ -36,6 +49,7 @@ libpolkit_agent_1include_HEADERS =                        				\
 	polkitagenttypes.h								\
 	polkitagentsession.h								\
 	polkitagentlistener.h								\
+	polkitagenttextlistener.h							\
         $(NULL)
 
 libpolkit_agent_1_la_SOURCES =                                   			\
@@ -45,6 +59,7 @@ libpolkit_agent_1_la_SOURCES =                                   			\
 	polkitagenttypes.h								\
 	polkitagentsession.h			polkitagentsession.c			\
 	polkitagentlistener.h			polkitagentlistener.c			\
+	polkitagenttextlistener.h		polkitagenttextlistener.c		\
         $(NULL)
 
 libpolkit_agent_1_la_CFLAGS =                                        	\
diff --git a/src/polkitagent/polkitagent.h b/src/polkitagent/polkitagent.h
index 5045d67..6f163d1 100644
--- a/src/polkitagent/polkitagent.h
+++ b/src/polkitagent/polkitagent.h
@@ -28,7 +28,9 @@
 
 #define _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H 1
 #include <polkitagent/polkitagenttypes.h>
+#include <polkitagent/polkitagentenumtypes.h>
 #include <polkitagent/polkitagentlistener.h>
+#include <polkitagent/polkitagenttextlistener.h>
 #include <polkitagent/polkitagentsession.h>
 #undef _POLKIT_AGENT_INSIDE_POLKIT_AGENT_H
 
diff --git a/src/polkitagent/polkitagentenumtypes.c.template b/src/polkitagent/polkitagentenumtypes.c.template
new file mode 100644
index 0000000..e6cb139
--- /dev/null
+++ b/src/polkitagent/polkitagentenumtypes.c.template
@@ -0,0 +1,39 @@
+/*** BEGIN file-header ***/
+#include <polkitagent/polkitagent.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ at enum_name@_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const G at Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_ at type@_register_static (g_intern_static_string ("@EnumName@"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/src/polkitagent/polkitagentenumtypes.h.template b/src/polkitagent/polkitagentenumtypes.h.template
new file mode 100644
index 0000000..24d6f90
--- /dev/null
+++ b/src/polkitagent/polkitagentenumtypes.h.template
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __POLKIT_AGENT_ENUM_TYPES_H__
+#define __POLKIT_AGENT_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name at _get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX at _TYPE_@ENUMSHORT@ (@enum_name at _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __POLKIT_AGENT_ENUM_TYPES_H__ */
+/*** END file-tail ***/
diff --git a/src/polkitagent/polkitagenthelper-pam.c b/src/polkitagent/polkitagenthelper-pam.c
index 5e8b54c..d1eed85 100644
--- a/src/polkitagent/polkitagenthelper-pam.c
+++ b/src/polkitagent/polkitagenthelper-pam.c
@@ -204,15 +204,32 @@ conversation_function (int n, const struct pam_message **msg, struct pam_respons
         {
 
         case PAM_PROMPT_ECHO_OFF:
+#ifdef PAH_DEBUG
+          fprintf (stderr, "polkit-agent-helper-1: writing `PAM_PROMPT_ECHO_OFF ' to stdout\n");
+#endif /* PAH_DEBUG */
           fprintf (stdout, "PAM_PROMPT_ECHO_OFF ");
           goto conv1;
 
         case PAM_PROMPT_ECHO_ON:
+#ifdef PAH_DEBUG
+          fprintf (stderr, "polkit-agent-helper-1: writing `PAM_PROMPT_ECHO_ON ' to stdout\n");
+#endif /* PAH_DEBUG */
           fprintf (stdout, "PAM_PROMPT_ECHO_ON ");
         conv1:
+#ifdef PAH_DEBUG
+          fprintf (stderr, "polkit-agent-helper-1: writing `%s' to stdout\n", msg[i]->msg);
+#endif /* PAH_DEBUG */
           fputs (msg[i]->msg, stdout);
           if (strlen (msg[i]->msg) > 0 && msg[i]->msg[strlen (msg[i]->msg) - 1] != '\n')
-            fputc ('\n', stdout);
+            {
+#ifdef PAH_DEBUG
+              fprintf (stderr, "polkit-agent-helper-1: writing newline to stdout\n");
+#endif /* PAH_DEBUG */
+              fputc ('\n', stdout);
+            }
+#ifdef PAH_DEBUG
+          fprintf (stderr, "polkit-agent-helper-1: flushing stdout\n");
+#endif /* PAH_DEBUG */
           fflush (stdout);
 
           if (fgets (buf, sizeof buf, stdin) == NULL)
diff --git a/src/polkitagent/polkitagenthelperprivate.h b/src/polkitagent/polkitagenthelperprivate.h
index 7294d46..aeca2c7 100644
--- a/src/polkitagent/polkitagenthelperprivate.h
+++ b/src/polkitagent/polkitagenthelperprivate.h
@@ -30,7 +30,7 @@
  * sensitive information.
  */
 #undef PAH_DEBUG
-// #define PAH_DEBUG
+/* #define PAH_DEBUG */
 
 #ifdef HAVE_SOLARIS
 #  define LOG_AUTHPRIV    (10<<3)
diff --git a/src/polkitagent/polkitagentlistener.c b/src/polkitagent/polkitagentlistener.c
index f8321f4..52d71ff 100644
--- a/src/polkitagent/polkitagentlistener.c
+++ b/src/polkitagent/polkitagentlistener.c
@@ -51,6 +51,8 @@ typedef struct
   GDBusConnection *system_bus;
   guint auth_agent_registration_id;
 
+  GDBusInterfaceInfo *interface_info;
+
   PolkitAuthority *authority;
   gulong notify_owner_handler_id;
 
@@ -62,6 +64,12 @@ typedef struct
   gchar *object_path;
 
   GHashTable *cookie_to_pending_auth;
+
+  GThread *thread;
+  GError *thread_initialization_error;
+  gboolean thread_initialized;
+  GMainContext *thread_context;
+  GMainLoop *thread_loop;
 } Server;
 
 static void
@@ -82,6 +90,21 @@ server_free (Server *server)
         }
     }
 
+  if (server->thread_initialization_error != NULL)
+    g_error_free (server->thread_initialization_error);
+
+  if (server->thread_context != NULL)
+    g_main_context_unref (server->thread_context);
+
+  if (server->thread_loop != NULL)
+    g_main_loop_unref (server->thread_loop);
+
+  if (server->interface_info != NULL)
+    g_dbus_interface_info_unref (server->interface_info);
+
+  if (server->listener != NULL)
+    g_object_unref (server->listener);
+
   if (server->auth_agent_registration_id > 0)
     g_dbus_connection_unregister_object (server->system_bus, server->auth_agent_registration_id);
 
@@ -109,13 +132,18 @@ server_register (Server   *server,
 {
   GError *local_error;
   gboolean ret;
+  const gchar *locale;
 
   ret = FALSE;
 
+  locale = g_getenv ("LANG");
+  if (locale == NULL)
+    locale = "en_US.UTF-8";
+
   local_error = NULL;
   if (!polkit_authority_register_authentication_agent_sync (server->authority,
                                                             server->subject,
-                                                            g_getenv ("LANG"),
+                                                            locale,
                                                             server->object_path,
                                                             NULL,
                                                             &local_error))
@@ -230,15 +258,6 @@ server_new (PolkitSubject  *subject,
   return server;
 }
 
-static void
-listener_died (gpointer user_data,
-               GObject *where_the_object_was)
-{
-  Server *server = user_data;
-
-  server_free (server);
-}
-
 static void auth_agent_handle_begin_authentication (Server                 *server,
                                                     GVariant               *parameters,
                                                     GDBusMethodInvocation  *invocation);
@@ -295,75 +314,202 @@ static const GDBusInterfaceVTable auth_agent_vtable =
   NULL  /* _handle_set_property */
 };
 
+static gboolean
+server_export_object (Server  *server,
+                      GError **error)
+{
+  gboolean ret;
+  ret = FALSE;
+  server->auth_agent_registration_id = g_dbus_connection_register_object (server->system_bus,
+                                                                          server->object_path,
+                                                                          server->interface_info,
+                                                                          &auth_agent_vtable,
+                                                                          server,
+                                                                          NULL, /* user_data GDestroyNotify */
+                                                                          error);
+  if (server->auth_agent_registration_id > 0)
+    ret = TRUE;
+  return ret;
+}
+
+static gpointer
+server_thread_func (gpointer user_data)
+{
+  Server *server = user_data;
+
+  server->thread_context = g_main_context_new ();
+  server->thread_loop = g_main_loop_new (server->thread_context, FALSE);
+
+  g_main_context_push_thread_default (server->thread_context);
+
+  if (!server_export_object (server, &server->thread_initialization_error))
+    {
+      server->thread_initialized = TRUE;
+      goto out;
+    }
+
+  server->thread_initialized = TRUE;
+
+  g_main_loop_run (server->thread_loop);
+
+ out:
+  g_main_context_pop_thread_default (server->thread_context);
+  return NULL;
+}
+
 /**
- * polkit_agent_register_listener:
- * @listener: An instance of a class that is derived from #PolkitAgentListener.
+ * polkit_agent_listener_register:
+ * @listener: A #PolkitAgentListener.
+ * @flags: A set of flags from the #PolkitAgentRegisterFlags enumeration.
  * @subject: The subject to become an authentication agent for, typically a #PolkitUnixSession object.
  * @object_path: The D-Bus object path to use for the authentication agent or %NULL for the default object path.
+ * @cancellable: A #GCancellable or %NULL.
  * @error: Return location for error.
  *
- * Registers @listener with the PolicyKit daemon as an authentication agent for @subject. This
- * is implemented by registering a D-Bus object at @object_path on the unique name assigned by the
- * system message bus.
- *
- * Whenever the PolicyKit daemon needs to authenticate a processes that is related @subject, the methods
- * polkit_agent_listener_initiate_authentication() and polkit_agent_listener_initiate_authentication_finish()
- * will be invoked on @listener.
+ * Registers @listener with the PolicyKit daemon as an authentication
+ * agent for @subject. This is implemented by registering a D-Bus
+ * object at @object_path on the unique name assigned by the system
+ * message bus.
  *
- * Note that registration of an authentication agent can fail; for example another authentication agent may
- * already be registered.
+ * Whenever the PolicyKit daemon needs to authenticate a processes
+ * that is related to @subject, the methods
+ * polkit_agent_listener_initiate_authentication() and
+ * polkit_agent_listener_initiate_authentication_finish() will be
+ * invoked on @listener.
  *
- * To unregister @listener, simply free it with g_object_unref().
+ * Note that registration of an authentication agent can fail; for
+ * example another authentication agent may already be registered for
+ * @subject.
  *
- * Returns: %TRUE if @listener has been registered, %FALSE if @error is set.
- **/
-gboolean
-polkit_agent_register_listener (PolkitAgentListener  *listener,
-                                PolkitSubject        *subject,
-                                const gchar          *object_path,
-                                GError              **error)
+ * Returns: %NULL if @error is set, otherwise a registration handle
+ * that can be used with polkit_agent_listener_unregister().
+ */
+gpointer
+polkit_agent_listener_register (PolkitAgentListener      *listener,
+                                PolkitAgentRegisterFlags  flags,
+                                PolkitSubject            *subject,
+                                const gchar              *object_path,
+                                GCancellable             *cancellable,
+                                GError                  **error)
 {
   Server *server;
-  gboolean ret;
   GDBusNodeInfo *node_info;
 
-  g_return_val_if_fail (POLKIT_AGENT_IS_LISTENER (listener), FALSE);
-  g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), FALSE);
-  g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (POLKIT_AGENT_IS_LISTENER (listener), NULL);
+  g_return_val_if_fail (POLKIT_IS_SUBJECT (subject), NULL);
+  g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), NULL);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  ret = FALSE;
+  if (object_path == NULL)
+    object_path = "/org/freedesktop/PolicyKit1/AuthenticationAgent";
 
-  server = server_new (subject, object_path, NULL, error);
+  server = server_new (subject, object_path, cancellable, error);
   if (server == NULL)
     goto out;
 
   node_info = g_dbus_node_info_new_for_xml (auth_agent_introspection_data, error);
   if (node_info == NULL)
-    goto out;
-
-  server->listener = listener;
-  server->auth_agent_registration_id = g_dbus_connection_register_object (server->system_bus,
-                                                                          server->object_path,
-                                                                          g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.PolicyKit1.AuthenticationAgent"),
-                                                                          &auth_agent_vtable,
-                                                                          server,
-                                                                          NULL, /* user_data GDestroyNotify */
-                                                                          error);
-  g_dbus_node_info_unref (node_info);
-
-  if (server->auth_agent_registration_id == 0)
     {
       server_free (server);
+      server = NULL;
       goto out;
     }
+  server->interface_info = g_dbus_interface_info_ref (g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.PolicyKit1.AuthenticationAgent"));
+  g_dbus_node_info_unref (node_info);
+
+  server->listener = g_object_ref (listener);
+
+  if (flags & POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD)
+    {
+      server->thread = g_thread_create (server_thread_func,
+                                        server,
+                                        TRUE,
+                                        error);
+      if (server->thread == NULL)
+        {
+          server_free (server);
+          server = NULL;
+          goto out;
+        }
+
+      /* wait for the thread to export and object (TODO: probably use a condition variable instead) */
+      while (!server->thread_initialized)
+        g_thread_yield ();
+      if (server->thread_initialization_error != NULL)
+        {
+          g_propagate_error (error, server->thread_initialization_error);
+          server->thread_initialization_error = NULL;
+          g_thread_join (server->thread);
+          server_free (server);
+          goto out;
+        }
+    }
+  else
+    {
+      if (!server_export_object (server, error))
+        {
+          server_free (server);
+          server = NULL;
+          goto out;
+        }
+    }
 
   if (!server_register (server, error))
     {
       server_free (server);
+      server = NULL;
       goto out;
     }
 
+ out:
+  return server;
+}
+
+/**
+ * polkit_agent_listener_unregister:
+ * @registration_handle: A handle obtained from polkit_agent_listener_register().
+ *
+ * Unregisters @listener.
+ */
+void
+polkit_agent_listener_unregister (gpointer registration_handle)
+{
+  Server *server = registration_handle;
+  if (server->thread != NULL)
+    {
+      g_main_loop_quit (server->thread_loop);
+      g_thread_join (server->thread);
+    }
+  server_free (server);
+}
+
+
+static void
+listener_died (gpointer user_data,
+               GObject *where_the_object_was)
+{
+  Server *server = user_data;
+  server_free (server);
+}
+
+gboolean
+polkit_agent_register_listener (PolkitAgentListener  *listener,
+                                PolkitSubject        *subject,
+                                const gchar          *object_path,
+                                GError              **error)
+{
+  Server *server;
+  gboolean ret;
+
+  ret = FALSE;
+
+  server = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_NONE, subject, object_path, NULL, error);
+  if (server == NULL)
+    goto out;
+
+  /* drop the ref that server took */
+  g_object_unref (server->listener);
   /* take a weak ref and kill server when listener dies */
   g_object_weak_ref (G_OBJECT (server->listener), listener_died, server);
 
diff --git a/src/polkitagent/polkitagentlistener.h b/src/polkitagent/polkitagentlistener.h
index a20feb8..191b265 100644
--- a/src/polkitagent/polkitagentlistener.h
+++ b/src/polkitagent/polkitagentlistener.h
@@ -94,25 +94,47 @@ struct _PolkitAgentListenerClass
 
 GType     polkit_agent_listener_get_type                        (void) G_GNUC_CONST;
 
-void      polkit_agent_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);
-
-gboolean  polkit_agent_listener_initiate_authentication_finish  (PolkitAgentListener  *listener,
-                                                                 GAsyncResult         *res,
-                                                                 GError              **error);
-
-gboolean  polkit_agent_register_listener                        (PolkitAgentListener  *listener,
-                                                                 PolkitSubject        *subject,
-                                                                 const gchar          *object_path,
-                                                                 GError              **error);
+void      polkit_agent_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);
+
+gboolean  polkit_agent_listener_initiate_authentication_finish  (PolkitAgentListener      *listener,
+                                                                 GAsyncResult             *res,
+                                                                 GError                  **error);
+
+gboolean  polkit_agent_register_listener                        (PolkitAgentListener      *listener,
+                                                                 PolkitSubject            *subject,
+                                                                 const gchar              *object_path,
+                                                                 GError                  **error) G_GNUC_DEPRECATED_FOR (polkit_authority_listener_register);
+
+/**
+ * PolkitAgentRegisterFlags:
+ * @POLKIT_AGENT_REGISTER_FLAGS_NONE: No flags are set.
+ * @POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD: Run the listener in a dedicated thread.
+ *
+ * Flags used in polkit_agent_listener_register().
+ */
+typedef enum
+{
+  POLKIT_AGENT_REGISTER_FLAGS_NONE = 0,
+  POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD = (1<<0)
+} PolkitAgentRegisterFlags;
+
+gpointer  polkit_agent_listener_register                        (PolkitAgentListener      *listener,
+                                                                 PolkitAgentRegisterFlags  flags,
+                                                                 PolkitSubject            *subject,
+                                                                 const gchar              *object_path,
+                                                                 GCancellable             *cancellable,
+                                                                 GError                  **error);
+
+void      polkit_agent_listener_unregister                      (gpointer                  registration_handle);
 
 G_END_DECLS
 
diff --git a/src/polkitagent/polkitagentsession.c b/src/polkitagent/polkitagentsession.c
index 1c17c84..3780b7c 100644
--- a/src/polkitagent/polkitagentsession.c
+++ b/src/polkitagent/polkitagentsession.c
@@ -78,8 +78,8 @@ struct _PolkitAgentSession
   int child_stdout;
   GPid child_pid;
 
-  int child_watch_id;
-  int child_stdout_watch_id;
+  GSource *child_watch_source;
+  GSource *child_stdout_watch_source;
   GIOChannel *child_stdout_channel;
 
   gboolean success;
@@ -269,16 +269,18 @@ kill_helper (PolkitAgentSession *session)
       session->child_pid = 0;
     }
 
-  if (session->child_watch_id > 0)
+  if (session->child_watch_source != NULL)
     {
-      g_source_remove (session->child_watch_id);
-      session->child_watch_id = 0;
+      g_source_destroy (session->child_watch_source);
+      g_source_unref (session->child_watch_source);
+      session->child_watch_source = NULL;
     }
 
-  if (session->child_stdout_watch_id > 0)
+  if (session->child_stdout_watch_source != NULL)
     {
-      g_source_remove (session->child_stdout_watch_id);
-      session->child_stdout_watch_id = 0;
+      g_source_destroy (session->child_stdout_watch_source);
+      g_source_unref (session->child_stdout_watch_source);
+      session->child_stdout_watch_source = NULL;
     }
 
   if (session->child_stdout_channel != NULL)
@@ -487,9 +489,15 @@ polkit_agent_session_initiate (PolkitAgentSession *session)
       goto error;
     }
 
-  session->child_watch_id = g_child_watch_add (session->child_pid, child_watch_func, session);
+  session->child_watch_source = g_child_watch_source_new (session->child_pid);
+  g_source_set_callback (session->child_watch_source, (GSourceFunc) child_watch_func, session, NULL);
+  g_source_attach (session->child_watch_source, g_main_context_get_thread_default ());
+
   session->child_stdout_channel = g_io_channel_unix_new (session->child_stdout);
-  session->child_stdout_watch_id = g_io_add_watch (session->child_stdout_channel, G_IO_IN, io_watch_have_data, session);
+  session->child_stdout_watch_source = g_io_create_watch (session->child_stdout_channel, G_IO_IN);
+  g_source_set_callback (session->child_stdout_watch_source, (GSourceFunc) io_watch_have_data, session, NULL);
+  g_source_attach (session->child_stdout_watch_source, g_main_context_get_thread_default ());
+
 
   session->success = FALSE;
 
diff --git a/src/polkitagent/polkitagenttextlistener.c b/src/polkitagent/polkitagenttextlistener.c
new file mode 100644
index 0000000..b5c8a3f
--- /dev/null
+++ b/src/polkitagent/polkitagenttextlistener.c
@@ -0,0 +1,565 @@
+/*
+ * 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 at 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 <polkit/polkitprivate.h>
+
+#include "polkitagentlistener.h"
+#include "polkitagenttextlistener.h"
+#include "polkitagentsession.h"
+
+/**
+ * SECTION:polkitagenttextlistener
+ * @title: PolkitAgentTextListener
+ * @short_description: Text-based Authentication Agent
+ * @stability: Unstable
+ *
+ * #PolkitAgentTextListener is an #PolkitAgentListener implementation
+ * that interacts with the user using a textual interface.
+ */
+
+/**
+ * PolkitAgentTextListener:
+ *
+ * The #PolkitAgentTextListener struct should not be accessed directly.
+ */
+struct _PolkitAgentTextListener
+{
+  PolkitAgentListener parent_instance;
+
+  GSimpleAsyncResult *simple;
+  PolkitAgentSession *active_session;
+  gulong cancel_id;
+  GCancellable *cancellable;
+
+  FILE *tty;
+};
+
+typedef struct
+{
+  PolkitAgentListenerClass parent_class;
+} PolkitAgentTextListenerClass;
+
+static void 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 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 (PolkitAgentTextListener, polkit_agent_text_listener, POLKIT_AGENT_TYPE_LISTENER,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
+
+static void
+polkit_agent_text_listener_init (PolkitAgentTextListener *listener)
+{
+}
+
+static void
+polkit_agent_text_listener_finalize (GObject *object)
+{
+  PolkitAgentTextListener *listener = 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 (polkit_agent_text_listener_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (polkit_agent_text_listener_parent_class)->finalize (object);
+}
+
+static void
+polkit_agent_text_listener_class_init (PolkitAgentTextListenerClass *klass)
+{
+  GObjectClass *gobject_class;
+  PolkitAgentListenerClass *listener_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = polkit_agent_text_listener_finalize;
+
+  listener_class = POLKIT_AGENT_LISTENER_CLASS (klass);
+  listener_class->initiate_authentication        = polkit_agent_text_listener_initiate_authentication;
+  listener_class->initiate_authentication_finish = polkit_agent_text_listener_initiate_authentication_finish;
+}
+
+/**
+ * polkit_agent_text_listener_new:
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Creates a new #PolkitAgentTextListener for authenticating the user
+ * via an textual interface on the controlling terminal
+ * (e.g. <filename>/dev/tty</filename>). This can fail if e.g. the
+ * current process has no controlling terminal.
+ *
+ * Returns: A #PolkitAgentTextListener or %NULL if @error is set. Free with g_object_unref() when done with it.
+ */
+PolkitAgentListener *
+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 (POLKIT_AGENT_TYPE_TEXT_LISTENER,
+                                                cancellable,
+                                                error,
+                                                NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+initable_init (GInitable     *initable,
+               GCancellable  *cancellable,
+               GError       **error)
+{
+  PolkitAgentTextListener *listener = 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)
+{
+  PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data);
+
+  fprintf (listener->tty, "\x1B[1;31m");
+  if (gained_authorization)
+    fprintf (listener->tty, "==== AUTHENTICATION COMPLETE ===\n");
+  else
+    fprintf (listener->tty, "==== AUTHENTICATION FAILED ===\n");
+  fprintf (listener->tty, "\x1B[0m");
+  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)
+{
+  PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (user_data);
+  struct termios ts, ots;
+  GString *str;
+
+  fprintf (listener->tty, "%s", request);
+  fflush (listener->tty);
+
+  setbuf (listener->tty, NULL);
+
+  /* TODO: We really ought to block SIGINT and STGSTP (and probably
+   *       other signals too) so we can restore the terminal (since we
+   *       turn off echoing). See e.g. Advanced Programming in the
+   *       UNIX Environment 2nd edition (Steves and Rago) section
+   *       18.10, pg 660 where this is suggested. See also various
+   *       getpass(3) implementations
+   *
+   *       However, since we are a library routine the user could have
+   *       multiple threads - in fact, typical usage of
+   *       PolkitAgentTextListener is to run it in a thread. And
+   *       unfortunately threads and POSIX signals is a royal PITA.
+   *
+   *       Maybe we could fork(2) and ask for the password in the
+   *       child and send it back to the parent over a pipe? (we are
+   *       guaranteed that there is only one thread in the child
+   *       process).
+   *
+   *       (Side benefit of doing this in a child process is that we
+   *       could avoid blocking the thread where the
+   *       PolkitAgentTextListener object is being serviced from. But
+   *       since this class is normally used in a dedicated thread
+   *       it doesn't really matter *anyway*.)
+   *
+   *       Anyway, On modern Linux not doing this doesn't seem to be a
+   *       problem - looks like modern shells restore echoing anyway
+   *       on the first input. So maybe it's not even worth solving
+   *       the problem.
+   */
+
+  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)
+{
+  PolkitAgentTextListener *listener = 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)
+{
+  PolkitAgentTextListener *listener = 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)
+{
+  PolkitAgentTextListener *listener = 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 (PolkitAgentTextListener *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
+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)
+{
+  PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (_listener);
+  GSimpleAsyncResult *simple;
+  PolkitIdentity *identity;
+
+  simple = g_simple_async_result_new (G_OBJECT (listener),
+                                      callback,
+                                      user_data,
+                                      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);
+
+  fprintf (listener->tty, "\x1B[1;31m");
+  fprintf (listener->tty,
+           "==== AUTHENTICATING FOR %s ===\n",
+           action_id);
+  fprintf (listener->tty, "\x1B[0m");
+  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
+polkit_agent_text_listener_initiate_authentication_finish (PolkitAgentListener  *_listener,
+                                                           GAsyncResult         *res,
+                                                           GError              **error)
+{
+  PolkitAgentTextListener *listener = POLKIT_AGENT_TEXT_LISTENER (_listener);
+  gboolean ret;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) ==
+                  polkit_agent_text_listener_initiate_authentication);
+  g_assert (listener->active_session == NULL);
+
+  ret = FALSE;
+
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
diff --git a/src/polkitagent/polkitagenttextlistener.h b/src/polkitagent/polkitagenttextlistener.h
new file mode 100644
index 0000000..87aa503
--- /dev/null
+++ b/src/polkitagent/polkitagenttextlistener.h
@@ -0,0 +1,45 @@
+/*
+ * 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 at redhat.com>
+ */
+
+#if !defined(_POLKIT_AGENT_INSIDE_POLKIT_AGENT_H) && !defined (_POLKIT_AGENT_COMPILATION)
+#error "Only <polkitagent/polkitagent.h> can be included directly, this file may disappear or change contents"
+#endif
+
+#ifndef __POLKIT_AGENT_TEXT_LISTENER_H
+#define __POLKIT_AGENT_TEXT_LISTENER_H
+
+#include <polkit/polkit.h>
+#include <polkitagent/polkitagenttypes.h>
+
+G_BEGIN_DECLS
+
+#define POLKIT_AGENT_TYPE_TEXT_LISTENER          (polkit_agent_text_listener_get_type())
+#define POLKIT_AGENT_TEXT_LISTENER(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_AGENT_TYPE_TEXT_LISTENER, PolkitAgentTextListener))
+#define POLKIT_AGENT_IS_TEXT_LISTENER(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_AGENT_TYPE_TEXT_LISTENER))
+
+GType                polkit_agent_text_listener_get_type (void) G_GNUC_CONST;
+PolkitAgentListener *polkit_agent_text_listener_new      (GCancellable   *cancellable,
+                                                          GError        **error);
+
+
+G_END_DECLS
+
+#endif /* __POLKIT_AGENT_TEXT_LISTENER_H */
diff --git a/src/polkitagent/polkitagenttypes.h b/src/polkitagent/polkitagenttypes.h
index 8847c91..1de03c6 100644
--- a/src/polkitagent/polkitagenttypes.h
+++ b/src/polkitagent/polkitagenttypes.h
@@ -33,6 +33,9 @@ G_BEGIN_DECLS
 struct _PolkitAgentListener;
 typedef struct _PolkitAgentListener PolkitAgentListener;
 
+struct _PolkitAgentTextListener;
+typedef struct _PolkitAgentTextListener PolkitAgentTextListener;
+
 struct _PolkitAgentSession;
 typedef struct _PolkitAgentSession PolkitAgentSession;
 
diff --git a/src/polkitbackend/polkitbackendinteractiveauthority.c b/src/polkitbackend/polkitbackendinteractiveauthority.c
index ab783b4..31e60df 100644
--- a/src/polkitbackend/polkitbackendinteractiveauthority.c
+++ b/src/polkitbackend/polkitbackendinteractiveauthority.c
@@ -85,7 +85,8 @@ typedef void (*AuthenticationAgentCallback) (AuthenticationAgent         *agent,
                                              PolkitIdentity              *authenticated_identity,
                                              gpointer                     user_data);
 
-static void                authentication_agent_free (AuthenticationAgent *agent);
+static AuthenticationAgent *authentication_agent_ref   (AuthenticationAgent *agent);
+static void                 authentication_agent_unref (AuthenticationAgent *agent);
 
 static void                authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
                                                                     PolkitSubject               *subject,
@@ -99,7 +100,7 @@ static void                authentication_agent_initiate_challenge (Authenticati
                                                                     AuthenticationAgentCallback  callback,
                                                                     gpointer                     user_data);
 
-static PolkitSubject *authentication_agent_get_session (AuthenticationAgent *agent);
+static PolkitSubject *authentication_agent_get_scope (AuthenticationAgent *agent);
 
 static AuthenticationAgent *get_authentication_agent_for_subject (PolkitBackendInteractiveAuthority *authority,
                                                                   PolkitSubject *subject);
@@ -195,7 +196,17 @@ typedef struct
 
   TemporaryAuthorizationStore *temporary_authorization_store;
 
-  GHashTable *hash_session_to_authentication_agent;
+  /* Maps from PolkitSubject* to AuthenticationAgent* - currently the
+   * following PolkitSubject-derived types are used
+   *
+   *  - PolkitSystemBusName - for authentication agents handling interaction for a single well-known name
+   *    - typically pkexec(1) launched via e.g. ssh(1) or login(1)
+   *
+   *  - PolkitUnixSession - for authentication agents handling interaction for a whole login session
+   *    - typically a desktop environment session
+   *
+   */
+  GHashTable *hash_scope_to_authentication_agent;
 
   GDBusConnection *system_bus_connection;
   guint name_owner_changed_signal_id;
@@ -276,10 +287,10 @@ polkit_backend_interactive_authority_init (PolkitBackendInteractiveAuthority *au
 
   priv->temporary_authorization_store = temporary_authorization_store_new (authority);
 
-  priv->hash_session_to_authentication_agent = g_hash_table_new_full ((GHashFunc) polkit_subject_hash,
-                                                                      (GEqualFunc) polkit_subject_equal,
-                                                                      (GDestroyNotify) g_object_unref,
-                                                                      (GDestroyNotify) authentication_agent_free);
+  priv->hash_scope_to_authentication_agent = g_hash_table_new_full ((GHashFunc) polkit_subject_hash,
+                                                                    (GEqualFunc) polkit_subject_equal,
+                                                                    (GDestroyNotify) g_object_unref,
+                                                                    (GDestroyNotify) authentication_agent_unref);
 
   priv->session_monitor = polkit_backend_session_monitor_new ();
   g_signal_connect (priv->session_monitor,
@@ -334,7 +345,7 @@ polkit_backend_interactive_authority_finalize (GObject *object)
 
   temporary_authorization_store_free (priv->temporary_authorization_store);
 
-  g_hash_table_unref (priv->hash_session_to_authentication_agent);
+  g_hash_table_unref (priv->hash_scope_to_authentication_agent);
 
   G_OBJECT_CLASS (polkit_backend_interactive_authority_parent_class)->finalize (object);
 }
@@ -410,7 +421,9 @@ polkit_backend_interactive_authority_enumerate_actions (PolkitBackendAuthority
 
 struct AuthenticationAgent
 {
-  PolkitSubject *session;
+  volatile gint ref_count;
+
+  PolkitSubject *scope;
 
   gchar *locale;
   gchar *object_path;
@@ -457,9 +470,9 @@ _polkit_subject_get_cmdline (PolkitSubject *subject)
                                                          &error);
       if (process == NULL)
         {
-          g_warning ("Error getting process for system bus name `%s': %s",
-                     polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
-                     error->message);
+          g_printerr ("Error getting process for system bus name `%s': %s\n",
+                      polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
+                      error->message);
           g_error_free (error);
           goto out;
         }
@@ -479,9 +492,9 @@ _polkit_subject_get_cmdline (PolkitSubject *subject)
                             &contents_len,
                             &error))
     {
-      g_warning ("Error opening `%s': %s",
-                 filename,
-                 error->message);
+      g_printerr ("Error opening `%s': %s\n",
+                  filename,
+                  error->message);
       g_error_free (error);
       goto out;
     }
@@ -575,7 +588,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
   PolkitBackendInteractiveAuthorityPrivate *priv;
   PolkitAuthorizationResult *result;
-  gchar *session_str;
+  gchar *scope_str;
   gchar *subject_str;
   gchar *user_of_subject_str;
   gchar *authenticated_identity_str;
@@ -586,7 +599,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
 
   result = NULL;
 
-  session_str = polkit_subject_to_string (agent->session);
+  scope_str = polkit_subject_to_string (agent->scope);
   subject_str = polkit_subject_to_string (subject);
   user_of_subject_str = polkit_identity_to_string (user_of_subject);
   authenticated_identity_str = NULL;
@@ -622,7 +635,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
 
           id = temporary_authorization_store_add_authorization (priv->temporary_authorization_store,
                                                                 subject,
-                                                                authentication_agent_get_session (agent),
+                                                                authentication_agent_get_scope (agent),
                                                                 action_id);
 
           polkit_details_insert (details, "polkit.temporary_authorization_id", id);
@@ -648,7 +661,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
           polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
                                         "Operator of %s successfully authenticated as %s to gain "
                                         "TEMPORARY authorization for action %s for %s [%s] (owned by %s)",
-                                        session_str,
+                                        scope_str,
                                         authenticated_identity_str,
                                         action_id,
                                         subject_str,
@@ -660,7 +673,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
           polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
                                         "Operator of %s successfully authenticated as %s to gain "
                                         "ONE-SHOT authorization for action %s for %s [%s] (owned by %s)",
-                                        session_str,
+                                        scope_str,
                                         authenticated_identity_str,
                                         action_id,
                                         subject_str,
@@ -673,7 +686,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
       polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
                                     "Operator of %s FAILED to authenticate to gain "
                                     "authorization for action %s for %s [%s] (owned by %s)",
-                                    session_str,
+                                    scope_str,
                                     action_id,
                                     subject_str,
                                     subject_cmdline,
@@ -692,7 +705,7 @@ check_authorization_challenge_cb (AuthenticationAgent         *agent,
   g_free (authenticated_identity_str);
   g_free (user_of_subject_str);
   g_free (subject_str);
-  g_free (session_str);
+  g_free (scope_str);
 }
 
 static PolkitAuthorizationResult *
@@ -1282,7 +1295,7 @@ authentication_session_new (AuthenticationAgent         *agent,
   AuthenticationSession *session;
 
   session = g_new0 (AuthenticationSession, 1);
-  session->agent = agent;
+  session->agent = authentication_agent_ref (agent);
   session->cookie = g_strdup (cookie);
   session->subject = g_object_ref (subject);
   session->user_of_subject = g_object_ref (user_of_subject);
@@ -1311,6 +1324,7 @@ authentication_session_new (AuthenticationAgent         *agent,
 static void
 authentication_session_free (AuthenticationSession *session)
 {
+  authentication_agent_unref (session->agent);
   g_free (session->cookie);
   g_list_foreach (session->identities, (GFunc) g_object_unref, NULL);
   g_list_free (session->identities);
@@ -1340,13 +1354,13 @@ authentication_agent_new_cookie (AuthenticationAgent *agent)
 }
 
 static PolkitSubject *
-authentication_agent_get_session (AuthenticationAgent *agent)
+authentication_agent_get_scope (AuthenticationAgent *agent)
 {
-  return agent->session;
+  return agent->scope;
 }
 
 static void
-authentication_agent_free (AuthenticationAgent *agent)
+authentication_agent_cancel_all_sessions (AuthenticationAgent *agent)
 {
   /* cancel all active authentication sessions; use a copy of the list since
    * callbacks will modify the list
@@ -1360,24 +1374,37 @@ authentication_agent_free (AuthenticationAgent *agent)
       for (l = active_sessions; l != NULL; l = l->next)
         {
           AuthenticationSession *session = l->data;
-
           authentication_session_cancel (session);
         }
       g_list_free (active_sessions);
     }
+}
 
-  if (agent->proxy != NULL)
-    g_object_unref (agent->proxy);
+static AuthenticationAgent *
+authentication_agent_ref (AuthenticationAgent *agent)
+{
+  g_atomic_int_inc (&agent->ref_count);
+  return agent;
+}
 
-  g_object_unref (agent->session);
-  g_free (agent->locale);
-  g_free (agent->object_path);
-  g_free (agent->unique_system_bus_name);
-  g_free (agent);
+static void
+authentication_agent_unref (AuthenticationAgent *agent)
+{
+  if (g_atomic_int_dec_and_test (&agent->ref_count))
+    {
+      if (agent->proxy != NULL)
+        g_object_unref (agent->proxy);
+
+      g_object_unref (agent->scope);
+      g_free (agent->locale);
+      g_free (agent->object_path);
+      g_free (agent->unique_system_bus_name);
+      g_free (agent);
+    }
 }
 
 static AuthenticationAgent *
-authentication_agent_new (PolkitSubject *session,
+authentication_agent_new (PolkitSubject *scope,
                           const gchar *unique_system_bus_name,
                           const gchar *locale,
                           const gchar *object_path)
@@ -1387,7 +1414,8 @@ authentication_agent_new (PolkitSubject *session,
 
   agent = g_new0 (AuthenticationAgent, 1);
 
-  agent->session = g_object_ref (session);
+  agent->ref_count = 1;
+  agent->scope = g_object_ref (scope);
   agent->object_path = g_strdup (object_path);
   agent->unique_system_bus_name = g_strdup (unique_system_bus_name);
   agent->locale = g_strdup (locale);
@@ -1425,13 +1453,41 @@ get_authentication_agent_for_subject (PolkitBackendInteractiveAuthority *authori
   agent = NULL;
   session_for_subject = NULL;
 
+  agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject);
+  if (agent != NULL)
+    goto out;
+
+  if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
+    {
+      PolkitSubject *process;
+      process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject),
+                                                         NULL,
+                                                         NULL);
+      if (process != NULL)
+        {
+          agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, process);
+          if (agent != NULL)
+            {
+              g_object_unref (process);
+              goto out;
+            }
+          g_object_unref (process);
+        }
+    }
+
+  /* Now, we should also cover the case where @subject is a
+   * UnixProcess but the agent is a SystemBusName. However, this can't
+   * happen because we only allow registering agents for UnixProcess
+   * and UnixSession subjects!
+   */
+
   session_for_subject = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor,
                                                                                 subject,
                                                                                 NULL);
   if (session_for_subject == NULL)
     goto out;
 
-  agent = g_hash_table_lookup (priv->hash_session_to_authentication_agent, session_for_subject);
+  agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, session_for_subject);
 
  out:
   if (session_for_subject != NULL)
@@ -1455,7 +1511,7 @@ get_authentication_session_for_cookie (PolkitBackendInteractiveAuthority *author
 
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (authority);
 
-  g_hash_table_iter_init (&hash_iter, priv->hash_session_to_authentication_agent);
+  g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent);
   while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent))
     {
       GList *l;
@@ -1491,7 +1547,7 @@ get_authentication_sessions_initiated_by_system_bus_unique_name (PolkitBackendIn
 
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (authority);
 
-  g_hash_table_iter_init (&hash_iter, priv->hash_session_to_authentication_agent);
+  g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent);
   while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent))
     {
       GList *l;
@@ -1525,7 +1581,7 @@ get_authentication_sessions_for_system_bus_unique_name_subject (PolkitBackendInt
 
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (authority);
 
-  g_hash_table_iter_init (&hash_iter, priv->hash_session_to_authentication_agent);
+  g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent);
   while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent))
     {
       GList *l;
@@ -1557,7 +1613,7 @@ get_authentication_agent_by_unique_system_bus_name (PolkitBackendInteractiveAuth
 
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (authority);
 
-  g_hash_table_iter_init (&hash_iter, priv->hash_session_to_authentication_agent);
+  g_hash_table_iter_init (&hash_iter, priv->hash_scope_to_authentication_agent);
   while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &agent))
     {
       if (strcmp (agent->unique_system_bus_name, unique_system_bus_name) == 0)
@@ -1584,7 +1640,7 @@ authentication_agent_begin_cb (GDBusProxy   *proxy,
                                  res,
                                  &error))
     {
-      g_warning ("Error performing authentication: %s", error->message);
+      g_printerr ("Error performing authentication: %s\n", error->message);
       g_error_free (error);
       gained_authorization = FALSE;
     }
@@ -1681,7 +1737,7 @@ get_localized_data_for_challenge (PolkitBackendInteractiveAuthority *authority,
   /* Set LANG and locale so gettext() + friends work when running the code in the extensions */
   if (setlocale (LC_ALL, locale) == NULL)
     {
-      g_warning ("Invalid locale '%s'", locale);
+      g_printerr ("Invalid locale '%s'\n", locale);
     }
   g_setenv ("LANG", locale, TRUE);
 
@@ -1861,7 +1917,7 @@ authentication_agent_cancel_cb (GDBusProxy   *proxy,
   error = NULL;
   if (!g_dbus_proxy_call_finish (proxy, res, &error))
     {
-      g_warning ("Error cancelling authentication: %s", error->message);
+      g_printerr ("Error cancelling authentication: %s\n", error->message);
       g_error_free (error);
     }
 }
@@ -1892,93 +1948,145 @@ polkit_backend_interactive_authority_register_authentication_agent (PolkitBacken
   PolkitBackendInteractiveAuthority *interactive_authority;
   PolkitBackendInteractiveAuthorityPrivate *priv;
   PolkitSubject *session_for_caller;
+  PolkitIdentity *user_of_caller;
+  PolkitIdentity *user_of_subject;
   AuthenticationAgent *agent;
   gboolean ret;
-  gchar *agent_cmdline;
+  gchar *caller_cmdline;
+  gchar *subject_as_string;
 
-  session_for_caller = NULL;
   ret = FALSE;
 
+  session_for_caller = NULL;
+  user_of_caller = NULL;
+  user_of_subject = NULL;
+  subject_as_string = NULL;
+  caller_cmdline = NULL;
+  agent = NULL;
+
+  /* TODO: validate that object path is well-formed */
+
   interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority);
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (interactive_authority);
 
-  if (!POLKIT_IS_UNIX_SESSION (subject))
+  if (POLKIT_IS_UNIX_SESSION (subject))
+    {
+      session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor,
+                                                                                   caller,
+                                                                                   NULL);
+      if (session_for_caller == NULL)
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Cannot determine session the caller is in");
+          goto out;
+        }
+      if (!polkit_subject_equal (session_for_caller, subject))
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Passed session and the session the caller is in differs. They must be equal for now.");
+          goto out;
+        }
+    }
+  else if (POLKIT_IS_UNIX_PROCESS (subject))
+    {
+      /* explicitly OK */
+    }
+  else
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Can only register PolkitUnixSession objects for now.");
+                   "Only unix-process and unix-session subjects can be used for authentication agents.");
       goto out;
     }
 
-  session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor,
-                                                                               caller,
-                                                                               NULL);
-  if (session_for_caller == NULL)
+  user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL);
+  if (user_of_caller == NULL)
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Cannot determine session the caller is in");
+                   "Cannot determine user of caller");
       goto out;
     }
-
-  if (!polkit_subject_equal (session_for_caller, subject))
+  user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, NULL);
+  if (user_of_subject == NULL)
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Passed session and the session the caller is in differs. They must be equal for now.");
+                   "Cannot determine user of subject");
       goto out;
     }
+  if (!polkit_identity_equal (user_of_caller, user_of_subject))
+    {
+      if (POLKIT_IS_UNIX_USER (user_of_caller) && polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_of_caller)) == 0)
+        {
+          /* explicitly allow uid 0 to register for other users */
+        }
+      else
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "User of caller and user of subject differs.");
+          goto out;
+        }
+    }
 
-  agent = g_hash_table_lookup (priv->hash_session_to_authentication_agent, session_for_caller);
+  agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject);
   if (agent != NULL)
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "An authentication agent already exists for session");
+                   "An authentication agent already exists for the given subject");
       goto out;
     }
 
-  /* TODO: validate that object path is well-formed */
-
-  agent = authentication_agent_new (session_for_caller,
+  agent = authentication_agent_new (subject,
                                     polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)),
                                     locale,
                                     object_path);
 
-  g_hash_table_insert (priv->hash_session_to_authentication_agent,
-                       g_object_ref (session_for_caller),
+  g_hash_table_insert (priv->hash_scope_to_authentication_agent,
+                       g_object_ref (subject),
                        agent);
 
-  agent_cmdline = _polkit_subject_get_cmdline (caller);
-  if (agent_cmdline == NULL)
-    agent_cmdline = g_strdup ("<unknown>");
+  caller_cmdline = _polkit_subject_get_cmdline (caller);
+  if (caller_cmdline == NULL)
+    caller_cmdline = g_strdup ("<unknown>");
 
-  g_debug ("Added authentication agent for session %s at name %s [%s], object path %s, locale %s",
-           polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session_for_caller)),
+  subject_as_string = polkit_subject_to_string (subject);
+
+  g_debug ("Added authentication agent for %s at name %s [%s], object path %s, locale %s",
+           subject_as_string,
            polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)),
-           agent_cmdline,
+           caller_cmdline,
            object_path,
            locale);
 
   polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                "Registered Authentication Agent for session %s "
+                                "Registered Authentication Agent for %s "
                                 "(system bus name %s [%s], object path %s, locale %s)",
-                                polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session_for_caller)),
+                                subject_as_string,
                                 polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller)),
-                                agent_cmdline,
+                                caller_cmdline,
                                 object_path,
                                 locale);
-
-  g_free (agent_cmdline);
-
-
   ret = TRUE;
 
  out:
+  g_free (caller_cmdline);
+  g_free (subject_as_string);
+  if (user_of_caller != NULL)
+    g_object_unref (user_of_caller);
+  if (user_of_subject != NULL)
+    g_object_unref (user_of_subject);
   if (session_for_caller != NULL)
     g_object_unref (session_for_caller);
 
@@ -1995,46 +2103,91 @@ polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBack
   PolkitBackendInteractiveAuthority *interactive_authority;
   PolkitBackendInteractiveAuthorityPrivate *priv;
   PolkitSubject *session_for_caller;
+  PolkitIdentity *user_of_caller;
+  PolkitIdentity *user_of_subject;
   AuthenticationAgent *agent;
   gboolean ret;
+  gchar *scope_str;
 
   interactive_authority = POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority);
   priv = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_GET_PRIVATE (interactive_authority);
 
   ret = FALSE;
   session_for_caller = NULL;
+  user_of_caller = NULL;
+  user_of_subject = NULL;
 
-  if (!POLKIT_IS_UNIX_SESSION (subject))
+  if (POLKIT_IS_UNIX_SESSION (subject))
+    {
+      session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor,
+                                                                                   caller,
+                                                                                   NULL);
+      if (session_for_caller == NULL)
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Cannot determine session the caller is in");
+          goto out;
+        }
+
+      if (!polkit_subject_equal (session_for_caller, subject))
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Passed session and the session the caller is in differs. They must be equal for now.");
+          goto out;
+        }
+    }
+  else if (POLKIT_IS_UNIX_PROCESS (subject))
+    {
+      /* explicitly OK */
+    }
+  else
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Can only unregister PolkitUnixSession objects for now.");
+                   "Only unix-process and unix-session subjects can be used for authentication agents.");
       goto out;
     }
 
-  session_for_caller = polkit_backend_session_monitor_get_session_for_subject (priv->session_monitor,
-                                                                               caller,
-                                                                               NULL);
-  if (session_for_caller == NULL)
+  user_of_caller = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, caller, NULL);
+  if (user_of_caller == NULL)
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Cannot determine session the caller is in");
+                   "Cannot determine user of caller");
       goto out;
     }
-
-  if (!polkit_subject_equal (session_for_caller, subject))
+  user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor, subject, NULL);
+  if (user_of_subject == NULL)
     {
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Passed session and the session the caller is in differs. They must be equal for now.");
+                   "Cannot determine user of subject");
       goto out;
     }
+  if (!polkit_identity_equal (user_of_caller, user_of_subject))
+    {
+      if (POLKIT_IS_UNIX_USER (user_of_caller) && polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_of_caller)) == 0)
+        {
+          /* explicitly allow uid 0 to register for other users */
+        }
+      else
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "User of caller and user of subject differs.");
+          goto out;
+        }
+    }
 
-  agent = g_hash_table_lookup (priv->hash_session_to_authentication_agent, session_for_caller);
+  agent = g_hash_table_lookup (priv->hash_scope_to_authentication_agent, subject);
   if (agent == NULL)
     {
       g_set_error (error,
@@ -2044,8 +2197,7 @@ polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBack
       goto out;
     }
 
-  if (strcmp (agent->unique_system_bus_name,
-              polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller))) != 0)
+  if (g_strcmp0 (agent->unique_system_bus_name, polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (caller))) != 0)
     {
       g_set_error (error,
                    POLKIT_ERROR,
@@ -2054,7 +2206,7 @@ polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBack
       goto out;
     }
 
-  if (strcmp (agent->object_path, object_path) != 0)
+  if (g_strcmp0 (agent->object_path, object_path) != 0)
     {
       g_set_error (error,
                    POLKIT_ERROR,
@@ -2063,27 +2215,34 @@ polkit_backend_interactive_authority_unregister_authentication_agent (PolkitBack
       goto out;
     }
 
-
-  g_debug ("Removing authentication agent for session %s at name %s, object path %s, locale %s",
-           polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (agent->session)),
+  scope_str = polkit_subject_to_string (agent->scope);
+  g_debug ("Removing authentication agent for %s at name %s, object path %s, locale %s",
+           scope_str,
            agent->unique_system_bus_name,
            agent->object_path,
            agent->locale);
 
   polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                "Unregistered Authentication Agent for session %s "
+                                "Unregistered Authentication Agent for %s "
                                 "(system bus name %s, object path %s, locale %s)",
-                                polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (agent->session)),
+                                scope_str,
                                 agent->unique_system_bus_name,
                                 agent->object_path,
                                 agent->locale);
+  g_free (scope_str);
 
+  authentication_agent_cancel_all_sessions (agent);
   /* this works because we have exactly one agent per session */
-  g_hash_table_remove (priv->hash_session_to_authentication_agent, agent->session);
+  /* this frees agent... */
+  g_hash_table_remove (priv->hash_scope_to_authentication_agent, agent->scope);
 
   ret = TRUE;
 
  out:
+  if (user_of_caller != NULL)
+    g_object_unref (user_of_caller);
+  if (user_of_subject != NULL)
+    g_object_unref (user_of_subject);
   if (session_for_caller != NULL)
     g_object_unref (session_for_caller);
   return ret;
@@ -2204,21 +2363,27 @@ polkit_backend_interactive_authority_system_bus_name_owner_changed (PolkitBacken
       agent = get_authentication_agent_by_unique_system_bus_name (interactive_authority, name);
       if (agent != NULL)
         {
-          g_debug ("Removing authentication agent for session %s at name %s, object path %s (disconnected from bus)",
-                   polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (agent->session)),
+          gchar *scope_str;
+
+          scope_str = polkit_subject_to_string (agent->scope);
+          g_debug ("Removing authentication agent for %s at name %s, object path %s (disconnected from bus)",
+                   scope_str,
                    agent->unique_system_bus_name,
                    agent->object_path);
 
           polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                        "Unregistered Authentication Agent for session %s "
+                                        "Unregistered Authentication Agent for %s "
                                         "(system bus name %s, object path %s, locale %s) (disconnected from bus)",
-                                        polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (agent->session)),
+                                        scope_str,
                                         agent->unique_system_bus_name,
                                         agent->object_path,
                                         agent->locale);
+          g_free (scope_str);
 
+          authentication_agent_cancel_all_sessions (agent);
           /* this works because we have exactly one agent per session */
-          g_hash_table_remove (priv->hash_session_to_authentication_agent, agent->session);
+          /* this frees agent... */
+          g_hash_table_remove (priv->hash_scope_to_authentication_agent, agent->scope);
         }
 
       /* cancel all authentication sessions initiated by the process owning the vanished name */
@@ -2266,7 +2431,7 @@ struct TemporaryAuthorization
 {
   TemporaryAuthorizationStore *store;
   PolkitSubject *subject;
-  PolkitSubject *session;
+  PolkitSubject *scope;
   gchar *id;
   gchar *action_id;
   guint64 time_granted;
@@ -2280,7 +2445,7 @@ temporary_authorization_free (TemporaryAuthorization *authorization)
 {
   g_free (authorization->id);
   g_object_unref (authorization->subject);
-  g_object_unref (authorization->session);
+  g_object_unref (authorization->scope);
   g_free (authorization->action_id);
   if (authorization->expiration_timeout_id > 0)
     g_source_remove (authorization->expiration_timeout_id);
@@ -2333,9 +2498,9 @@ temporary_authorization_store_has_authorization (TemporaryAuthorizationStore *st
                                                                 &error);
       if (subject_to_use == NULL)
         {
-          g_warning ("Error getting process for system bus name `%s': %s",
-                     polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
-                     error->message);
+          g_printerr ("Error getting process for system bus name `%s': %s\n",
+                      polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
+                      error->message);
           g_error_free (error);
           subject_to_use = g_object_ref (subject);
         }
@@ -2403,7 +2568,7 @@ on_unix_process_check_vanished_timeout (gpointer user_data)
     {
       if (error != NULL)
         {
-          g_warning ("Error checking if process exists: %s", error->message);
+          g_printerr ("Error checking if process exists: %s\n", error->message);
           g_error_free (error);
         }
       else
@@ -2472,7 +2637,7 @@ temporary_authorization_store_remove_authorizations_for_system_bus_name (Tempora
 static const gchar *
 temporary_authorization_store_add_authorization (TemporaryAuthorizationStore *store,
                                                  PolkitSubject               *subject,
-                                                 PolkitSubject               *session,
+                                                 PolkitSubject               *scope,
                                                  const gchar                 *action_id)
 {
   TemporaryAuthorization *authorization;
@@ -2494,9 +2659,9 @@ temporary_authorization_store_add_authorization (TemporaryAuthorizationStore *st
                                                                 &error);
       if (subject_to_use == NULL)
         {
-          g_warning ("Error getting process for system bus name `%s': %s",
-                     polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
-                     error->message);
+          g_printerr ("Error getting process for system bus name `%s': %s\n",
+                      polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
+                      error->message);
           g_error_free (error);
           subject_to_use = g_object_ref (subject);
         }
@@ -2517,7 +2682,7 @@ temporary_authorization_store_add_authorization (TemporaryAuthorizationStore *st
   authorization->id = g_strdup_printf ("tmpauthz%" G_GUINT64_FORMAT, store->serial++);
   authorization->store = store;
   authorization->subject = g_object_ref (subject_to_use);
-  authorization->session = g_object_ref (session);
+  authorization->scope = g_object_ref (scope);
   authorization->action_id = g_strdup (action_id);
   authorization->time_granted = time (NULL);
   authorization->time_expires = authorization->time_granted + expiration_seconds;
@@ -2617,7 +2782,7 @@ polkit_backend_interactive_authority_enumerate_temporary_authorizations (PolkitB
       TemporaryAuthorization *ta = l->data;
       PolkitTemporaryAuthorization *tmp_authz;
 
-      if (!polkit_subject_equal (ta->session, subject))
+      if (!polkit_subject_equal (ta->scope, subject))
         continue;
 
       tmp_authz = polkit_temporary_authorization_new (ta->id,
@@ -2695,7 +2860,7 @@ polkit_backend_interactive_authority_revoke_temporary_authorizations (PolkitBack
 
       ll = l->next;
 
-      if (!polkit_subject_equal (ta->session, subject))
+      if (!polkit_subject_equal (ta->scope, subject))
         continue;
 
       priv->temporary_authorization_store->authorizations = g_list_remove (priv->temporary_authorization_store->authorizations, ta);
@@ -2760,7 +2925,7 @@ polkit_backend_interactive_authority_revoke_temporary_authorization_by_id (Polki
       if (strcmp (ta->id, id) != 0)
         continue;
 
-      if (!polkit_subject_equal (session_for_caller, ta->session))
+      if (!polkit_subject_equal (session_for_caller, ta->scope))
         {
           g_set_error (error,
                        POLKIT_ERROR,
diff --git a/src/programs/Makefile.am b/src/programs/Makefile.am
index 5c8a26f..99bd299 100644
--- a/src/programs/Makefile.am
+++ b/src/programs/Makefile.am
@@ -31,6 +31,7 @@ pkexec_CFLAGS =                             				\
 pkexec_LDADD =  	                      				\
 	$(GLIB_LIBS)							\
 	$(top_builddir)/src/polkit/libpolkit-gobject-1.la		\
+	$(top_builddir)/src/polkitagent/libpolkit-agent-1.la		\
 	$(NULL)
 
 polkitmodulesdir = $(libdir)/polkit-1/extensions
diff --git a/src/programs/pkcheck.c b/src/programs/pkcheck.c
index fb61b33..cda9e8e 100644
--- a/src/programs/pkcheck.c
+++ b/src/programs/pkcheck.c
@@ -198,7 +198,7 @@ main (int argc, char *argv[])
     }
   else if (opt_show_version)
     {
-      g_print ("pkexec version %s\n", PACKAGE_VERSION);
+      g_print ("pkcheck version %s\n", PACKAGE_VERSION);
       ret = 0;
       goto out;
     }
diff --git a/src/programs/pkexec.c b/src/programs/pkexec.c
index 4a3d55d..f4480ff 100644
--- a/src/programs/pkexec.c
+++ b/src/programs/pkexec.c
@@ -43,6 +43,8 @@
 #include <stdarg.h>
 
 #include <polkit/polkit.h>
+#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
+#include <polkitagent/polkitagent.h>
 
 static gchar *original_user_name = NULL;
 static gchar *original_cwd = NULL;
@@ -361,6 +363,7 @@ validate_environment_variable (const gchar *key,
   return ret;
 }
 
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 int
@@ -417,6 +420,7 @@ main (int argc, char *argv[])
   gchar *opt_user;
   pid_t pid_of_caller;
   uid_t uid_of_caller;
+  gpointer local_agent_handle;
 
   ret = 127;
   authority = NULL;
@@ -428,6 +432,7 @@ main (int argc, char *argv[])
   path = NULL;
   command_line = NULL;
   opt_user = NULL;
+  local_agent_handle = NULL;
 
   /* check for correct invocation */
   if (geteuid () != 0)
@@ -642,6 +647,7 @@ main (int argc, char *argv[])
   action_id = find_action_for_path (authority, path);
   g_assert (action_id != NULL);
 
+ try_again:
   error = NULL;
   result = polkit_authority_check_authorization_sync (authority,
                                                       subject,
@@ -664,8 +670,40 @@ main (int argc, char *argv[])
     }
   else if (polkit_authorization_result_get_is_challenge (result))
     {
-      g_printerr ("Error executing command as another user: No authentication agent was found.\n");
-      goto out;
+      if (local_agent_handle == NULL)
+        {
+          PolkitAgentListener *listener;
+          error = NULL;
+          /* this will fail if we can't find a controlling terminal */
+          listener = polkit_agent_text_listener_new (NULL, &error);
+          if (listener == NULL)
+            {
+              g_printerr ("Error creating textual authentication agent: %s\n", error->message);
+              g_error_free (error);
+              goto out;
+            }
+          local_agent_handle = polkit_agent_listener_register (listener,
+                                                               POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD,
+                                                               subject,
+                                                               NULL, /* object_path */
+                                                               NULL, /* GCancellable */
+                                                               &error);
+          g_object_unref (listener);
+          if (local_agent_handle == NULL)
+            {
+              g_printerr ("Error registering local authentication agent: %s\n", error->message);
+              g_error_free (error);
+              goto out;
+            }
+          g_object_unref (result);
+          result = NULL;
+          goto try_again;
+        }
+      else
+        {
+          g_printerr ("Error executing command as another user.\n");
+          goto out;
+        }
     }
   else
     {
@@ -802,6 +840,10 @@ main (int argc, char *argv[])
   g_assert_not_reached ();
 
  out:
+  /* if applicable, nuke the local authentication agent */
+  if (local_agent_handle != NULL)
+    polkit_agent_listener_unregister (local_agent_handle);
+
   if (result != NULL)
     g_object_unref (result);
 
commit 17f0600529dc926ae4a0c85dc56c393cc09e4011
Author: David Zeuthen <davidz at redhat.com>
Date:   Thu Aug 12 16:49:25 2010 -0400

    Fix scanning of unix-process subjects
    
    In particular accept both "unix-process:<pid>,<starttime>" and
    "unix-process:<pid>". For the latter, return an error if we cannot
    lookup the starttime (for example if the given pid references a
    non-existing process).
    
    Signed-off-by: David Zeuthen <davidz at redhat.com>

diff --git a/src/polkit/polkitsubject.c b/src/polkit/polkitsubject.c
index 19d60b9..51e60e0 100644
--- a/src/polkit/polkitsubject.c
+++ b/src/polkit/polkitsubject.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <string.h>
+#include <stdio.h>
 
 #include "polkitsubject.h"
 #include "polkitunixprocess.h"
@@ -222,8 +223,6 @@ polkit_subject_from_string  (const gchar   *str,
                              GError       **error)
 {
   PolkitSubject *subject;
-  guint64 val;
-  gchar *endptr;
 
   g_return_val_if_fail (str != NULL, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
@@ -234,12 +233,15 @@ polkit_subject_from_string  (const gchar   *str,
 
   if (g_str_has_prefix (str, "unix-process:"))
     {
-      val = g_ascii_strtoull (str + sizeof "unix-process:" - 1,
-                              &endptr,
-                              10);
-      if (*endptr == '\0')
+      gint scanned_pid;
+      guint64 scanned_starttime;
+      if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT, &scanned_pid, &scanned_starttime) == 2)
         {
-          subject = polkit_unix_process_new ((gint) val);
+          subject = polkit_unix_process_new_full (scanned_pid, scanned_starttime);
+        }
+      else if (sscanf (str, "unix-process:%d", &scanned_pid) == 1)
+        {
+          subject = polkit_unix_process_new_full (scanned_pid, 0);
           if (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)) == 0)
             {
               g_object_unref (subject);
@@ -247,8 +249,8 @@ polkit_subject_from_string  (const gchar   *str,
               g_set_error (error,
                            POLKIT_ERROR,
                            POLKIT_ERROR_FAILED,
-                           "No process with pid %" G_GUINT64_FORMAT,
-                           val);
+                           "Unable to determine start time for process with pid %d",
+                           scanned_pid);
             }
         }
     }
@@ -266,7 +268,7 @@ polkit_subject_from_string  (const gchar   *str,
       g_set_error (error,
                    POLKIT_ERROR,
                    POLKIT_ERROR_FAILED,
-                   "Malformed subject string '%s'",
+                   "Malformed subject string `%s'",
                    str);
     }
 


More information about the hal-commit mailing list