--- a/libpurple/protocols/yahoo/yahoo.c Fri Jun 19 00:19:03 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Fri Jun 19 00:56:37 2009 +0000 @@ -42,8 +42,6 @@ #include "yahoo.h" #include "yahoochat.h" #include "yahoo_aliases.h" -#include "yahoo_auth.h" -#include "yahoo_crypt.h" #include "yahoo_doodle.h" #include "yahoo_filexfer.h" #include "yahoo_friend.h" @@ -1340,11 +1338,19 @@ NULL, NULL); } } + +/* We use this structure once while we authenticate */ +struct yahoo_auth_data +{ + PurpleConnection *gc; + char *seed; +}; + /* This is the y64 alphabet... it's like base64, but has a . and a _ */ static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; -/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function - * in util.c, but it has a bug I don't feel like finding right now ;) */ +/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function + * in util.c, but it is different from the one yahoo uses */ static void to_y64(char *out, const unsigned char *in, gsize inlen) /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ { @@ -1371,605 +1377,261 @@ *out = '\0'; } -static void yahoo_process_auth_old(PurpleConnection *gc, const char *seed) +static void yahoo_auth16_stage3(PurpleConnection *gc, char *crypt) { - struct yahoo_packet *pack; + struct yahoo_data *yd = gc->proto_data; PurpleAccount *account = purple_connection_get_account(gc); const char *name = purple_normalize(account, purple_account_get_username(account)); - const char *pass = purple_connection_get_password(gc); - struct yahoo_data *yd = gc->proto_data; - - /* So, Yahoo has stopped supporting its older clients in India, and undoubtedly - * will soon do so in the rest of the world. - * - * The new clients use this authentication method. I warn you in advance, it's - * bizarre, convoluted, inordinately complicated. It's also no more secure than - * crypt() was. The only purpose this scheme could serve is to prevent third - * party clients from connecting to their servers. - * - * Sorry, Yahoo. - */ - - PurpleCipher *cipher; - PurpleCipherContext *context; - guchar digest[16]; - - char *crypt_result; - char password_hash[25]; - char crypt_hash[25]; - char *hash_string_p = g_malloc(50 + strlen(name)); - char *hash_string_c = g_malloc(50 + strlen(name)); - - char checksum; - - int sv; - - char result6[25]; - char result96[25]; - - sv = seed[15]; - sv = sv % 8; - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - - purple_cipher_context_append(context, (const guchar *)pass, strlen(pass)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - - to_y64(password_hash, digest, 16); - - crypt_result = yahoo_crypt(pass, "$1$_2S43d5f$"); - - purple_cipher_context_reset(context, NULL); - purple_cipher_context_append(context, (const guchar *)crypt_result, strlen(crypt_result)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - to_y64(crypt_hash, digest, 16); - - switch (sv) { - case 1: - case 6: - checksum = seed[seed[9] % 16]; - g_snprintf(hash_string_p, strlen(name) + 50, - "%c%s%s%s", checksum, name, seed, password_hash); - g_snprintf(hash_string_c, strlen(name) + 50, - "%c%s%s%s", checksum, name, seed, crypt_hash); - break; - case 2: - case 7: - checksum = seed[seed[15] % 16]; - g_snprintf(hash_string_p, strlen(name) + 50, - "%c%s%s%s", checksum, seed, password_hash, name); - g_snprintf(hash_string_c, strlen(name) + 50, - "%c%s%s%s", checksum, seed, crypt_hash, name); - break; - case 3: - checksum = seed[seed[1] % 16]; - g_snprintf(hash_string_p, strlen(name) + 50, - "%c%s%s%s", checksum, name, password_hash, seed); - g_snprintf(hash_string_c, strlen(name) + 50, - "%c%s%s%s", checksum, name, crypt_hash, seed); - break; - case 4: - checksum = seed[seed[3] % 16]; - g_snprintf(hash_string_p, strlen(name) + 50, - "%c%s%s%s", checksum, password_hash, seed, name); - g_snprintf(hash_string_c, strlen(name) + 50, - "%c%s%s%s", checksum, crypt_hash, seed, name); - break; - case 0: - case 5: - checksum = seed[seed[7] % 16]; - g_snprintf(hash_string_p, strlen(name) + 50, - "%c%s%s%s", checksum, password_hash, name, seed); - g_snprintf(hash_string_c, strlen(name) + 50, - "%c%s%s%s", checksum, crypt_hash, name, seed); - break; - } - - purple_cipher_context_reset(context, NULL); - purple_cipher_context_append(context, (const guchar *)hash_string_p, strlen(hash_string_p)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - to_y64(result6, digest, 16); - - purple_cipher_context_reset(context, NULL); - purple_cipher_context_append(context, (const guchar *)hash_string_c, strlen(hash_string_c)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(context); - to_y64(result96, digest, 16); - - pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_AVAILABLE, 0); - - if(yd->jp) { - yahoo_packet_hash(pack, "sssss", - 0, name, - 6, result6, - 96, result96, - 1, name, - 135, YAHOOJP_CLIENT_VERSION); - } else { - yahoo_packet_hash(pack, "ssssss", - 0, name, - 6, result6, - 96, result96, - 1, name, - 244, YAHOO_CLIENT_VERSION_ID, - 135, YAHOO_CLIENT_VERSION); - } - - yahoo_packet_send_and_free(pack, yd); - - g_free(hash_string_p); - g_free(hash_string_c); -} - -/* I'm dishing out some uber-mad props to Cerulean Studios for cracking this - * and sending the fix! Thanks guys. */ - -static void yahoo_process_auth_new(PurpleConnection *gc, const char *seed) -{ - struct yahoo_packet *pack = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - const char *name = purple_normalize(account, purple_account_get_username(account)); - const char *pass = purple_connection_get_password(gc); - char *enc_pass; - struct yahoo_data *yd = gc->proto_data; - - PurpleCipher *md5_cipher; - PurpleCipherContext *md5_ctx; - guchar md5_digest[16]; - - PurpleCipher *sha1_cipher; - PurpleCipherContext *sha1_ctx1; - PurpleCipherContext *sha1_ctx2; - - char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ"; - char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop"; - - char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5"; - char *operand_lookup = "+|&%/*^-"; - char *delimit_lookup = ",;"; - - char *password_hash = (char *)g_malloc(25); - char *crypt_hash = (char *)g_malloc(25); - char *crypt_result = NULL; - - unsigned char pass_hash_xor1[64]; - unsigned char pass_hash_xor2[64]; - unsigned char crypt_hash_xor1[64]; - unsigned char crypt_hash_xor2[64]; - char resp_6[100]; - char resp_96[100]; - - unsigned char digest1[20]; - unsigned char digest2[20]; - unsigned char comparison_src[20]; - unsigned char magic_key_char[4]; - const char *magic_ptr; - - unsigned int magic[64]; - unsigned int magic_work = 0; - unsigned int magic_4 = 0; - - int x; - int y; - int cnt = 0; - int magic_cnt = 0; - int magic_len; - - memset(password_hash, 0, 25); - memset(crypt_hash, 0, 25); - memset(&pass_hash_xor1, 0, 64); - memset(&pass_hash_xor2, 0, 64); - memset(&crypt_hash_xor1, 0, 64); - memset(&crypt_hash_xor2, 0, 64); - memset(&digest1, 0, 20); - memset(&digest2, 0, 20); - memset(&magic, 0, 64); - memset(&resp_6, 0, 100); - memset(&resp_96, 0, 100); - memset(&magic_key_char, 0, 4); - memset(&comparison_src, 0, 20); + PurpleCipher *md5_cipher; + PurpleCipherContext *md5_ctx; + guchar md5_digest[16]; + gchar base64_string[25]; + struct yahoo_packet *pkt; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n"); md5_cipher = purple_ciphers_find_cipher("md5"); md5_ctx = purple_cipher_context_new(md5_cipher, NULL); - - sha1_cipher = purple_ciphers_find_cipher("sha1"); - sha1_ctx1 = purple_cipher_context_new(sha1_cipher, NULL); - sha1_ctx2 = purple_cipher_context_new(sha1_cipher, NULL); - - /* - * Magic: Phase 1. Generate what seems to be a 30 byte value (could change if base64 - * ends up differently? I don't remember and I'm tired, so use a 64 byte buffer. - */ - - magic_ptr = seed; - - while (*magic_ptr != '\0') { - char *loc; - - /* Ignore parentheses. */ - - if (*magic_ptr == '(' || *magic_ptr == ')') { - magic_ptr++; - continue; + purple_cipher_context_append(md5_ctx,(guchar *)crypt, strlen(crypt)); + purple_cipher_context_digest(md5_ctx, sizeof(md5_digest),md5_digest, NULL); + + to_y64(base64_string, md5_digest, 16); + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_WEBLOGIN, yd->session_id); + if(yd->jp) { + yahoo_packet_hash(pkt, "ssssssss", + 1, name, + 0, name, + 277, yd->cookie_y, + 278, yd->cookie_t, + 307, base64_string, + 2, name, + 2, "1", + 135, YAHOOJP_CLIENT_VERSION); + } else { + yahoo_packet_hash(pkt, "sssssssss", + 1, name, + 0, name, + 277, yd->cookie_y, + 278, yd->cookie_t, + 307, base64_string, + 244, YAHOO_CLIENT_VERSION_ID, + 2, name, + 2, "1", + 135, YAHOO_CLIENT_VERSION); + } + if (yd->picture_checksum) + yahoo_packet_hash_int(pkt, 192, yd->picture_checksum); + yahoo_packet_send_and_free(pkt, yd); + + purple_cipher_context_destroy(md5_ctx); + g_free(crypt); +} + +static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *url_data2, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message) +{ + struct yahoo_auth_data *auth_data = user_data; + PurpleConnection *gc = auth_data->gc; + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + gchar **split_data = NULL; + int totalelements; + int response_no; + char *crumb = NULL; + char *error_reason = NULL; + char *crypt = NULL; + gboolean try_login_on_error = FALSE; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n"); + + g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); + + if (error_message != NULL) { + purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); + g_free(auth_data->seed); + g_free(auth_data); + return; + } + else if (len > 0 && ret_data && *ret_data) { + split_data = g_strsplit(ret_data, "\r\n", -1); + totalelements = g_strv_length(split_data); + if(totalelements >= 5) { + response_no = strtol(*(split_data+1), NULL, 10); + crumb = g_strdup(*(split_data+2)+6); + yd->cookie_y = g_strdup(*(split_data+3)+2); + yd->cookie_t = g_strdup(*(split_data+4)+2); } - - /* Characters and digits verify against the challenge lookup. */ - - if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) { - loc = strchr(challenge_lookup, *magic_ptr); - if (!loc) { - /* SME XXX Error - disconnect here */ - } - - /* Get offset into lookup table and shl 3. */ - - magic_work = loc - challenge_lookup; - magic_work <<= 3; - - magic_ptr++; - continue; - } else { - unsigned int local_store; - - loc = strchr(operand_lookup, *magic_ptr); - if (!loc) { - /* SME XXX Disconnect */ - } - - local_store = loc - operand_lookup; - - /* Oops; how did this happen? */ - - if (magic_cnt >= 64) - break; - - magic[magic_cnt++] = magic_work | local_store; - magic_ptr++; - continue; - } + else + response_no = -1; + + g_strfreev(split_data); + + if(response_no != 0) { + /* Some error in the login process */ + PurpleConnectionError error; + switch(response_no) { + case -1: + /* Some error in the received stream */ + error_reason = g_strdup(_("Error in the received data")); + error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 100: + /* Unknown error */ + error_reason = g_strdup(_("Unknown error")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + default: + /* if we have everything we need, why not try to login irrespective of response */ + if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) { + try_login_on_error = TRUE; + break; + } + error_reason = g_strdup(_("Unknown error")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; } - - magic_len = magic_cnt; - magic_cnt = 0; - - /* Magic: Phase 2. Take generated magic value and sprinkle fairy - * dust on the values. - */ - - for (magic_cnt = magic_len - 2; magic_cnt >= 0; magic_cnt--) { - unsigned char byte1; - unsigned char byte2; - - /* Bad. Abort. */ - - if ((magic_cnt + 1 > magic_len) || (magic_cnt > magic_len)) - break; - - byte1 = magic[magic_cnt]; - byte2 = magic[magic_cnt+1]; - - byte1 *= 0xcd; - byte1 ^= byte2; - - magic[magic_cnt+1] = byte1; - } - - /* - * Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic - * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key - * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets - * into particular functions we'll later call to potentially alter the magic key. - * - * %-) - */ - - magic_cnt = 1; - x = 0; - - do { - unsigned int bl = 0; - unsigned int cl = magic[magic_cnt++]; - - if (magic_cnt >= magic_len) - break; - - if (cl > 0x7F) { - if (cl < 0xe0) - bl = cl = (cl & 0x1f) << 6; - else { - bl = magic[magic_cnt++]; - cl = (cl & 0x0f) << 6; - bl = ((bl & 0x3f) + cl) << 6; - } - - cl = magic[magic_cnt++]; - bl = (cl & 0x3f) + bl; - } else - bl = cl; - - comparison_src[x++] = (bl & 0xff00) >> 8; - comparison_src[x++] = bl & 0xff; - } while (x < 20); - - /* First four bytes are magic key. */ - memcpy(&magic_key_char[0], comparison_src, 4); - magic_4 = magic_key_char[0] | (magic_key_char[1] << 8) | - (magic_key_char[2] << 16) | (magic_key_char[3] << 24); - - /* - * Magic: Phase 4. Determine what function to use later by getting outside/inside - * loop values until we match our previous buffer. - */ - for (x = 0; x < 65535; x++) { - int leave = 0; - - for (y = 0; y < 5; y++) { - unsigned char test[3]; - - /* Calculate buffer. */ - test[0] = x; - test[1] = x >> 8; - test[2] = y; - - purple_cipher_context_reset(md5_ctx, NULL); - purple_cipher_context_append(md5_ctx, magic_key_char, 4); - purple_cipher_context_append(md5_ctx, test, 3); - purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), - md5_digest, NULL); - - if (!memcmp(md5_digest, comparison_src+4, 16)) { - leave = 1; - break; + if(error_reason) { + purple_debug_error("yahoo","Authentication error: %s", error_reason); + purple_connection_error_reason(gc, error, error_reason); + g_free(error_reason); } } - - if (leave == 1) - break; - } - - /* If y != 0, we need some help. */ - if (y != 0) { - unsigned int updated_key; - - /* Update magic stuff. - * Call it twice because Yahoo's encryption is super bad ass. - */ - updated_key = yahoo_auth_finalCountdown(magic_4, 0x60, y, x); - updated_key = yahoo_auth_finalCountdown(updated_key, 0x60, y, x); - - magic_key_char[0] = updated_key & 0xff; - magic_key_char[1] = (updated_key >> 8) & 0xff; - magic_key_char[2] = (updated_key >> 16) & 0xff; - magic_key_char[3] = (updated_key >> 24) & 0xff; + if((response_no == 0) || try_login_on_error) { + crypt = g_strconcat(crumb, auth_data->seed, NULL); + yahoo_auth16_stage3(gc, crypt); + g_free(crumb); + } } - - enc_pass = yahoo_string_encode(gc, pass, NULL); - - /* Get password and crypt hashes as per usual. */ - purple_cipher_context_reset(md5_ctx, NULL); - purple_cipher_context_append(md5_ctx, (const guchar *)enc_pass, strlen(enc_pass)); - purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), - md5_digest, NULL); - to_y64(password_hash, md5_digest, 16); - - crypt_result = yahoo_crypt(enc_pass, "$1$_2S43d5f$"); - - g_free(enc_pass); - enc_pass = NULL; - - purple_cipher_context_reset(md5_ctx, NULL); - purple_cipher_context_append(md5_ctx, (const guchar *)crypt_result, strlen(crypt_result)); - purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), - md5_digest, NULL); - to_y64(crypt_hash, md5_digest, 16); - - /* Our first authentication response is based off of the password hash. */ - for (x = 0; x < (int)strlen(password_hash); x++) - pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36; - - if (cnt < 64) - memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt); - - cnt = 0; - - for (x = 0; x < (int)strlen(password_hash); x++) - pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c; - - if (cnt < 64) - memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt); - - /* - * The first context gets the password hash XORed with 0x36 plus a magic value - * which we previously extrapolated from our challenge. - */ - - purple_cipher_context_append(sha1_ctx1, pass_hash_xor1, 64); - if (y >= 3) - purple_cipher_context_set_option(sha1_ctx1, "sizeLo", GINT_TO_POINTER(0x1ff)); - purple_cipher_context_append(sha1_ctx1, magic_key_char, 4); - purple_cipher_context_digest(sha1_ctx1, sizeof(digest1), digest1, NULL); - - /* - * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest - * of the first context. - */ - - purple_cipher_context_append(sha1_ctx2, pass_hash_xor2, 64); - purple_cipher_context_append(sha1_ctx2, digest1, 20); - purple_cipher_context_digest(sha1_ctx2, sizeof(digest2), digest2, NULL); - - /* - * Now that we have digest2, use it to fetch characters from an alphabet to construct - * our first authentication response. - */ - - for (x = 0; x < 20; x += 2) { - unsigned int val = 0; - unsigned int lookup = 0; - - char byte[6]; - - memset(&byte, 0, 6); - - /* First two bytes of digest stuffed together. */ - - val = digest2[x]; - val <<= 8; - val += digest2[x+1]; - - lookup = (val >> 0x0b); - lookup &= 0x1f; - if (lookup >= strlen(alphabet1)) - break; - sprintf(byte, "%c", alphabet1[lookup]); - strcat(resp_6, byte); - strcat(resp_6, "="); - - lookup = (val >> 0x06); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_6, byte); - - lookup = (val >> 0x01); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_6, byte); - - lookup = (val & 0x01); - if (lookup >= strlen(delimit_lookup)) - break; - sprintf(byte, "%c", delimit_lookup[lookup]); - strcat(resp_6, byte); + g_free(auth_data->seed); + g_free(auth_data); +} + +static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message) +{ + struct yahoo_auth_data *auth_data = user_data; + PurpleConnection *gc = auth_data->gc; + gchar **split_data = NULL; + int response_no; + int totalelements; + char *error_reason = NULL; + PurpleUtilFetchUrlData *url_data2 = NULL; + char *token = NULL; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n"); + + g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); + + if (error_message != NULL) { + purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); + g_free(auth_data->seed); + g_free(auth_data); + return; } - - /* Our second authentication response is based off of the crypto hash. */ - - cnt = 0; - memset(&digest1, 0, 20); - memset(&digest2, 0, 20); - - for (x = 0; x < (int)strlen(crypt_hash); x++) - crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36; - - if (cnt < 64) - memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt); - - cnt = 0; - - for (x = 0; x < (int)strlen(crypt_hash); x++) - crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c; - - if (cnt < 64) - memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt); - - purple_cipher_context_reset(sha1_ctx1, NULL); - purple_cipher_context_reset(sha1_ctx2, NULL); - - /* - * The first context gets the password hash XORed with 0x36 plus a magic value - * which we previously extrapolated from our challenge. - */ - - purple_cipher_context_append(sha1_ctx1, crypt_hash_xor1, 64); - if (y >= 3) { - purple_cipher_context_set_option(sha1_ctx1, "sizeLo", - GINT_TO_POINTER(0x1ff)); + else if (len > 0 && ret_data && *ret_data) { + split_data = g_strsplit(ret_data, "\r\n", -1); + totalelements = g_strv_length(split_data); + + if(totalelements >= 5) { + response_no = strtol(*(split_data+1), NULL, 10); + token = g_strdup(*(split_data+2)+6); + } + else + response_no = -1; + + g_strfreev(split_data); + + if(response_no != 0) { + /* Some error in the login process */ + PurpleConnectionError error; + switch(response_no) { + case -1: + /* Some error in the received stream */ + error_reason = g_strdup(_("Error in the received data")); + error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; + break; + case 1212: + /* Password incorrect */ + error_reason = g_strdup(_("Incorrect Password")); + error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + case 1213: + /* security lock from too many failed login attempts */ + error_reason = g_strdup(_("Login locked: Too many failed login attempts")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + case 1235: + /* the username does not exist */ + error_reason = g_strdup(_("Username does not exist")); + error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; + break; + case 1236: + /* indicates a lock of some description */ + error_reason = g_strdup(_("Login locked: See the debug log")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + case 100: + /* username or password missing */ + error_reason = g_strdup(_("Username or password missing")); + error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; + break; + default: + /* Unknown error! */ + error_reason = g_strdup(_("Unkown error")); + error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; + break; + } + purple_debug_error("yahoo","Authentication error: %s", error_reason); + purple_connection_error_reason(gc, error, error_reason); + g_free(error_reason); + g_free(auth_data->seed); + g_free(auth_data); + } + else { + /* OK to login, correct information provided */ + char *url = NULL; + gboolean yahoojp = purple_account_get_bool(purple_connection_get_account(gc), + "yahoojp", 0); + + url = g_strdup_printf(yahoojp ? + "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s" : + "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s", + token); + url_data2 = purple_util_fetch_url_request(url, TRUE, "Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, NULL, FALSE, yahoo_auth16_stage2, auth_data); + g_free(url); + g_free(token); + } } - purple_cipher_context_append(sha1_ctx1, magic_key_char, 4); - purple_cipher_context_digest(sha1_ctx1, sizeof(digest1), digest1, NULL); - - /* - * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest - * of the first context. - */ - - purple_cipher_context_append(sha1_ctx2, crypt_hash_xor2, 64); - purple_cipher_context_append(sha1_ctx2, digest1, 20); - purple_cipher_context_digest(sha1_ctx2, sizeof(digest2), digest2, NULL); - - /* - * Now that we have digest2, use it to fetch characters from an alphabet to construct - * our first authentication response. - */ - - for (x = 0; x < 20; x += 2) { - unsigned int val = 0; - unsigned int lookup = 0; - - char byte[6]; - - memset(&byte, 0, 6); - - /* First two bytes of digest stuffed together. */ - - val = digest2[x]; - val <<= 8; - val += digest2[x+1]; - - lookup = (val >> 0x0b); - lookup &= 0x1f; - if (lookup >= strlen(alphabet1)) - break; - sprintf(byte, "%c", alphabet1[lookup]); - strcat(resp_96, byte); - strcat(resp_96, "="); - - lookup = (val >> 0x06); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_96, byte); - - lookup = (val >> 0x01); - lookup &= 0x1f; - if (lookup >= strlen(alphabet2)) - break; - sprintf(byte, "%c", alphabet2[lookup]); - strcat(resp_96, byte); - - lookup = (val & 0x01); - if (lookup >= strlen(delimit_lookup)) - break; - sprintf(byte, "%c", delimit_lookup[lookup]); - strcat(resp_96, byte); +} + +static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed) +{ + PurpleUtilFetchUrlData *url_data = NULL; + struct yahoo_auth_data *auth_data = NULL; + char *url = NULL; + gboolean yahoojp; + + purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1\n"); + + if(!purple_ssl_is_supported()) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("Server requires TLS/SSL for login. No TLS/SSL support found.")); + return; } - purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status); - pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, 0); - - if(yd->jp) { - yahoo_packet_hash(pack, "sssss", - 0, name, - 6, resp_6, - 96, resp_96, - 1, name, - 135, YAHOOJP_CLIENT_VERSION); - } else { - yahoo_packet_hash(pack, "ssssss", - 0, name, - 6, resp_6, - 96, resp_96, - 1, name, - 244, YAHOO_CLIENT_VERSION_ID, - 135, YAHOO_CLIENT_VERSION); - } - - if (yd->picture_checksum) - yahoo_packet_hash_int(pack, 192, yd->picture_checksum); - - yahoo_packet_send_and_free(pack, yd); - - purple_cipher_context_destroy(md5_ctx); - purple_cipher_context_destroy(sha1_ctx1); - purple_cipher_context_destroy(sha1_ctx2); - - g_free(password_hash); - g_free(crypt_hash); + + yahoojp = purple_account_get_bool(purple_connection_get_account(gc), + "yahoojp", 0); + auth_data = g_new0(struct yahoo_auth_data, 1); + auth_data->gc = gc; + auth_data->seed = g_strdup(seed); + + url = g_strdup_printf(yahoojp ? + "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" : + "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s", + purple_account_get_username(purple_connection_get_account(gc)), + purple_connection_get_password(gc), seed); + + url_data = purple_util_fetch_url_request(url, TRUE, "Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, NULL, FALSE, yahoo_auth16_stage1_cb, auth_data); + g_free(url); } static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -1994,11 +1656,10 @@ if (seed) { switch (m) { case 0: - yahoo_process_auth_old(gc, seed); - break; + /* used to be for really old auth routine, dont support now */ case 1: - case 2: /* This case seems to work, could probably use testing */ - yahoo_process_auth_new(gc, seed); + case 2: /* Yahoo ver 16 authentication */ + yahoo_auth16_stage1(gc, seed); break; default: { @@ -2011,7 +1672,7 @@ purple_notify_error(gc, "", _("Failed Yahoo! Authentication"), buf); g_free(buf); - yahoo_process_auth_new(gc, seed); /* Can't hurt to try it anyway. */ + yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */ break; } }