mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 23:46:15 +01:00
Merge branch 'issue1831' into 'master'
Avoid race condition authenticating GDBusServer with libdbus client (#1831) Closes #1831 See merge request GNOME/glib!1176
This commit is contained in:
commit
d44e00fb98
@ -265,6 +265,35 @@ g_credentials_to_string (GCredentials *credentials)
|
|||||||
|
|
||||||
/* ---------------------------------------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||||
|
/*
|
||||||
|
* Check whether @native contains invalid data. If getsockopt SO_PEERCRED
|
||||||
|
* is used on a TCP socket, it succeeds but yields a credentials structure
|
||||||
|
* with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
|
||||||
|
* receiving Unix socket when the sending socket did not also enable
|
||||||
|
* SO_PASSCRED, it can succeed but yield a credentials structure with
|
||||||
|
* pid 0, uid /proc/sys/kernel/overflowuid and gid
|
||||||
|
* /proc/sys/kernel/overflowgid.
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
linux_ucred_check_valid (struct ucred *native,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (native->pid == 0
|
||||||
|
|| native->uid == -1
|
||||||
|
|| native->gid == -1)
|
||||||
|
{
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
_("GCredentials contains invalid data"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_credentials_is_same_user:
|
* g_credentials_is_same_user:
|
||||||
* @credentials: A #GCredentials.
|
* @credentials: A #GCredentials.
|
||||||
@ -294,7 +323,8 @@ g_credentials_is_same_user (GCredentials *credentials,
|
|||||||
|
|
||||||
ret = FALSE;
|
ret = FALSE;
|
||||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||||
if (credentials->native.uid == other_credentials->native.uid)
|
if (linux_ucred_check_valid (&credentials->native, NULL)
|
||||||
|
&& credentials->native.uid == other_credentials->native.uid)
|
||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||||
if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
|
if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
|
||||||
@ -453,7 +483,10 @@ g_credentials_get_unix_user (GCredentials *credentials,
|
|||||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||||
|
|
||||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||||
ret = credentials->native.uid;
|
if (linux_ucred_check_valid (&credentials->native, error))
|
||||||
|
ret = credentials->native.uid;
|
||||||
|
else
|
||||||
|
ret = -1;
|
||||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||||
ret = credentials->native.cmcred_euid;
|
ret = credentials->native.cmcred_euid;
|
||||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||||
@ -499,7 +532,10 @@ g_credentials_get_unix_pid (GCredentials *credentials,
|
|||||||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||||||
|
|
||||||
#if G_CREDENTIALS_USE_LINUX_UCRED
|
#if G_CREDENTIALS_USE_LINUX_UCRED
|
||||||
ret = credentials->native.pid;
|
if (linux_ucred_check_valid (&credentials->native, error))
|
||||||
|
ret = credentials->native.pid;
|
||||||
|
else
|
||||||
|
ret = -1;
|
||||||
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||||
ret = credentials->native.cmcred_pid;
|
ret = credentials->native.cmcred_pid;
|
||||||
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||||
|
@ -22,6 +22,77 @@
|
|||||||
#include "gio/gcredentials.h"
|
#include "gio/gcredentials.h"
|
||||||
#include "gio/gnetworking.h"
|
#include "gio/gnetworking.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_SUPPORTED:
|
||||||
|
*
|
||||||
|
* Defined to 1 if GCredentials works.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_SUPPORTED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_USE_LINUX_UCRED, etc.:
|
||||||
|
*
|
||||||
|
* Defined to 1 if GCredentials uses Linux `struct ucred`, etc.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_USE_LINUX_UCRED
|
||||||
|
#undef G_CREDENTIALS_USE_FREEBSD_CMSGCRED
|
||||||
|
#undef G_CREDENTIALS_USE_NETBSD_UNPCBID
|
||||||
|
#undef G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
|
||||||
|
#undef G_CREDENTIALS_USE_SOLARIS_UCRED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_NATIVE_TYPE:
|
||||||
|
*
|
||||||
|
* Defined to one of G_CREDENTIALS_TYPE_LINUX_UCRED, etc.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_NATIVE_TYPE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_NATIVE_SIZE:
|
||||||
|
*
|
||||||
|
* Defined to the size of the %G_CREDENTIALS_NATIVE_TYPE
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_NATIVE_SIZE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED:
|
||||||
|
*
|
||||||
|
* Defined to 1 if we have a message-passing API in which credentials
|
||||||
|
* are attached to a particular message, such as `SCM_CREDENTIALS` on Linux
|
||||||
|
* or `SCM_CREDS` on FreeBSD.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED:
|
||||||
|
*
|
||||||
|
* Defined to 1 if we have a `getsockopt()`-style API in which one end of
|
||||||
|
* a socket connection can directly query the credentials of the process
|
||||||
|
* that initiated the other end, such as `getsockopt SO_PEERCRED` on Linux
|
||||||
|
* or `getpeereid()` on multiple operating systems.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_SPOOFING_SUPPORTED:
|
||||||
|
*
|
||||||
|
* Defined to 1 if privileged processes can spoof their credentials when
|
||||||
|
* using the message-passing API.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_SPOOFING_SUPPORTED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CREDENTIALS_PREFER_MESSAGE_PASSING:
|
||||||
|
*
|
||||||
|
* Defined to 1 if the data structure transferred by the message-passing
|
||||||
|
* API is strictly more informative than the one transferred by the
|
||||||
|
* `getsockopt()`-style API, and hence should be preferred, even for
|
||||||
|
* protocols like D-Bus that are defined in terms of the credentials of
|
||||||
|
* the (process that opened the) socket, as opposed to the credentials
|
||||||
|
* of an individual message.
|
||||||
|
*/
|
||||||
|
#undef G_CREDENTIALS_PREFER_MESSAGE_PASSING
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#define G_CREDENTIALS_SUPPORTED 1
|
#define G_CREDENTIALS_SUPPORTED 1
|
||||||
#define G_CREDENTIALS_USE_LINUX_UCRED 1
|
#define G_CREDENTIALS_USE_LINUX_UCRED 1
|
||||||
@ -41,6 +112,12 @@
|
|||||||
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct cmsgcred))
|
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct cmsgcred))
|
||||||
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
|
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
|
||||||
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
|
||||||
|
/* GLib doesn't implement it yet, but FreeBSD's getsockopt()-style API
|
||||||
|
* is getpeereid(), which is not as informative as struct cmsgcred -
|
||||||
|
* it does not tell us the PID. As a result, libdbus prefers to use
|
||||||
|
* SCM_CREDS, and if we implement getpeereid() in future, we should
|
||||||
|
* do the same. */
|
||||||
|
#define G_CREDENTIALS_PREFER_MESSAGE_PASSING 1
|
||||||
|
|
||||||
#elif defined(__NetBSD__)
|
#elif defined(__NetBSD__)
|
||||||
#define G_CREDENTIALS_SUPPORTED 1
|
#define G_CREDENTIALS_SUPPORTED 1
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "gdbusutils.h"
|
#include "gdbusutils.h"
|
||||||
#include "gioenumtypes.h"
|
#include "gioenumtypes.h"
|
||||||
#include "gcredentials.h"
|
#include "gcredentials.h"
|
||||||
|
#include "gcredentialsprivate.h"
|
||||||
#include "gdbusprivate.h"
|
#include "gdbusprivate.h"
|
||||||
#include "giostream.h"
|
#include "giostream.h"
|
||||||
#include "gdatainputstream.h"
|
#include "gdatainputstream.h"
|
||||||
@ -969,9 +970,31 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
|
|||||||
|
|
||||||
g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
|
g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
|
||||||
|
|
||||||
/* first read the NUL-byte */
|
/* read the NUL-byte, possibly with credentials attached */
|
||||||
#ifdef G_OS_UNIX
|
#ifdef G_OS_UNIX
|
||||||
if (G_IS_UNIX_CONNECTION (auth->priv->stream))
|
#ifndef G_CREDENTIALS_PREFER_MESSAGE_PASSING
|
||||||
|
if (G_IS_SOCKET_CONNECTION (auth->priv->stream))
|
||||||
|
{
|
||||||
|
GSocket *sock = g_socket_connection_get_socket (G_SOCKET_CONNECTION (auth->priv->stream));
|
||||||
|
|
||||||
|
local_error = NULL;
|
||||||
|
credentials = g_socket_get_credentials (sock, &local_error);
|
||||||
|
|
||||||
|
if (credentials == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
|
||||||
|
{
|
||||||
|
g_propagate_error (error, local_error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Clear the error indicator, so we can retry with
|
||||||
|
* g_unix_connection_receive_credentials() if necessary */
|
||||||
|
g_clear_error (&local_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (credentials == NULL && G_IS_UNIX_CONNECTION (auth->priv->stream))
|
||||||
{
|
{
|
||||||
local_error = NULL;
|
local_error = NULL;
|
||||||
credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream),
|
credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream),
|
||||||
|
491
gio/tests/gdbus-server-auth.c
Normal file
491
gio/tests/gdbus-server-auth.c
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Collabora Ltd.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS1
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
INTEROP_FLAGS_EXTERNAL = (1 << 0),
|
||||||
|
INTEROP_FLAGS_ANONYMOUS = (1 << 1),
|
||||||
|
INTEROP_FLAGS_SHA1 = (1 << 2),
|
||||||
|
INTEROP_FLAGS_TCP = (1 << 3),
|
||||||
|
INTEROP_FLAGS_LIBDBUS = (1 << 4),
|
||||||
|
INTEROP_FLAGS_NONE = 0
|
||||||
|
} InteropFlags;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
allow_external_cb (G_GNUC_UNUSED GDBusAuthObserver *observer,
|
||||||
|
const char *mechanism,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
|
||||||
|
{
|
||||||
|
g_debug ("Accepting EXTERNAL authentication");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_debug ("Rejecting \"%s\" authentication: not EXTERNAL", mechanism);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
allow_anonymous_cb (G_GNUC_UNUSED GDBusAuthObserver *observer,
|
||||||
|
const char *mechanism,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (mechanism, "ANONYMOUS") == 0)
|
||||||
|
{
|
||||||
|
g_debug ("Accepting ANONYMOUS authentication");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_debug ("Rejecting \"%s\" authentication: not ANONYMOUS", mechanism);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
allow_sha1_cb (G_GNUC_UNUSED GDBusAuthObserver *observer,
|
||||||
|
const char *mechanism,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (mechanism, "DBUS_COOKIE_SHA1") == 0)
|
||||||
|
{
|
||||||
|
g_debug ("Accepting DBUS_COOKIE_SHA1 authentication");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_debug ("Rejecting \"%s\" authentication: not DBUS_COOKIE_SHA1",
|
||||||
|
mechanism);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
allow_any_mechanism_cb (G_GNUC_UNUSED GDBusAuthObserver *observer,
|
||||||
|
const char *mechanism,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
g_debug ("Accepting \"%s\" authentication", mechanism);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
authorize_any_authenticated_peer_cb (G_GNUC_UNUSED GDBusAuthObserver *observer,
|
||||||
|
G_GNUC_UNUSED GIOStream *stream,
|
||||||
|
GCredentials *credentials,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
if (credentials == NULL)
|
||||||
|
{
|
||||||
|
g_debug ("Authorizing peer with no credentials");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gchar *str = g_credentials_to_string (credentials);
|
||||||
|
|
||||||
|
g_debug ("Authorizing peer with credentials: %s", str);
|
||||||
|
g_free (str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GDBusMessage *
|
||||||
|
whoami_filter_cb (GDBusConnection *connection,
|
||||||
|
GDBusMessage *message,
|
||||||
|
gboolean incoming,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
if (!incoming)
|
||||||
|
return message;
|
||||||
|
|
||||||
|
if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL &&
|
||||||
|
g_strcmp0 (g_dbus_message_get_member (message), "WhoAmI") == 0)
|
||||||
|
{
|
||||||
|
GDBusMessage *reply = g_dbus_message_new_method_reply (message);
|
||||||
|
gint64 uid = -1;
|
||||||
|
gint64 pid = -1;
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
GCredentials *credentials = g_dbus_connection_get_peer_credentials (connection);
|
||||||
|
|
||||||
|
if (credentials != NULL)
|
||||||
|
{
|
||||||
|
uid = (gint64) g_credentials_get_unix_user (credentials, NULL);
|
||||||
|
pid = (gint64) g_credentials_get_unix_pid (credentials, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_dbus_message_set_body (reply,
|
||||||
|
g_variant_new ("(xx)", uid, pid));
|
||||||
|
g_dbus_connection_send_message (connection, reply,
|
||||||
|
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
/* handled */
|
||||||
|
g_object_unref (message);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
new_connection_cb (G_GNUC_UNUSED GDBusServer *server,
|
||||||
|
GDBusConnection *connection,
|
||||||
|
G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
GCredentials *credentials = g_dbus_connection_get_peer_credentials (connection);
|
||||||
|
|
||||||
|
if (credentials == NULL)
|
||||||
|
{
|
||||||
|
g_debug ("New connection from peer with no credentials");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gchar *str = g_credentials_to_string (credentials);
|
||||||
|
|
||||||
|
g_debug ("New connection from peer with credentials: %s", str);
|
||||||
|
g_free (str);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_ref (connection);
|
||||||
|
g_dbus_connection_add_filter (connection, whoami_filter_cb, NULL, NULL);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS1
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
DBusError error;
|
||||||
|
DBusConnection *conn;
|
||||||
|
DBusMessage *call;
|
||||||
|
DBusMessage *reply;
|
||||||
|
} LibdbusCall;
|
||||||
|
|
||||||
|
static void
|
||||||
|
libdbus_call_task_cb (GTask *task,
|
||||||
|
G_GNUC_UNUSED gpointer source_object,
|
||||||
|
gpointer task_data,
|
||||||
|
G_GNUC_UNUSED GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
LibdbusCall *libdbus_call = task_data;
|
||||||
|
|
||||||
|
libdbus_call->reply = dbus_connection_send_with_reply_and_block (libdbus_call->conn,
|
||||||
|
libdbus_call->call,
|
||||||
|
-1,
|
||||||
|
&libdbus_call->error);
|
||||||
|
}
|
||||||
|
#endif /* HAVE_DBUS1 */
|
||||||
|
|
||||||
|
static void
|
||||||
|
store_result_cb (G_GNUC_UNUSED GObject *source_object,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GAsyncResult **result = user_data;
|
||||||
|
|
||||||
|
g_assert_nonnull (result);
|
||||||
|
g_assert_null (*result);
|
||||||
|
*result = g_object_ref (res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
assert_expected_uid_pid (InteropFlags flags,
|
||||||
|
gint64 uid,
|
||||||
|
gint64 pid)
|
||||||
|
{
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
if (flags & (INTEROP_FLAGS_ANONYMOUS | INTEROP_FLAGS_SHA1 | INTEROP_FLAGS_TCP))
|
||||||
|
{
|
||||||
|
/* No assertion. There is no guarantee whether credentials will be
|
||||||
|
* passed even though we didn't send them. Conversely, if
|
||||||
|
* credentials were not passed,
|
||||||
|
* g_dbus_connection_get_peer_credentials() always returns the
|
||||||
|
* credentials of the socket, and not the uid that a
|
||||||
|
* client might have proved it has by using DBUS_COOKIE_SHA1. */
|
||||||
|
}
|
||||||
|
else /* We should prefer EXTERNAL whenever it is allowed. */
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
/* We know that both GDBus and libdbus support full credentials-passing
|
||||||
|
* on Linux. */
|
||||||
|
g_assert_cmpint (uid, ==, getuid ());
|
||||||
|
g_assert_cmpint (pid, ==, getpid ());
|
||||||
|
#else
|
||||||
|
g_test_message ("Please open a merge request to add appropriate "
|
||||||
|
"assertions for your platform");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* G_OS_UNIX */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_test_server_auth (const char *listenable_address,
|
||||||
|
InteropFlags flags)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
GDBusServer *server;
|
||||||
|
GDBusAuthObserver *observer;
|
||||||
|
GDBusServerFlags server_flags = G_DBUS_SERVER_FLAGS_RUN_IN_THREAD;
|
||||||
|
gchar *guid;
|
||||||
|
const char *connectable_address;
|
||||||
|
GDBusConnection *client;
|
||||||
|
GAsyncResult *result = NULL;
|
||||||
|
GVariant *tuple;
|
||||||
|
gint64 uid, pid;
|
||||||
|
#ifdef HAVE_DBUS1
|
||||||
|
/* GNOME/glib#1831 seems to involve a race condition, so try a few times
|
||||||
|
* to see if we can trigger it. */
|
||||||
|
gsize i;
|
||||||
|
gsize n = 20;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (g_str_has_prefix (listenable_address, "tcp:") ||
|
||||||
|
g_str_has_prefix (listenable_address, "nonce-tcp:"))
|
||||||
|
g_assert_cmpint (flags & INTEROP_FLAGS_TCP, !=, 0);
|
||||||
|
else
|
||||||
|
g_assert_cmpint (flags & INTEROP_FLAGS_TCP, ==, 0);
|
||||||
|
|
||||||
|
g_test_message ("Testing GDBus server at %s / libdbus client, with flags: "
|
||||||
|
"external:%s "
|
||||||
|
"anonymous:%s "
|
||||||
|
"sha1:%s "
|
||||||
|
"tcp:%s",
|
||||||
|
listenable_address,
|
||||||
|
(flags & INTEROP_FLAGS_EXTERNAL) ? "true" : "false",
|
||||||
|
(flags & INTEROP_FLAGS_ANONYMOUS) ? "true" : "false",
|
||||||
|
(flags & INTEROP_FLAGS_SHA1) ? "true" : "false",
|
||||||
|
(flags & INTEROP_FLAGS_TCP) ? "true" : "false");
|
||||||
|
|
||||||
|
#ifndef G_OS_UNIX
|
||||||
|
if (g_str_has_prefix (listenable_address, "unix:"))
|
||||||
|
{
|
||||||
|
g_test_skip ("unix: addresses only work on Unix");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED) \
|
||||||
|
&& !defined(G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED)
|
||||||
|
if (flags & INTEROP_FLAGS_EXTERNAL)
|
||||||
|
{
|
||||||
|
g_test_skip ("EXTERNAL authentication not implemented on this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (flags & INTEROP_FLAGS_ANONYMOUS)
|
||||||
|
server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
|
||||||
|
|
||||||
|
observer = g_dbus_auth_observer_new ();
|
||||||
|
|
||||||
|
if (flags & INTEROP_FLAGS_EXTERNAL)
|
||||||
|
g_signal_connect (observer, "allow-mechanism",
|
||||||
|
G_CALLBACK (allow_external_cb), NULL);
|
||||||
|
else if (flags & INTEROP_FLAGS_ANONYMOUS)
|
||||||
|
g_signal_connect (observer, "allow-mechanism",
|
||||||
|
G_CALLBACK (allow_anonymous_cb), NULL);
|
||||||
|
else if (flags & INTEROP_FLAGS_SHA1)
|
||||||
|
g_signal_connect (observer, "allow-mechanism",
|
||||||
|
G_CALLBACK (allow_sha1_cb), NULL);
|
||||||
|
else
|
||||||
|
g_signal_connect (observer, "allow-mechanism",
|
||||||
|
G_CALLBACK (allow_any_mechanism_cb), NULL);
|
||||||
|
|
||||||
|
g_signal_connect (observer, "authorize-authenticated-peer",
|
||||||
|
G_CALLBACK (authorize_any_authenticated_peer_cb),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
guid = g_dbus_generate_guid ();
|
||||||
|
server = g_dbus_server_new_sync (listenable_address,
|
||||||
|
server_flags,
|
||||||
|
guid,
|
||||||
|
observer,
|
||||||
|
NULL,
|
||||||
|
&error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (server);
|
||||||
|
g_signal_connect (server, "new-connection", G_CALLBACK (new_connection_cb), NULL);
|
||||||
|
g_dbus_server_start (server);
|
||||||
|
connectable_address = g_dbus_server_get_client_address (server);
|
||||||
|
|
||||||
|
result = NULL;
|
||||||
|
g_dbus_connection_new_for_address (connectable_address,
|
||||||
|
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
|
||||||
|
NULL, NULL, store_result_cb, &result);
|
||||||
|
|
||||||
|
while (result == NULL)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
client = g_dbus_connection_new_for_address_finish (result, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (client);
|
||||||
|
g_clear_object (&result);
|
||||||
|
|
||||||
|
g_dbus_connection_call (client, NULL, "/", "com.example.Test", "WhoAmI",
|
||||||
|
NULL, G_VARIANT_TYPE ("(xx)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE, -1, NULL, store_result_cb,
|
||||||
|
&result);
|
||||||
|
|
||||||
|
while (result == NULL)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
tuple = g_dbus_connection_call_finish (client, result, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (tuple);
|
||||||
|
g_clear_object (&result);
|
||||||
|
g_clear_object (&client);
|
||||||
|
|
||||||
|
uid = -2;
|
||||||
|
pid = -2;
|
||||||
|
g_variant_get (tuple, "(xx)", &uid, &pid);
|
||||||
|
|
||||||
|
g_debug ("Server says GDBus client is uid %" G_GINT64_FORMAT ", pid %" G_GINT64_FORMAT,
|
||||||
|
uid, pid);
|
||||||
|
|
||||||
|
assert_expected_uid_pid (flags, uid, pid);
|
||||||
|
|
||||||
|
g_clear_pointer (&tuple, g_variant_unref);
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS1
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
LibdbusCall libdbus_call = { DBUS_ERROR_INIT, NULL, NULL, NULL };
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
libdbus_call.conn = dbus_connection_open_private (connectable_address,
|
||||||
|
&libdbus_call.error);
|
||||||
|
g_assert_cmpstr (libdbus_call.error.name, ==, NULL);
|
||||||
|
g_assert_nonnull (libdbus_call.conn);
|
||||||
|
|
||||||
|
libdbus_call.call = dbus_message_new_method_call (NULL, "/",
|
||||||
|
"com.example.Test",
|
||||||
|
"WhoAmI");
|
||||||
|
|
||||||
|
if (libdbus_call.call == NULL)
|
||||||
|
g_error ("Out of memory");
|
||||||
|
|
||||||
|
result = NULL;
|
||||||
|
task = g_task_new (NULL, NULL, store_result_cb, &result);
|
||||||
|
g_task_set_task_data (task, &libdbus_call, NULL);
|
||||||
|
g_task_run_in_thread (task, libdbus_call_task_cb);
|
||||||
|
|
||||||
|
while (result == NULL)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
g_clear_object (&result);
|
||||||
|
|
||||||
|
g_assert_cmpstr (libdbus_call.error.name, ==, NULL);
|
||||||
|
g_assert_nonnull (libdbus_call.reply);
|
||||||
|
|
||||||
|
uid = -2;
|
||||||
|
pid = -2;
|
||||||
|
dbus_message_get_args (libdbus_call.reply, &libdbus_call.error,
|
||||||
|
DBUS_TYPE_INT64, &uid,
|
||||||
|
DBUS_TYPE_INT64, &pid,
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
g_assert_cmpstr (libdbus_call.error.name, ==, NULL);
|
||||||
|
|
||||||
|
g_debug ("Server says libdbus client %" G_GSIZE_FORMAT " is uid %" G_GINT64_FORMAT ", pid %" G_GINT64_FORMAT,
|
||||||
|
i, uid, pid);
|
||||||
|
assert_expected_uid_pid (flags | INTEROP_FLAGS_LIBDBUS, uid, pid);
|
||||||
|
|
||||||
|
dbus_connection_close (libdbus_call.conn);
|
||||||
|
dbus_connection_unref (libdbus_call.conn);
|
||||||
|
dbus_message_unref (libdbus_call.call);
|
||||||
|
dbus_message_unref (libdbus_call.reply);
|
||||||
|
g_clear_object (&task);
|
||||||
|
}
|
||||||
|
#else /* !HAVE_DBUS1 */
|
||||||
|
g_test_skip ("Testing interop with libdbus not supported");
|
||||||
|
#endif /* !HAVE_DBUS1 */
|
||||||
|
|
||||||
|
g_dbus_server_stop (server);
|
||||||
|
g_clear_object (&server);
|
||||||
|
g_clear_object (&observer);
|
||||||
|
g_free (guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("unix:tmpdir=/tmp/gdbus-test", INTEROP_FLAGS_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_tcp (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("tcp:host=127.0.0.1", INTEROP_FLAGS_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_anonymous (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("unix:tmpdir=/tmp/gdbus-test", INTEROP_FLAGS_ANONYMOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_anonymous_tcp (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("tcp:host=127.0.0.1", INTEROP_FLAGS_ANONYMOUS | INTEROP_FLAGS_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_external (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("unix:tmpdir=/tmp/gdbus-test", INTEROP_FLAGS_EXTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_sha1 (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("unix:tmpdir=/tmp/gdbus-test", INTEROP_FLAGS_SHA1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_server_auth_sha1_tcp (void)
|
||||||
|
{
|
||||||
|
do_test_server_auth ("tcp:host=127.0.0.1", INTEROP_FLAGS_SHA1 | INTEROP_FLAGS_TCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/gdbus/server-auth", test_server_auth);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/tcp", test_server_auth_tcp);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/anonymous", test_server_auth_anonymous);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/anonymous/tcp", test_server_auth_anonymous_tcp);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/external", test_server_auth_external);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/sha1", test_server_auth_sha1);
|
||||||
|
g_test_add_func ("/gdbus/server-auth/sha1/tcp", test_server_auth_sha1_tcp);
|
||||||
|
|
||||||
|
return g_test_run();
|
||||||
|
}
|
@ -115,7 +115,15 @@ if dbus1_dep.found()
|
|||||||
'gdbus-serialization' : {
|
'gdbus-serialization' : {
|
||||||
'extra_sources' : ['gdbus-tests.c'],
|
'extra_sources' : ['gdbus-tests.c'],
|
||||||
'dependencies' : [dbus1_dep],
|
'dependencies' : [dbus1_dep],
|
||||||
}
|
},
|
||||||
|
'gdbus-server-auth' : {
|
||||||
|
'dependencies' : [dbus1_dep],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# We can build a cut-down version of this test without libdbus
|
||||||
|
gio_tests += {
|
||||||
|
'gdbus-server-auth' : {},
|
||||||
}
|
}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user