cpw.darkrain42.xmpp.scram: a046e925: jabber: Handle the case where the server...
darkrain42 at pidgin.im
darkrain42 at pidgin.im
Wed Nov 11 15:35:55 EST 2009
-----------------------------------------------------------------
Revision: a046e925c84f1af65cc0bf41cc8ce55ba7ff3213
Ancestor: 25c0efe856fa80c7cff2cce110ae7f7e02524443
Author: darkrain42 at pidgin.im
Date: 2009-11-11T20:32:09
Branch: im.pidgin.cpw.darkrain42.xmpp.scram
URL: http://d.pidgin.im/viewmtn/revision/info/a046e925c84f1af65cc0bf41cc8ce55ba7ff3213
Modified files:
libpurple/protocols/jabber/auth_scram.c
libpurple/protocols/jabber/auth_scram.h
libpurple/tests/test_jabber_scram.c
ChangeLog:
jabber: Handle the case where the server success-with-data is sent as a challenge/response pair.
This should also make it easier to feed C/R pairs via the tester.
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/auth_scram.c 864bcebf6ada20e0ed5419a9bf07c296ea115153
+++ libpurple/protocols/jabber/auth_scram.c 88cf4d9e638ba870b6fb3c49bd909008e9e6c34e
@@ -160,13 +160,12 @@ gboolean
}
gboolean
-jabber_scram_calc_proofs(JabberScramData *data, const char *password,
- GString *salt, guint iterations)
+jabber_scram_calc_proofs(JabberScramData *data, GString *salt, guint iterations)
{
guint hash_len = hash_to_output_len(data->hash);
guint i;
- GString *pass = g_string_new(password);
+ GString *pass = g_string_new(data->password);
guchar *salted_password;
guchar client_key[hash_len];
@@ -206,124 +205,153 @@ static gboolean
}
static gboolean
-parse_challenge(JabberScramData *data, const char *challenge,
- gchar **out_nonce, GString **out_salt, guint *out_iterations)
+parse_server_step1(JabberScramData *data, const char *challenge,
+ gchar **out_nonce, GString **out_salt, guint *out_iterations)
{
- gsize cnonce_len;
- const char *cur;
- const char *end;
- const char *val_start, *val_end;
- char *tmp, *decoded;
- gsize decoded_len;
- char *nonce;
- GString *salt;
+ char **tokens;
+ char *token, *decoded, *tmp;
+ gsize len;
+ char *nonce = NULL;
+ GString *salt = NULL;
guint iterations;
- cur = challenge;
- end = challenge + strlen(challenge);
-
- if (cur[0] != 'r' || cur[1] != '=')
+ tokens = g_strsplit(challenge, ",", -1);
+ if (tokens == NULL)
return FALSE;
- val_start = cur + 2;
- val_end = strchr(val_start, ',');
- if (val_end == NULL)
- return FALSE;
+ token = tokens[0];
+ if (token[0] != 'r' || token[1] != '=')
+ goto err;
/* Ensure that the first cnonce_len bytes of the nonce are the original
* cnonce we sent to the server.
*/
- cnonce_len = strlen(data->cnonce);
- if ((val_end - val_start + 1) <= cnonce_len ||
- strncmp(data->cnonce, val_start, cnonce_len) != 0)
- return FALSE;
+ if (!g_str_equal(data->cnonce, token + 2))
+ goto err;
- nonce = g_strndup(val_start, val_end - val_start + 1);
+ nonce = g_strdup(token + 2);
/* The Salt, base64-encoded */
- cur = val_end + 1;
- if (cur[0] != 's' || cur[1] != '=') {
- g_free(nonce);
- return FALSE;
- }
+ token = tokens[1];
+ if (token[0] != 's' || token[1] != '=')
+ goto err;
- val_start = cur + 2;
- val_end = strchr(val_start, ',');
- if (val_end == NULL) {
- g_free(nonce);
- return FALSE;
+ decoded = (gchar *)purple_base64_decode(token + 2, &len);
+ if (!decoded || *decoded == '\0') {
+ g_free(decoded);
+ goto err;
}
-
- tmp = g_strndup(val_start, val_end - val_start + 1);
- decoded = (gchar *)purple_base64_decode(tmp, &decoded_len);
- g_free(tmp);
- salt = g_string_new_len(decoded, decoded_len);
+ salt = g_string_new_len(decoded, len);
g_free(decoded);
/* The iteration count */
- cur = val_end + 1;
- if (cur[0] != 'i' || cur[1] != '=') {
- g_free(nonce);
- g_string_free(salt, TRUE);
- return FALSE;
- }
+ token = tokens[2];
+ if (token[0] != 'i' || token[1] != '=' || token[2] == '\0')
+ goto err;
- val_start = cur + 2;
- val_end = strchr(val_start, ',');
- if (val_end == NULL)
- /* There could be extensions. This should possibly be a hard fail. */
- val_end = end - 1;
-
/* Validate the string */
- for (tmp = (gchar *)val_start; tmp != val_end; ++tmp) {
- if (!g_ascii_isdigit(*tmp)) {
- g_free(nonce);
- g_string_free(salt, TRUE);
- return FALSE;
- }
- }
+ for (tmp = token + 2; *tmp; ++tmp)
+ if (!g_ascii_isdigit(*tmp))
+ goto err;
- tmp = g_strndup(val_start, val_end - val_start + 1);
- iterations = strtoul(tmp, NULL, 10);
- g_free(tmp);
+ iterations = strtoul(token + 2, NULL, 10);
+ g_strfreev(tokens);
*out_nonce = nonce;
*out_salt = salt;
*out_iterations = iterations;
return TRUE;
+
+err:
+ g_free(nonce);
+ g_string_free(salt, TRUE);
+ g_strfreev(tokens);
+ return FALSE;
}
static gboolean
-parse_success(JabberScramData *data, const char *success,
- gchar **out_verifier)
+parse_server_step2(JabberScramData *data, const char *challenge, gchar **out_verifier)
{
- const char *cur;
- const char *val_start, *val_end;
- const char *end;
+ char **tokens;
+ char *token;
- char *verifier;
+ tokens = g_strsplit(challenge, ",", -1);
+ if (tokens == NULL)
+ return FALSE;
+ token = tokens[0];
+ if (token[0] != 'v' || token[1] != '=' || token[2] == '\0') {
+ g_strfreev(tokens);
+ return FALSE;
+ }
+
+ *out_verifier = g_strdup(token + 2);
+ g_strfreev(tokens);
+ return TRUE;
+}
+
+static gboolean
+feed_parser(JabberScramData *data, gchar *in, gchar **out)
+{
+ gboolean ret;
+
g_return_val_if_fail(data != NULL, FALSE);
- g_return_val_if_fail(success != NULL, FALSE);
- g_return_val_if_fail(out_verifier != NULL, FALSE);
- cur = success;
- end = cur + strlen(cur);
+ g_string_append_c(data->auth_message, ',');
+ g_string_append(data->auth_message, in);
- if (cur[0] != 'v' || cur[1] != '=') {
- /* TODO: Error handling */
- return FALSE;
- }
+ if (data->step == 1) {
+ gchar *nonce, *proof;
+ GString *salt;
+ guint iterations;
- val_start = cur + 2;
- val_end = strchr(val_start, ',');
- if (val_end == NULL)
- /* TODO: Maybe make this a strict check on not having any extensions? */
- val_end = end - 1;
+ ret = parse_server_step1(data, in, &nonce, &salt, &iterations);
+ if (!ret)
+ return FALSE;
- verifier = g_strndup(val_start, val_end - val_start + 1);
+ g_string_append_c(data->auth_message, ',');
- *out_verifier = verifier;
+ /* "biwsCg==" is the base64 encoding of "n,,". I promise. */
+ g_string_append_printf(data->auth_message, "c=%s,r=%s", "biwsCg==", nonce);
+#ifdef CHANNEL_BINDING
+#error fix this
+#endif
+
+ ret = jabber_scram_calc_proofs(data, salt, iterations);
+ if (!ret)
+ return FALSE;
+
+ proof = purple_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
+ *out = g_strdup_printf("c=%s,r=%s,p=%s", "biwsCg==", nonce, proof);
+ g_free(proof);
+ } else if (data->step == 2) {
+ gchar *server_sig, *enc_server_sig;
+ gsize len;
+
+ ret = parse_server_step2(data, in, &enc_server_sig);
+ if (!ret)
+ return FALSE;
+
+ server_sig = (gchar *)purple_base64_decode(enc_server_sig, &len);
+ g_free(enc_server_sig);
+
+ if (server_sig == NULL || len != data->server_signature->len) {
+ g_free(server_sig);
+ return FALSE;
+ }
+
+ if (0 != memcmp(server_sig, data->server_signature->str, len)) {
+ g_free(server_sig);
+ return FALSE;
+ }
+ g_free(server_sig);
+
+ *out = NULL;
+ } else {
+ purple_debug_error("jabber", "SCRAM: There is no step %d\n", data->step);
+ return FALSE;
+ }
+
return TRUE;
}
@@ -339,6 +367,7 @@ static xmlnode *scram_start(JabberStream
data = js->auth_mech_data = g_new0(JabberScramData, 1);
data->hash = mech_to_hash(js->auth_mech->name);
+ data->password = purple_connection_get_password(js->gc);
#ifdef CHANNEL_BINDING
if (strstr(js->auth_mech_name, "-PLUS"))
@@ -352,6 +381,8 @@ static xmlnode *scram_start(JabberStream
js->user->node /* TODO: SaslPrep */,
data->cnonce);
+ data->step = 1;
+
reply = xmlnode_new("auth");
xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name);
@@ -373,70 +404,51 @@ static xmlnode *scram_handle_challenge(J
{
JabberScramData *data = js->auth_mech_data;
xmlnode *reply;
-
gchar *enc_in, *dec_in;
- gchar *enc_out, *dec_out;
- gsize decoded_size;
+ gchar *enc_out = NULL, *dec_out = NULL;
+ gsize len;
- gchar *enc_proof;
-
- gchar *nonce;
- GString *salt;
- guint iterations;
-
- g_return_val_if_fail(data != NULL, NULL);
-
enc_in = xmlnode_get_data(challenge);
- /* TODO: Error handling */
- g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', NULL);
+ if (!enc_in || *enc_in == '\0') {
+ reply = xmlnode_new("abort");
+ xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
+ data->step = -1;
+ goto out;
+ }
- dec_in = (gchar *)purple_base64_decode(enc_in, &decoded_size);
+ dec_in = (gchar *)purple_base64_decode(enc_in, &len);
g_free(enc_in);
- if (!dec_in || decoded_size != strlen(dec_in)) {
+ if (!dec_in || len != strlen(dec_in)) {
/* Danger afoot; SCRAM shouldn't contain NUL bytes */
- /* TODO: Error handling */
- g_free(dec_in);
- return NULL;
+ reply = xmlnode_new("abort");
+ xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
+ data->step = -1;
+ goto out;
}
- purple_debug_misc("jabber", "decoded challenge (%" G_GSIZE_FORMAT "): %s\n",
- decoded_size, dec_in);
+ purple_debug_misc("jabber", "decoded challenge: %s\n", dec_in);
- g_string_append_c(data->auth_message, ',');
- g_string_append(data->auth_message, dec_in);
-
- if (!parse_challenge(data, dec_in, &nonce, &salt, &iterations)) {
- /* TODO: Error handling */
- return NULL;
+ if (!feed_parser(data, dec_in, &dec_out)) {
+ reply = xmlnode_new("abort");
+ xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
+ data->step = -1;
+ goto out;
}
- g_string_append_c(data->auth_message, ',');
- /* "biwsCg==" is the base64 encoding of "n,,". I promise. */
- g_string_append_printf(data->auth_message, "c=%s,r=%s", "biwsCg==", nonce);
-#ifdef CHANNEL_BINDING
- #error fix this
-#endif
+ data->step += 1;
- if (!jabber_scram_calc_proofs(data, purple_connection_get_password(js->gc), salt, iterations)) {
- /* TODO: Error handling */
- return NULL;
- }
-
reply = xmlnode_new("response");
xmlnode_set_namespace(reply, "urn:ietf:params:xml:ns:xmpp-sasl");
- enc_proof = purple_base64_encode((guchar *)data->client_proof->str, data->client_proof->len);
- dec_out = g_strdup_printf("c=%s,r=%s,p=%s", "biwsCg==", nonce, enc_proof);
- enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
+ purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)");
+ if (dec_out) {
+ enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out));
+ xmlnode_insert_data(reply, enc_out, -1);
+ }
- purple_debug_misc("jabber", "decoded response (%" G_GSIZE_FORMAT "): %s\n",
- strlen(dec_out), dec_out);
-
- xmlnode_insert_data(reply, enc_out, -1);
-
+out:
g_free(enc_out);
g_free(dec_out);
- g_free(enc_proof);
return reply;
}
@@ -445,48 +457,30 @@ static gboolean scram_handle_success(Jab
{
JabberScramData *data = js->auth_mech_data;
char *enc_in, *dec_in;
- char *enc_server_signature;
- guchar *server_signature;
- gsize decoded_size;
+ char *dec_out = NULL;
+ gsize len;
enc_in = xmlnode_get_data(packet);
- /* TODO: Error handling */
g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', FALSE);
- dec_in = (gchar *)purple_base64_decode(enc_in, &decoded_size);
- g_free(enc_in);
- if (!dec_in || decoded_size != strlen(dec_in)) {
- /* Danger afoot; SCRAM shouldn't contain NUL bytes */
- /* TODO: Error handling */
- g_free(dec_in);
- return FALSE;
- }
+ if (data->step == 3)
+ return TRUE;
- purple_debug_misc("jabber", "decoded success (%" G_GSIZE_FORMAT "): %s\n",
- decoded_size, dec_in);
-
- if (!parse_success(data, dec_in, &enc_server_signature)) {
- /* TODO: Error handling */
+ if (data->step != 2)
return FALSE;
- }
- server_signature = purple_base64_decode(enc_server_signature, &decoded_size);
- if (server_signature == NULL) {
- /* TODO: Error handling */
+ 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;
}
- if (decoded_size != data->server_signature->len) {
- /* TODO: Error handling */
- purple_debug_error("jabber", "SCRAM server signature wrong length (was "
- "(was %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT ")\n",
- decoded_size, data->server_signature->len);
- return FALSE;
- }
+ purple_debug_misc("jabber", "decoded success: %s\n", dec_in);
- if (0 != memcmp(server_signature, data->server_signature->str, decoded_size)) {
- /* TODO: Error handling */
- purple_debug_error("jabber", "SCRAM server signature did not match!\n");
+ if (!feed_parser(data, dec_in, &dec_out) || dec_out != NULL) {
+ g_free(dec_out);
return FALSE;
}
============================================================
--- libpurple/protocols/jabber/auth_scram.h c2d2a43170a4e75c9a4420f4eb4075ef262326fc
+++ libpurple/protocols/jabber/auth_scram.h 7ad94b01a66b2f4bb2502911ea9b2f8771209366
@@ -40,7 +40,10 @@ typedef struct {
GString *client_proof;
GString *server_signature;
+
+ const gchar *password;
gboolean channel_binding;
+ int step;
} JabberScramData;
#include "auth.h"
@@ -70,13 +73,12 @@ guchar *jabber_scram_hi(const char *hash
* @param data A JabberScramData structure. hash and auth_message must be
* set. client_proof and server_signature will be set as a result
* of this function.
- * @param password The user's password.
* @param salt The salt (as specified by the server)
* @param iterations The number of iterations to perform.
*
* @returns TRUE if the proofs were successfully calculated. FALSE otherwise.
*/
-gboolean jabber_scram_calc_proofs(JabberScramData *data, const char *password,
- GString *salt, guint iterations);
+gboolean jabber_scram_calc_proofs(JabberScramData *data, GString *salt,
+ guint iterations);
#endif /* PURPLE_JABBER_AUTH_SCRAM_H_ */
============================================================
--- libpurple/tests/test_jabber_scram.c e1ac9975df228f6315ab555cabc8f6e462b3919c
+++ libpurple/tests/test_jabber_scram.c 9270cca3f1c1479943600432c715c77f0e02c47b
@@ -38,13 +38,14 @@ START_TEST(test_proofs)
/* const char *server_signature; */
data->hash = "sha1";
+ data->password = "password";
data->auth_message = g_string_new("n=username at jabber.org,r=8jLxB5515dhFxBil5A0xSXMH,"
"r=8jLxB5515dhFxBil5A0xSXMHabc,s=c2FsdA==,i=1,"
"c=biws,r=8jLxB5515dhFxBil5A0xSXMHabc");
client_proof = "\x48\x61\x30\xa5\x61\x0b\xae\xb9\xe4\x11\xa8\xfd\xa5\xcd\x34\x1d\x8a\x3c\x28\x17";
salt = g_string_new("salt");
- ret = jabber_scram_calc_proofs(data, "password", salt, 1);
+ ret = jabber_scram_calc_proofs(data, salt, 1);
fail_if(ret == FALSE, "Failed to calculate SCRAM proofs!");
fail_unless(0 == memcmp(client_proof, data->client_proof->str, 20));
More information about the Commits
mailing list