Blame gio/gdbusauth.c

Packit ae235b
/* GDBus - GLib D-Bus Library
Packit ae235b
 *
Packit ae235b
 * Copyright (C) 2008-2010 Red Hat, Inc.
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General
Packit ae235b
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: David Zeuthen <davidz@redhat.com>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include "gdbusauth.h"
Packit ae235b
Packit ae235b
#include "gdbusauthmechanismanon.h"
Packit ae235b
#include "gdbusauthmechanismexternal.h"
Packit ae235b
#include "gdbusauthmechanismsha1.h"
Packit ae235b
#include "gdbusauthobserver.h"
Packit ae235b
Packit ae235b
#include "gdbuserror.h"
Packit ae235b
#include "gdbusutils.h"
Packit ae235b
#include "gioenumtypes.h"
Packit ae235b
#include "gcredentials.h"
Packit ae235b
#include "gdbusprivate.h"
Packit ae235b
#include "giostream.h"
Packit ae235b
#include "gdatainputstream.h"
Packit ae235b
#include "gdataoutputstream.h"
Packit ae235b
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
#include "gnetworking.h"
Packit ae235b
#include "gunixconnection.h"
Packit ae235b
#include "gunixcredentialsmessage.h"
Packit ae235b
#endif
Packit ae235b
Packit ae235b
#include "glibintl.h"
Packit ae235b
Packit ae235b
G_GNUC_PRINTF(1, 2)
Packit ae235b
static void
Packit ae235b
debug_print (const gchar *message, ...)
Packit ae235b
{
Packit ae235b
  if (G_UNLIKELY (_g_dbus_debug_authentication ()))
Packit ae235b
    {
Packit ae235b
      gchar *s;
Packit ae235b
      GString *str;
Packit ae235b
      va_list var_args;
Packit ae235b
      guint n;
Packit ae235b
Packit ae235b
      _g_dbus_debug_print_lock ();
Packit ae235b
Packit ae235b
      va_start (var_args, message);
Packit ae235b
      s = g_strdup_vprintf (message, var_args);
Packit ae235b
      va_end (var_args);
Packit ae235b
Packit ae235b
      str = g_string_new (NULL);
Packit ae235b
      for (n = 0; s[n] != '\0'; n++)
Packit ae235b
        {
Packit ae235b
          if (G_UNLIKELY (s[n] == '\r'))
Packit ae235b
            g_string_append (str, "\\r");
Packit ae235b
          else if (G_UNLIKELY (s[n] == '\n'))
Packit ae235b
            g_string_append (str, "\\n");
Packit ae235b
          else
Packit ae235b
            g_string_append_c (str, s[n]);
Packit ae235b
        }
Packit ae235b
      g_print ("GDBus-debug:Auth: %s\n", str->str);
Packit ae235b
      g_string_free (str, TRUE);
Packit ae235b
      g_free (s);
Packit ae235b
Packit ae235b
      _g_dbus_debug_print_unlock ();
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  const gchar *name;
Packit ae235b
  gint priority;
Packit ae235b
  GType gtype;
Packit ae235b
} Mechanism;
Packit ae235b
Packit ae235b
static void mechanism_free (Mechanism *m);
Packit ae235b
Packit ae235b
struct _GDBusAuthPrivate
Packit ae235b
{
Packit ae235b
  GIOStream *stream;
Packit ae235b
Packit ae235b
  /* A list of available Mechanism, sorted according to priority  */
Packit ae235b
  GList *available_mechanisms;
