diff options
-rw-r--r-- | clients/cli/agent.c | 36 | ||||
-rw-r--r-- | clients/cli/polkit-agent.c | 23 | ||||
-rw-r--r-- | clients/common/nm-polkit-listener.c | 320 | ||||
-rw-r--r-- | clients/common/nm-polkit-listener.h | 9 |
4 files changed, 253 insertions, 135 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c index f4057df9e9..93b5ca079a 100644 --- a/clients/cli/agent.c +++ b/clients/cli/agent.c @@ -16,6 +16,7 @@ #include "utils.h" #include "nm-secret-agent-simple.h" #include "polkit-agent.h" +#include "nm-polkit-listener.h" static void usage (void) @@ -149,27 +150,50 @@ do_agent_secret (NmCli *nmc, int argc, char **argv) return nmc->return_value; } +static void +polkit_registered (gpointer instance, + gpointer user_data) +{ + g_print (_("nmcli successfully registered as a polkit agent.\n")); +} + +static void +polkit_error (gpointer instance, + const char *error, + gpointer user_data) +{ + g_main_loop_quit (loop); +} + static NMCResultCode do_agent_polkit (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; + gs_free_error GError *error = NULL; next_arg (nmc, &argc, &argv, NULL); if (nmc->complete) return nmc->return_value; - /* Initialize polkit agent */ if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { g_dbus_error_strip_remote_error (error); - g_string_printf (nmc->return_text, _("Error: polkit agent initialization failed: %s"), + g_string_printf (nmc->return_text, + _("Error: polkit agent initialization failed: %s"), error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - g_error_free (error); } else { /* We keep running */ nmc->should_wait++; - - g_print (_("nmcli successfully registered as a polkit agent.\n")); + g_signal_connect (nmc->pk_listener, + NM_POLKIT_LISTENER_SIGNAL_ERROR, + G_CALLBACK (polkit_error), + NULL); + g_signal_connect (nmc->pk_listener, + NM_POLKIT_LISTENER_SIGNAL_REGISTERED, + G_CALLBACK (polkit_registered), + NULL); + + /* keep running */ + nmc->should_wait++; } return nmc->return_value; diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c index 99159225b7..376a2f5d52 100644 --- a/clients/cli/polkit-agent.c +++ b/clients/cli/polkit-agent.c @@ -38,7 +38,7 @@ polkit_error (gpointer instance, const char *error, gpointer user_data) { - g_printerr (_("Warning: polkit agent error: %s\n"), error); + g_printerr (_("Error: polkit agent failed: %s\n"), error); } gboolean @@ -49,19 +49,20 @@ nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error) g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (nmc && nmc->client && NM_IS_CLIENT (nmc->client)) { + if (nmc->client && nm_client_get_dbus_connection (nmc->client)) { dbus_connection = nm_client_get_dbus_connection (nmc->client); - listener = nm_polkit_listener_new (dbus_connection); + listener = nm_polkit_listener_new (dbus_connection, for_session); } else { dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); - listener = nm_polkit_listener_new (dbus_connection); - g_object_unref (dbus_connection); - } - if (!listener) { - return FALSE; + if (!dbus_connection) { + return FALSE; + } + + listener = nm_polkit_listener_new (dbus_connection, for_session); + g_object_unref (dbus_connection); } g_signal_connect (listener, @@ -81,12 +82,6 @@ void nmc_polkit_agent_fini (NmCli* nmc) { if (nmc->pk_listener) { - g_signal_handlers_disconnect_by_func (nmc->pk_listener, - polkit_read_passwd, - nmc); - g_signal_handlers_disconnect_by_func (nmc->pk_listener, - polkit_error, - NULL); g_clear_object (&nmc->pk_listener); } } diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c index 4abe499aa9..f1c1ba45a6 100644 --- a/clients/common/nm-polkit-listener.c +++ b/clients/common/nm-polkit-listener.c @@ -32,6 +32,7 @@ #include "nm-libnm-core-intern/nm-auth-subject.h" #include "c-list/src/c-list.h" +#define LOGIND_BUS_NAME "org.freedesktop.login1" #define POLKIT_BUS_NAME "org.freedesktop.PolicyKit1" #define POLKIT_AUTHORITY_OBJ_PATH "/org/freedesktop/PolicyKit1/Authority" @@ -40,11 +41,16 @@ #define POLKIT_AGENT_OBJ_PATH "/org/freedesktop/PolicyKit1/AuthenticationAgent" #define POLKIT_AGENT_DBUS_INTERFACE "org.freedesktop.PolicyKit1.AuthenticationAgent" +#define LOGIND_OBJ_PATH "/org/freedesktop/login1" +#define LOGIND_MANAGER_INTERFACE "org.freedesktop.login1.Manager" + #define NM_POLKIT_LISTENER_DBUS_CONNECTION "dbus-connection" +#define NM_POLKIT_LISTENER_SESSION_AGENT "session-agent" /*****************************************************************************/ enum { + REGISTERED, REQUEST, ERROR, LAST_SIGNAL @@ -61,6 +67,7 @@ struct _NMPolkitListener { guint name_owner_changed_id; GCancellable *cancellable; GMainContext *main_context; + gboolean session_agent; CList request_lst_head; }; @@ -122,7 +129,9 @@ remove_request (AuthRequest *request) nm_clear_g_source_inst (&request->child_stdout_watch_source); nm_clear_g_source_inst (&request->child_stdin_watch_source); - nm_free_secret (g_string_free (request->out_buffer, FALSE)); + nm_explicit_bzero (request->out_buffer->str, + request->out_buffer->len); + g_string_free (request->out_buffer, TRUE); g_string_free (request->in_buffer, TRUE); if (request->child_stdout != -1) { @@ -153,7 +162,7 @@ uid_to_name (uid_t uid) static gboolean find_identity (uid_t uid, gpointer user_data) { - return nm_streq0 (user_data, uid_to_name(uid)); + return nm_streq0 (user_data, uid_to_name (uid)); } static gboolean @@ -199,7 +208,7 @@ choose_identity (GVariant *identities) /* Choose identity. First try current user, then root, and else * take the first one */ - user = getenv("USER"); + user = getenv ("USER"); if ((id = _choose_identity (identities, find_identity, (gpointer) user)) >= 0) { return id; @@ -223,28 +232,41 @@ agent_register_cb (GObject *source_object, ret = g_dbus_connection_call_finish (dbus_connection, res, &error); - if (!res) { + + if (nm_utils_error_is_cancelled (error, FALSE)) { + return; + } + + if (ret) { + g_signal_emit (listener, + signals[REGISTERED], + 0); + } else { g_signal_emit (listener, signals[ERROR], 0, error->message); - return; } } static void -agent_register (NMPolkitListener *self) +agent_register (NMPolkitListener *self, const char *session_id) { const char *locale = NULL; - NMAuthSubject *subject = NULL; + gs_unref_object NMAuthSubject *subject = NULL; GVariant *subject_variant = NULL; locale = g_getenv ("LANG"); - if (locale == NULL) + if (locale == NULL) { locale = "en_US.UTF-8"; + } - subject = nm_auth_subject_new_unix_process_self (); - subject_variant = nm_auth_subject_unix_to_polkit_gvariant(subject); + if (self->session_agent) { + subject = nm_auth_subject_new_unix_session (session_id); + } else { + subject = nm_auth_subject_new_unix_process_self (); + } + subject_variant = nm_auth_subject_unix_to_polkit_gvariant (subject); g_dbus_connection_call (self->dbus_connection, self->name_owner, @@ -261,17 +283,16 @@ agent_register (NMPolkitListener *self) self->cancellable, agent_register_cb, self); - g_object_unref (subject); } static void agent_unregister (NMPolkitListener *self) { - NMAuthSubject *subject = NULL; + gs_unref_object NMAuthSubject *subject = NULL; GVariant *subject_variant = NULL; subject = nm_auth_subject_new_unix_process_self (); - subject_variant = nm_auth_subject_unix_to_polkit_gvariant(subject); + subject_variant = nm_auth_subject_unix_to_polkit_gvariant (subject); g_dbus_connection_call (self->dbus_connection, self->name_owner, @@ -287,28 +308,68 @@ agent_unregister (NMPolkitListener *self) NULL, NULL, self); - g_object_unref (subject); } static void -queue_response_to_helper (AuthRequest *request, const char *response) +retrieve_session_id_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { - const char line_terminator[] = "\n"; - gboolean add_newline = FALSE; - gsize response_len; - - g_return_if_fail (response); + NMPolkitListener *listener = NM_POLKIT_LISTENER (user_data); + char *session_id; + guint32 session_uid; + nm_auto_free_variant_iter GVariantIter *iter; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + gs_free char *err_str = NULL; + uid_t uid = getuid (); - response_len = strlen (response); + ret = g_dbus_connection_call_finish (listener->dbus_connection, + res, + &error); - if (response_len > 0) { - add_newline = (response[response_len - 1] != '\n'); + if (nm_utils_error_is_cancelled (error, FALSE)) { + return; } - g_string_append (request->out_buffer, response); - if (add_newline) { - g_string_append (request->out_buffer, line_terminator); + if (ret) { + g_variant_get_child (ret, 0, "a(susso)", &iter); + + while (g_variant_iter_next (iter, "(&su@s@s@o)", + &session_id, + &session_uid, + NULL, NULL, NULL)) { + if (session_uid == uid) { + agent_register (listener, session_id); + return; + } + } } + + err_str = g_strdup_printf (_("Could not retrieve session id: %s"), + error->message); + + g_signal_emit (listener, + signals[ERROR], + 0, + err_str); +} + +static void +retrieve_session_id (NMPolkitListener *self) +{ + g_dbus_connection_call (self->dbus_connection, + LOGIND_BUS_NAME, + LOGIND_OBJ_PATH, + LOGIND_MANAGER_INTERFACE, + "ListSessions", + NULL, + G_VARIANT_TYPE ("(a(susso))"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable, + retrieve_session_id_cb, + self); } static void @@ -316,11 +377,11 @@ complete_authentication (AuthRequest *request, gboolean result) { if (result) { - g_dbus_method_invocation_return_value(request->dbus_invocation, NULL); + g_dbus_method_invocation_return_value (request->dbus_invocation, NULL); } else { - g_dbus_method_invocation_return_dbus_error(request->dbus_invocation, - "org.freedesktop.PolicyKit1.Error.Failed", - ""); + g_dbus_method_invocation_return_dbus_error (request->dbus_invocation, + "org.freedesktop.PolicyKit1.Error.Failed", + ""); } remove_request (request); } @@ -332,48 +393,66 @@ io_watch_can_write (int fd, { AuthRequest *request = user_data; ssize_t n_written; + gboolean done = FALSE; if (condition & G_IO_HUP || condition & G_IO_ERR) { - g_signal_emit (request->listener, - signals[ERROR], - 0, - "Error occured while writing data to PolicyKit setuid helper"); - return G_SOURCE_REMOVE; + done = TRUE; + goto done; } - if (request->out_buffer->len < 1) { - return G_SOURCE_CONTINUE; + n_written = write (request->child_stdin, + &request->out_buffer->str[request->out_buffer_offset], + request->out_buffer->len - request->out_buffer_offset); + + if (n_written < 0 && errno != EAGAIN) { + done = TRUE; + goto done; } - n_written = - write (request->child_stdin, - &request->out_buffer->str[request->out_buffer_offset], - request->out_buffer->len - request->out_buffer_offset); + if (n_written > 0) { + if ((size_t) n_written == (request->out_buffer->len - request->out_buffer_offset)) { + done = TRUE; + goto done; + } + request->out_buffer_offset += n_written; + } - if (n_written < 0 && errno != EAGAIN) { +done: + if (done) { nm_explicit_bzero (request->out_buffer->str, request->out_buffer->len); - - g_signal_emit (request->listener, - signals[ERROR], - 0, - "Error in communicating with the PolicyKit setuid helper"); - } else if (n_written > 0) { - if (n_written == (request->out_buffer->len - request->out_buffer_offset)) { - nm_explicit_bzero (request->out_buffer->str, - request->out_buffer->len); - - request->out_buffer_offset = 0; - g_string_set_size (request->out_buffer, 0); - } else { - request->out_buffer_offset = n_written; - } + g_string_set_size (request->out_buffer, 0); + request->out_buffer_offset = 0; + nm_clear_g_source_inst (&request->child_stdin_watch_source); } return G_SOURCE_CONTINUE; } +static void +queue_string_to_helper (AuthRequest *request, const char *response) +{ + g_return_if_fail (response); + + g_string_append (request->out_buffer, response); + + if ( request->out_buffer->len == 0 + || request->out_buffer->str[request->out_buffer->len - 1] != '\n') + g_string_append_c (request->out_buffer, '\n'); + + if (!request->child_stdin_watch_source) { + request->child_stdin_watch_source = g_unix_fd_source_new (request->child_stdin, + G_IO_OUT | G_IO_ERR | G_IO_HUP); + g_source_set_callback (request->child_stdin_watch_source, + G_SOURCE_FUNC (io_watch_can_write), + request, + NULL); + g_source_attach (request->child_stdin_watch_source, + request->listener->main_context); + } +} + static gboolean io_watch_have_data (int fd, GIOCondition condition, @@ -389,10 +468,6 @@ io_watch_have_data (int fd, if (condition & G_IO_HUP || condition & G_IO_ERR) { - g_signal_emit (request->listener, - signals[ERROR], - 0, - "Error occured while reading data from PolicyKit setuid helper"); complete_auth = TRUE; auth_result = FALSE; goto out; @@ -400,11 +475,11 @@ io_watch_have_data (int fd, n_read = nm_utils_fd_read (fd, request->in_buffer); + if (n_read == -EAGAIN) { + return G_SOURCE_CONTINUE; + } + if (n_read < 0) { - g_signal_emit (request->listener, - signals[ERROR], - 0, - "Error reading line from PolicyKit setuid helper"); complete_auth = TRUE; auth_result = FALSE; goto out; @@ -421,15 +496,15 @@ io_watch_have_data (int fd, if (NM_STR_HAS_PREFIX (unescaped, "PAM_PROMPT_ECHO")) { /* emit signal and wait for response */ g_signal_emit (request->listener, - signals[REQUEST], - 0, - request->action_id, - request->message, - request->username, - &response); + signals[REQUEST], + 0, + request->action_id, + request->message, + request->username, + &response); if (response) { - queue_response_to_helper (request, response); + queue_string_to_helper (request, response); nm_free_secret (response); } else { complete_auth = TRUE; @@ -460,7 +535,6 @@ begin_authentication (AuthRequest *request) { int fd_flags; char *helper_argv[3]; - size_t cookie_len = strlen (request->cookie); helper_argv[0] = POLKIT_PACKAGE_PREFIX "/lib/polkit-1/polkit-agent-helper-1"; helper_argv[1] = request->username; @@ -484,22 +558,6 @@ begin_authentication (AuthRequest *request) fd_flags = fcntl (request->child_stdin, F_GETFD, 0); fcntl (request->child_stdin, F_SETFL, fd_flags | O_NONBLOCK); - request->child_stdin_watch_source = g_unix_fd_source_new (request->child_stdin, - G_IO_OUT | G_IO_ERR | G_IO_HUP); - g_source_set_callback (request->child_stdin_watch_source, - G_SOURCE_FUNC (io_watch_can_write), - request, - NULL); - g_source_attach (request->child_stdin_watch_source, - request->listener->main_context); - - /* Write the cookie on stdin so it can't be seen by other processes */ - request->cookie[cookie_len] = '\n'; - g_string_append_len (request->out_buffer, - request->cookie, - cookie_len + 1); - request->cookie[cookie_len] = '\0'; - fd_flags = fcntl (request->child_stdout, F_GETFD, 0); fcntl (request->child_stdout, F_SETFL, fd_flags | O_NONBLOCK); @@ -512,6 +570,9 @@ begin_authentication (AuthRequest *request) g_source_attach (request->child_stdout_watch_source, request->listener->main_context); + /* Write the cookie on stdin so it can't be seen by other processes */ + queue_string_to_helper (request, request->cookie); + return; } @@ -546,11 +607,10 @@ create_request (NMPolkitListener *listener, request->username = g_strdup (username); request->cookie = g_strdup (cookie); request->in_buffer = g_string_new (""); - request->out_buffer = g_string_new (""); /* preallocate a large enough buffer so that * secrets don't get reallocated, thus leaked */ - g_string_set_size (request->out_buffer, 1024); + request->out_buffer = g_string_sized_new (1024); c_list_link_tail (&listener->request_lst_head, &request->request_lst); return request; @@ -566,7 +626,7 @@ dbus_method_call_cb (GDBusConnection *connection, GDBusMethodInvocation *invocation, gpointer user_data) { - NMPolkitListener *listener = user_data; + NMPolkitListener *listener = NM_POLKIT_LISTENER (user_data); const char *action_id; const char *message; const char *cookie; @@ -584,7 +644,7 @@ dbus_method_call_cb (GDBusConnection *connection, &cookie, &identities_gvariant); - uid = choose_identity(identities_gvariant); + uid = choose_identity (identities_gvariant); request = create_request (listener, invocation, @@ -592,7 +652,7 @@ dbus_method_call_cb (GDBusConnection *connection, message, uid_to_name (uid), cookie); - begin_authentication(request); + begin_authentication (request); } else if (nm_streq (method_name, "CancelAuthentication")) { g_variant_get (parameters, "&s", @@ -641,7 +701,7 @@ export_dbus_iface (NMPolkitListener *self, GError **error) static void name_owner_changed (NMPolkitListener *self, - const char *name_owner) + const char *name_owner) { gs_free_error GError *error = NULL; @@ -659,18 +719,27 @@ name_owner_changed (NMPolkitListener *self, } if (export_dbus_iface (self, &error)) { - agent_register (self); + if (self->session_agent) { + retrieve_session_id (self); + } else { + agent_register (self, NULL); + } + } else { + g_signal_emit (self, + signals[ERROR], + 0, + "Could not export the PolicyKit Authentication Agent DBus interface"); } } static void name_owner_changed_cb (GDBusConnection *connection, - const char *sender_name, - const char *object_path, - const char *interface_name, - const char *signal_name, - GVariant *parameters, - gpointer user_data) + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { NMPolkitListener *self = user_data; const char *new_owner; @@ -703,11 +772,14 @@ get_name_owner_cb (const char *name_owner, NM_GOBJECT_PROPERTIES_DEFINE (NMPolkitListener, PROP_DBUS_CONNECTION, + PROP_SESSION_AGENT, ); static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { NMPolkitListener *self = NM_POLKIT_LISTENER (object); @@ -715,6 +787,9 @@ set_property (GObject *object, guint prop_id, case PROP_DBUS_CONNECTION: self->dbus_connection = g_value_dup_object (value); break; + case PROP_SESSION_AGENT: + self->session_agent = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -725,7 +800,7 @@ static void nm_polkit_listener_init (NMPolkitListener *self) { c_list_init (&self->request_lst_head); - self->main_context = g_main_context_ref_thread_default(); + self->main_context = g_main_context_ref_thread_default (); } static void @@ -733,7 +808,7 @@ constructed (GObject *object) { NMPolkitListener *self = NM_POLKIT_LISTENER (object); - self->cancellable = g_cancellable_new(); + self->cancellable = g_cancellable_new (); self->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (self->dbus_connection, @@ -755,17 +830,19 @@ constructed (GObject *object) /** * nm_polkit_listener_new: * @dbus_connection: a open DBus connection + * @session_agent: TRUE if a session agent is wanted, FALSE for a process agent * * Creates a new #NMPolkitListener and registers it as a polkit agent. * * Returns: a new #NMPolkitListener */ NMPolkitListener * -nm_polkit_listener_new (GDBusConnection *dbus_connection) +nm_polkit_listener_new (GDBusConnection *dbus_connection, gboolean session_agent) { - return g_object_new(NM_TYPE_POLKIT_LISTENER, - NM_POLKIT_LISTENER_DBUS_CONNECTION, dbus_connection, - NULL); + return g_object_new (NM_TYPE_POLKIT_LISTENER, + NM_POLKIT_LISTENER_DBUS_CONNECTION, dbus_connection, + NM_POLKIT_LISTENER_SESSION_AGENT, session_agent, + NULL); } static void @@ -811,6 +888,13 @@ nm_polkit_listener_class_init (NMPolkitListenerClass *klass) G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_SESSION_AGENT] = + g_param_spec_boolean (NM_POLKIT_LISTENER_SESSION_AGENT, "", "", + FALSE, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); @@ -818,7 +902,7 @@ nm_polkit_listener_class_init (NMPolkitListenerClass *klass) signals[REQUEST] = g_signal_new (NM_POLKIT_LISTENER_SIGNAL_REQUEST, NM_TYPE_POLKIT_LISTENER, - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -828,10 +912,22 @@ nm_polkit_listener_class_init (NMPolkitListenerClass *klass) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + signals[REGISTERED] = + g_signal_new (NM_POLKIT_LISTENER_SIGNAL_REGISTERED, + NM_TYPE_POLKIT_LISTENER, + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + signals[ERROR] = g_signal_new (NM_POLKIT_LISTENER_SIGNAL_ERROR, NM_TYPE_POLKIT_LISTENER, - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h index da2de5f3b8..d84f3a1bb5 100644 --- a/clients/common/nm-polkit-listener.h +++ b/clients/common/nm-polkit-listener.h @@ -9,10 +9,13 @@ #define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type ()) G_DECLARE_FINAL_TYPE (NMPolkitListener, nm_polkit_listener, NM, POLKIT_LISTENER, GObject) -NMPolkitListener *nm_polkit_listener_new (GDBusConnection *dbus_connection); +NMPolkitListener *nm_polkit_listener_new (GDBusConnection *dbus_connection, gboolean session_agent); /* Signals */ -#define NM_POLKIT_LISTENER_SIGNAL_REQUEST "secret-request" -#define NM_POLKIT_LISTENER_SIGNAL_ERROR "error" +#define NM_POLKIT_LISTENER_SIGNAL_REGISTERED "registered" +#define NM_POLKIT_LISTENER_SIGNAL_REQUEST "secret-request" +#define NM_POLKIT_LISTENER_SIGNAL_AUTH_SUCCESS "auth-success" +#define NM_POLKIT_LISTENER_SIGNAL_AUTH_FAILURE "auth-failure" +#define NM_POLKIT_LISTENER_SIGNAL_ERROR "error" #endif /* __NM_POLKIT_LISTENER_H__ */ |