diff --git a/Makefile.am b/Makefile.am index abee1d13c5..249a188c81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -476,6 +476,8 @@ shared_nm_libnm_core_intern_libnm_libnm_core_intern_la_SOURCES = \ shared/nm-libnm-core-intern/nm-ethtool-utils.h \ shared/nm-libnm-core-intern/nm-libnm-core-utils.c \ shared/nm-libnm-core-intern/nm-libnm-core-utils.h \ + shared/nm-libnm-core-intern/nm-auth-subject.c \ + shared/nm-libnm-core-intern/nm-auth-subject.h \ $(NULL) shared_nm_libnm_core_intern_libnm_libnm_core_intern_la_LDFLAGS = \ @@ -2244,8 +2246,6 @@ src_libNetworkManager_la_SOURCES = \ src/nm-proxy-config.h \ src/nm-auth-manager.c \ src/nm-auth-manager.h \ - src/nm-auth-subject.c \ - src/nm-auth-subject.h \ src/nm-auth-utils.c \ src/nm-auth-utils.h \ src/nm-manager.c \ @@ -4255,6 +4255,8 @@ clients_common_libnmc_base_la_SOURCES = \ clients/common/nm-vpn-helpers.h \ clients/common/nm-client-utils.c \ clients/common/nm-client-utils.h \ + clients/common/nm-polkit-listener.c \ + clients/common/nm-polkit-listener.h \ $(NULL) EXTRA_DIST += \ @@ -4446,14 +4448,6 @@ clients_cli_nmcli_LDADD = \ $(GLIB_LIBS) \ $(READLINE_LIBS) -if WITH_POLKIT_AGENT -clients_cli_nmcli_CPPFLAGS += $(POLKIT_CFLAGS) -clients_cli_nmcli_SOURCES += \ - clients/common/nm-polkit-listener.c \ - clients/common/nm-polkit-listener.h -clients_cli_nmcli_LDADD += $(POLKIT_LIBS) -endif - clients_cli_nmcli_LDFLAGS = \ -Wl,--version-script="$(srcdir)/linker-script-binary.ver" \ $(SANITIZER_EXEC_LDFLAGS) 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_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); - g_print (_("nmcli successfully registered as a polkit agent.\n")); + /* keep running */ + nmc->should_wait++; } return nmc->return_value; diff --git a/clients/cli/meson.build b/clients/cli/meson.build index 1c8148920c..b1b2426910 100644 --- a/clients/cli/meson.build +++ b/clients/cli/meson.build @@ -6,18 +6,6 @@ install_data( install_dir: join_paths(nm_datadir, 'bash-completion', 'completions'), ) -sources = files( - 'agent.c', - 'common.c', - 'connections.c', - 'devices.c', - 'general.c', - 'nmcli.c', - 'polkit-agent.c', - 'settings.c', - 'utils.c', -) - deps = [ libnmc_base_dep, libnmc_dep, @@ -25,14 +13,19 @@ deps = [ libnm_libnm_aux_dep, ] -if enable_polkit_agent - sources += nm_polkit_listener - deps += polkit_agent_dep -endif - executable( name, - sources, + files( + 'agent.c', + 'common.c', + 'connections.c', + 'devices.c', + 'general.c', + 'nmcli.c', + 'polkit-agent.c', + 'settings.c', + 'utils.c', + ), dependencies: deps, c_args: clients_c_flags + ['-DG_LOG_DOMAIN="@0@"'.format(name)], link_args: ldflags_linker_script_binary, diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c index 69bfb68dd3..376a2f5d52 100644 --- a/clients/cli/polkit-agent.c +++ b/clients/cli/polkit-agent.c @@ -14,16 +14,12 @@ #include "nm-polkit-listener.h" #include "common.h" -#if WITH_POLKIT_AGENT static char * -polkit_request (NMPolkitListener *listener, - const char *request, - const char *action_id, - const char *message, - const char *icon_name, - const char *user, - gboolean echo_on, - gpointer user_data) +polkit_read_passwd (gpointer instance, + const char *action_id, + const char *message, + const char *user, + gpointer user_data) { NmCli *nmc = user_data; @@ -32,87 +28,68 @@ polkit_request (NMPolkitListener *listener, /* Ask user for polkit authorization password */ if (user) { - gs_free char *tmp = NULL; - char *p; - - /* chop of ": " if present */ - tmp = g_strdup (request); - p = strrchr (tmp, ':'); - if (p && nm_streq (p, ": ")) - *p = '\0'; - return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s (%s): ", tmp, user); + return nmc_readline_echo (&nmc->nmc_config, FALSE, "password (%s): ", user); } - - return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s", request); + return nmc_readline_echo (&nmc->nmc_config, FALSE, "password: "); } static void -polkit_show_info (NMPolkitListener *listener, - const char *text, - gpointer user_data) +polkit_error (gpointer instance, + const char *error, + gpointer user_data) { - g_print (_("Authentication message: %s\n"), text); + g_printerr (_("Error: polkit agent failed: %s\n"), error); } -static void -polkit_show_error (NMPolkitListener *listener, - const char *text, - gpointer user_data) -{ - g_print (_("Authentication error: %s\n"), text); -} - -static void -polkit_completed (NMPolkitListener *listener, - gboolean gained_authorization, - gpointer user_data) -{ - /* We don't print anything here. The outcome will be evident from - * the operation result anyway. */ -} -#endif - gboolean nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error) { -#if WITH_POLKIT_AGENT - static const NMPolkitListenVtable vtable = { - .on_request = polkit_request, - .on_show_info = polkit_show_info, - .on_show_error = polkit_show_error, - .on_completed = polkit_completed, - }; NMPolkitListener *listener; + GDBusConnection *dbus_connection = NULL; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - listener = nm_polkit_listener_new (for_session, error); - if (!listener) - return FALSE; + 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, for_session); + } else { + dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + NULL, + error); - nm_polkit_listener_set_vtable (listener, &vtable, nmc); + if (!dbus_connection) { + return FALSE; + } + + listener = nm_polkit_listener_new (dbus_connection, for_session); + g_object_unref (dbus_connection); + } + + g_signal_connect (listener, + NM_POLKIT_LISTENER_SIGNAL_REQUEST, + G_CALLBACK (polkit_read_passwd), + nmc); + g_signal_connect (listener, + NM_POLKIT_LISTENER_SIGNAL_ERROR, + G_CALLBACK (polkit_error), + NULL); nmc->pk_listener = listener; -#endif return TRUE; } void nmc_polkit_agent_fini (NmCli* nmc) { -#if WITH_POLKIT_AGENT if (nmc->pk_listener) { - nm_polkit_listener_set_vtable (nmc->pk_listener, NULL, NULL); g_clear_object (&nmc->pk_listener); } -#endif } gboolean nmc_start_polkit_agent_start_try (NmCli *nmc) { -#if WITH_POLKIT_AGENT - GError *error = NULL; + gs_free_error GError *error = NULL; /* We don't register polkit agent at all when running non-interactively */ if (!nmc->ask) @@ -121,9 +98,7 @@ nmc_start_polkit_agent_start_try (NmCli *nmc) if (!nmc_polkit_agent_init (nmc, FALSE, &error)) { g_printerr (_("Warning: polkit agent initialization failed: %s\n"), error->message); - g_error_free (error); return FALSE; } -#endif return TRUE; } diff --git a/clients/common/meson.build b/clients/common/meson.build index afa257b1df..f68435ec9e 100644 --- a/clients/common/meson.build +++ b/clients/common/meson.build @@ -1,7 +1,5 @@ common_inc = include_directories('.') -nm_polkit_listener = files('nm-polkit-listener.c') - common_deps = [ libnm_dep, libnm_nm_default_dep, @@ -13,6 +11,7 @@ sources = files( 'nm-client-utils.c', 'nm-secret-agent-simple.c', 'nm-vpn-helpers.c', + 'nm-polkit-listener.c', ) libnmc_base = static_library( diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c index b294165237..f1c1ba45a6 100644 --- a/clients/common/nm-polkit-listener.c +++ b/clients/common/nm-polkit-listener.c @@ -22,339 +22,917 @@ #include "nm-polkit-listener.h" -#include -#include +#include +#include +#include -#if WITH_POLKIT_AGENT +#include "nm-glib-aux/nm-dbus-aux.h" +#include "nm-glib-aux/nm-secret-utils.h" +#include "nm-glib-aux/nm-io-utils.h" +#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" +#define POLKIT_AUTHORITY_IFACE_NAME "org.freedesktop.PolicyKit1.Authority" + +#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 +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _NMPolkitListener { + GObject parent; + + GDBusConnection *dbus_connection; + char *name_owner; + guint pk_auth_agent_reg_id; + guint name_owner_changed_id; + GCancellable *cancellable; + GMainContext *main_context; + gboolean session_agent; + CList request_lst_head; +}; + +G_DEFINE_TYPE (NMPolkitListener, nm_polkit_listener, G_TYPE_OBJECT) /*****************************************************************************/ typedef struct { - gpointer reg_handle; /* handle of polkit agent registration */ - - GSimpleAsyncResult *simple; - PolkitAgentSession *active_session; - gulong cancel_id; - GCancellable *cancellable; + CList request_lst; + NMPolkitListener *listener; char *action_id; char *message; - char *icon_name; - char *identity; + char *username; + char *cookie; + GString *in_buffer; + GString *out_buffer; + size_t out_buffer_offset; - const NMPolkitListenVtable *vtable; - gpointer vtable_user_data; -} NMPolkitListenerPrivate; + int child_stdout; + int child_stdin; + GSource *child_stdout_watch_source; + GSource *child_stdin_watch_source; + GDBusMethodInvocation *dbus_invocation; +} AuthRequest; -G_DEFINE_TYPE (NMPolkitListener, nm_polkit_listener, POLKIT_AGENT_TYPE_LISTENER) - -#define NM_POLKIT_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerPrivate)) - -/*****************************************************************************/ - -void -nm_polkit_listener_set_vtable (NMPolkitListener *self, - const NMPolkitListenVtable *vtable, - gpointer user_data) -{ - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); - - priv->vtable = vtable; - priv->vtable_user_data = user_data; -} - -/*****************************************************************************/ +static const GDBusInterfaceInfo interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + POLKIT_AGENT_DBUS_INTERFACE, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_GDBUS_METHOD_INFO ( + "BeginAuthentication", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("action_id", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("message", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("icon_name", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("details", "a{ss}"), + NM_DEFINE_GDBUS_ARG_INFO ("cookie", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("identities", "a(sa{sv})"), + ), + ), + NM_DEFINE_GDBUS_METHOD_INFO ( + "CancelAuthentication", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("cookie", "s"), + ), + ), + ), +); static void -on_request (PolkitAgentSession *session, - const char *request, - gboolean echo_on, - gpointer user_data) +remove_request (AuthRequest *request) { - NMPolkitListener *self = NM_POLKIT_LISTENER (user_data); - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); - gs_free char *response = NULL; + c_list_unlink (&request->request_lst); - if (priv->vtable && priv->vtable->on_request) { - response = priv->vtable->on_request (self, - request, priv->action_id, - priv->message, priv->icon_name, - priv->identity, echo_on, - priv->vtable_user_data); + nm_clear_g_free (&request->action_id); + nm_clear_g_free (&request->message); + nm_clear_g_free (&request->username); + nm_clear_g_free (&request->cookie); + nm_clear_g_source_inst (&request->child_stdout_watch_source); + nm_clear_g_source_inst (&request->child_stdin_watch_source); + + 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) { + nm_close (request->child_stdout); + request->child_stdout = -1; } - if (response) - polkit_agent_session_response (session, response); - else { - //FIXME: polkit_agent_session_cancel() should emit "completed", but it doesn't work for me ??? - //polkit_agent_session_cancel (session); - polkit_agent_session_response (session, ""); + if (request->child_stdin != -1) { + nm_close (request->child_stdin); + request->child_stdin = -1; } + + g_slice_free (AuthRequest, request); } -static void -on_show_info (PolkitAgentSession *session, - const char *text, - gpointer user_data) +static const char * +uid_to_name (uid_t uid) { - NMPolkitListener *self = NM_POLKIT_LISTENER (user_data); - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); + const char *name = NULL; + struct passwd *passwd; - if (priv->vtable && priv->vtable->on_show_info) { - priv->vtable->on_show_info (self, text, - priv->vtable_user_data); - } -} - -static void -on_show_error (PolkitAgentSession *session, - const char *text, - gpointer user_data) -{ - NMPolkitListener *self = NM_POLKIT_LISTENER (user_data); - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); - - if (priv->vtable && priv->vtable->on_show_error) { - priv->vtable->on_show_error (self, text, - priv->vtable_user_data); - } -} - -static void -on_completed (PolkitAgentSession *session, - gboolean gained_authorization, - gpointer user_data) -{ - NMPolkitListener *self = NM_POLKIT_LISTENER (user_data); - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); - - if (priv->vtable->on_completed) { - priv->vtable->on_completed (self, gained_authorization, - priv->vtable_user_data); - } - - g_simple_async_result_complete_in_idle (priv->simple); - - g_object_unref (priv->simple); - g_object_unref (priv->active_session); - if (priv->cancellable) { - g_cancellable_disconnect (priv->cancellable, priv->cancel_id); - g_object_unref (priv->cancellable); - } - - priv->simple = NULL; - priv->active_session = NULL; - priv->cancel_id = 0; - - g_clear_pointer (&priv->action_id, g_free); - g_clear_pointer (&priv->message, g_free); - g_clear_pointer (&priv->icon_name, g_free); - g_clear_pointer (&priv->identity, g_free); -} - -static void -on_cancelled (GCancellable *cancellable, gpointer user_data) -{ - NMPolkitListener *self = NM_POLKIT_LISTENER (user_data); - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (self); - - polkit_agent_session_cancel (priv->active_session); -} - -static int -compare_users (gconstpointer a, gconstpointer b) -{ - char *user; - int ret; - - if (POLKIT_IS_UNIX_USER (a)) - user = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (a))); - else - user = polkit_identity_to_string (POLKIT_IDENTITY (a)); - - ret = g_strcmp0 ((const char *) user, (const char *) b); - g_free (user); - return ret; -} - -static PolkitIdentity * -choose_identity (GList *identities) -{ - const char *user; - GList *elem; - - /* Choose identity. First try current user, then root, and else - * take the first one */ - user = getenv("USER"); - elem = g_list_find_custom (identities, user, (GCompareFunc) compare_users); - if (!elem) { - elem = g_list_find_custom (identities, "root", (GCompareFunc) compare_users); - if (!elem) - elem = identities; - } - - return elem->data; -} - -static void -initiate_authentication (PolkitAgentListener *listener, - const char *action_id, - const char *message, - const char *icon_name, - PolkitDetails *details, - const char *cookie, - GList *identities, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); - GSimpleAsyncResult *simple; - PolkitIdentity *identity; - - simple = g_simple_async_result_new (G_OBJECT (listener), - callback, - user_data, - initiate_authentication); - if (cancellable) - g_simple_async_result_set_check_cancellable (simple, cancellable); - if (priv->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); - return; - } - - /* Choose identity */ - identity = choose_identity (identities); - - priv->active_session = polkit_agent_session_new (identity, cookie); - g_signal_connect (priv->active_session, - "completed", - G_CALLBACK (on_completed), - listener); - g_signal_connect (priv->active_session, - "request", - G_CALLBACK (on_request), - listener); - g_signal_connect (priv->active_session, - "show-info", - G_CALLBACK (on_show_info), - listener); - g_signal_connect (priv->active_session, - "show-error", - G_CALLBACK (on_show_error), - listener); - - priv->action_id = g_strdup (action_id); - priv->message = g_strdup (message); - priv->icon_name = g_strdup (icon_name); - if (POLKIT_IS_UNIX_USER (identity)) - priv->identity = g_strdup (polkit_unix_user_get_name (POLKIT_UNIX_USER (identity))); - else - priv->identity = polkit_identity_to_string (identity); - - priv->simple = simple; - priv->cancellable = g_object_ref (cancellable); - priv->cancel_id = g_cancellable_connect (cancellable, - G_CALLBACK (on_cancelled), - listener, - NULL); - - polkit_agent_session_initiate (priv->active_session); + passwd = getpwuid (uid); + if (passwd != NULL) + name = passwd->pw_name; + return name; } static gboolean -initiate_authentication_finish (PolkitAgentListener *listener, - GAsyncResult *result, - GError **error) +find_identity (uid_t uid, gpointer user_data) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); + return nm_streq0 (user_data, uid_to_name (uid)); +} + +static gboolean +first_identity (uid_t uid, gpointer user_data) +{ + return true; +} + +static gint64 +_choose_identity (GVariant *identities, + gboolean (*predicate) (uid_t uid, gpointer user_data), + gpointer user_data) +{ + GVariantIter identity_iter; + GVariantIter *identity_details_iter; + GVariant *unix_id_variant; + uid_t unix_id; + + g_return_val_if_fail (predicate != NULL, FALSE); + + g_variant_iter_init (&identity_iter, identities); + + while (g_variant_iter_loop (&identity_iter, "(&sa{sv})", NULL, &identity_details_iter)) { + while (g_variant_iter_loop (identity_details_iter, "{sv}", NULL, &unix_id_variant)) { + unix_id = g_variant_get_uint32 (unix_id_variant); + + if (predicate (unix_id, user_data)) { + g_variant_unref (unix_id_variant); + g_variant_iter_free (identity_details_iter); + return unix_id; + } + } + g_variant_iter_free (identity_details_iter); + } + return -1; +} + +static uid_t +choose_identity (GVariant *identities) +{ + const char *user; + gint64 id; + + /* Choose identity. First try current user, then root, and else + * take the first one */ + user = getenv ("USER"); + + if ((id = _choose_identity (identities, find_identity, (gpointer) user)) >= 0) { + return id; + } else if ((id = _choose_identity (identities, find_identity, "root")) >= 0) { + return id; + } + + return _choose_identity (identities, first_identity, NULL); +} + +static void +agent_register_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMPolkitListener *listener = NM_POLKIT_LISTENER (user_data); + GDBusConnection *dbus_connection = G_DBUS_CONNECTION (source_object); + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; + + ret = g_dbus_connection_call_finish (dbus_connection, + res, + &error); + + 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); + } +} + +static void +agent_register (NMPolkitListener *self, const char *session_id) +{ + const char *locale = NULL; + gs_unref_object NMAuthSubject *subject = NULL; + GVariant *subject_variant = NULL; + + locale = g_getenv ("LANG"); + if (locale == NULL) { + locale = "en_US.UTF-8"; + } + + 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, + POLKIT_AUTHORITY_OBJ_PATH, + POLKIT_AUTHORITY_IFACE_NAME, + "RegisterAuthenticationAgent", + g_variant_new ("(@(sa{sv})ss)", + subject_variant, + locale, + POLKIT_AGENT_OBJ_PATH), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable, + agent_register_cb, + self); +} + +static void +agent_unregister (NMPolkitListener *self) +{ + 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); + + g_dbus_connection_call (self->dbus_connection, + self->name_owner, + POLKIT_AUTHORITY_OBJ_PATH, + POLKIT_AUTHORITY_IFACE_NAME, + "UnregisterAuthenticationAgent", + g_variant_new ("(@(sa{sv})s)", + subject_variant, + POLKIT_AGENT_OBJ_PATH), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + self); +} + +static void +retrieve_session_id_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + 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 (); + + ret = g_dbus_connection_call_finish (listener->dbus_connection, + res, + &error); + + if (nm_utils_error_is_cancelled (error, FALSE)) { + return; + } + + 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 +complete_authentication (AuthRequest *request, + gboolean result) +{ + if (result) { + 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", + ""); + } + remove_request (request); +} + +static gboolean +io_watch_can_write (int fd, + GIOCondition condition, + gpointer user_data) +{ + AuthRequest *request = user_data; + ssize_t n_written; + gboolean done = FALSE; + + if (condition & G_IO_HUP || + condition & G_IO_ERR) { + 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 && errno != EAGAIN) { + done = TRUE; + goto done; + } + + 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; + } + +done: + if (done) { + nm_explicit_bzero (request->out_buffer->str, + request->out_buffer->len); + 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, + gpointer user_data) +{ + AuthRequest *request = user_data; + gs_free char *unescaped = NULL; + char *response = NULL; + char* line_terminator = 0; + gboolean auth_result = FALSE; + gboolean complete_auth = FALSE; + ssize_t n_read; + + if (condition & G_IO_HUP || + condition & G_IO_ERR) { + complete_auth = TRUE; + auth_result = FALSE; + goto out; + } + + n_read = nm_utils_fd_read (fd, request->in_buffer); + + if (n_read == -EAGAIN) { + return G_SOURCE_CONTINUE; + } + + if (n_read < 0) { + complete_auth = TRUE; + auth_result = FALSE; + goto out; + } + + line_terminator = strchr (request->in_buffer->str, '\n'); + if (!line_terminator) { + return G_SOURCE_CONTINUE; + } + *line_terminator = '\0'; + + unescaped = g_strcompress (request->in_buffer->str); + + 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); + + if (response) { + queue_string_to_helper (request, response); + nm_free_secret (response); + } else { + complete_auth = TRUE; + auth_result = FALSE; + } + } else if (NM_STR_HAS_PREFIX (unescaped, "SUCCESS")) { + complete_auth = TRUE; + auth_result = TRUE; + } else if (NM_STR_HAS_PREFIX (unescaped, "FAILURE")) { + complete_auth = TRUE; + auth_result = FALSE; + } else { + complete_auth = TRUE; + auth_result = FALSE; + } + +out: + g_string_set_size (request->in_buffer, 0); + + if (complete_auth) { + complete_authentication (request, auth_result); + } + return G_SOURCE_CONTINUE; +} + +static void +begin_authentication (AuthRequest *request) +{ + int fd_flags; + char *helper_argv[3]; + + helper_argv[0] = POLKIT_PACKAGE_PREFIX "/lib/polkit-1/polkit-agent-helper-1"; + helper_argv[1] = request->username; + helper_argv[2] = NULL; + + if (!g_spawn_async_with_pipes (NULL, + helper_argv, + NULL, + G_SPAWN_DEFAULT, + NULL, + NULL, + NULL, + &request->child_stdin, + &request->child_stdout, + NULL, + NULL)) { + complete_authentication (request, FALSE); + return; + } + + fd_flags = fcntl (request->child_stdin, F_GETFD, 0); + fcntl (request->child_stdin, F_SETFL, fd_flags | O_NONBLOCK); + + fd_flags = fcntl (request->child_stdout, F_GETFD, 0); + fcntl (request->child_stdout, F_SETFL, fd_flags | O_NONBLOCK); + + request->child_stdout_watch_source = g_unix_fd_source_new (request->child_stdout, + G_IO_IN | G_IO_ERR | G_IO_HUP); + g_source_set_callback (request->child_stdout_watch_source, + G_SOURCE_FUNC (io_watch_have_data), + request, + NULL); + 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; +} + +static AuthRequest* +get_request (NMPolkitListener *listener, + const char *cookie) +{ + AuthRequest *request; + + c_list_for_each_entry (request, &listener->request_lst_head, request_lst) { + if (nm_streq0 (cookie, request->cookie)) { + return request; + } + } + return NULL; +} + +static AuthRequest* +create_request (NMPolkitListener *listener, + GDBusMethodInvocation *invocation, + const char *action_id, + const char *message, + const char *username, + const char *cookie) +{ + AuthRequest *request = g_slice_new0(AuthRequest); + + request->listener = listener; + request->dbus_invocation = invocation; + request->action_id = g_strdup (action_id); + request->message = g_strdup (message); + request->username = g_strdup (username); + request->cookie = g_strdup (cookie); + request->in_buffer = g_string_new (""); + + /* preallocate a large enough buffer so that + * secrets don't get reallocated, thus leaked */ + request->out_buffer = g_string_sized_new (1024); + + c_list_link_tail (&listener->request_lst_head, &request->request_lst); + return request; +} + +static void +dbus_method_call_cb (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + NMPolkitListener *listener = NM_POLKIT_LISTENER (user_data); + const char *action_id; + const char *message; + const char *cookie; + AuthRequest *request; + gs_unref_variant GVariant *identities_gvariant; + uid_t uid; + + if (nm_streq (method_name, "BeginAuthentication")) { + g_variant_get (parameters, + "(&s&s&s@a{ss}&s@a(sa{sv}))", + &action_id, + &message, + NULL, + NULL, + &cookie, + &identities_gvariant); + + uid = choose_identity (identities_gvariant); + + request = create_request (listener, + invocation, + action_id, + message, + uid_to_name (uid), + cookie); + begin_authentication (request); + } else if (nm_streq (method_name, "CancelAuthentication")) { + g_variant_get (parameters, + "&s", + &cookie); + request = get_request (listener, cookie); + + if (request) { + complete_authentication (request, FALSE); + } + } +} + +static gboolean +export_dbus_iface (NMPolkitListener *self, GError **error) +{ + GDBusInterfaceVTable interface_vtable = { + .method_call = dbus_method_call_cb, + .set_property = NULL, + .get_property = NULL, + }; + + g_return_val_if_fail (NM_IS_POLKIT_LISTENER (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* Agent listener iface has been exported already */ + if (self->pk_auth_agent_reg_id) { + return TRUE; + } + + self->pk_auth_agent_reg_id = + g_dbus_connection_register_object (self->dbus_connection, + POLKIT_AGENT_OBJ_PATH, + (GDBusInterfaceInfo*) &interface_info, + &interface_vtable, + self, + NULL, + error); + if (!self->pk_auth_agent_reg_id) { + g_signal_emit (self, + signals[ERROR], + 0, + "Could not register as a PolicyKit Authentication Agent"); + } + return self->pk_auth_agent_reg_id; +} + +static void +name_owner_changed (NMPolkitListener *self, + const char *name_owner) +{ + gs_free_error GError *error = NULL; + + name_owner = nm_str_not_empty (name_owner); + + if (nm_streq0 (self->name_owner, name_owner)) { + return; + } + + g_free (self->name_owner); + self->name_owner = g_strdup (name_owner); + + if (!self->name_owner) { + return; + } + + if (export_dbus_iface (self, &error)) { + 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) +{ + NMPolkitListener *self = user_data; + const char *new_owner; + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) { + return; + } + + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); + + name_owner_changed (self, new_owner); +} + +static void +get_name_owner_cb (const char *name_owner, + GError *error, + gpointer user_data) +{ + if (!name_owner && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + return; + } + name_owner_changed (user_data, name_owner); } /*****************************************************************************/ +NM_GOBJECT_PROPERTIES_DEFINE (NMPolkitListener, + PROP_DBUS_CONNECTION, + PROP_SESSION_AGENT, +); + static void -nm_polkit_listener_init (NMPolkitListener *agent) +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { + NMPolkitListener *self = NM_POLKIT_LISTENER (object); + + switch (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; + } +} + +static void +nm_polkit_listener_init (NMPolkitListener *self) +{ + c_list_init (&self->request_lst_head); + self->main_context = g_main_context_ref_thread_default (); +} + +static void +constructed (GObject *object) +{ + NMPolkitListener *self = NM_POLKIT_LISTENER (object); + + self->cancellable = g_cancellable_new (); + + self->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed (self->dbus_connection, + POLKIT_BUS_NAME, + name_owner_changed_cb, + self, + NULL); + + nm_dbus_connection_call_get_name_owner (self->dbus_connection, + POLKIT_BUS_NAME, + -1, + self->cancellable, + get_name_owner_cb, + self); + + G_OBJECT_CLASS (nm_polkit_listener_parent_class)->constructed (object); } /** * nm_polkit_listener_new: - * @for_session: %TRUE for registering the polkit agent for the user session, - * %FALSE for registering it for the running process - * @vtable: mandatory callbacks - * @user_data: user-data pointer for callbacks - * @error: location to store error, or %NULL + * @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 (gboolean for_session, - GError **error) +nm_polkit_listener_new (GDBusConnection *dbus_connection, gboolean session_agent) { - NMPolkitListener *listener; - PolkitSubject* session; - NMPolkitListenerPrivate *priv; - - g_return_val_if_fail (!error || !*error, NULL); - - listener = g_object_new (NM_TYPE_POLKIT_LISTENER, NULL); - - priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); - - if (for_session) { - session = polkit_unix_session_new_for_process_sync (getpid (), NULL, error); - if (!session) - return NULL; - } else - session = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); - - priv->reg_handle = polkit_agent_listener_register (POLKIT_AGENT_LISTENER (listener), - POLKIT_AGENT_REGISTER_FLAGS_NONE, - session, NULL, NULL, error); - if (!priv->reg_handle) { - g_object_unref (listener); - g_object_unref (session); - return NULL; - } - - return listener; + 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 -nm_polkit_listener_finalize (GObject *object) +dispose (GObject *object) { - NMPolkitListenerPrivate *priv = NM_POLKIT_LISTENER_GET_PRIVATE (object); + NMPolkitListener *self = NM_POLKIT_LISTENER (object); + AuthRequest *request; - if (priv->reg_handle) - polkit_agent_listener_unregister (priv->reg_handle); + nm_clear_g_cancellable (&self->cancellable); - g_free (priv->action_id); - g_free (priv->message); - g_free (priv->icon_name); - g_free (priv->identity); + while ((request = c_list_first_entry (&self->request_lst_head, AuthRequest, request_lst))) { + remove_request (request); + } - G_OBJECT_CLASS (nm_polkit_listener_parent_class)->finalize (object); + if (self->dbus_connection) { + nm_clear_g_dbus_connection_signal (self->dbus_connection, + &self->name_owner_changed_id); + g_dbus_connection_unregister_object (self->dbus_connection, + self->pk_auth_agent_reg_id); + agent_unregister (self); + nm_clear_g_free (&self->name_owner); + g_clear_object (&self->dbus_connection); + } + + nm_clear_pointer (&self->main_context, g_main_context_unref); + + G_OBJECT_CLASS (nm_polkit_listener_parent_class)->dispose (object); } static void nm_polkit_listener_class_init (NMPolkitListenerClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - PolkitAgentListenerClass *pkal_class = POLKIT_AGENT_LISTENER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (NMPolkitListenerPrivate)); + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->dispose = dispose; - gobject_class->finalize = nm_polkit_listener_finalize; + obj_properties[PROP_DBUS_CONNECTION] = + g_param_spec_object (NM_POLKIT_LISTENER_DBUS_CONNECTION, "", "", + G_TYPE_DBUS_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS); - pkal_class->initiate_authentication = initiate_authentication; - pkal_class->initiate_authentication_finish = initiate_authentication_finish; + 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); + + signals[REQUEST] = + g_signal_new (NM_POLKIT_LISTENER_SIGNAL_REQUEST, + NM_TYPE_POLKIT_LISTENER, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_STRING, + 3, + 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_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); } - -#endif /* WITH_POLKIT_AGENT */ diff --git a/clients/common/nm-polkit-listener.h b/clients/common/nm-polkit-listener.h index 42da1bc13f..d84f3a1bb5 100644 --- a/clients/common/nm-polkit-listener.h +++ b/clients/common/nm-polkit-listener.h @@ -6,133 +6,16 @@ #ifndef __NM_POLKIT_LISTENER_H__ #define __NM_POLKIT_LISTENER_H__ -#if WITH_POLKIT_AGENT - -typedef struct _NMPolkitListener NMPolkitListener; -typedef struct _NMPolkitListenerClass NMPolkitListenerClass; - -typedef struct { - - /* - * @request: the request asked by polkit agent - * @action_id: the action_id of the polkit request - * @message: the message of the polkit request - * @icon_name: the icon name of the polkit request - * @user: user name - * @echo_on: whether the response to the request should be echoed to the screen - * @user_data: user data for the callback - * - * Called as a result of a request by polkit. The function should obtain response - * to the request from user, i.e. get the password required. - */ - char *(*on_request) (NMPolkitListener *self, - const char *request, - const char *action_id, - const char *message, - const char *icon_name, - const char *user, - gboolean echo_on, - gpointer user_data); - - /* - * @text: the info text from polkit - * - * Called as a result of show-info signal by polkit. - */ - void (*on_show_info) (NMPolkitListener *self, - const char *text, - gpointer user_data); - - /* - * @text: the error text from polkit - * - * Called as a result of show-error signal by polkit. - */ - void (*on_show_error) (NMPolkitListener *self, - const char *text, - gpointer user_data); - - /* - * @gained_authorization: whether the authorization was successful - * - * Called as a result of completed signal by polkit. - */ - void (*on_completed) (NMPolkitListener *self, - gboolean gained_authorization, - gpointer user_data); -} NMPolkitListenVtable; - -/*****************************************************************************/ - -#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE -#include - #define NM_TYPE_POLKIT_LISTENER (nm_polkit_listener_get_type ()) -#define NM_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListener)) -#define NM_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) -#define NM_IS_POLKIT_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_POLKIT_LISTENER)) -#define NM_IS_POLKIT_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_POLKIT_LISTENER)) -#define NM_POLKIT_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_POLKIT_LISTENER, NMPolkitListenerClass)) +G_DECLARE_FINAL_TYPE (NMPolkitListener, nm_polkit_listener, NM, POLKIT_LISTENER, GObject) -/** - * NMPolkitListenerOnRequestFunc: - * @request: the request asked by polkit agent - * @action_id: the action_id of the polkit request - * @message: the message of the polkit request - * @icon_name: the icon name of the polkit request - * @user: user name - * @echo_on: whether the response to the request should be echoed to the screen - * @user_data: user data for the callback - * - * Called as a result of a request by polkit. The function should obtain response - * to the request from user, i.e. get the password required. - */ -typedef char * (*NMPolkitListenerOnRequestFunc) (const char *request, - const char *action_id, - const char *message, - const char *icon_name, - const char *user, - gboolean echo_on, - gpointer user_data); -/** - * NMPolkitListenerOnShowInfoFunc: - * @text: the info text from polkit - * - * Called as a result of show-info signal by polkit. - */ -typedef void (*NMPolkitListenerOnShowInfoFunc) (const char *text); -/** - * NMPolkitListenerOnShowErrorFunc: - * @text: the error text from polkit - * - * Called as a result of show-error signal by polkit. - */ -typedef void (*NMPolkitListenerOnShowErrorFunc) (const char *text); -/** - * NMPolkitListenerCompletedFunc: - * @gained_authorization: whether the authorization was successful - * - * Called as a result of completed signal by polkit. - */ -typedef void (*NMPolkitListenerOnCompletedFunc) (gboolean gained_authorization); +NMPolkitListener *nm_polkit_listener_new (GDBusConnection *dbus_connection, gboolean session_agent); -struct _NMPolkitListener { - PolkitAgentListener parent; -}; - -struct _NMPolkitListenerClass { - PolkitAgentListenerClass parent; -}; - -GType nm_polkit_listener_get_type (void); - -NMPolkitListener *nm_polkit_listener_new (gboolean for_session, - GError **error); - -void nm_polkit_listener_set_vtable (NMPolkitListener *self, - const NMPolkitListenVtable *vtable, - gpointer user_data); - -#endif +/* Signals */ +#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__ */ diff --git a/config.h.meson b/config.h.meson index 0514418138..009c635da4 100644 --- a/config.h.meson +++ b/config.h.meson @@ -221,8 +221,8 @@ /* Define if you have oFono support (experimental) */ #mesondefine WITH_OFONO -/* Define if you have polkit agent */ -#mesondefine WITH_POLKIT_AGENT +/* Define the polkit agent package prefix */ +#mesondefine POLKIT_PACKAGE_PREFIX /* Define if you have PPP support */ #mesondefine WITH_PPP diff --git a/configure.ac b/configure.ac index 24b3f7c435..22cecb6efc 100644 --- a/configure.ac +++ b/configure.ac @@ -655,18 +655,13 @@ AC_DEFINE_UNQUOTED(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "$enable_polkit", [The de AC_SUBST(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT, "$enable_polkit") PKG_CHECK_MODULES(POLKIT, [polkit-agent-1 >= 0.97], [have_pk_agent=yes],[have_pk_agent=no]) -AC_ARG_ENABLE(polkit-agent, - AS_HELP_STRING([--enable-polkit-agent], [enable polkit agent for clients]), - [enable_polkit_agent=${enableval}], [enable_polkit_agent=${have_pk_agent}]) -if (test "${enable_polkit_agent}" = "yes"); then - if test x"$have_pk_agent" = x"no"; then - AC_MSG_ERROR(Polkit agent is required) - fi - AC_DEFINE(WITH_POLKIT_AGENT, 1, [Define if you have polkit agent]) -else - AC_DEFINE(WITH_POLKIT_AGENT, 0, [Define if you have polkit agent]) +if test x"$have_pk_agent" = x"no"; then + AC_MSG_ERROR(Polkit agent is required) fi -AM_CONDITIONAL(WITH_POLKIT_AGENT, test "${enable_polkit_agent}" = "yes") +POLKIT_PACKAGE_PREFIX=`$PKG_CONFIG --variable=prefix polkit-agent-1` +AC_DEFINE_UNQUOTED([POLKIT_PACKAGE_PREFIX], + ["$POLKIT_PACKAGE_PREFIX"], + [polkit-agent package prefix]) AC_ARG_ENABLE(modify-system, AS_HELP_STRING([--enable-modify-system], [Allow users to modify system connections])) if test "${enable_modify_system}" = "yes"; then @@ -1323,7 +1318,6 @@ if test "${enable_modify_system}" = "yes"; then else echo " policykit: main.auth-polkit=${enable_polkit} (restrictive modify.system)" fi -echo " polkit agent: ${enable_polkit_agent}" echo " selinux: $have_selinux" echo " systemd-journald: $have_systemd_journal (default: logging.backend=${nm_config_logging_backend_default})" echo " hostname persist: ${hostname_persist}" diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index d7e3612dc4..c706ce1b79 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -586,7 +586,6 @@ This tool is still experimental. -Dselinux=true \ -Dpolkit=true \ -Dconfig_auth_polkit_default=true \ - -Dpolkit_agent=true \ -Dmodify_system=true \ -Dconcheck=true \ %if 0%{?fedora} @@ -717,7 +716,6 @@ intltoolize --automake --copy --force %endif --with-selinux=yes \ --enable-polkit=yes \ - --enable-polkit-agent \ --enable-modify-system=yes \ --enable-concheck \ %if 0%{?fedora} diff --git a/libnm-core/meson.build b/libnm-core/meson.build index 4c72590e7b..857e0330a1 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -85,7 +85,7 @@ libnm_core_enum_sources = gnome.mkenums_simple( libnm_libnm_core_intern = static_library( 'nm-libnm-core-intern', - sources: nm_ethtool_utils_source + nm_libnm_core_utils_source + [libnm_core_enum_sources[1]], + sources: nm_ethtool_utils_source + nm_libnm_core_utils_source + nm_auth_subject_source + [libnm_core_enum_sources[1]], dependencies: libnm_core_nm_default_dep, c_args: common_c_flags, ) diff --git a/meson.build b/meson.build index 69bbccd818..0d7b29e2bf 100644 --- a/meson.build +++ b/meson.build @@ -494,11 +494,12 @@ config_h.set_quoted('NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT', config_auth_polkit_def enable_modify_system = get_option('modify_system') -enable_polkit_agent = get_option('polkit_agent') -if enable_polkit_agent - polkit_agent_dep = dependency('polkit-agent-1', version: '>= 0.97') +polkit_agent_dep = dependency('polkit-agent-1', version: '>= 0.97', required : false) +if polkit_agent_dep.found() + config_h.set_quoted('POLKIT_PACKAGE_PREFIX', polkit_agent_dep.get_pkgconfig_variable('prefix')) +else + config_h.set_quoted('POLKIT_PACKAGE_PREFIX', '/usr') endif -config_h.set10('WITH_POLKIT_AGENT', enable_polkit_agent) crypto = get_option('crypto') @@ -971,7 +972,6 @@ if enable_polkit output += ' modify.system)' endif output += '\n' -output += ' polkit agent: ' + enable_polkit_agent.to_string() + '\n' output += ' selinux: ' + enable_selinux.to_string() + '\n' output += ' systemd-journald: ' + enable_systemd_journal.to_string() + ' (default: logging.backend=' + config_logging_backend_default + ')\n' output += ' hostname persist: ' + hostname_persist + '\n' diff --git a/shared/meson.build b/shared/meson.build index dd350cd0aa..481df98317 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -119,6 +119,8 @@ nm_test_utils_impl_source = files('nm-test-utils-impl.c') nm_vpn_plugin_utils_source = files('nm-utils/nm-vpn-plugin-utils.c') +nm_auth_subject_source = files('nm-libnm-core-intern/nm-auth-subject.c') + c_flags = [ '-DG_LOG_DOMAIN="@0@"'.format(libnm_name), '-DNETWORKMANAGER_COMPILATION=0', diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c index ec0cd4c812..a08fcf4b10 100644 --- a/shared/nm-glib-aux/nm-io-utils.c +++ b/shared/nm-glib-aux/nm-io-utils.c @@ -438,3 +438,35 @@ nm_utils_file_stat (const char *filename, struct stat *out_st) return -NM_ERRNO_NATIVE (errno); return 0; } + +/** + * nm_utils_fd_read: + * @fd: the fd to read from. + * @out_string: (out): output string where read bytes will be stored. + * + * Returns: <0 on failure, which is -(errno) + * 0 on EOF or if the call would block (if the fd is nonblocking), + * >0 on success, which is the number of bytes read */ +ssize_t +nm_utils_fd_read (int fd, GString *out_string) +{ + size_t start_len; + ssize_t n_read; + + g_return_val_if_fail (fd >= 0, -1); + g_return_val_if_fail (out_string, -1); + + start_len = out_string->len; + g_string_set_size (out_string, start_len + 1024); + + n_read = read (fd, &out_string->str[start_len], 1024); + if (n_read < 0) { + if (errno != EAGAIN) { + return -NM_ERRNO_NATIVE (errno); + } + n_read = 0; + } else { + g_string_set_size (out_string, start_len + n_read); + } + return n_read; +} diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h index 18dc5f7428..dd2f499d6f 100644 --- a/shared/nm-glib-aux/nm-io-utils.h +++ b/shared/nm-glib-aux/nm-io-utils.h @@ -47,6 +47,8 @@ gboolean nm_utils_file_set_contents (const char *filename, int *out_errsv, GError **error); +ssize_t nm_utils_fd_read (int fd, GString *out_string); + struct stat; int nm_utils_file_stat (const char *filename, struct stat *out_st); diff --git a/src/nm-auth-subject.c b/shared/nm-libnm-core-intern/nm-auth-subject.c similarity index 72% rename from src/nm-auth-subject.c rename to shared/nm-libnm-core-intern/nm-auth-subject.c index ecf6b0138f..96bc6b0887 100644 --- a/src/nm-auth-subject.c +++ b/shared/nm-libnm-core-intern/nm-auth-subject.c @@ -17,14 +17,13 @@ #include -#include "nm-dbus-manager.h" - enum { PROP_0, PROP_SUBJECT_TYPE, PROP_UNIX_PROCESS_DBUS_SENDER, PROP_UNIX_PROCESS_PID, PROP_UNIX_PROCESS_UID, + PROP_UNIX_SESSION_ID, PROP_LAST, }; @@ -37,6 +36,10 @@ typedef struct { guint64 start_time; char *dbus_sender; } unix_process; + + struct { + char *id; + } unix_session; } NMAuthSubjectPrivate; struct _NMAuthSubject { @@ -78,6 +81,10 @@ nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len) case NM_AUTH_SUBJECT_TYPE_INTERNAL: g_strlcpy (buf, "internal", buf_len); break; + case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: + g_snprintf (buf, buf_len, "unix-session[id=%s]", + priv->unix_session.id); + break; default: g_strlcpy (buf, "invalid", buf_len); break; @@ -87,23 +94,32 @@ nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len) /* returns a floating variant */ GVariant * -nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self) +nm_auth_subject_unix_to_polkit_gvariant (NMAuthSubject *self) { GVariantBuilder builder; - GVariant *dict; - GVariant *ret; - CHECK_SUBJECT_TYPED (self, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, NULL); + CHECK_SUBJECT (self, NULL); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "{sv}", "pid", - g_variant_new_uint32 (priv->unix_process.pid)); - g_variant_builder_add (&builder, "{sv}", "start-time", - g_variant_new_uint64 (priv->unix_process.start_time)); - g_variant_builder_add (&builder, "{sv}", "uid", - g_variant_new_int32 (priv->unix_process.uid)); - dict = g_variant_builder_end (&builder); - ret = g_variant_new ("(s@a{sv})", "unix-process", dict); - return ret; + switch (priv->subject_type) { + + case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "session-id", + g_variant_new_string (priv->unix_session.id)); + return g_variant_new ("(sa{sv})", "unix-session", &builder); + + case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS: + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "pid", + g_variant_new_uint32 (priv->unix_process.pid)); + g_variant_builder_add (&builder, "{sv}", "start-time", + g_variant_new_uint64 (priv->unix_process.start_time)); + g_variant_builder_add (&builder, "{sv}", "uid", + g_variant_new_int32 (priv->unix_process.uid)); + return g_variant_new ("(sa{sv})", "unix-process", &builder); + + default: + g_return_val_if_reached (NULL); + } } NMAuthSubjectType @@ -114,18 +130,6 @@ nm_auth_subject_get_subject_type (NMAuthSubject *subject) return priv->subject_type; } -gboolean -nm_auth_subject_is_internal (NMAuthSubject *subject) -{ - return nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL; -} - -gboolean -nm_auth_subject_is_unix_process (NMAuthSubject *subject) -{ - return nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; -} - gulong nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject) { @@ -150,76 +154,16 @@ nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject) return priv->unix_process.dbus_sender; } +const char * +nm_auth_subject_get_unix_session_id (NMAuthSubject *subject) +{ + CHECK_SUBJECT_TYPED (subject, NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, NULL); + + return priv->unix_session.id; +} + /*****************************************************************************/ -static NMAuthSubject * -_new_unix_process (GDBusMethodInvocation *context, - GDBusConnection *connection, - GDBusMessage *message) -{ - NMAuthSubject *self; - const char *dbus_sender = NULL; - gulong uid = 0; - gulong pid = 0; - gboolean success; - - g_return_val_if_fail (context || (connection && message), NULL); - - if (context) { - success = nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), - context, - &dbus_sender, - &uid, - &pid); - } else { - nm_assert (message); - success = nm_dbus_manager_get_caller_info_from_message (nm_dbus_manager_get (), - connection, - message, - &dbus_sender, - &uid, - &pid); - } - - if (!success) - return NULL; - - g_return_val_if_fail (dbus_sender && *dbus_sender, NULL); - /* polkit glib library stores uid and pid as int. There might be some - * pitfalls if the id ever happens to be larger then that. Just assert against - * it here. */ - g_return_val_if_fail (uid <= MIN (G_MAXINT, G_MAXINT32), NULL); - g_return_val_if_fail (pid > 0 && pid <= MIN (G_MAXINT, G_MAXINT32), NULL); - - self = NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, - NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, - NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, dbus_sender, - NM_AUTH_SUBJECT_UNIX_PROCESS_PID, (gulong) pid, - NM_AUTH_SUBJECT_UNIX_PROCESS_UID, (gulong) uid, - NULL)); - - if (NM_AUTH_SUBJECT_GET_PRIVATE (self)->subject_type != NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { - /* this most likely happened because the process is gone (start_time==0). - * Either that is not assert-worthy, or constructed() already asserted. - * Just return NULL. */ - g_clear_object (&self); - } - return self; -} - -NMAuthSubject * -nm_auth_subject_new_unix_process_from_context (GDBusMethodInvocation *context) -{ - return _new_unix_process (context, NULL, NULL); -} - -NMAuthSubject * -nm_auth_subject_new_unix_process_from_message (GDBusConnection *connection, - GDBusMessage *message) -{ - return _new_unix_process (NULL, connection, message); -} - /** * nm_auth_subject_new_internal(): * @@ -231,10 +175,57 @@ NMAuthSubject * nm_auth_subject_new_internal (void) { return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, - NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, + NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, + NULL)); +} + +/** + * nm_auth_subject_new_unix_session(): + * + * Creates a new auth subject representing a given unix session. + * + * Returns: the new #NMAuthSubject + */ +NMAuthSubject * +nm_auth_subject_new_unix_session (const char *session_id) +{ + return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, + NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, + NM_AUTH_SUBJECT_UNIX_SESSION_ID, session_id, NULL)); } +/** + * nm_auth_subject_new_unix_process(): + * + * Creates a new auth subject representing a given unix process. + * + * Returns: the new #NMAuthSubject + */ +NMAuthSubject * +nm_auth_subject_new_unix_process (const char *dbus_sender, gulong pid, gulong uid) +{ + return NM_AUTH_SUBJECT (g_object_new (NM_TYPE_AUTH_SUBJECT, + NM_AUTH_SUBJECT_SUBJECT_TYPE, (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, + NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER, dbus_sender, + NM_AUTH_SUBJECT_UNIX_PROCESS_PID, pid, + NM_AUTH_SUBJECT_UNIX_PROCESS_UID, uid, + NULL)); +} + +/** + * nm_auth_subject_new_unix_process_self(): + * + * Creates a new auth subject representing the current executing process. + * + * Returns: the new #NMAuthSubject + */ +NMAuthSubject * +nm_auth_subject_new_unix_process_self (void) +{ + return nm_auth_subject_new_unix_process (NULL, getpid(), getuid()); +} + /*****************************************************************************/ static void @@ -255,6 +246,9 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_UNIX_PROCESS_UID: g_value_set_ulong (value, priv->unix_process.uid); break; + case PROP_UNIX_SESSION_ID: + g_value_set_string (value, priv->unix_session.id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -274,7 +268,10 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p case PROP_SUBJECT_TYPE: /* construct-only */ i = g_value_get_int (value); - g_return_if_fail (NM_IN_SET (i, (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS)); + g_return_if_fail (NM_IN_SET (i, + (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, + (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, + (int) NM_AUTH_SUBJECT_TYPE_UNIX_SESSION)); subject_type = i; priv->subject_type |= subject_type; g_return_if_fail (priv->subject_type == subject_type); @@ -303,6 +300,14 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p priv->unix_process.uid = id; } break; + case PROP_UNIX_SESSION_ID: + /* construct-only */ + if ((str = g_value_get_string (value))) { + priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_SESSION; + g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_SESSION); + priv->unix_session.id = g_strdup (str); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -317,7 +322,9 @@ _clear_private (NMAuthSubject *self) priv->subject_type = NM_AUTH_SUBJECT_TYPE_INVALID; priv->unix_process.pid = G_MAXULONG; priv->unix_process.uid = G_MAXULONG; - g_clear_pointer (&priv->unix_process.dbus_sender, g_free); + nm_clear_g_free (&priv->unix_process.dbus_sender); + + nm_clear_g_free (&priv->unix_session.id); } static void @@ -349,8 +356,6 @@ constructed (GObject *object) * Don't bother and require the user id as parameter. */ break; } - if (!priv->unix_process.dbus_sender || !*priv->unix_process.dbus_sender) - break; priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid, NULL, NULL); @@ -369,6 +374,8 @@ constructed (GObject *object) * start-time, but polkit is not. */ } return; + case NM_AUTH_SUBJECT_TYPE_UNIX_SESSION: + return; default: break; } @@ -399,7 +406,7 @@ nm_auth_subject_class_init (NMAuthSubjectClass *config_class) (object_class, PROP_SUBJECT_TYPE, g_param_spec_int (NM_AUTH_SUBJECT_SUBJECT_TYPE, "", "", NM_AUTH_SUBJECT_TYPE_INVALID, - NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, + NM_AUTH_SUBJECT_TYPE_UNIX_SESSION, NM_AUTH_SUBJECT_TYPE_INVALID, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | @@ -429,4 +436,11 @@ nm_auth_subject_class_init (NMAuthSubjectClass *config_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_UNIX_SESSION_ID, + g_param_spec_string (NM_AUTH_SUBJECT_UNIX_SESSION_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } diff --git a/src/nm-auth-subject.h b/shared/nm-libnm-core-intern/nm-auth-subject.h similarity index 63% rename from src/nm-auth-subject.h rename to shared/nm-libnm-core-intern/nm-auth-subject.h index 7a75aca922..e885c4ea96 100644 --- a/src/nm-auth-subject.h +++ b/shared/nm-libnm-core-intern/nm-auth-subject.h @@ -17,37 +17,40 @@ typedef enum { NM_AUTH_SUBJECT_TYPE_INVALID = 0, NM_AUTH_SUBJECT_TYPE_INTERNAL = 1, NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS = 2, + NM_AUTH_SUBJECT_TYPE_UNIX_SESSION = 4, } NMAuthSubjectType; -#define NM_AUTH_SUBJECT_SUBJECT_TYPE "subject-type" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER "unix-process-dbus-sender" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_PID "unix-process-pid" -#define NM_AUTH_SUBJECT_UNIX_PROCESS_UID "unix-process-uid" +#define NM_AUTH_SUBJECT_SUBJECT_TYPE "subject-type" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_DBUS_SENDER "unix-process-dbus-sender" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_PID "unix-process-pid" +#define NM_AUTH_SUBJECT_UNIX_PROCESS_UID "unix-process-uid" +#define NM_AUTH_SUBJECT_UNIX_SESSION_ID "unix-session-id" typedef struct _NMAuthSubjectClass NMAuthSubjectClass; +typedef struct _NMAuthSubject NMAuthSubject; GType nm_auth_subject_get_type (void); NMAuthSubject *nm_auth_subject_new_internal (void); -NMAuthSubject *nm_auth_subject_new_unix_process_from_context (GDBusMethodInvocation *context); +NMAuthSubject *nm_auth_subject_new_unix_session (const char *session_id); -NMAuthSubject *nm_auth_subject_new_unix_process_from_message (GDBusConnection *connection, GDBusMessage *message); +NMAuthSubject *nm_auth_subject_new_unix_process (const char *dbus_sender, gulong pid, gulong uid); + +NMAuthSubject *nm_auth_subject_new_unix_process_self (void); NMAuthSubjectType nm_auth_subject_get_subject_type (NMAuthSubject *subject); -gboolean nm_auth_subject_is_internal (NMAuthSubject *subject); - -gboolean nm_auth_subject_is_unix_process (NMAuthSubject *subject); - gulong nm_auth_subject_get_unix_process_pid (NMAuthSubject *subject); const char *nm_auth_subject_get_unix_process_dbus_sender (NMAuthSubject *subject); gulong nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject); +const char *nm_auth_subject_get_unix_session_id (NMAuthSubject *subject); + const char *nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len); -GVariant * nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self); +GVariant *nm_auth_subject_unix_to_polkit_gvariant (NMAuthSubject *self); #endif /* __NETWORKMANAGER_AUTH_SUBJECT_H__ */ diff --git a/src/meson.build b/src/meson.build index 748fa519bc..9251cfd1ab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -125,7 +125,6 @@ sources = files( 'nm-act-request.c', 'nm-audit-manager.c', 'nm-auth-manager.c', - 'nm-auth-subject.c', 'nm-auth-utils.c', 'nm-dbus-manager.c', 'nm-checkpoint.c', diff --git a/src/nm-act-request.c b/src/nm-act-request.c index ef36a40d45..1c252052bb 100644 --- a/src/nm-act-request.c +++ b/src/nm-act-request.c @@ -19,7 +19,7 @@ #include "devices/nm-device.h" #include "nm-active-connection.h" #include "settings/nm-settings-connection.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" typedef struct { char *table; diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index bf50e55d86..7b5053a5b6 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -14,7 +14,7 @@ #include "nm-simple-connection.h" #include "nm-auth-utils.h" #include "nm-auth-manager.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-keep-alive.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" @@ -598,7 +598,9 @@ nm_active_connection_get_user_requested (NMActiveConnection *self) { g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE); - return nm_auth_subject_is_unix_process (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject); + return nm_auth_subject_get_subject_type ( + NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->subject + ) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS; } NMDevice * diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index b219b35a13..26ba48f528 100644 --- a/src/nm-audit-manager.c +++ b/src/nm-audit-manager.c @@ -11,8 +11,9 @@ #include #endif -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-config.h" +#include "nm-dbus-manager.h" #include "settings/nm-settings-connection.h" /*****************************************************************************/ @@ -195,11 +196,12 @@ _audit_log_helper (NMAuditManager *self, else if (G_IS_DBUS_METHOD_INVOCATION (subject_context)) { GDBusMethodInvocation *context = subject_context; - subject = subject_free = nm_auth_subject_new_unix_process_from_context (context); + subject = subject_free = nm_dbus_manager_new_auth_subject_from_context (context); } else g_warn_if_reached (); } - if (subject && nm_auth_subject_is_unix_process (subject)) { + if (subject && + nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { pid = nm_auth_subject_get_unix_process_pid (subject); uid = nm_auth_subject_get_unix_process_uid (subject); if (pid != G_MAXULONG) { diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c index 3f248aeeec..186d56e41f 100644 --- a/src/nm-auth-manager.c +++ b/src/nm-auth-manager.c @@ -322,7 +322,7 @@ nm_auth_manager_check_authorization (NMAuthManager *self, }; c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst); - if (nm_auth_subject_is_internal (subject)) { + if (nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL) { _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for internal request)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); call_id->idle_id = g_idle_add (_call_on_idle, call_id); } else if (nm_auth_subject_get_unix_process_uid (subject) == 0) { @@ -339,7 +339,7 @@ nm_auth_manager_check_authorization (NMAuthManager *self, GVariant *subject_value; GVariant *details_value; - subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject); + subject_value = nm_auth_subject_unix_to_polkit_gvariant (subject); nm_assert (g_variant_is_floating (subject_value)); /* ((PolkitDetails *)NULL) */ diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h index ab924e69a5..0a17055e43 100644 --- a/src/nm-auth-manager.h +++ b/src/nm-auth-manager.h @@ -6,7 +6,7 @@ #ifndef NM_AUTH_MANAGER_H #define NM_AUTH_MANAGER_H -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-config-data.h" /*****************************************************************************/ diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index a5b951ab78..099fcfbfc3 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -9,9 +9,10 @@ #include "nm-glib-aux/nm-c-list.h" #include "nm-setting-connection.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-auth-manager.h" #include "nm-session-monitor.h" +#include "nm-dbus-manager.h" /*****************************************************************************/ @@ -341,8 +342,10 @@ nm_auth_chain_add_call_unsafe (NMAuthChain *self, g_return_if_fail (!self->is_finishing); g_return_if_fail (!self->is_destroyed); g_return_if_fail (permission && *permission); - nm_assert ( nm_auth_subject_is_unix_process (self->subject) - || nm_auth_subject_is_internal (self->subject)); + nm_assert ( nm_auth_subject_get_subject_type (self->subject) + == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS + || nm_auth_subject_get_subject_type (self->subject) + == NM_AUTH_SUBJECT_TYPE_INTERNAL); /* duplicate permissions are not supported, also because nm_auth_chain_get_result() * can only return one-permission. */ @@ -395,7 +398,7 @@ nm_auth_chain_new_context (GDBusMethodInvocation *context, g_return_val_if_fail (context, NULL); nm_assert (done_func); - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) return NULL; @@ -416,8 +419,10 @@ nm_auth_chain_new_subject (NMAuthSubject *subject, NMAuthChain *self; g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - nm_assert ( nm_auth_subject_is_unix_process (subject) - || nm_auth_subject_is_internal (subject)); + nm_assert ( nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS + || nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_INTERNAL); nm_assert (done_func); self = g_slice_new (NMAuthChain); @@ -504,10 +509,12 @@ nm_auth_is_subject_in_acl (NMConnection *connection, g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE); - nm_assert ( nm_auth_subject_is_internal (subject) - || nm_auth_subject_is_unix_process (subject)); + nm_assert ( nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_INTERNAL + || nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); - if (nm_auth_subject_is_internal (subject)) + if (nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL) return TRUE; uid = nm_auth_subject_get_unix_process_uid (subject); diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index 1ab0f07d36..8d3fab765a 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -9,7 +9,7 @@ #include "nm-active-connection.h" #include "nm-act-request.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-core-utils.h" #include "nm-dbus-interface.h" #include "devices/nm-device.h" diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index f2d63b4bdc..3e0c8ada3d 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -19,6 +19,7 @@ #include "nm-std-aux/nm-dbus-compat.h" #include "nm-dbus-object.h" #include "NetworkManagerUtils.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" /* The base path for our GDBusObjectManagerServers. They do not contain * "NetworkManager" because GDBusObjectManagerServer requires that all @@ -1669,3 +1670,66 @@ nm_dbus_manager_class_init (NMDBusManagerClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } + +static NMAuthSubject * +_new_unix_process (GDBusMethodInvocation *context, + GDBusConnection *connection, + GDBusMessage *message) +{ + NMAuthSubject *self; + const char *dbus_sender = NULL; + gulong uid = 0; + gulong pid = 0; + gboolean success; + + g_return_val_if_fail (context || (connection && message), NULL); + + if (context) { + success = nm_dbus_manager_get_caller_info (nm_dbus_manager_get (), + context, + &dbus_sender, + &uid, + &pid); + } else { + nm_assert (message); + success = nm_dbus_manager_get_caller_info_from_message (nm_dbus_manager_get (), + connection, + message, + &dbus_sender, + &uid, + &pid); + } + + if (!success) + return NULL; + + g_return_val_if_fail (dbus_sender && *dbus_sender, NULL); + /* polkit glib library stores uid and pid as int. There might be some + * pitfalls if the id ever happens to be larger then that. Just assert against + * it here. */ + g_return_val_if_fail (uid <= MIN (G_MAXINT, G_MAXINT32), NULL); + g_return_val_if_fail (pid > 0 && pid <= MIN (G_MAXINT, G_MAXINT32), NULL); + + self = nm_auth_subject_new_unix_process (dbus_sender, pid, uid); + + if (nm_auth_subject_get_subject_type (self) != NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { + /* this most likely happened because the process is gone (start_time==0). + * Either that is not assert-worthy, or constructed() already asserted. + * Just return NULL. */ + g_clear_object (&self); + } + return self; +} + +NMAuthSubject * +nm_dbus_manager_new_auth_subject_from_context (GDBusMethodInvocation *context) +{ + return _new_unix_process (context, NULL, NULL); +} + +NMAuthSubject * +nm_dbus_manager_new_auth_subject_from_message (GDBusConnection *connection, + GDBusMessage *message) +{ + return _new_unix_process (NULL, connection, message); +} diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h index a609493891..2878587409 100644 --- a/src/nm-dbus-manager.h +++ b/src/nm-dbus-manager.h @@ -88,4 +88,9 @@ void nm_dbus_manager_private_server_register (NMDBusManager *self, const char *path, const char *tag); +NMAuthSubject *nm_dbus_manager_new_auth_subject_from_context (GDBusMethodInvocation *context); + +NMAuthSubject *nm_dbus_manager_new_auth_subject_from_message (GDBusConnection *connection, + GDBusMessage *message); + #endif /* __NM_DBUS_MANAGER_H__ */ diff --git a/src/nm-manager.c b/src/nm-manager.c index be61d7dcfa..1b00b1d320 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -2437,7 +2437,7 @@ device_auth_request_cb (NMDevice *device, char *permission_dup; /* Validate the caller */ - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -4514,7 +4514,7 @@ unmanaged_to_disconnected (NMDevice *device) static NMActivationStateFlags _activation_bind_lifetime_to_profile_visibility (NMAuthSubject *subject) { - if ( nm_auth_subject_is_internal (subject) + if ( nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL || nm_auth_subject_get_unix_process_uid (subject) == 0) { /* internal requests and requests from root are always unbound. */ return NM_ACTIVATION_STATE_FLAG_NONE; @@ -5098,8 +5098,10 @@ nm_manager_activate_connection (NMManager *self, if ( sett_conn == nm_active_connection_get_settings_connection (active) && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object) && (!device || nm_active_connection_get_device (active) == device) - && nm_auth_subject_is_internal (nm_active_connection_get_subject (active)) - && nm_auth_subject_is_internal (subject) + && nm_auth_subject_get_subject_type (nm_active_connection_get_subject (active)) + == NM_AUTH_SUBJECT_TYPE_INTERNAL + && nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_INTERNAL && nm_active_connection_get_activation_reason (active) == activation_reason) return active; } @@ -5174,7 +5176,7 @@ validate_activation_request (NMManager *self, connection = nm_settings_connection_get_connection (sett_conn); /* Validate the caller */ - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) { g_set_error_literal (error, NM_MANAGER_ERROR, @@ -5824,7 +5826,7 @@ impl_manager_deactivate_connection (NMDBusObject *obj, } /* Validate the caller */ - subject = nm_auth_subject_new_unix_process_from_context (invocation); + subject = nm_dbus_manager_new_auth_subject_from_context (invocation); if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -6108,7 +6110,7 @@ impl_manager_sleep (NMDBusObject *obj, g_variant_get (parameters, "(b)", &do_sleep); - subject = nm_auth_subject_new_unix_process_from_context (invocation); + subject = nm_dbus_manager_new_auth_subject_from_context (invocation); if (priv->sleeping == do_sleep) { error = g_error_new (NM_MANAGER_ERROR, @@ -6935,7 +6937,7 @@ nm_manager_dbus_set_property_handle (NMDBusObject *obj, gs_unref_object NMAuthSubject *subject = NULL; DBusSetPropertyHandle *handle_data; - subject = nm_auth_subject_new_unix_process_from_context (invocation); + subject = nm_dbus_manager_new_auth_subject_from_context (invocation); if (!subject) { error_message = NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN; goto err; diff --git a/src/nm-policy.c b/src/nm-policy.c index 7fd818b675..56bec87b3d 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1633,7 +1633,8 @@ activate_slave_connections (NMPolicy *self, NMDevice *device) } subject = nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)); - internal_activation = subject && nm_auth_subject_is_internal (subject); + internal_activation = subject + && (nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL); } changed = FALSE; diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index ecca0eb3ac..af6358a360 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -374,7 +374,7 @@ agent_manager_register_with_capabilities (NMAgentManager *self, NMSecretAgent *agent; NMAuthChain *chain; - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) { error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, @@ -626,7 +626,7 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data) b_pid = nm_secret_agent_get_pid (b); /* Prefer agents in the process the request came from */ - if (nm_auth_subject_is_unix_process (req->subject)) { + if (nm_auth_subject_get_subject_type (req->subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { requester = nm_auth_subject_get_unix_process_pid (req->subject); if (a_pid != b_pid) { @@ -682,7 +682,7 @@ request_add_agent (Request *req, NMSecretAgent *agent) } /* If the request should filter agents by UID, do that now */ - if (nm_auth_subject_is_unix_process (req->subject)) { + if (nm_auth_subject_get_subject_type (req->subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) { uid_t agent_uid, subject_uid; agent_uid = nm_secret_agent_get_owner_uid (agent); @@ -1409,8 +1409,8 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (manager); GHashTableIter iter; NMSecretAgent *agent; - gboolean subject_is_unix_process = nm_auth_subject_is_unix_process (subject); - gulong subject_uid = subject_is_unix_process ? nm_auth_subject_get_unix_process_uid (subject) : 0; + gboolean subject_is_unix_process = (nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS); + gulong subject_uid = subject_is_unix_process ? nm_auth_subject_get_unix_process_uid (subject) : 0u; g_hash_table_iter_init (&iter, priv->agents); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) { diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 74bd9b2f59..82a8c904a5 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -14,7 +14,7 @@ #include "nm-glib-aux/nm-dbus-aux.h" #include "nm-dbus-interface.h" #include "nm-core-internal.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-simple-connection.h" #include "NetworkManagerUtils.h" #include "c-list/src/c-list.h" @@ -709,7 +709,8 @@ nm_secret_agent_new (GDBusMethodInvocation *context, g_return_val_if_fail (context != NULL, NULL); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - g_return_val_if_fail (nm_auth_subject_is_unix_process (subject), NULL); + g_return_val_if_fail (nm_auth_subject_get_subject_type (subject) + == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, NULL); g_return_val_if_fail (identifier != NULL, NULL); dbus_connection = g_dbus_method_invocation_get_connection (context); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 555d1e1a6f..3368359980 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -18,12 +18,12 @@ #include "nm-session-monitor.h" #include "nm-auth-manager.h" #include "nm-auth-utils.h" -#include "nm-auth-subject.h" #include "nm-agent-manager.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" #include "nm-audit-manager.h" #include "nm-settings.h" +#include "nm-dbus-manager.h" #include "settings/plugins/keyfile/nms-keyfile-storage.h" #define AUTOCONNECT_RETRIES_UNSET -2 @@ -1249,7 +1249,7 @@ _new_auth_subject (GDBusMethodInvocation *context, GError **error) { NMAuthSubject *subject; - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) { g_set_error_literal (error, NM_SETTINGS_ERROR, diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index bbf4595463..d4fff4f2fa 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -54,7 +54,7 @@ #include "nm-settings-plugin.h" #include "nm-dbus-manager.h" #include "nm-auth-utils.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-session-monitor.h" #include "plugins/keyfile/nms-keyfile-plugin.h" #include "plugins/keyfile/nms-keyfile-storage.h" @@ -2574,7 +2574,7 @@ settings_add_connection_helper (NMSettings *self, return; } - subject = nm_auth_subject_new_unix_process_from_context (context); + subject = nm_dbus_manager_new_auth_subject_from_context (context); if (!subject) { g_dbus_method_invocation_return_error_literal (context, NM_SETTINGS_ERROR, @@ -2918,7 +2918,7 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj, goto error; } - subject = nm_auth_subject_new_unix_process_from_context (invocation); + subject = nm_dbus_manager_new_auth_subject_from_context (invocation); if (!subject) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index 8b9c0ceb40..52e9defd29 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -14,7 +14,7 @@ #include "nm-supplicant-settings-verify.h" #include "nm-setting.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-setting-ip4-config.h" diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h index 43242a8e86..b2d3e37025 100644 --- a/src/vpn/nm-vpn-connection.h +++ b/src/vpn/nm-vpn-connection.h @@ -9,7 +9,7 @@ #include "nm-vpn-dbus-interface.h" #include "devices/nm-device.h" -#include "nm-auth-subject.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" #include "nm-active-connection.h" #include "nm-vpn-plugin-info.h"