NetworkManager/src/nm-auth-manager.c
Dan Winship da8d79f1c5 core: drop all remaining core-internal error domains
A number of classes in core had their own error domains that aren't
really necessary.

In the case of NMDcbError, NMDhcpManagerError, NMDnsManagerError,
NMDnsmasqManagerError, NMPppManagerError, and NMSessionMonitorError,
most of the codes they defined weren't even being used, and at any
rate, the errors were always returned into contexts where they would
just have their message extracted and then get thrown away without
anyone ever looking at the domain or code. So all uses of those
domains can just be replaced with NM_MANAGER_ERROR_FAILED without any
loss of information.

NMAuthManagerError only had 1 error code, and it just indicated
"something went wrong", so it can be replaced with
NM_MANAGER_ERROR_FAILED without loss of information.
(nm-auth-manager.c has also been fixed to return
NM_MANAGER_ERROR_FAILED when the CheckAuthorization D-Bus call fails,
rather than returning whatever error domain/code the D-Bus call
returned.)

NMVpnManagerError used 2 of its 4 error codes, and they could actually
end up getting returned across D-Bus in some cases. But there are
NMManagerError codes that are semantically similar enough to make the
NMVpnManagerError ones unnecessary.
2014-10-22 08:29:10 -04:00

655 lines
20 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2014 Red Hat, Inc.
*/
#include "nm-auth-manager.h"
#include "nm-logging.h"
#include "nm-errors.h"
#define POLKIT_SERVICE "org.freedesktop.PolicyKit1"
#define POLKIT_OBJECT_PATH "/org/freedesktop/PolicyKit1/Authority"
#define POLKIT_INTERFACE "org.freedesktop.PolicyKit1.Authority"
#define _LOG_DEFAULT_DOMAIN LOGD_CORE
#define _LOG(level, domain, ...) \
G_STMT_START { \
if (nm_logging_enabled ((level), (domain))) { \
char __prefix[30] = "auth"; \
\
if ((self) != _instance) \
g_snprintf (__prefix, sizeof (__prefix), "auth[%p]", (self)); \
nm_log ((level), (domain), \
"%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
__prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} G_STMT_END
#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
#define _LOGI(...) _LOG (LOGL_INFO, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
#define _LOGW(...) _LOG (LOGL_WARN, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
#define _LOGE(...) _LOG (LOGL_ERR, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
enum {
PROP_0,
PROP_POLKIT_ENABLED,
LAST_PROP
};
enum {
CHANGED_SIGNAL,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = {0};
typedef struct {
gboolean polkit_enabled;
#if WITH_POLKIT
guint call_id_counter;
GCancellable *new_proxy_cancellable;
GSList *queued_calls;
GDBusProxy *proxy;
#endif
} NMAuthManagerPrivate;
static NMAuthManager *_instance = NULL;
G_DEFINE_TYPE (NMAuthManager, nm_auth_manager, G_TYPE_OBJECT)
#define NM_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_MANAGER, NMAuthManagerPrivate))
/*****************************************************************************/
gboolean
nm_auth_manager_get_polkit_enabled (NMAuthManager *self)
{
g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE);
return NM_AUTH_MANAGER_GET_PRIVATE (self)->polkit_enabled;
}
/*****************************************************************************/
#if WITH_POLKIT
typedef enum {
POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE = 0,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0),
} PolkitCheckAuthorizationFlags;
typedef struct {
guint call_id;
NMAuthManager *self;
GSimpleAsyncResult *simple;
gchar *cancellation_id;
GVariant *dbus_parameters;
GCancellable *cancellable;
} CheckAuthData;
static void
_check_auth_data_free (CheckAuthData *data)
{
if (data->dbus_parameters)
g_variant_unref (data->dbus_parameters);
g_object_unref (data->self);
g_object_unref (data->simple);
g_clear_object (&data->cancellable);
g_free (data->cancellation_id);
g_free (data);
}
static void
_call_check_authorization_complete_with_error (CheckAuthData *data,
const char *error_message)
{
NMAuthManager *self = data->self;
_LOGD ("call[%u]: CheckAuthorization failed due to internal error: %s", data->call_id, error_message);
g_simple_async_result_set_error (data->simple,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_FAILED,
"Authorization check failed: %s",
error_message);
g_simple_async_result_complete_in_idle (data->simple);
_check_auth_data_free (data);
}
static void
cancel_check_authorization_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
NMAuthManager *self = user_data;
GVariant *value;
GError *error= NULL;
value = g_dbus_proxy_call_finish (proxy, res, &error);
if (value == NULL) {
_LOGD ("Error cancelling authorization check: %s", error->message);
g_error_free (error);
} else
g_variant_unref (value);
g_object_unref (self);
}
typedef struct {
gboolean is_authorized;
gboolean is_challenge;
} CheckAuthorizationResult;
static void
check_authorization_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
CheckAuthData *data = user_data;
NMAuthManager *self = data->self;
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
GVariant *value;
GError *error = NULL;
value = g_dbus_proxy_call_finish (proxy, res, &error);
if (value == NULL) {
if (data->cancellation_id != NULL &&
(!g_dbus_error_is_remote_error (error) &&
error->domain == G_IO_ERROR &&
error->code == G_IO_ERROR_CANCELLED)) {
_LOGD ("call[%u]: CheckAuthorization cancelled", data->call_id);
g_dbus_proxy_call (priv->proxy,
"CancelCheckAuthorization",
g_variant_new ("(s)", data->cancellation_id),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL, /* GCancellable */
(GAsyncReadyCallback) cancel_check_authorization_cb,
g_object_ref (self));
} else
_LOGD ("call[%u]: CheckAuthorization failed: %s", data->call_id, error->message);
g_simple_async_result_set_error (data->simple,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_FAILED,
"Authorization check failed: %s",
error->message);
g_error_free (error);
} else {
GVariant *result_value;
CheckAuthorizationResult *result;
result = g_new0 (CheckAuthorizationResult, 1);
result_value = g_variant_get_child_value (value, 0);
g_variant_get (result_value,
"(bb@a{ss})",
&result->is_authorized,
&result->is_challenge,
NULL);
g_variant_unref (result_value);
g_variant_unref (value);
_LOGD ("call[%u]: CheckAuthorization succeeded: (is_authorized=%d, is_challenge=%d)", data->call_id, result->is_authorized, result->is_challenge);
g_simple_async_result_set_op_res_gpointer (data->simple, result, g_free);
}
g_simple_async_result_complete (data->simple);
_check_auth_data_free (data);
}
static void
_call_check_authorization (CheckAuthData *data)
{
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (data->self);
g_dbus_proxy_call (priv->proxy,
"CheckAuthorization",
data->dbus_parameters,
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT, /* no timeout */
data->cancellable,
(GAsyncReadyCallback) check_authorization_cb,
data);
g_clear_object (&data->cancellable);
data->dbus_parameters = NULL;
}
void
nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self,
NMAuthSubject *subject,
const char *action_id,
gboolean allow_user_interaction,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMAuthManagerPrivate *priv;
char subject_buf[64];
GVariantBuilder builder;
PolkitCheckAuthorizationFlags flags;
GVariant *subject_value;
GVariant *details_value;
CheckAuthData *data;
g_return_if_fail (NM_IS_AUTH_MANAGER (self));
g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
g_return_if_fail (nm_auth_subject_is_unix_process (subject));
g_return_if_fail (action_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
g_return_if_fail (priv->polkit_enabled);
flags = allow_user_interaction
? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION
: POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject);
g_assert (g_variant_is_floating (subject_value));
/* ((PolkitDetails *)NULL) */
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
details_value = g_variant_builder_end (&builder);
data = g_new0 (CheckAuthData, 1);
data->call_id = ++priv->call_id_counter;
data->self = g_object_ref (self);
data->simple = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
nm_auth_manager_polkit_authority_check_authorization);
if (cancellable != NULL) {
data->cancellation_id = g_strdup_printf ("cancellation-id-%u", data->call_id);
data->cancellable = g_object_ref (cancellable);
}
data->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)",
subject_value,
action_id,
details_value,
(guint32) flags,
data->cancellation_id != NULL ? data->cancellation_id : "");
if (priv->new_proxy_cancellable) {
_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (wait for proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
priv->queued_calls = g_slist_prepend (priv->queued_calls, data);
} else if (!priv->proxy) {
_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (fails due to invalid DBUS proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
_call_check_authorization_complete_with_error (data, "invalid DBUS proxy");
} else {
_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
_call_check_authorization (data);
}
}
gboolean
nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self,
GAsyncResult *res,
gboolean *out_is_authorized,
gboolean *out_is_challenge,
GError **error)
{
gboolean success = FALSE;
gboolean is_authorized = FALSE;
gboolean is_challenge = FALSE;
g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) {
CheckAuthorizationResult *result;
result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
is_authorized = !!result->is_authorized;
is_challenge = !!result->is_challenge;
success = TRUE;
}
g_assert ((success && !error) || (!success || error));
if (out_is_authorized)
*out_is_authorized = is_authorized;
if (out_is_challenge)
*out_is_challenge = is_challenge;
return success;
}
/*****************************************************************************/
static void
_emit_changed_signal (NMAuthManager *self)
{
_LOGD ("emit changed signal");
g_signal_emit_by_name (self, NM_AUTH_MANAGER_SIGNAL_CHANGED);
}
static void
_log_name_owner (NMAuthManager *self, char **out_name_owner)
{
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
char *name_owner;
name_owner = g_dbus_proxy_get_name_owner (priv->proxy);
if (name_owner)
_LOGD ("dbus name owner: '%s'", name_owner);
else
_LOGD ("dbus name owner: none");
if (out_name_owner)
*out_name_owner = name_owner;
else
g_free (name_owner);
}
static void
_dbus_on_name_owner_notify_cb (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
NMAuthManager *self = user_data;
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
char *name_owner;
g_return_if_fail (priv->proxy == (void *) object);
_log_name_owner (self, &name_owner);
if (!name_owner) {
/* when the name disappears, we also want to raise a emit signal.
* When it appears, we raise one already. */
_emit_changed_signal (self);
}
g_free (name_owner);
}
static void
_dbus_on_g_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
NMAuthManager *self = user_data;
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
g_return_if_fail (priv->proxy == proxy);
_LOGD ("dbus signal: \"%s\"", signal_name ? signal_name : "(null)");
if (g_strcmp0 (signal_name, "Changed") == 0)
_emit_changed_signal (self);
}
static void
_dbus_new_proxy_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
NMAuthManager **p_self = user_data;
NMAuthManager *self = NULL;
NMAuthManagerPrivate *priv;
GError *error = NULL;
GDBusProxy *proxy;
CheckAuthData *data;
proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (!*p_self) {
_LOGD ("_dbus_new_proxy_cb(): manager destroyed before callback finished. Abort");
g_clear_object (&proxy);
g_clear_error (&error);
g_free (p_self);
return;
}
self = *p_self;
g_object_remove_weak_pointer (G_OBJECT (self), (void **)p_self);
g_free (p_self);
priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
g_return_if_fail (priv->new_proxy_cancellable);
g_return_if_fail (!priv->proxy);
g_clear_object (&priv->new_proxy_cancellable);
priv->queued_calls = g_slist_reverse (priv->queued_calls);
priv->proxy = proxy;
if (!priv->proxy) {
_LOGE ("could not get polkit proxy: %s", error->message);
g_clear_error (&error);
while (priv->queued_calls) {
data = priv->queued_calls->data;
priv->queued_calls = g_slist_remove (priv->queued_calls, data);
_call_check_authorization_complete_with_error (data, "error creating DBUS proxy");
}
return;
}
g_signal_connect (priv->proxy,
"notify::g-name-owner",
G_CALLBACK (_dbus_on_name_owner_notify_cb),
self);
g_signal_connect (priv->proxy,
"g-signal",
G_CALLBACK (_dbus_on_g_signal_cb),
self);
_log_name_owner (self, NULL);
while (priv->queued_calls) {
data = priv->queued_calls->data;
priv->queued_calls = g_slist_remove (priv->queued_calls, data);
_LOGD ("call[%u]: CheckAuthorization invoke now", data->call_id);
_call_check_authorization (data);
}
_emit_changed_signal (self);
}
#endif
/*****************************************************************************/
NMAuthManager *
nm_auth_manager_get ()
{
g_return_val_if_fail (_instance, NULL);
return _instance;
}
NMAuthManager *
nm_auth_manager_setup (gboolean polkit_enabled)
{
NMAuthManager *self;
g_return_val_if_fail (!_instance, _instance);
self = g_object_new (NM_TYPE_AUTH_MANAGER,
NM_AUTH_MANAGER_POLKIT_ENABLED, polkit_enabled,
NULL);
_LOGD ("set instance");
return (_instance = self);
}
/*****************************************************************************/
static void
get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object);
switch (prop_id) {
case PROP_POLKIT_ENABLED:
g_value_set_boolean (value, priv->polkit_enabled);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (object);
switch (prop_id) {
case PROP_POLKIT_ENABLED:
/* construct only */
priv->polkit_enabled = !!g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nm_auth_manager_init (NMAuthManager *self)
{
}
static void
constructed (GObject *object)
{
NMAuthManager *self = NM_AUTH_MANAGER (object);
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object);
#if WITH_POLKIT
_LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled");
if (priv->polkit_enabled) {
NMAuthManager **p_self;
priv->new_proxy_cancellable = g_cancellable_new ();
p_self = g_new (NMAuthManager *, 1);
*p_self = self;
g_object_add_weak_pointer (G_OBJECT (self), (void **) p_self);
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
POLKIT_SERVICE,
POLKIT_OBJECT_PATH,
POLKIT_INTERFACE,
priv->new_proxy_cancellable,
_dbus_new_proxy_cb,
p_self);
}
#else
if (priv->polkit_enabled)
_LOGW ("create auth-manager: polkit disabled at compile time. All authentication requests will fail");
else
_LOGD ("create auth-manager: polkit disabled at compile time");
#endif
}
static void
dispose (GObject *object)
{
NMAuthManager* self = NM_AUTH_MANAGER (object);
#if WITH_POLKIT
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
#endif
_LOGD ("dispose");
#if WITH_POLKIT
/* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */
g_assert (!priv->queued_calls);
if (priv->new_proxy_cancellable) {
g_cancellable_cancel (priv->new_proxy_cancellable);
g_clear_object (&priv->new_proxy_cancellable);
}
if (priv->proxy) {
g_signal_handlers_disconnect_by_func (priv->proxy, _dbus_on_name_owner_notify_cb, self);
g_signal_handlers_disconnect_by_func (priv->proxy, _dbus_on_g_signal_cb, self);
g_clear_object (&priv->proxy);
}
#endif
G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
NMAuthManager* self = NM_AUTH_MANAGER (object);
G_OBJECT_CLASS (nm_auth_manager_parent_class)->finalize (object);
if (self == _instance) {
_instance = NULL;
_LOGD ("unset instance");
}
}
static void
nm_auth_manager_class_init (NMAuthManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (NMAuthManagerPrivate));
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
g_object_class_install_property
(object_class, PROP_POLKIT_ENABLED,
g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
signals[CHANGED_SIGNAL] = g_signal_new (NM_AUTH_MANAGER_SIGNAL_CHANGED,
NM_TYPE_AUTH_MANAGER,
G_SIGNAL_RUN_LAST,
0, /* class offset */
NULL, /* accumulator */
NULL, /* accumulator data */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
}