# HG changeset patch # User Paul Aurich # Date 1259652773 0 # Node ID 4f45aae3ace1f3379ae450648e28f9fddc55cdab # Parent 554ae3e789524dcc5bf69abfe3e6736003d2c0e8 Let's try a more complex set of return states / values for auth mechs. This won't build with Cyrus support yet. diff -r 554ae3e78952 -r 4f45aae3ace1 libpurple/protocols/jabber/auth.c --- a/libpurple/protocols/jabber/auth.c Tue Dec 01 04:21:40 2009 +0000 +++ b/libpurple/protocols/jabber/auth.c Tue Dec 01 07:32:53 2009 +0000 @@ -162,8 +162,10 @@ { GSList *mechanisms = NULL; GSList *l; - xmlnode *response; + xmlnode *response = NULL; xmlnode *mechs, *mechnode; + JabberSaslState state; + const char *msg = NULL; if(js->registration) { jabber_register_start(js); @@ -214,8 +216,12 @@ return; } - response = js->auth_mech->start(js, mechs); - if (response) { + state = js->auth_mech->start(js, mechs, &response, &msg); + if (state == JABBER_SASL_STATE_FAIL) { + purple_connection_error_reason(js->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + msg ? msg : _("Unknown Error")); + } else if (response) { jabber_send(js, response); xmlnode_free(response); } @@ -413,8 +419,14 @@ } if (js->auth_mech && js->auth_mech->handle_challenge) { - xmlnode *response = js->auth_mech->handle_challenge(js, packet); - if (response != NULL) { + xmlnode *response = NULL; + const char *msg = NULL; + JabberSaslState state = js->auth_mech->handle_challenge(js, packet, &response, &msg); + if (state == JABBER_SASL_STATE_FAIL) { + purple_connection_error_reason(js->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + msg ? msg : _("Invalid challenge from server")); + } else if (response) { jabber_send(js, response); xmlnode_free(response); } @@ -433,9 +445,21 @@ return; } - if (js->auth_mech && js->auth_mech->handle_success && - !js->auth_mech->handle_success(js, packet)) { - return; + if (js->auth_mech && js->auth_mech->handle_success) { + const char *msg = NULL; + JabberSaslState state = js->auth_mech->handle_success(js, packet, &msg); + + if (state == JABBER_SASL_STATE_FAIL) { + purple_connection_error_reason(js->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + msg ? msg : _("Invalid response from server")); + return; + } else if (state == JABBER_SASL_STATE_CONTINUE) { + purple_connection_error_reason(js->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + msg ? msg : _("Server thinks authentication is complete, but client does not")); + return; + } } /* @@ -452,8 +476,11 @@ char *msg; if (js->auth_mech && js->auth_mech->handle_failure) { - xmlnode *stanza = js->auth_mech->handle_failure(js, packet); - if (stanza) { + xmlnode *stanza = NULL; + const char *msg = NULL; + JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg); + + if (state != JABBER_SASL_STATE_FAIL && stanza) { jabber_send(js, stanza); xmlnode_free(stanza); return; diff -r 554ae3e78952 -r 4f45aae3ace1 libpurple/protocols/jabber/auth.h --- a/libpurple/protocols/jabber/auth.h Tue Dec 01 04:21:40 2009 +0000 +++ b/libpurple/protocols/jabber/auth.h Tue Dec 01 07:32:53 2009 +0000 @@ -29,13 +29,19 @@ #include "jabber.h" #include "xmlnode.h" +typedef enum { + JABBER_SASL_STATE_FAIL = -1, /* Abort, Retry, Fail? */ + JABBER_SASL_STATE_OK = 0, /* Hooray! */ + JABBER_SASL_STATE_CONTINUE = 1 /* More authentication required */ +} JabberSaslState; + struct _JabberSaslMech { gint8 priority; /* Higher priority will be tried before lower priority */ const gchar *name; - xmlnode *(*start)(JabberStream *js, xmlnode *mechanisms); - xmlnode *(*handle_challenge)(JabberStream *js, xmlnode *packet); - gboolean (*handle_success)(JabberStream *js, xmlnode *packet); - xmlnode *(*handle_failure)(JabberStream *js, xmlnode *packet); + JabberSaslState (*start)(JabberStream *js, xmlnode *mechanisms, xmlnode **reply, const char **msg); + JabberSaslState (*handle_challenge)(JabberStream *js, xmlnode *packet, xmlnode **reply, const char **msg); + JabberSaslState (*handle_success)(JabberStream *js, xmlnode *packet, const char **msg); + JabberSaslState (*handle_failure)(JabberStream *js, xmlnode *packet, xmlnode **reply, const char **msg); void (*dispose)(JabberStream *js); }; diff -r 554ae3e78952 -r 4f45aae3ace1 libpurple/protocols/jabber/auth_digest_md5.c --- a/libpurple/protocols/jabber/auth_digest_md5.c Tue Dec 01 04:21:40 2009 +0000 +++ b/libpurple/protocols/jabber/auth_digest_md5.c Tue Dec 01 07:32:53 2009 +0000 @@ -30,15 +30,16 @@ #include "auth.h" #include "jabber.h" -static xmlnode *digest_md5_start(JabberStream *js, xmlnode *packet) +static JabberSaslState +digest_md5_start(JabberStream *js, xmlnode *packet, xmlnode **response, + const char **msg) { - xmlnode *auth; - - auth = xmlnode_new("auth"); + xmlnode *auth = xmlnode_new("auth"); xmlnode_set_namespace(auth, NS_XMPP_SASL); xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5"); - return auth; + *response = auth; + return JABBER_SASL_STATE_CONTINUE; } /* Parts of this algorithm are inspired by stuff in libgsasl */ @@ -163,19 +164,20 @@ return z; } -static xmlnode *digest_md5_handle_challenge(JabberStream *js, xmlnode *packet) +static JabberSaslState +digest_md5_handle_challenge(JabberStream *js, xmlnode *packet, + xmlnode **response, const char **msg) { xmlnode *reply = NULL; char *enc_in = xmlnode_get_data(packet); char *dec_in; char *enc_out; GHashTable *parts; + JabberSaslState state = JABBER_SASL_STATE_CONTINUE; if (!enc_in) { - purple_connection_error_reason(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server")); - return NULL; + *msg = _("Invalid response from server"); + return JABBER_SASL_STATE_FAIL; } dec_in = (char *)purple_base64_decode(enc_in, NULL); @@ -191,9 +193,8 @@ reply = xmlnode_new("response"); xmlnode_set_namespace(reply, NS_XMPP_SASL); } else { - purple_connection_error_reason(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid challenge from server")); + *msg = _("Invalid challenge from server"); + state = JABBER_SASL_STATE_FAIL; } g_free(js->expected_rspauth); js->expected_rspauth = NULL; @@ -216,11 +217,10 @@ if(!realm) realm = js->user->domain; - if (nonce == NULL || realm == NULL) - purple_connection_error_reason(js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid challenge from server")); - else { + if (nonce == NULL || realm == NULL) { + *msg = _("Invalid challenge from server"); + state = JABBER_SASL_STATE_FAIL; + } else { GString *response = g_string_new(""); char *a2; char *auth_resp; @@ -272,7 +272,8 @@ g_free(dec_in); g_hash_table_destroy(parts); - return reply; + *response = reply; + return state; } static JabberSaslMech digest_md5_mech = { diff -r 554ae3e78952 -r 4f45aae3ace1 libpurple/protocols/jabber/auth_plain.c --- a/libpurple/protocols/jabber/auth_plain.c Tue Dec 01 04:21:40 2009 +0000 +++ b/libpurple/protocols/jabber/auth_plain.c Tue Dec 01 07:32:53 2009 +0000 @@ -80,13 +80,16 @@ _("Server requires plaintext authentication over an unencrypted stream")); } -static xmlnode *jabber_plain_start(JabberStream *js, xmlnode *packet) +static JabberSaslState +jabber_plain_start(JabberStream *js, xmlnode *packet, xmlnode **response, const char **error) { PurpleAccount *account = purple_connection_get_account(js->gc); char *msg; - if (jabber_stream_is_ssl(js) || purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) - return finish_plaintext_authentication(js); + if (jabber_stream_is_ssl(js) || purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) { + *response = finish_plaintext_authentication(js); + return JABBER_SASL_STATE_OK; + } msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); @@ -97,7 +100,7 @@ account, NULL, NULL, account, allow_plaintext_auth, disallow_plaintext_auth); g_free(msg); - return NULL; + return JABBER_SASL_STATE_CONTINUE; } static JabberSaslMech plain_mech = { diff -r 554ae3e78952 -r 4f45aae3ace1 libpurple/protocols/jabber/auth_scram.c --- a/libpurple/protocols/jabber/auth_scram.c Tue Dec 01 04:21:40 2009 +0000 +++ b/libpurple/protocols/jabber/auth_scram.c Tue Dec 01 07:32:53 2009 +0000 @@ -341,7 +341,8 @@ return tmp2; } -static xmlnode *scram_start(JabberStream *js, xmlnode *mechanisms) +static JabberSaslState +scram_start(JabberStream *js, xmlnode *mechanisms, xmlnode **out, const char **error) { xmlnode *reply; JabberScramData *data; @@ -355,8 +356,8 @@ prepped_node = jabber_saslprep(js->user->node); if (!prepped_node) { - /* TODO: Error handling in the response value from scram_start */ - return NULL; + *error = _("Unable to canonicalize username"); + return JABBER_SASL_STATE_FAIL; } tmp = escape_username(prepped_node); @@ -366,7 +367,8 @@ prepped_pass = jabber_saslprep(purple_connection_get_password(js->gc)); if (!prepped_pass) { g_free(prepped_node); - return NULL; + *error = _("Unable to canonicalize password"); + return JABBER_SASL_STATE_FAIL; } data = js->auth_mech_data = g_new0(JabberScramData, 1); @@ -401,22 +403,26 @@ g_free(enc_out); g_free(dec_out); - return reply; + *out = reply; + return JABBER_SASL_STATE_CONTINUE; } -static xmlnode *scram_handle_challenge(JabberStream *js, xmlnode *challenge) +static JabberSaslState +scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, const char **error) { JabberScramData *data = js->auth_mech_data; xmlnode *reply; gchar *enc_in, *dec_in; gchar *enc_out = NULL, *dec_out = NULL; gsize len; + JabberSaslState state = JABBER_SASL_STATE_FAIL; enc_in = xmlnode_get_data(challenge); if (!enc_in || *enc_in == '\0') { reply = xmlnode_new("abort"); xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; + *error = _("Invalid challenge from server"); goto out; } @@ -427,6 +433,7 @@ reply = xmlnode_new("abort"); xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; + *error = _("Malicious challenge from server"); goto out; } @@ -436,6 +443,7 @@ reply = xmlnode_new("abort"); xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; + *error = _("Invalid challenge from server"); goto out; } @@ -450,14 +458,18 @@ xmlnode_insert_data(reply, enc_out, -1); } + state = JABBER_SASL_STATE_CONTINUE; + out: g_free(enc_out); g_free(dec_out); - return reply; + *out = reply; + return state; } -static gboolean scram_handle_success(JabberStream *js, xmlnode *packet) +static JabberSaslState +scram_handle_success(JabberStream *js, xmlnode *packet, const char **error) { JabberScramData *data = js->auth_mech_data; char *enc_in, *dec_in; @@ -468,28 +480,32 @@ g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', FALSE); if (data->step == 3) - return TRUE; + return JABBER_SASL_STATE_OK; - if (data->step != 2) - return FALSE; + if (data->step != 2) { + *error = _("Unexpected response from server"); + return JABBER_SASL_STATE_FAIL; + } dec_in = (gchar *)purple_base64_decode(enc_in, &len); g_free(enc_in); if (!dec_in || len != strlen(dec_in)) { /* Danger afoot; SCRAM shouldn't contain NUL bytes */ g_free(dec_in); - return FALSE; + *error = _("Invalid challenge from server"); + return JABBER_SASL_STATE_FAIL; } purple_debug_misc("jabber", "decoded success: %s\n", dec_in); if (!jabber_scram_feed_parser(data, dec_in, &dec_out) || dec_out != NULL) { g_free(dec_out); - return FALSE; + *error = _("Invalid challenge from server"); + return JABBER_SASL_STATE_FAIL; } /* Hooray */ - return TRUE; + return JABBER_SASL_STATE_OK; } void jabber_scram_data_destroy(JabberScramData *data)