static void
gabble_server_sasl_channel_respond (
    TpSvcChannelInterfaceSASLAuthentication *channel,
    const GArray *in_Response_Data,
    DBusGMethodInvocation *context)
{
  GabbleServerSaslChannel *self =
    GABBLE_SERVER_SASL_CHANNEL (channel);
  GString *response_data;
  GSimpleAsyncResult *r = self->priv->result;

  if (self->priv->sasl_status != TP_SASL_STATUS_IN_PROGRESS)
    {
      gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE,
          "You can only respond to challenges in state In_Progress, not %u",
          self->priv->sasl_status);
      DEBUG ("cannot respond: state %u != In_Progress",
          self->priv->sasl_status);
      return;
    }

  if (r == NULL)
    {
      gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE,
          "You already responded to the most recent challenge");
      DEBUG ("cannot respond: already responded");
      return;
    }

  g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (r),
        G_OBJECT (self), gabble_server_sasl_channel_challenge_async));

  /* The response might be secret (for PLAIN etc.), and also might
   * not be UTF-8 or even text, so we just output the length */
  DEBUG ("responding with %u bytes", in_Response_Data->len);

  self->priv->result = NULL;

  if (in_Response_Data->len > 0)
    response_data = g_string_new_len (in_Response_Data->data,
        in_Response_Data->len);
  else
    response_data = NULL;

  g_simple_async_result_set_op_res_gpointer (r, response_data,
      (GDestroyNotify) wocky_g_string_free);

  g_simple_async_result_complete_in_idle (r);
  g_object_unref (r);

  tp_svc_channel_interface_sasl_authentication_return_from_respond (
      context);
}
示例#2
0
static void
gabble_auth_manager_start_auth_cb (GObject *channel,
    GAsyncResult *result,
    gpointer user_data)
{
  GObject *self_object = g_async_result_get_source_object (user_data);
  GabbleAuthManager *self = GABBLE_AUTH_MANAGER (self_object);
  WockyAuthRegistryStartData *start_data = NULL;
  GError *error = NULL;

  if (gabble_server_sasl_channel_start_auth_finish (
        GABBLE_SERVER_SASL_CHANNEL (channel), result, &start_data, &error))
    {
      if (!tp_strdiff (start_data->mechanism, X_TELEPATHY_PASSWORD))
        {
          /* restart authentication using our own base class */
          g_assert (start_data->initial_response != NULL);

          self->priv->falling_back = TRUE;
          WOCKY_AUTH_REGISTRY_CLASS (
              gabble_auth_manager_parent_class)->start_auth_async_func (
                  WOCKY_AUTH_REGISTRY (self), self->priv->mechanisms,
                  self->priv->allow_plain, self->priv->is_secure_channel,
                  self->priv->username,
                  start_data->initial_response->str,
                  self->priv->server,
                  self->priv->session_id,
                  gabble_auth_manager_start_fallback_cb, user_data);
          /* we've transferred ownership of the result */
          goto finally;
        }
      else
        {
          g_simple_async_result_set_op_res_gpointer (user_data, start_data,
              (GDestroyNotify) wocky_auth_registry_start_data_free);
        }
    }
  else
    {
      g_simple_async_result_set_from_error (user_data, error);
      g_clear_error (&error);
    }

  g_simple_async_result_complete (user_data);
  g_object_unref (user_data);
finally:
  g_object_unref (self_object);
}
static void
gabble_server_sasl_channel_finalize (GObject *object)
{
  GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (object);
  GabbleServerSaslChannelPrivate *priv = self->priv;

  /* a ref is held for the channel's lifetime */
  g_assert (tp_base_channel_is_destroyed ((TpBaseChannel *) self));
  g_assert (priv->result == NULL);

  g_strfreev (priv->available_mechanisms);

  g_free (priv->sasl_error);
  g_hash_table_unref (priv->sasl_error_details);

  if (G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->finalize)
    G_OBJECT_CLASS (gabble_server_sasl_channel_parent_class)->finalize (object);
}
GabbleServerSaslChannel *
gabble_server_sasl_channel_new (GabbleConnection *conn,
    GStrv available_mechanisms,
    gboolean secure,
    const gchar *session_id)
{
  GabbleServerSaslChannel *obj;

  g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL);

  obj = GABBLE_SERVER_SASL_CHANNEL (
      g_object_new (GABBLE_TYPE_SERVER_SASL_CHANNEL,
        "connection", conn,
        "available-mechanisms", available_mechanisms,
        "secure", secure,
        NULL));

  return obj;
}
static void
gabble_server_sasl_channel_close (TpBaseChannel *channel)
{
  GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel);
  GabbleServerSaslChannelPrivate *priv = self->priv;

  DEBUG ("called on %p", self);

  if (priv->result != NULL)
    {
      GSimpleAsyncResult *r = priv->result;

      DEBUG ("closed channel");

      priv->result = NULL;
      g_simple_async_result_set_error (r, WOCKY_AUTH_ERROR,
          WOCKY_AUTH_ERROR_FAILURE,
          "%s", "Client aborted authentication.");
      g_simple_async_result_complete_in_idle (r);
      g_object_unref (r);
    }

  tp_base_channel_destroyed (channel);
}
static void
gabble_server_sasl_channel_set_property (GObject *object,
                                   guint property_id,
                                   const GValue *value,
                                   GParamSpec *pspec)
{
  GabbleServerSaslChannel *chan = GABBLE_SERVER_SASL_CHANNEL (object);
  GabbleServerSaslChannelPrivate *priv = chan->priv;

  switch (property_id)
    {
      case PROP_SECURE:
        priv->secure = g_value_get_boolean (value);
        break;

      case PROP_AVAILABLE_MECHANISMS:
        priv->available_mechanisms = g_value_dup_boxed (value);
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        break;
    }
}
static void
gabble_server_sasl_channel_abort_sasl (
    TpSvcChannelInterfaceSASLAuthentication *channel,
    guint in_Reason,
    const gchar *in_Debug_Message,
    DBusGMethodInvocation *context)
{
  GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel);
  GSimpleAsyncResult *r = self->priv->result;
  guint code;
  const gchar *dbus_error;

  switch (self->priv->sasl_status)
    {
      case TP_SASL_STATUS_SERVER_FAILED:
      case TP_SASL_STATUS_CLIENT_FAILED:
        DEBUG ("ignoring attempt to abort: we already failed");
        break;

      case TP_SASL_STATUS_SUCCEEDED:
      case TP_SASL_STATUS_CLIENT_ACCEPTED:
        DEBUG ("cannot abort: client already called AcceptSASL");
        gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE,
            "Authentication has already succeeded - too late to abort");
        return;

      case TP_SASL_STATUS_NOT_STARTED:
      case TP_SASL_STATUS_IN_PROGRESS:
      case TP_SASL_STATUS_SERVER_SUCCEEDED:
        switch (in_Reason)
          {
            case TP_SASL_ABORT_REASON_INVALID_CHALLENGE:
              DEBUG ("invalid challenge (%s)", in_Debug_Message);
              code = WOCKY_AUTH_ERROR_INVALID_REPLY;
              dbus_error = TP_ERROR_STR_SERVICE_CONFUSED;
              break;

            case TP_SASL_ABORT_REASON_USER_ABORT:
              DEBUG ("user aborted auth (%s)", in_Debug_Message);
              code = WOCKY_AUTH_ERROR_FAILURE;
              dbus_error = TP_ERROR_STR_CANCELLED;
              break;

            default:
              DEBUG ("unknown reason code %u, treating as User_Abort (%s)",
                  in_Reason, in_Debug_Message);
              code = WOCKY_AUTH_ERROR_FAILURE;
              dbus_error = TP_ERROR_STR_CANCELLED;
              break;
          }

        if (r != NULL)
          {
            self->priv->result = NULL;

            /* If Not_Started, we're returning failure from start_auth_async.
             * If In_Progress, we might be returning failure from
             *  challenge_async, if one is outstanding.
             * If Server_Succeeded, we're returning failure from success_async.
             */

            g_simple_async_result_set_error (r, WOCKY_AUTH_ERROR, code,
                "Authentication aborted: %s", in_Debug_Message);

            g_simple_async_result_complete_in_idle (r);
            g_object_unref (r);
          }

        change_current_state (self, TP_SASL_STATUS_CLIENT_FAILED,
            dbus_error, in_Debug_Message);
        break;

      default:
        g_assert_not_reached ();
    }

  tp_svc_channel_interface_sasl_authentication_return_from_abort_sasl (
      context);
}
static void
gabble_server_sasl_channel_accept_sasl (
    TpSvcChannelInterfaceSASLAuthentication *channel,
    DBusGMethodInvocation *context)
{
  GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (channel);
  GSimpleAsyncResult *r = self->priv->result;
  const gchar *message = NULL;


  switch (self->priv->sasl_status)
    {
    case TP_SASL_STATUS_NOT_STARTED:
      message = "Authentication has not yet begun (Not_Started)";
      break;

    case TP_SASL_STATUS_IN_PROGRESS:
      /* In this state, the only valid time to call this method is in response
       * to a challenge, to indicate that, actually, that challenge was
       * additional data for a successful authentication. */
      if (r == NULL)
        {
          message = "In_Progress, but you already responded to the last "
            "challenge";
        }
      else
        {
          DEBUG ("client says the last challenge was actually final data "
              "and has accepted it");
          g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (r),
                G_OBJECT (self), gabble_server_sasl_channel_challenge_async));
          change_current_state (self, TP_SASL_STATUS_CLIENT_ACCEPTED, NULL,
              NULL);
        }
      break;

    case TP_SASL_STATUS_SERVER_SUCCEEDED:
      /* The server has already said yes, and the caller is waiting for
       * success_async(), i.e. waiting for the UI to check whether it's
       * happy too. AcceptSASL means that it is. */
      DEBUG ("client has accepted server's success");
      g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (r),
            G_OBJECT (self), gabble_server_sasl_channel_success_async));
      change_current_state (self, TP_SASL_STATUS_SUCCEEDED, NULL, NULL);
      break;

    case TP_SASL_STATUS_CLIENT_ACCEPTED:
      message = "Client already accepted authentication (Client_Accepted)";
      break;

    case TP_SASL_STATUS_SUCCEEDED:
      message = "Authentication already succeeded (Succeeded)";
      break;

    case TP_SASL_STATUS_SERVER_FAILED:
      message = "Authentication has already failed (Server_Failed)";
      break;

    case TP_SASL_STATUS_CLIENT_FAILED:
      message = "Authentication has already been aborted (Client_Failed)";
      break;

    default:
      g_assert_not_reached ();
    }

  if (message != NULL)
    {
      DEBUG ("cannot accept SASL: %s", message);
      gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE,
          "%s", message);
      return;
    }

  if (r != NULL)
    {
      /* This is a bit weird - this code is run for two different async
       * results. In the In_Progress case, this code results in
       * success with the GSimpleAsyncResult's op_res left as NULL, which
       * is what Wocky wants for an empty response. In the Server_Succeeded
       * response, the async result is just success or error - we succeed. */
      self->priv->result = NULL;

      /* We want want to complete not in an idle because if we do we
       * will hit fd.o#32278. This is safe because we're being called
       * from dbus-glib in the main loop. */
      g_simple_async_result_complete (r);
      g_object_unref (r);
    }

  tp_svc_channel_interface_sasl_authentication_return_from_accept_sasl (
      context);
}
/* When called from start_mechanism, initial_data can be NULL. When called
 * from D-Bus as StartMechanismWithData, it can't. */
