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); }
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."); } }
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; } }