* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-
#include "gdbusauth.h"
#include "gdbusauthmechanismanon.h"
#include "gioenumtypes.h"
#include "gcredentials.h"
#include "gdbusprivate.h"
+#include "giostream.h"
+#include "gdatainputstream.h"
+#include "gdataoutputstream.h"
#ifdef G_OS_UNIX
+#include "gnetworking.h"
#include "gunixconnection.h"
#include "gunixcredentialsmessage.h"
#endif
#include "glibintl.h"
-#include "gioalias.h"
-
-#define DEBUG_ENABLED 0
+G_GNUC_PRINTF(1, 2)
static void
debug_print (const gchar *message, ...)
{
-#if DEBUG_ENABLED
if (G_UNLIKELY (_g_dbus_debug_authentication ()))
{
gchar *s;
va_list var_args;
guint n;
+ _g_dbus_debug_print_lock ();
+
va_start (var_args, message);
s = g_strdup_vprintf (message, var_args);
va_end (var_args);
g_print ("GDBus-debug:Auth: %s\n", str->str);
g_string_free (str, TRUE);
g_free (s);
+
+ _g_dbus_debug_print_unlock ();
}
-#endif
}
typedef struct
PROP_STREAM
};
-G_DEFINE_TYPE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT)
/* ---------------------------------------------------------------------------------------------------- */
if (auth->priv->stream != NULL)
g_object_unref (auth->priv->stream);
- g_list_foreach (auth->priv->available_mechanisms, (GFunc) mechanism_free, NULL);
- g_list_free (auth->priv->available_mechanisms);
+ g_list_free_full (auth->priv->available_mechanisms, (GDestroyNotify) mechanism_free);
if (G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize (object);
{
GObjectClass *gobject_class;
- g_type_class_add_private (klass, sizeof (GDBusAuthPrivate));
-
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = _g_dbus_auth_get_property;
gobject_class->set_property = _g_dbus_auth_set_property;
}
static void
-add_mechanism (GDBusAuth *auth,
- GType mechanism_type)
+add_mechanism (GDBusAuth *auth,
+ GDBusAuthObserver *observer,
+ GType mechanism_type)
{
- Mechanism *m;
-
- m = g_new0 (Mechanism, 1);
- m->name = _g_dbus_auth_mechanism_get_name (mechanism_type);
- m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type);
- m->gtype = mechanism_type;
+ const gchar *name;
- auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m);
+ name = _g_dbus_auth_mechanism_get_name (mechanism_type);
+ if (observer == NULL || g_dbus_auth_observer_allow_mechanism (observer, name))
+ {
+ Mechanism *m;
+ m = g_new0 (Mechanism, 1);
+ m->name = name;
+ m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type);
+ m->gtype = mechanism_type;
+ auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m);
+ }
}
static gint
static void
_g_dbus_auth_init (GDBusAuth *auth)
{
- auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, G_TYPE_DBUS_AUTH, GDBusAuthPrivate);
+ auth->priv = _g_dbus_auth_get_instance_private (auth);
+}
+static void
+_g_dbus_auth_add_mechs (GDBusAuth *auth,
+ GDBusAuthObserver *observer)
+{
/* TODO: trawl extension points */
- add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_ANON);
- add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_SHA1);
- add_mechanism (auth, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL);
+ add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_ANON);
+ add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_SHA1);
+ add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL);
auth->priv->available_mechanisms = g_list_sort (auth->priv->available_mechanisms,
(GCompareFunc) mech_compare_func);
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "Error hexdecoding string `%s' around position %d",
+ "Error hexdecoding string '%s' around position %d",
str, n);
goto out;
}
"stream", auth->priv->stream,
"credentials", credentials_that_were_sent,
NULL);
- debug_print ("CLIENT: Trying mechanism `%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
+ debug_print ("CLIENT: Trying mechanism '%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
g_ptr_array_add (attempted_auth_mechs, (gpointer) _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
/* the auth mechanism may not be supported
*/
if (!_g_dbus_auth_mechanism_is_supported (mech))
{
- debug_print ("CLIENT: Mechanism `%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
+ debug_print ("CLIENT: Mechanism '%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
g_object_unref (mech);
mech = NULL;
goto again;
initial_response = _g_dbus_auth_mechanism_client_initiate (mech,
&initial_response_len);
#if 0
- g_printerr ("using auth mechanism with name `%s' of type `%s' with initial response `%s'\n",
+ g_printerr ("using auth mechanism with name '%s' of type '%s' with initial response '%s'\n",
_g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
g_type_name (G_TYPE_FROM_INSTANCE (mech)),
initial_response);
#endif
if (initial_response != NULL)
{
- //g_printerr ("initial_response = `%s'\n", initial_response);
+ //g_printerr ("initial_response = '%s'\n", initial_response);
encoded = hexencode (initial_response);
s = g_strdup_printf ("AUTH %s %s\r\n",
_g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
{
s = g_strdup_printf ("AUTH %s\r\n", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
}
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_object_unref (mech);
gchar *
_g_dbus_auth_run_client (GDBusAuth *auth,
+ GDBusAuthObserver *observer,
GDBusCapabilityFlags offered_capabilities,
GDBusCapabilityFlags *out_negotiated_capabilities,
GCancellable *cancellable,
debug_print ("CLIENT: initiating");
+ _g_dbus_auth_add_mechs (auth, observer);
+
ret_guid = NULL;
supported_auth_mechs = NULL;
attempted_auth_mechs = g_ptr_array_new ();
dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE);
+ g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE);
g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
#ifdef G_OS_UNIX
- if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ())
+ if (G_IS_UNIX_CONNECTION (auth->priv->stream))
{
credentials = g_credentials_new ();
if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream),
if (G_UNLIKELY (_g_dbus_debug_authentication ()))
{
s = g_credentials_to_string (credentials);
- debug_print ("CLIENT: sent credentials `%s'", s);
+ debug_print ("CLIENT: sent credentials '%s'", s);
g_free (s);
}
}
/* Get list of supported authentication mechanisms */
s = "AUTH\r\n";
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
state = CLIENT_STATE_WAITING_FOR_REJECT;
if (line == NULL)
goto out;
debug_print ("CLIENT: WaitingForReject, read '%s'", line);
- foobar:
+
+ choose_mechanism:
if (!g_str_has_prefix (line, "REJECTED "))
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "In WaitingForReject: Expected `REJECTED am1 am2 ... amN', got `%s'",
+ "In WaitingForReject: Expected 'REJECTED am1 am2 ... amN', got '%s'",
line);
g_free (line);
goto out;
supported_auth_mechs = g_strsplit (line + sizeof ("REJECTED ") - 1, " ", 0);
#if 0
for (n = 0; supported_auth_mechs != NULL && supported_auth_mechs[n] != NULL; n++)
- g_printerr ("supported_auth_mechs[%d] = `%s'\n", n, supported_auth_mechs[n]);
+ g_printerr ("supported_auth_mechs[%d] = '%s'\n", n, supported_auth_mechs[n]);
#endif
}
g_free (line);
line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
if (line == NULL)
goto out;
- debug_print ("CLIENT: WaitingForOK, read `%s'", line);
+ debug_print ("CLIENT: WaitingForOK, read '%s'", line);
if (g_str_has_prefix (line, "OK "))
{
if (!g_dbus_is_guid (line + 3))
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "Invalid OK response `%s'",
+ "Invalid OK response '%s'",
line);
g_free (line);
goto out;
if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
{
s = "NEGOTIATE_UNIX_FD\r\n";
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
state = CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD;
else
{
s = "BEGIN\r\n";
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
/* and we're done! */
}
else if (g_str_has_prefix (line, "REJECTED "))
{
- goto foobar;
+ goto choose_mechanism;
}
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "In WaitingForOk: unexpected response `%s'",
+ "In WaitingForOk: unexpected response '%s'",
line);
g_free (line);
goto out;
line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
if (line == NULL)
goto out;
- debug_print ("CLIENT: WaitingForAgreeUnixFD, read=`%s'", line);
+ debug_print ("CLIENT: WaitingForAgreeUnixFD, read='%s'", line);
if (g_strcmp0 (line, "AGREE_UNIX_FD") == 0)
{
+ g_free (line);
negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
s = "BEGIN\r\n";
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
/* and we're done! */
}
else if (g_str_has_prefix (line, "ERROR") && (line[5] == 0 || g_ascii_isspace (line[5])))
{
- //g_strstrip (line + 5); g_debug ("bah, no unix_fd: `%s'", line + 5);
+ //g_strstrip (line + 5); g_debug ("bah, no unix_fd: '%s'", line + 5);
g_free (line);
s = "BEGIN\r\n";
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
/* and we're done! */
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "In WaitingForAgreeUnixFd: unexpected response `%s'",
+ "In WaitingForAgreeUnixFd: unexpected response '%s'",
line);
g_free (line);
goto out;
line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
if (line == NULL)
goto out;
- debug_print ("CLIENT: WaitingForData, read=`%s'", line);
+ debug_print ("CLIENT: WaitingForData, read='%s'", line);
if (g_str_has_prefix (line, "DATA "))
{
gchar *encoded;
gchar *decoded_data;
- gsize decoded_data_len;
+ gsize decoded_data_len = 0;
encoded = g_strdup (line + 5);
g_free (line);
s = g_strdup_printf ("DATA %s\r\n", encoded_data);
g_free (encoded_data);
g_free (data);
- debug_print ("CLIENT: writing `%s'", s);
+ debug_print ("CLIENT: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
}
state = CLIENT_STATE_WAITING_FOR_OK;
}
+ else if (g_str_has_prefix (line, "REJECTED "))
+ {
+ /* could be the chosen authentication method just doesn't work. Try
+ * another one...
+ */
+ goto choose_mechanism;
+ }
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "In WaitingForData: unexpected response `%s'",
+ "In WaitingForData: unexpected response '%s'",
line);
g_free (line);
goto out;
g_object_unref (mech);
g_ptr_array_unref (attempted_auth_mechs);
g_strfreev (supported_auth_mechs);
- g_object_ref (dis);
- g_object_ref (dos);
+ g_object_unref (dis);
+ g_object_unref (dos);
/* ensure return value is NULL if error is set */
if (error != NULL && *error != NULL)
debug_print ("SERVER: initiating");
+ _g_dbus_auth_add_mechs (auth, observer);
+
ret = FALSE;
dis = NULL;
dos = NULL;
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "The given guid `%s' is not valid",
+ "The given guid '%s' is not valid",
guid);
goto out;
}
dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE);
+ g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE);
g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
/* first read the NUL-byte (TODO: read credentials if using a unix domain socket) */
#ifdef G_OS_UNIX
- if (G_IS_UNIX_CONNECTION (auth->priv->stream) && g_unix_credentials_message_is_supported ())
+ if (G_IS_UNIX_CONNECTION (auth->priv->stream))
{
local_error = NULL;
credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream),
cancellable,
&local_error);
- if (credentials == NULL)
+ if (credentials == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
{
g_propagate_error (error, local_error);
goto out;
{
local_error = NULL;
byte = g_data_input_stream_read_byte (dis, cancellable, &local_error);
+ byte = byte; /* To avoid -Wunused-but-set-variable */
if (local_error != NULL)
{
g_propagate_error (error, local_error);
#else
local_error = NULL;
byte = g_data_input_stream_read_byte (dis, cancellable, &local_error);
+ byte = byte; /* To avoid -Wunused-but-set-variable */
if (local_error != NULL)
{
g_propagate_error (error, local_error);
if (G_UNLIKELY (_g_dbus_debug_authentication ()))
{
s = g_credentials_to_string (credentials);
- debug_print ("SERVER: received credentials `%s'", s);
+ debug_print ("SERVER: received credentials '%s'", s);
g_free (s);
}
}
case SERVER_STATE_WAITING_FOR_AUTH:
debug_print ("SERVER: WaitingForAuth");
line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
- debug_print ("SERVER: WaitingForAuth, read `%s'", line);
+ debug_print ("SERVER: WaitingForAuth, read '%s'", line);
if (line == NULL)
goto out;
if (g_strcmp0 (line, "AUTH") == 0)
{
s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "Unexpected line `%s' while in WaitingForAuth state",
+ "Unexpected line '%s' while in WaitingForAuth state",
line);
g_strfreev (tokens);
goto out;
}
/* TODO: record that the client has attempted to use this mechanism */
- //g_debug ("client is trying `%s'", mech_name);
+ //g_debug ("client is trying '%s'", mech_name);
auth_mech_to_use_gtype = find_mech_by_name (auth, mech_name);
if ((auth_mech_to_use_gtype == (GType) 0) ||
/* We don't support this auth mechanism */
g_strfreev (tokens);
s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
else
{
s = g_strdup_printf ("OK %s\r\n", guid);
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
case G_DBUS_AUTH_MECHANISM_STATE_REJECTED:
s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
s = g_strdup_printf ("DATA %s\r\n", encoded_data);
g_free (encoded_data);
g_free (data);
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
{
g_free (s);
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "Unexpected line `%s' while in WaitingForAuth state",
+ "Unexpected line '%s' while in WaitingForAuth state",
line);
g_free (line);
goto out;
case SERVER_STATE_WAITING_FOR_DATA:
debug_print ("SERVER: WaitingForData");
line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
- debug_print ("SERVER: WaitingForData, read `%s'", line);
+ debug_print ("SERVER: WaitingForData, read '%s'", line);
if (line == NULL)
goto out;
if (g_str_has_prefix (line, "DATA "))
{
gchar *encoded;
gchar *decoded_data;
- gsize decoded_data_len;
+ gsize decoded_data_len = 0;
encoded = g_strdup (line + 5);
g_free (line);
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- "Unexpected line `%s' while in WaitingForData state",
+ "Unexpected line '%s' while in WaitingForData state",
line);
g_free (line);
}
&line_length,
cancellable,
error);
- debug_print ("SERVER: WaitingForBegin, read `%s'", line);
+ debug_print ("SERVER: WaitingForBegin, read '%s'", line);
if (line == NULL)
goto out;
if (g_strcmp0 (line, "BEGIN") == 0)
}
else if (g_strcmp0 (line, "NEGOTIATE_UNIX_FD") == 0)
{
+ g_free (line);
if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
{
negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
s = "AGREE_UNIX_FD\r\n";
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
}
else
{
s = "ERROR \"fd passing not offered\"\r\n";
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
}
}
else
{
- g_debug ("Unexpected line `%s' while in WaitingForBegin state", line);
+ g_debug ("Unexpected line '%s' while in WaitingForBegin state", line);
g_free (line);
s = "ERROR \"Unknown Command\"\r\n";
- debug_print ("SERVER: writing `%s'", s);
+ debug_print ("SERVER: writing '%s'", s);
if (!g_data_output_stream_put_string (dos, s, cancellable, error))
goto out;
}
if (mech != NULL)
g_object_unref (mech);
if (dis != NULL)
- g_object_ref (dis);
- if (dis != NULL)
- g_object_ref (dos);
+ g_object_unref (dis);
+ if (dos != NULL)
+ g_object_unref (dos);
/* ensure return value is FALSE if error is set */
if (error != NULL && *error != NULL)
}
/* ---------------------------------------------------------------------------------------------------- */
-
-#define __G_DBUS_AUTH_C__
-#include "gioaliasdef.c"