static void
gabble_server_sasl_channel_start_mechanism_with_data (
    TpSvcChannelInterfaceSASLAuthentication *iface,
    const gchar *in_Mechanism,
    const GArray *in_InitialData,
    DBusGMethodInvocation *context)
{
  GabbleServerSaslChannel *self = GABBLE_SERVER_SASL_CHANNEL (iface);
  GabbleServerSaslChannelPrivate *priv = self->priv;
  WockyAuthRegistryStartData *start_data;
  GSimpleAsyncResult *r = priv->result;
  GString *initial_data = NULL;

  if (self->priv->sasl_status != TP_SASL_STATUS_NOT_STARTED)
    {
      gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_AVAILABLE,
          "Mechanisms can only be started in state Not_Started, not %u",
          self->priv->sasl_status);
      DEBUG ("cannot start: state %u != Not_Started", self->priv->sasl_status);
      return;
    }

  /* NotStarted state is entered by creating the channel: the caller must
   * call start_auth_async immediately */
  g_assert (r != NULL);
  g_assert (g_simple_async_result_is_valid (G_ASYNC_RESULT (r),
        G_OBJECT (self), gabble_server_sasl_channel_start_auth_async));

  if (tp_strv_contains ((const gchar * const *) priv->available_mechanisms,
        in_Mechanism))
    {
      priv->result = NULL;

      if (in_InitialData != NULL)
        {
          /* The initial data might be secret (for PLAIN etc.), and also might
           * not be UTF-8 or even text, so we just output the length */
          DEBUG ("Starting %s authentication with %u bytes of initial data",
              in_Mechanism, in_InitialData->len);
          initial_data = g_string_new_len (in_InitialData->data,
              in_InitialData->len);
        }
      else
        {
          DEBUG ("Starting %s authentication without initial data",
              in_Mechanism);
        }

      change_current_state (self, TP_SASL_STATUS_IN_PROGRESS, NULL, NULL);
      dbus_g_method_return (context);

      start_data =
        wocky_auth_registry_start_data_new (in_Mechanism, initial_data);

      g_simple_async_result_set_op_res_gpointer (r,
          start_data, (GDestroyNotify) wocky_auth_registry_start_data_free);
      g_simple_async_result_complete_in_idle (r);
      g_object_unref (r);

      if (initial_data != NULL)
        g_string_free (initial_data, TRUE);
    }
  else
    {
      DEBUG ("cannot start: %s is not a supported mechanism", in_Mechanism);
      gabble_server_sasl_channel_raise (context, TP_ERROR_NOT_IMPLEMENTED,
          "Selected mechanism is not available.");
    }
}
示例#10
0
static void
gabble_server_sasl_channel_get_property (GObject *object,
    guint property_id,
    GValue *value,
    GParamSpec *pspec)
{
  TpBaseChannel *channel = TP_BASE_CHANNEL (object);
  GabbleServerSaslChannel *self =
    GABBLE_SERVER_SASL_CHANNEL (object);
  GabbleServerSaslChannelPrivate *priv = self->priv;

  switch (property_id)
    {
    case PROP_SASL_STATUS:
      g_value_set_uint (value, priv->sasl_status);
      break;
    case PROP_SASL_ERROR:
      g_value_set_string (value, priv->sasl_error);
      break;
    case PROP_SASL_ERROR_DETAILS:
      g_value_set_boxed (value, priv->sasl_error_details);
      break;
    case PROP_AUTH_METHOD:
      g_value_set_static_string (value,
          TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION);
      break;
    case PROP_AVAILABLE_MECHANISMS:
      g_value_set_boxed (value, priv->available_mechanisms);
      break;
    case PROP_SECURE:
      g_value_set_boolean (value, priv->secure);
      break;
    case PROP_CAN_TRY_AGAIN:
      /* Wocky can't retry SASL authentication (although XMPP can) */
      g_value_set_boolean (value, FALSE);
      break;
    case PROP_HAS_INITIAL_DATA:
      /* Yes, XMPP has "initial data" in its SASL */
      g_value_set_boolean (value, TRUE);
      break;
    case PROP_AUTHORIZATION_IDENTITY:
      /* As per RFC 3920, the authorization identity for c2s connections
       * is the desired JID. We can't use conn_util_get_bare_self_jid at
       * this stage of the connection process, because it hasn't been
       * initialized yet. */
        {
          gchar *jid, *username, *stream_server;

          g_object_get (tp_base_channel_get_connection (channel),
              "username", &username,
              "stream-server", &stream_server,
              NULL);
          jid = g_strconcat (username, "@", stream_server, NULL);
          g_free (username);
          g_free (stream_server);

          g_value_take_string (value, jid);
        }
      break;
    case PROP_DEFAULT_REALM:
      /* Like WockySaslDigestMd5, we use the stream server as the default
       * realm, for interoperability with servers that fail to supply a
       * realm but expect us to have this default. */
      g_object_get_property (
          G_OBJECT (tp_base_channel_get_connection (channel)), "stream-server",
          value);
      break;
    case PROP_DEFAULT_USERNAME:
      /* In practice, XMPP servers normally want us to authenticate as the
       * local-part of the JID. */
      g_object_get_property (
          G_OBJECT (tp_base_channel_get_connection (channel)), "username",
          value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}