Packit ae235b
};
Packit ae235b
Packit ae235b
enum
Packit ae235b
{
Packit ae235b
  PROP_0,
Packit ae235b
  PROP_STREAM
Packit ae235b
};
Packit ae235b
Packit ae235b
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT)
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_finalize (GObject *object)
Packit ae235b
{
Packit ae235b
  GDBusAuth *auth = G_DBUS_AUTH (object);
Packit ae235b
Packit ae235b
  if (auth->priv->stream != NULL)
Packit ae235b
    g_object_unref (auth->priv->stream);
Packit ae235b
  g_list_free_full (auth->priv->available_mechanisms, (GDestroyNotify) mechanism_free);
Packit ae235b
Packit ae235b
  if (G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize != NULL)
Packit ae235b
    G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize (object);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_get_property (GObject    *object,
Packit ae235b
                           guint       prop_id,
Packit ae235b
                           GValue     *value,
Packit ae235b
                           GParamSpec *pspec)
Packit ae235b
{
Packit ae235b
  GDBusAuth *auth = G_DBUS_AUTH (object);
Packit ae235b
Packit ae235b
  switch (prop_id)
Packit ae235b
    {
Packit ae235b
    case PROP_STREAM:
Packit ae235b
      g_value_set_object (value, auth->priv->stream);
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_set_property (GObject      *object,
Packit ae235b
                           guint         prop_id,
Packit ae235b
                           const GValue *value,
Packit ae235b
                           GParamSpec   *pspec)
Packit ae235b
{
Packit ae235b
  GDBusAuth *auth = G_DBUS_AUTH (object);
Packit ae235b
Packit ae235b
  switch (prop_id)
Packit ae235b
    {
Packit ae235b
    case PROP_STREAM:
Packit ae235b
      auth->priv->stream = g_value_dup_object (value);
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_class_init (GDBusAuthClass *klass)
Packit ae235b
{
Packit ae235b
  GObjectClass *gobject_class;
Packit ae235b
Packit ae235b
  gobject_class = G_OBJECT_CLASS (klass);
Packit ae235b
  gobject_class->get_property = _g_dbus_auth_get_property;
Packit ae235b
  gobject_class->set_property = _g_dbus_auth_set_property;
Packit ae235b
  gobject_class->finalize     = _g_dbus_auth_finalize;
Packit ae235b
Packit ae235b
  g_object_class_install_property (gobject_class,
Packit ae235b
                                   PROP_STREAM,
Packit ae235b
                                   g_param_spec_object ("stream",
Packit ae235b
                                                        P_("IO Stream"),
Packit ae235b
                                                        P_("The underlying GIOStream used for I/O"),
Packit ae235b
                                                        G_TYPE_IO_STREAM,
Packit ae235b
                                                        G_PARAM_READABLE |
Packit ae235b
                                                        G_PARAM_WRITABLE |
Packit ae235b
                                                        G_PARAM_CONSTRUCT_ONLY |
Packit ae235b
                                                        G_PARAM_STATIC_NAME |
Packit ae235b
                                                        G_PARAM_STATIC_BLURB |
Packit ae235b
                                                        G_PARAM_STATIC_NICK));
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
mechanism_free (Mechanism *m)
Packit ae235b
{
Packit ae235b
  g_free (m);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
add_mechanism (GDBusAuth         *auth,
Packit ae235b
               GDBusAuthObserver *observer,
Packit ae235b
               GType              mechanism_type)
Packit ae235b
{
Packit ae235b
  const gchar *name;
Packit ae235b
Packit ae235b
  name = _g_dbus_auth_mechanism_get_name (mechanism_type);
Packit ae235b
  if (observer == NULL || g_dbus_auth_observer_allow_mechanism (observer, name))
Packit ae235b
    {
Packit ae235b
      Mechanism *m;
Packit ae235b
      m = g_new0 (Mechanism, 1);
Packit ae235b
      m->name = name;
Packit ae235b
      m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type);
Packit ae235b
      m->gtype = mechanism_type;
Packit ae235b
      auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static gint
Packit ae235b
mech_compare_func (Mechanism *a, Mechanism *b)
Packit ae235b
{
Packit ae235b
  gint ret;
Packit ae235b
  /* ensure deterministic order */
Packit ae235b
  ret = b->priority - a->priority;
Packit ae235b
  if (ret == 0)
Packit ae235b
    ret = g_strcmp0 (b->name, a->name);
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_init (GDBusAuth *auth)
Packit ae235b
{
Packit ae235b
  auth->priv = _g_dbus_auth_get_instance_private (auth);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
_g_dbus_auth_add_mechs (GDBusAuth         *auth,
Packit ae235b
                        GDBusAuthObserver *observer)
Packit ae235b
{
Packit ae235b
  /* TODO: trawl extension points */
Packit ae235b
  add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_ANON);
Packit ae235b
  add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_SHA1);
Packit ae235b
  add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL);
Packit ae235b
Packit ae235b
  auth->priv->available_mechanisms = g_list_sort (auth->priv->available_mechanisms,
Packit ae235b
                                                  (GCompareFunc) mech_compare_func);
Packit ae235b
}
Packit ae235b
Packit ae235b
static GType
Packit ae235b
find_mech_by_name (GDBusAuth *auth,
Packit ae235b
                   const gchar *name)
Packit ae235b
{
Packit ae235b
  GType ret;
Packit ae235b
  GList *l;
Packit ae235b
Packit ae235b
  ret = (GType) 0;
Packit ae235b
Packit ae235b
  for (l = auth->priv->available_mechanisms; l != NULL; l = l->next)
Packit ae235b
    {
Packit ae235b
      Mechanism *m = l->data;
Packit ae235b
      if (g_strcmp0 (name, m->name) == 0)
Packit ae235b
        {
Packit ae235b
          ret = m->gtype;
Packit ae235b
          goto out;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
GDBusAuth  *
Packit ae235b
_g_dbus_auth_new (GIOStream *stream)
Packit ae235b
{
Packit ae235b
  return g_object_new (G_TYPE_DBUS_AUTH,
Packit ae235b
                       "stream", stream,
Packit ae235b
                       NULL);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
/* like g_data_input_stream_read_line() but sets error if there's no content to read */
Packit ae235b
static gchar *
Packit ae235b
_my_g_data_input_stream_read_line (GDataInputStream  *dis,
Packit ae235b
                                   gsize             *out_line_length,
Packit ae235b
                                   GCancellable      *cancellable,
Packit ae235b
                                   GError           **error)
Packit ae235b
{
Packit ae235b
  gchar *ret;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Packit ae235b
Packit ae235b
  ret = g_data_input_stream_read_line (dis,
Packit ae235b
                                       out_line_length,
Packit ae235b
                                       cancellable,
Packit ae235b
                                       error);
Packit ae235b
  if (ret == NULL && error != NULL && *error == NULL)
Packit ae235b
    {
Packit ae235b
      g_set_error_literal (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           _("Unexpected lack of content trying to read a line"));
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* This function is to avoid situations like this
Packit ae235b
 *
Packit ae235b
 * BEGIN\r\nl\0\0\1...
Packit ae235b
 *
Packit ae235b
 * e.g. where we read into the first D-Bus message while waiting for
Packit ae235b
 * the final line from the client (TODO: file bug against gio for
Packit ae235b
 * this)
Packit ae235b
 */
Packit ae235b
static gchar *
Packit ae235b
_my_g_input_stream_read_line_safe (GInputStream  *i,
Packit ae235b
                                   gsize         *out_line_length,
Packit ae235b
                                   GCancellable  *cancellable,
Packit ae235b
                                   GError       **error)
Packit ae235b
{
Packit ae235b
  GString *str;
Packit ae235b
  gchar c;
Packit ae235b
  gssize num_read;
Packit ae235b
  gboolean last_was_cr;
Packit ae235b
Packit ae235b
  str = g_string_new (NULL);
Packit ae235b
Packit ae235b
  last_was_cr = FALSE;
Packit ae235b
  while (TRUE)
Packit ae235b
    {
Packit ae235b
      num_read = g_input_stream_read (i,
Packit ae235b
                                      &c,
Packit ae235b
                                      1,
Packit ae235b
                                      cancellable,
Packit ae235b
                                      error);
Packit ae235b
      if (num_read == -1)
Packit ae235b
        goto fail;
Packit ae235b
      if (num_read == 0)
Packit ae235b
        {
Packit ae235b
          if (error != NULL && *error == NULL)
Packit ae235b
            {
Packit ae235b
              g_set_error_literal (error,
Packit ae235b
                                   G_IO_ERROR,
Packit ae235b
                                   G_IO_ERROR_FAILED,
Packit ae235b
                                   _("Unexpected lack of content trying to (safely) read a line"));
Packit ae235b
            }
Packit ae235b
          goto fail;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_string_append_c (str, (gint) c);
Packit ae235b
      if (last_was_cr)
Packit ae235b
        {
Packit ae235b
          if (c == 0x0a)
Packit ae235b
            {
Packit ae235b
              g_assert (str->len >= 2);
Packit ae235b
              g_string_set_size (str, str->len - 2);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
      last_was_cr = (c == 0x0d);
Packit ae235b
    }
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  if (out_line_length != NULL)
Packit ae235b
    *out_line_length = str->len;
Packit ae235b
  return g_string_free (str, FALSE);
Packit ae235b
Packit ae235b
 fail:
Packit ae235b
  g_assert (error == NULL || *error != NULL);
Packit ae235b
  g_string_free (str, TRUE);
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
static void
Packit ae235b
append_nibble (GString *s, gint val)
Packit ae235b
{
Packit ae235b
  g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
Packit ae235b
}
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
hexdecode (const gchar  *str,
Packit ae235b
           gsize        *out_len,
Packit ae235b
           GError      **error)
Packit ae235b
{
Packit ae235b
  gchar *ret;
Packit ae235b
  GString *s;
Packit ae235b
  guint n;
Packit ae235b
Packit ae235b
  ret = NULL;
Packit ae235b
  s = g_string_new (NULL);
Packit ae235b
Packit ae235b
  for (n = 0; str[n] != '\0'; n += 2)
Packit ae235b
    {
Packit ae235b
      gint upper_nibble;
Packit ae235b
      gint lower_nibble;
Packit ae235b
      guint value;
Packit ae235b
Packit ae235b
      upper_nibble = g_ascii_xdigit_value (str[n]);
Packit ae235b
      lower_nibble = g_ascii_xdigit_value (str[n + 1]);
Packit ae235b
      if (upper_nibble == -1 || lower_nibble == -1)
Packit ae235b
        {
Packit ae235b
          g_set_error (error,
Packit ae235b
                       G_IO_ERROR,
Packit ae235b
                       G_IO_ERROR_FAILED,
Packit ae235b
                       "Error hexdecoding string '%s' around position %d",
Packit ae235b
                       str, n);
Packit ae235b
          goto out;
Packit ae235b
        }
Packit ae235b
      value = (upper_nibble<<4) | lower_nibble;
Packit ae235b
      g_string_append_c (s, value);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  ret = g_string_free (s, FALSE);
Packit ae235b
  s = NULL;
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  if (s != NULL)
Packit ae235b
    g_string_free (s, TRUE);
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* TODO: take len */
Packit ae235b
static gchar *
Packit ae235b
hexencode (const gchar *str)
Packit ae235b
{
Packit ae235b
  guint n;
Packit ae235b
  GString *s;
Packit ae235b
Packit ae235b
  s = g_string_new (NULL);
Packit ae235b
  for (n = 0; str[n] != '\0'; n++)
Packit ae235b
    {
Packit ae235b
      gint val;
Packit ae235b
      gint upper_nibble;
Packit ae235b
      gint lower_nibble;
Packit ae235b
Packit ae235b
      val = ((const guchar *) str)[n];
Packit ae235b
      upper_nibble = val >> 4;
Packit ae235b
      lower_nibble = val & 0x0f;
Packit ae235b
Packit ae235b
      append_nibble (s, upper_nibble);
Packit ae235b
      append_nibble (s, lower_nibble);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return g_string_free (s, FALSE);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
static GDBusAuthMechanism *
Packit ae235b
client_choose_mech_and_send_initial_response (GDBusAuth           *auth,
Packit ae235b
                                              GCredentials        *credentials_that_were_sent,
Packit ae235b
                                              const gchar* const  *supported_auth_mechs,
Packit ae235b
                                              GPtrArray           *attempted_auth_mechs,
Packit ae235b
                                              GDataOutputStream   *dos,
Packit ae235b
                                              GCancellable        *cancellable,
Packit ae235b
                                              GError             **error)
Packit ae235b
{
Packit ae235b
  GDBusAuthMechanism *mech;
Packit ae235b
  GType auth_mech_to_use_gtype;
Packit ae235b
  guint n;
Packit ae235b
  guint m;
Packit ae235b
  gchar *initial_response;
Packit ae235b
  gsize initial_response_len;
Packit ae235b
  gchar *encoded;
Packit ae235b
  gchar *s;
Packit ae235b
Packit ae235b
 again:
Packit ae235b
  mech = NULL;
Packit ae235b
Packit ae235b
  debug_print ("CLIENT: Trying to choose mechanism");
Packit ae235b
Packit ae235b
  /* find an authentication mechanism to try, if any */
Packit ae235b
  auth_mech_to_use_gtype = (GType) 0;
Packit ae235b
  for (n = 0; supported_auth_mechs[n] != NULL; n++)
Packit ae235b
    {
Packit ae235b
      gboolean attempted_already;
Packit ae235b
      attempted_already = FALSE;
Packit ae235b
      for (m = 0; m < attempted_auth_mechs->len; m++)
Packit ae235b
        {
Packit ae235b
          if (g_strcmp0 (supported_auth_mechs[n], attempted_auth_mechs->pdata[m]) == 0)
Packit ae235b
            {
Packit ae235b
              attempted_already = TRUE;
Packit ae235b
              break;
Packit ae235b
            }
Packit ae235b
        }
Packit ae235b
      if (!attempted_already)
Packit ae235b
        {
Packit ae235b
          auth_mech_to_use_gtype = find_mech_by_name (auth, supported_auth_mechs[n]);
Packit ae235b
          if (auth_mech_to_use_gtype != (GType) 0)
Packit ae235b
            break;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (auth_mech_to_use_gtype == (GType) 0)
Packit ae235b
    {
Packit ae235b
      guint n;
Packit ae235b
      gchar *available;
Packit ae235b
      GString *tried_str;
Packit ae235b
Packit ae235b
      debug_print ("CLIENT: Exhausted all available mechanisms");
Packit ae235b
Packit ae235b
      available = g_strjoinv (", ", (gchar **) supported_auth_mechs);
Packit ae235b
Packit ae235b
      tried_str = g_string_new (NULL);
Packit ae235b
      for (n = 0; n < attempted_auth_mechs->len; n++)
Packit ae235b
        {
Packit ae235b
          if (n > 0)
Packit ae235b
            g_string_append (tried_str, ", ");
Packit ae235b
          g_string_append (tried_str, attempted_auth_mechs->pdata[n]);
Packit ae235b
        }
Packit ae235b
      g_set_error (error,
Packit ae235b
                   G_IO_ERROR,
Packit ae235b
                   G_IO_ERROR_FAILED,
Packit ae235b
                   _("Exhausted all available authentication mechanisms (tried: %s) (available: %s)"),
Packit ae235b
                   tried_str->str,
Packit ae235b
                   available);
Packit ae235b
      g_string_free (tried_str, TRUE);
Packit ae235b
      g_free (available);
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* OK, decided on a mechanism - let's do this thing */
Packit ae235b
  mech = g_object_new (auth_mech_to_use_gtype,
Packit ae235b
                       "stream", auth->priv->stream,
Packit ae235b
                       "credentials", credentials_that_were_sent,
Packit ae235b
                       NULL);
Packit ae235b
  debug_print ("CLIENT: Trying mechanism '%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
Packit ae235b
  g_ptr_array_add (attempted_auth_mechs, (gpointer) _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
Packit ae235b
Packit ae235b
  /* the auth mechanism may not be supported
Packit ae235b
   * (for example, EXTERNAL only works if credentials were exchanged)
Packit ae235b
   */
Packit ae235b
  if (!_g_dbus_auth_mechanism_is_supported (mech))
Packit ae235b
    {
Packit ae235b
      debug_print ("CLIENT: Mechanism '%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
Packit ae235b
      g_object_unref (mech);
Packit ae235b
      mech = NULL;
Packit ae235b
      goto again;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  initial_response_len = -1;
Packit ae235b
  initial_response = _g_dbus_auth_mechanism_client_initiate (mech,
Packit ae235b
                                                             &initial_response_len);
Packit ae235b
#if 0
Packit ae235b
  g_printerr ("using auth mechanism with name '%s' of type '%s' with initial response '%s'\n",
Packit ae235b
              _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
Packit ae235b
              g_type_name (G_TYPE_FROM_INSTANCE (mech)),
Packit ae235b
              initial_response);
Packit ae235b
#endif
Packit ae235b
  if (initial_response != NULL)
Packit ae235b
    {
Packit ae235b
      //g_printerr ("initial_response = '%s'\n", initial_response);
Packit ae235b
      encoded = hexencode (initial_response);
Packit ae235b
      s = g_strdup_printf ("AUTH %s %s\r\n",
Packit ae235b
                           _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype),
Packit ae235b
                           encoded);
Packit ae235b
      g_free (initial_response);
Packit ae235b
      g_free (encoded);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      s = g_strdup_printf ("AUTH %s\r\n", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype));
Packit ae235b
    }
Packit ae235b
  debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
    {
Packit ae235b
      g_object_unref (mech);
Packit ae235b
      mech = NULL;
Packit ae235b
      g_free (s);
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
  g_free (s);
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  return mech;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
typedef enum
Packit ae235b
{
Packit ae235b
  CLIENT_STATE_WAITING_FOR_DATA,
Packit ae235b
  CLIENT_STATE_WAITING_FOR_OK,
Packit ae235b
  CLIENT_STATE_WAITING_FOR_REJECT,
Packit ae235b
  CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD
Packit ae235b
} ClientState;
Packit ae235b
Packit ae235b
gchar *
Packit ae235b
_g_dbus_auth_run_client (GDBusAuth     *auth,
Packit ae235b
                         GDBusAuthObserver     *observer,
Packit ae235b
                         GDBusCapabilityFlags offered_capabilities,
Packit ae235b
                         GDBusCapabilityFlags *out_negotiated_capabilities,
Packit ae235b
                         GCancellable  *cancellable,
Packit ae235b
                         GError       **error)
Packit ae235b
{
Packit ae235b
  gchar *s;
Packit ae235b
  GDataInputStream *dis;
Packit ae235b
  GDataOutputStream *dos;
Packit ae235b
  GCredentials *credentials;
Packit ae235b
  gchar *ret_guid;
Packit ae235b
  gchar *line;
Packit ae235b
  gsize line_length;
Packit ae235b
  gchar **supported_auth_mechs;
Packit ae235b
  GPtrArray *attempted_auth_mechs;
Packit ae235b
  GDBusAuthMechanism *mech;
Packit ae235b
  ClientState state;
Packit ae235b
  GDBusCapabilityFlags negotiated_capabilities;
Packit ae235b
Packit ae235b
  debug_print ("CLIENT: initiating");
Packit ae235b
Packit ae235b
  _g_dbus_auth_add_mechs (auth, observer);
Packit ae235b
Packit ae235b
  ret_guid = NULL;
Packit ae235b
  supported_auth_mechs = NULL;
Packit ae235b
  attempted_auth_mechs = g_ptr_array_new ();
Packit ae235b
  mech = NULL;
Packit ae235b
  negotiated_capabilities = 0;
Packit ae235b
  credentials = NULL;
Packit ae235b
Packit ae235b
  dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
Packit ae235b
  dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
Packit ae235b
  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE);
Packit ae235b
  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE);
Packit ae235b
Packit ae235b
  g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
Packit ae235b
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
  if (G_IS_UNIX_CONNECTION (auth->priv->stream))
Packit ae235b
    {
Packit ae235b
      credentials = g_credentials_new ();
Packit ae235b
      if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream),
Packit ae235b
                                               cancellable,
Packit ae235b
                                               error))
Packit ae235b
        goto out;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error))
Packit ae235b
        goto out;
Packit ae235b
    }
Packit ae235b
#else
Packit ae235b
  if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error))
Packit ae235b
    goto out;
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  if (credentials != NULL)
Packit ae235b
    {
Packit ae235b
      if (G_UNLIKELY (_g_dbus_debug_authentication ()))
Packit ae235b
        {
Packit ae235b
          s = g_credentials_to_string (credentials);
Packit ae235b
          debug_print ("CLIENT: sent credentials '%s'", s);
Packit ae235b
          g_free (s);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      debug_print ("CLIENT: didn't send any credentials");
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* TODO: to reduce roundtrips, try to pick an auth mechanism to start with */
Packit ae235b
Packit ae235b
  /* Get list of supported authentication mechanisms */
Packit ae235b
  s = "AUTH\r\n";
Packit ae235b
  debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
    goto out;
Packit ae235b
  state = CLIENT_STATE_WAITING_FOR_REJECT;
Packit ae235b
Packit ae235b
  while (TRUE)
Packit ae235b
    {
Packit ae235b
      switch (state)
Packit ae235b
        {
Packit ae235b
        case CLIENT_STATE_WAITING_FOR_REJECT:
Packit ae235b
          debug_print ("CLIENT: WaitingForReject");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          debug_print ("CLIENT: WaitingForReject, read '%s'", line);
Packit ae235b
Packit ae235b
        choose_mechanism:
Packit ae235b
          if (!g_str_has_prefix (line, "REJECTED "))
Packit ae235b
            {
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "In WaitingForReject: Expected 'REJECTED am1 am2 ... amN', got '%s'",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          if (supported_auth_mechs == NULL)
Packit ae235b
            {
Packit ae235b
              supported_auth_mechs = g_strsplit (line + sizeof ("REJECTED ") - 1, " ", 0);
Packit ae235b
#if 0
Packit ae235b
              for (n = 0; supported_auth_mechs != NULL && supported_auth_mechs[n] != NULL; n++)
Packit ae235b
                g_printerr ("supported_auth_mechs[%d] = '%s'\n", n, supported_auth_mechs[n]);
Packit ae235b
#endif
Packit ae235b
            }
Packit ae235b
          g_free (line);
Packit ae235b
          mech = client_choose_mech_and_send_initial_response (auth,
Packit ae235b
                                                               credentials,
Packit ae235b
                                                               (const gchar* const *) supported_auth_mechs,
Packit ae235b
                                                               attempted_auth_mechs,
Packit ae235b
                                                               dos,
Packit ae235b
                                                               cancellable,
Packit ae235b
                                                               error);
Packit ae235b
          if (mech == NULL)
Packit ae235b
            goto out;
Packit ae235b
          if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA)
Packit ae235b
            state = CLIENT_STATE_WAITING_FOR_DATA;
Packit ae235b
          else
Packit ae235b
            state = CLIENT_STATE_WAITING_FOR_OK;
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        case CLIENT_STATE_WAITING_FOR_OK:
Packit ae235b
          debug_print ("CLIENT: WaitingForOK");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          debug_print ("CLIENT: WaitingForOK, read '%s'", line);
Packit ae235b
          if (g_str_has_prefix (line, "OK "))
Packit ae235b
            {
Packit ae235b
              if (!g_dbus_is_guid (line + 3))
Packit ae235b
                {
Packit ae235b
                  g_set_error (error,
Packit ae235b
                               G_IO_ERROR,
Packit ae235b
                               G_IO_ERROR_FAILED,
Packit ae235b
                               "Invalid OK response '%s'",
Packit ae235b
                               line);
Packit ae235b
                  g_free (line);
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
              ret_guid = g_strdup (line + 3);
Packit ae235b
              g_free (line);
Packit ae235b
Packit ae235b
              if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
Packit ae235b
                {
Packit ae235b
                  s = "NEGOTIATE_UNIX_FD\r\n";
Packit ae235b
                  debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    goto out;
Packit ae235b
                  state = CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD;
Packit ae235b
                }
Packit ae235b
              else
Packit ae235b
                {
Packit ae235b
                  s = "BEGIN\r\n";
Packit ae235b
                  debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    goto out;
Packit ae235b
                  /* and we're done! */
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
            }
Packit ae235b
          else if (g_str_has_prefix (line, "REJECTED "))
Packit ae235b
            {
Packit ae235b
              goto choose_mechanism;
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              /* TODO: handle other valid responses */
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "In WaitingForOk: unexpected response '%s'",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        case CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD:
Packit ae235b
          debug_print ("CLIENT: WaitingForAgreeUnixFD");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          debug_print ("CLIENT: WaitingForAgreeUnixFD, read='%s'", line);
Packit ae235b
          if (g_strcmp0 (line, "AGREE_UNIX_FD") == 0)
Packit ae235b
            {
Packit ae235b
              g_free (line);
Packit ae235b
              negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
Packit ae235b
              s = "BEGIN\r\n";
Packit ae235b
              debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
              if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                goto out;
Packit ae235b
              /* and we're done! */
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          else if (g_str_has_prefix (line, "ERROR") && (line[5] == 0 || g_ascii_isspace (line[5])))
Packit ae235b
            {
Packit ae235b
              //g_strstrip (line + 5); g_debug ("bah, no unix_fd: '%s'", line + 5);
Packit ae235b
              g_free (line);
Packit ae235b
              s = "BEGIN\r\n";
Packit ae235b
              debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
              if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                goto out;
Packit ae235b
              /* and we're done! */
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              /* TODO: handle other valid responses */
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "In WaitingForAgreeUnixFd: unexpected response '%s'",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        case CLIENT_STATE_WAITING_FOR_DATA:
Packit ae235b
          debug_print ("CLIENT: WaitingForData");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          debug_print ("CLIENT: WaitingForData, read='%s'", line);
Packit ae235b
          if (g_str_has_prefix (line, "DATA "))
Packit ae235b
            {
Packit ae235b
              gchar *encoded;
Packit ae235b
              gchar *decoded_data;
Packit ae235b
              gsize decoded_data_len = 0;
Packit ae235b
Packit ae235b
              encoded = g_strdup (line + 5);
Packit ae235b
              g_free (line);
Packit ae235b
              g_strstrip (encoded);
Packit ae235b
              decoded_data = hexdecode (encoded, &decoded_data_len, error);
Packit ae235b
              g_free (encoded);
Packit ae235b
              if (decoded_data == NULL)
Packit ae235b
                {
Packit ae235b
                  g_prefix_error (error, "DATA response is malformed: ");
Packit ae235b
                  /* invalid encoding, disconnect! */
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
              _g_dbus_auth_mechanism_client_data_receive (mech, decoded_data, decoded_data_len);
Packit ae235b
              g_free (decoded_data);
Packit ae235b
Packit ae235b
              if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND)
Packit ae235b
                {
Packit ae235b
                  gchar *data;
Packit ae235b
                  gsize data_len;
Packit ae235b
                  gchar *encoded_data;
Packit ae235b
                  data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len);
Packit ae235b
                  encoded_data = hexencode (data);
Packit ae235b
                  s = g_strdup_printf ("DATA %s\r\n", encoded_data);
Packit ae235b
                  g_free (encoded_data);
Packit ae235b
                  g_free (data);
Packit ae235b
                  debug_print ("CLIENT: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    {
Packit ae235b
                      g_free (s);
Packit ae235b
                      goto out;
Packit ae235b
                    }
Packit ae235b
                  g_free (s);
Packit ae235b
                }
Packit ae235b
              state = CLIENT_STATE_WAITING_FOR_OK;
Packit ae235b
            }
Packit ae235b
          else if (g_str_has_prefix (line, "REJECTED "))
Packit ae235b
            {
Packit ae235b
              /* could be the chosen authentication method just doesn't work. Try
Packit ae235b
               * another one...
Packit ae235b
               */
Packit ae235b
              goto choose_mechanism;
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "In WaitingForData: unexpected response '%s'",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        default:
Packit ae235b
          g_assert_not_reached ();
Packit ae235b
          break;
Packit ae235b
        }
Packit ae235b
Packit ae235b
    }; /* main authentication client loop */
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  if (mech != NULL)
Packit ae235b
    g_object_unref (mech);
Packit ae235b
  g_ptr_array_unref (attempted_auth_mechs);
Packit ae235b
  g_strfreev (supported_auth_mechs);
Packit ae235b
  g_object_unref (dis);
Packit ae235b
  g_object_unref (dos);
Packit ae235b
Packit ae235b
  /* ensure return value is NULL if error is set */
Packit ae235b
  if (error != NULL && *error != NULL)
Packit ae235b
    {
Packit ae235b
      g_free (ret_guid);
Packit ae235b
      ret_guid = NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (ret_guid != NULL)
Packit ae235b
    {
Packit ae235b
      if (out_negotiated_capabilities != NULL)
Packit ae235b
        *out_negotiated_capabilities = negotiated_capabilities;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (credentials != NULL)
Packit ae235b
    g_object_unref (credentials);
Packit ae235b
Packit ae235b
  debug_print ("CLIENT: Done, authenticated=%d", ret_guid != NULL);
Packit ae235b
Packit ae235b
  return ret_guid;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
get_auth_mechanisms (GDBusAuth     *auth,
Packit ae235b
                     gboolean       allow_anonymous,
Packit ae235b
                     const gchar   *prefix,
Packit ae235b
                     const gchar   *suffix,
Packit ae235b
                     const gchar   *separator)
Packit ae235b
{
Packit ae235b
  GList *l;
Packit ae235b
  GString *str;
Packit ae235b
  gboolean need_sep;
Packit ae235b
Packit ae235b
  str = g_string_new (prefix);
Packit ae235b
  need_sep = FALSE;
Packit ae235b
  for (l = auth->priv->available_mechanisms; l != NULL; l = l->next)
Packit ae235b
    {
Packit ae235b
      Mechanism *m = l->data;
Packit ae235b
Packit ae235b
      if (!allow_anonymous && g_strcmp0 (m->name, "ANONYMOUS") == 0)
Packit ae235b
        continue;
Packit ae235b
Packit ae235b
      if (need_sep)
Packit ae235b
        g_string_append (str, separator);
Packit ae235b
      g_string_append (str, m->name);
Packit ae235b
      need_sep = TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_string_append (str, suffix);
Packit ae235b
  return g_string_free (str, FALSE);
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
typedef enum
Packit ae235b
{
Packit ae235b
  SERVER_STATE_WAITING_FOR_AUTH,
Packit ae235b
  SERVER_STATE_WAITING_FOR_DATA,
Packit ae235b
  SERVER_STATE_WAITING_FOR_BEGIN
Packit ae235b
} ServerState;
Packit ae235b
Packit ae235b
gboolean
Packit ae235b
_g_dbus_auth_run_server (GDBusAuth              *auth,
Packit ae235b
                         GDBusAuthObserver      *observer,
Packit ae235b
                         const gchar            *guid,
Packit ae235b
                         gboolean                allow_anonymous,
Packit ae235b
                         GDBusCapabilityFlags    offered_capabilities,
Packit ae235b
                         GDBusCapabilityFlags   *out_negotiated_capabilities,
Packit ae235b
                         GCredentials          **out_received_credentials,
Packit ae235b
                         GCancellable           *cancellable,
Packit ae235b
                         GError                **error)
Packit ae235b
{
Packit ae235b
  gboolean ret;
Packit ae235b
  ServerState state;
Packit ae235b
  GDataInputStream *dis;
Packit ae235b
  GDataOutputStream *dos;
Packit ae235b
  GError *local_error;
Packit ae235b
  gchar *line;
Packit ae235b
  gsize line_length;
Packit ae235b
  GDBusAuthMechanism *mech;
Packit ae235b
  gchar *s;
Packit ae235b
  GDBusCapabilityFlags negotiated_capabilities;
Packit ae235b
  GCredentials *credentials;
Packit ae235b
Packit ae235b
  debug_print ("SERVER: initiating");
Packit ae235b
Packit ae235b
  _g_dbus_auth_add_mechs (auth, observer);
Packit ae235b
Packit ae235b
  ret = FALSE;
Packit ae235b
  dis = NULL;
Packit ae235b
  dos = NULL;
Packit ae235b
  mech = NULL;
Packit ae235b
  negotiated_capabilities = 0;
Packit ae235b
  credentials = NULL;
Packit ae235b
Packit ae235b
  if (!g_dbus_is_guid (guid))
Packit ae235b
    {
Packit ae235b
      g_set_error (error,
Packit ae235b
                   G_IO_ERROR,
Packit ae235b
                   G_IO_ERROR_FAILED,
Packit ae235b
                   "The given guid '%s' is not valid",
Packit ae235b
                   guid);
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
Packit ae235b
  dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
Packit ae235b
  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE);
Packit ae235b
  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE);
Packit ae235b
Packit ae235b
  g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
Packit ae235b
Packit ae235b
  /* first read the NUL-byte */
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
  if (G_IS_UNIX_CONNECTION (auth->priv->stream))
Packit ae235b
    {
Packit ae235b
      local_error = NULL;
Packit ae235b
      credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream),
Packit ae235b
                                                           cancellable,
Packit ae235b
                                                           &local_error);
Packit ae235b
      if (credentials == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
Packit ae235b
        {
Packit ae235b
          g_propagate_error (error, local_error);
Packit ae235b
          goto out;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      local_error = NULL;
Packit ae235b
      (void)g_data_input_stream_read_byte (dis, cancellable, &local_error);
Packit ae235b
      if (local_error != NULL)
Packit ae235b
        {
Packit ae235b
          g_propagate_error (error, local_error);
Packit ae235b
          goto out;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
#else
Packit ae235b
  local_error = NULL;
Packit ae235b
  (void)g_data_input_stream_read_byte (dis, cancellable, &local_error);
Packit ae235b
  if (local_error != NULL)
Packit ae235b
    {
Packit ae235b
      g_propagate_error (error, local_error);
Packit ae235b
      goto out;
Packit ae235b
    }
Packit ae235b
#endif
Packit ae235b
  if (credentials != NULL)
Packit ae235b
    {
Packit ae235b
      if (G_UNLIKELY (_g_dbus_debug_authentication ()))
Packit ae235b
        {
Packit ae235b
          s = g_credentials_to_string (credentials);
Packit ae235b
          debug_print ("SERVER: received credentials '%s'", s);
Packit ae235b
          g_free (s);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      debug_print ("SERVER: didn't receive any credentials");
Packit ae235b
    }
Packit ae235b
Packit ae235b
  state = SERVER_STATE_WAITING_FOR_AUTH;
Packit ae235b
  while (TRUE)
Packit ae235b
    {
Packit ae235b
      switch (state)
Packit ae235b
        {
Packit ae235b
        case SERVER_STATE_WAITING_FOR_AUTH:
Packit ae235b
          debug_print ("SERVER: WaitingForAuth");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          debug_print ("SERVER: WaitingForAuth, read '%s'", line);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          if (g_strcmp0 (line, "AUTH") == 0)
Packit ae235b
            {
Packit ae235b
              s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
Packit ae235b
              debug_print ("SERVER: writing '%s'", s);
Packit ae235b
              if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                {
Packit ae235b
                  g_free (s);
Packit ae235b
                  g_free (line);
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
              g_free (s);
Packit ae235b
              g_free (line);
Packit ae235b
            }
Packit ae235b
          else if (g_str_has_prefix (line, "AUTH "))
Packit ae235b
            {
Packit ae235b
              gchar **tokens;
Packit ae235b
              const gchar *encoded;
Packit ae235b
              const gchar *mech_name;
Packit ae235b
              GType auth_mech_to_use_gtype;
Packit ae235b
Packit ae235b
              tokens = g_strsplit (line, " ", 0);
Packit ae235b
Packit ae235b
              switch (g_strv_length (tokens))
Packit ae235b
                {
Packit ae235b
                case 2:
Packit ae235b
                  /* no initial response */
Packit ae235b
                  mech_name = tokens[1];
Packit ae235b
                  encoded = NULL;
Packit ae235b
                  break;
Packit ae235b
Packit ae235b
                case 3:
Packit ae235b
                  /* initial response */
Packit ae235b
                  mech_name = tokens[1];
Packit ae235b
                  encoded = tokens[2];
Packit ae235b
                  break;
Packit ae235b
Packit ae235b
                default:
Packit ae235b
                  g_set_error (error,
Packit ae235b
                               G_IO_ERROR,
Packit ae235b
                               G_IO_ERROR_FAILED,
Packit ae235b
                               "Unexpected line '%s' while in WaitingForAuth state",
Packit ae235b
                               line);
Packit ae235b
                  g_strfreev (tokens);
Packit ae235b
                  g_free (line);
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
Packit ae235b
              g_free (line);
Packit ae235b
Packit ae235b
              /* TODO: record that the client has attempted to use this mechanism */
Packit ae235b
              //g_debug ("client is trying '%s'", mech_name);
Packit ae235b
Packit ae235b
              auth_mech_to_use_gtype = find_mech_by_name (auth, mech_name);
Packit ae235b
              if ((auth_mech_to_use_gtype == (GType) 0) ||
Packit ae235b
                  (!allow_anonymous && g_strcmp0 (mech_name, "ANONYMOUS") == 0))
Packit ae235b
                {
Packit ae235b
                  /* We don't support this auth mechanism */
Packit ae235b
                  g_strfreev (tokens);
Packit ae235b
                  s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
Packit ae235b
                  debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    {
Packit ae235b
                      g_free (s);
Packit ae235b
                      goto out;
Packit ae235b
                    }
Packit ae235b
                  g_free (s);
Packit ae235b
Packit ae235b
                  /* stay in WAITING FOR AUTH */
Packit ae235b
                  state = SERVER_STATE_WAITING_FOR_AUTH;
Packit ae235b
                }
Packit ae235b
              else
Packit ae235b
                {
Packit ae235b
                  gchar *initial_response;
Packit ae235b
                  gsize initial_response_len;
Packit ae235b
Packit ae235b
                  g_clear_object (&mech);
Packit ae235b
                  mech = g_object_new (auth_mech_to_use_gtype,
Packit ae235b
                                       "stream", auth->priv->stream,
Packit ae235b
                                       "credentials", credentials,
Packit ae235b
                                       NULL);
Packit ae235b
Packit ae235b
                  initial_response = NULL;
Packit ae235b
                  initial_response_len = 0;
Packit ae235b
                  if (encoded != NULL)
Packit ae235b
                    {
Packit ae235b
                      initial_response = hexdecode (encoded, &initial_response_len, error);
Packit ae235b
                      if (initial_response == NULL)
Packit ae235b
                        {
Packit ae235b
                          g_prefix_error (error, "Initial response is malformed: ");
Packit ae235b
                          /* invalid encoding, disconnect! */
Packit ae235b
                          g_strfreev (tokens);
Packit ae235b
                          goto out;
Packit ae235b
                        }
Packit ae235b
                    }
Packit ae235b
Packit ae235b
                  _g_dbus_auth_mechanism_server_initiate (mech,
Packit ae235b
                                                          initial_response,
Packit ae235b
                                                          initial_response_len);
Packit ae235b
                  g_free (initial_response);
Packit ae235b
                  g_strfreev (tokens);
Packit ae235b
Packit ae235b
                change_state:
Packit ae235b
                  switch (_g_dbus_auth_mechanism_server_get_state (mech))
Packit ae235b
                    {
Packit ae235b
                    case G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED:
Packit ae235b
                      if (observer != NULL &&
Packit ae235b
                          !g_dbus_auth_observer_authorize_authenticated_peer (observer,
Packit ae235b
                                                                              auth->priv->stream,
Packit ae235b
                                                                              credentials))
Packit ae235b
                        {
Packit ae235b
                          /* disconnect */
Packit ae235b
                          g_set_error_literal (error,
Packit ae235b
                                               G_IO_ERROR,
Packit ae235b
                                               G_IO_ERROR_FAILED,
Packit ae235b
                                               _("Cancelled via GDBusAuthObserver::authorize-authenticated-peer"));
Packit ae235b
                          goto out;
Packit ae235b
                        }
Packit ae235b
                      else
Packit ae235b
                        {
Packit ae235b
                          s = g_strdup_printf ("OK %s\r\n", guid);
Packit ae235b
                          debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                          if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                            {
Packit ae235b
                              g_free (s);
Packit ae235b
                              goto out;
Packit ae235b
                            }
Packit ae235b
                          g_free (s);
Packit ae235b
                          state = SERVER_STATE_WAITING_FOR_BEGIN;
Packit ae235b
                        }
Packit ae235b
                      break;
Packit ae235b
Packit ae235b
                    case G_DBUS_AUTH_MECHANISM_STATE_REJECTED:
Packit ae235b
                      s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " ");
Packit ae235b
                      debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                      if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                        {
Packit ae235b
                          g_free (s);
Packit ae235b
                          goto out;
Packit ae235b
                        }
Packit ae235b
                      g_free (s);
Packit ae235b
                      state = SERVER_STATE_WAITING_FOR_AUTH;
Packit ae235b
                      break;
Packit ae235b
Packit ae235b
                    case G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA:
Packit ae235b
                      state = SERVER_STATE_WAITING_FOR_DATA;
Packit ae235b
                      break;
Packit ae235b
Packit ae235b
                    case G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND:
Packit ae235b
                      {
Packit ae235b
                        gchar *data;
Packit ae235b
                        gsize data_len;
Packit ae235b
Packit ae235b
                        data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len);
Packit ae235b
                        if (data != NULL)
Packit ae235b
                          {
Packit ae235b
                            gchar *encoded_data;
Packit ae235b
Packit ae235b
                            encoded_data = hexencode (data);
Packit ae235b
                            s = g_strdup_printf ("DATA %s\r\n", encoded_data);
Packit ae235b
                            g_free (encoded_data);
Packit ae235b
                            g_free (data);
Packit ae235b
Packit ae235b
                            debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                            if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                              {
Packit ae235b
                                g_free (s);
Packit ae235b
                                goto out;
Packit ae235b
                              }
Packit ae235b
                            g_free (s);
Packit ae235b
                          }
Packit ae235b
                      }
Packit ae235b
                      goto change_state;
Packit ae235b
                      break;
Packit ae235b
Packit ae235b
                    default:
Packit ae235b
                      /* TODO */
Packit ae235b
                      g_assert_not_reached ();
Packit ae235b
                      break;
Packit ae235b
                    }
Packit ae235b
                }
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "Unexpected line '%s' while in WaitingForAuth state",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        case SERVER_STATE_WAITING_FOR_DATA:
Packit ae235b
          debug_print ("SERVER: WaitingForData");
Packit ae235b
          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
Packit ae235b
          debug_print ("SERVER: WaitingForData, read '%s'", line);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          if (g_str_has_prefix (line, "DATA "))
Packit ae235b
            {
Packit ae235b
              gchar *encoded;
Packit ae235b
              gchar *decoded_data;
Packit ae235b
              gsize decoded_data_len = 0;
Packit ae235b
Packit ae235b
              encoded = g_strdup (line + 5);
Packit ae235b
              g_free (line);
Packit ae235b
              g_strstrip (encoded);
Packit ae235b
              decoded_data = hexdecode (encoded, &decoded_data_len, error);
Packit ae235b
              g_free (encoded);
Packit ae235b
              if (decoded_data == NULL)
Packit ae235b
                {
Packit ae235b
                  g_prefix_error (error, "DATA response is malformed: ");
Packit ae235b
                  /* invalid encoding, disconnect! */
Packit ae235b
                  goto out;
Packit ae235b
                }
Packit ae235b
              _g_dbus_auth_mechanism_server_data_receive (mech, decoded_data, decoded_data_len);
Packit ae235b
              g_free (decoded_data);
Packit ae235b
              /* oh man, this goto-crap is so ugly.. really need to rewrite the state machine */
Packit ae235b
              goto change_state;
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              g_set_error (error,
Packit ae235b
                           G_IO_ERROR,
Packit ae235b
                           G_IO_ERROR_FAILED,
Packit ae235b
                           "Unexpected line '%s' while in WaitingForData state",
Packit ae235b
                           line);
Packit ae235b
              g_free (line);
Packit ae235b
            }
Packit ae235b
          goto out;
Packit ae235b
Packit ae235b
        case SERVER_STATE_WAITING_FOR_BEGIN:
Packit ae235b
          debug_print ("SERVER: WaitingForBegin");
Packit ae235b
          /* Use extremely slow (but reliable) line reader - this basically
Packit ae235b
           * does a recvfrom() system call per character
Packit ae235b
           *
Packit ae235b
           * (the problem with using GDataInputStream's read_line is that because of
Packit ae235b
           * buffering it might start reading into the first D-Bus message that
Packit ae235b
           * appears after "BEGIN\r\n"....)
Packit ae235b
           */
Packit ae235b
          line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
Packit ae235b
                                                    &line_length,
Packit ae235b
                                                    cancellable,
Packit ae235b
                                                    error);
Packit ae235b
          debug_print ("SERVER: WaitingForBegin, read '%s'", line);
Packit ae235b
          if (line == NULL)
Packit ae235b
            goto out;
Packit ae235b
          if (g_strcmp0 (line, "BEGIN") == 0)
Packit ae235b
            {
Packit ae235b
              /* YAY, done! */
Packit ae235b
              ret = TRUE;
Packit ae235b
              g_free (line);
Packit ae235b
              goto out;
Packit ae235b
            }
Packit ae235b
          else if (g_strcmp0 (line, "NEGOTIATE_UNIX_FD") == 0)
Packit ae235b
            {
Packit ae235b
              g_free (line);
Packit ae235b
              if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
Packit ae235b
                {
Packit ae235b
                  negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
Packit ae235b
                  s = "AGREE_UNIX_FD\r\n";
Packit ae235b
                  debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    goto out;
Packit ae235b
                }
Packit ae235b
              else
Packit ae235b
                {
Packit ae235b
                  s = "ERROR \"fd passing not offered\"\r\n";
Packit ae235b
                  debug_print ("SERVER: writing '%s'", s);
Packit ae235b
                  if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                    goto out;
Packit ae235b
                }
Packit ae235b
            }
Packit ae235b
          else
Packit ae235b
            {
Packit ae235b
              g_debug ("Unexpected line '%s' while in WaitingForBegin state", line);
Packit ae235b
              g_free (line);
Packit ae235b
              s = "ERROR \"Unknown Command\"\r\n";
Packit ae235b
              debug_print ("SERVER: writing '%s'", s);
Packit ae235b
              if (!g_data_output_stream_put_string (dos, s, cancellable, error))
Packit ae235b
                goto out;
Packit ae235b
            }
Packit ae235b
          break;
Packit ae235b
Packit ae235b
        default:
Packit ae235b
          g_assert_not_reached ();
Packit ae235b
          break;
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
Packit ae235b
  g_set_error_literal (error,
Packit ae235b
                       G_IO_ERROR,
Packit ae235b
                       G_IO_ERROR_FAILED,
Packit ae235b
                       "Not implemented (server)");
Packit ae235b
Packit ae235b
 out:
Packit ae235b
  if (mech != NULL)
Packit ae235b
    g_object_unref (mech);
Packit ae235b
  if (dis != NULL)
Packit ae235b
    g_object_unref (dis);
Packit ae235b
  if (dos != NULL)
Packit ae235b
    g_object_unref (dos);
Packit ae235b
Packit ae235b
  /* ensure return value is FALSE if error is set */
Packit ae235b
  if (error != NULL && *error != NULL)
Packit ae235b
    {
Packit ae235b
      ret = FALSE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (ret)
Packit ae235b
    {
Packit ae235b
      if (out_negotiated_capabilities != NULL)
Packit ae235b
        *out_negotiated_capabilities = negotiated_capabilities;
Packit ae235b
      if (out_received_credentials != NULL)
Packit ae235b
        *out_received_credentials = credentials != NULL ? g_object_ref (credentials) : NULL;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (credentials != NULL)
Packit ae235b
    g_object_unref (credentials);
Packit ae235b
Packit ae235b
  debug_print ("SERVER: Done, authenticated=%d", ret);
Packit ae235b
Packit ae235b
  return ret;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* ---------------------------------------------------------------------------------------------------- */