Mercurial > pidgin.yaz
comparison libpurple/protocols/yahoo/yahoo.c @ 26904:885320258863
Yahoo version 16 login. ---yet to do: prevent password from being displayed in debug logs---
author | Sulabh Mahajan <sulabh@soc.pidgin.im> |
---|---|
date | Wed, 06 May 2009 08:48:57 +0000 |
parents | 53b4f6ba8523 |
children | 644606f508e6 |
comparison
equal
deleted
inserted
replaced
26903:1a1c12c122c9 | 26904:885320258863 |
---|---|
42 #include "xmlnode.h" | 42 #include "xmlnode.h" |
43 | 43 |
44 #include "yahoo.h" | 44 #include "yahoo.h" |
45 #include "yahoochat.h" | 45 #include "yahoochat.h" |
46 #include "yahoo_aliases.h" | 46 #include "yahoo_aliases.h" |
47 #include "yahoo_auth.h" | |
48 #include "yahoo_crypt.h" | |
49 #include "yahoo_doodle.h" | 47 #include "yahoo_doodle.h" |
50 #include "yahoo_filexfer.h" | 48 #include "yahoo_filexfer.h" |
51 #include "yahoo_friend.h" | 49 #include "yahoo_friend.h" |
52 #include "yahoo_packet.h" | 50 #include "yahoo_packet.h" |
53 #include "yahoo_picture.h" | 51 #include "yahoo_picture.h" |
1525 | 1523 |
1526 purple_notify_emails(gc, count, FALSE, NULL, NULL, &to, &url, | 1524 purple_notify_emails(gc, count, FALSE, NULL, NULL, &to, &url, |
1527 NULL, NULL); | 1525 NULL, NULL); |
1528 } | 1526 } |
1529 } | 1527 } |
1528 | |
1529 /* We use this structure once while we authenticate */ | |
1530 struct yahoo_auth_data | |
1531 { | |
1532 PurpleConnection *gc; | |
1533 char *seed; | |
1534 }; | |
1535 | |
1530 /* This is the y64 alphabet... it's like base64, but has a . and a _ */ | 1536 /* This is the y64 alphabet... it's like base64, but has a . and a _ */ |
1531 static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; | 1537 static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; |
1532 | 1538 |
1533 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function | 1539 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function |
1534 * in util.c, but it has a bug I don't feel like finding right now ;) */ | 1540 * in util.c, but it is different from the one yahoo uses */ |
1535 static void to_y64(char *out, const unsigned char *in, gsize inlen) | 1541 static void to_y64(char *out, const unsigned char *in, gsize inlen) |
1536 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ | 1542 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ |
1537 { | 1543 { |
1538 for (; inlen >= 3; inlen -= 3) | 1544 for (; inlen >= 3; inlen -= 3) |
1539 { | 1545 { |
1556 *out++ = '-'; | 1562 *out++ = '-'; |
1557 } | 1563 } |
1558 *out = '\0'; | 1564 *out = '\0'; |
1559 } | 1565 } |
1560 | 1566 |
1561 static void yahoo_process_auth_old(PurpleConnection *gc, const char *seed) | 1567 static void yahoo_auth16_stage3(PurpleConnection *gc, char *crypt) |
1562 { | 1568 { |
1563 struct yahoo_packet *pack; | 1569 struct yahoo_data *yd = gc->proto_data; |
1564 PurpleAccount *account = purple_connection_get_account(gc); | 1570 PurpleAccount *account = purple_connection_get_account(gc); |
1565 const char *name = purple_normalize(account, purple_account_get_username(account)); | 1571 const char *name = purple_normalize(account, purple_account_get_username(account)); |
1566 const char *pass = purple_connection_get_password(gc); | 1572 PurpleCipher *md5_cipher; |
1567 struct yahoo_data *yd = gc->proto_data; | 1573 PurpleCipherContext *md5_ctx; |
1568 | 1574 guchar md5_digest[16]; |
1569 /* So, Yahoo has stopped supporting its older clients in India, and undoubtedly | 1575 gchar base64_string[25]; |
1570 * will soon do so in the rest of the world. | 1576 struct yahoo_packet *pkt; |
1571 * | 1577 |
1572 * The new clients use this authentication method. I warn you in advance, it's | 1578 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n"); |
1573 * bizarre, convoluted, inordinately complicated. It's also no more secure than | |
1574 * crypt() was. The only purpose this scheme could serve is to prevent third | |
1575 * party clients from connecting to their servers. | |
1576 * | |
1577 * Sorry, Yahoo. | |
1578 */ | |
1579 | |
1580 PurpleCipher *cipher; | |
1581 PurpleCipherContext *context; | |
1582 guchar digest[16]; | |
1583 | |
1584 char *crypt_result; | |
1585 char password_hash[25]; | |
1586 char crypt_hash[25]; | |
1587 char *hash_string_p = g_malloc(50 + strlen(name)); | |
1588 char *hash_string_c = g_malloc(50 + strlen(name)); | |
1589 | |
1590 char checksum; | |
1591 | |
1592 int sv; | |
1593 | |
1594 char result6[25]; | |
1595 char result96[25]; | |
1596 | |
1597 sv = seed[15]; | |
1598 sv = sv % 8; | |
1599 | |
1600 cipher = purple_ciphers_find_cipher("md5"); | |
1601 context = purple_cipher_context_new(cipher, NULL); | |
1602 | |
1603 purple_cipher_context_append(context, (const guchar *)pass, strlen(pass)); | |
1604 purple_cipher_context_digest(context, sizeof(digest), digest, NULL); | |
1605 | |
1606 to_y64(password_hash, digest, 16); | |
1607 | |
1608 crypt_result = yahoo_crypt(pass, "$1$_2S43d5f$"); | |
1609 | |
1610 purple_cipher_context_reset(context, NULL); | |
1611 purple_cipher_context_append(context, (const guchar *)crypt_result, strlen(crypt_result)); | |
1612 purple_cipher_context_digest(context, sizeof(digest), digest, NULL); | |
1613 to_y64(crypt_hash, digest, 16); | |
1614 | |
1615 switch (sv) { | |
1616 case 1: | |
1617 case 6: | |
1618 checksum = seed[seed[9] % 16]; | |
1619 g_snprintf(hash_string_p, strlen(name) + 50, | |
1620 "%c%s%s%s", checksum, name, seed, password_hash); | |
1621 g_snprintf(hash_string_c, strlen(name) + 50, | |
1622 "%c%s%s%s", checksum, name, seed, crypt_hash); | |
1623 break; | |
1624 case 2: | |
1625 case 7: | |
1626 checksum = seed[seed[15] % 16]; | |
1627 g_snprintf(hash_string_p, strlen(name) + 50, | |
1628 "%c%s%s%s", checksum, seed, password_hash, name); | |
1629 g_snprintf(hash_string_c, strlen(name) + 50, | |
1630 "%c%s%s%s", checksum, seed, crypt_hash, name); | |
1631 break; | |
1632 case 3: | |
1633 checksum = seed[seed[1] % 16]; | |
1634 g_snprintf(hash_string_p, strlen(name) + 50, | |
1635 "%c%s%s%s", checksum, name, password_hash, seed); | |
1636 g_snprintf(hash_string_c, strlen(name) + 50, | |
1637 "%c%s%s%s", checksum, name, crypt_hash, seed); | |
1638 break; | |
1639 case 4: | |
1640 checksum = seed[seed[3] % 16]; | |
1641 g_snprintf(hash_string_p, strlen(name) + 50, | |
1642 "%c%s%s%s", checksum, password_hash, seed, name); | |
1643 g_snprintf(hash_string_c, strlen(name) + 50, | |
1644 "%c%s%s%s", checksum, crypt_hash, seed, name); | |
1645 break; | |
1646 case 0: | |
1647 case 5: | |
1648 checksum = seed[seed[7] % 16]; | |
1649 g_snprintf(hash_string_p, strlen(name) + 50, | |
1650 "%c%s%s%s", checksum, password_hash, name, seed); | |
1651 g_snprintf(hash_string_c, strlen(name) + 50, | |
1652 "%c%s%s%s", checksum, crypt_hash, name, seed); | |
1653 break; | |
1654 } | |
1655 | |
1656 purple_cipher_context_reset(context, NULL); | |
1657 purple_cipher_context_append(context, (const guchar *)hash_string_p, strlen(hash_string_p)); | |
1658 purple_cipher_context_digest(context, sizeof(digest), digest, NULL); | |
1659 to_y64(result6, digest, 16); | |
1660 | |
1661 purple_cipher_context_reset(context, NULL); | |
1662 purple_cipher_context_append(context, (const guchar *)hash_string_c, strlen(hash_string_c)); | |
1663 purple_cipher_context_digest(context, sizeof(digest), digest, NULL); | |
1664 purple_cipher_context_destroy(context); | |
1665 to_y64(result96, digest, 16); | |
1666 | |
1667 pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_AVAILABLE, 0); | |
1668 | |
1669 if(yd->jp) { | |
1670 yahoo_packet_hash(pack, "sssss", | |
1671 0, name, | |
1672 6, result6, | |
1673 96, result96, | |
1674 1, name, | |
1675 135, YAHOOJP_CLIENT_VERSION); | |
1676 } else { | |
1677 yahoo_packet_hash(pack, "ssssss", | |
1678 0, name, | |
1679 6, result6, | |
1680 96, result96, | |
1681 1, name, | |
1682 244, YAHOO_CLIENT_VERSION_ID, | |
1683 135, YAHOO_CLIENT_VERSION); | |
1684 } | |
1685 | |
1686 yahoo_packet_send_and_free(pack, yd); | |
1687 | |
1688 g_free(hash_string_p); | |
1689 g_free(hash_string_c); | |
1690 } | |
1691 | |
1692 /* I'm dishing out some uber-mad props to Cerulean Studios for cracking this | |
1693 * and sending the fix! Thanks guys. */ | |
1694 | |
1695 static void yahoo_process_auth_new(PurpleConnection *gc, const char *seed) | |
1696 { | |
1697 struct yahoo_packet *pack = NULL; | |
1698 PurpleAccount *account = purple_connection_get_account(gc); | |
1699 const char *name = purple_normalize(account, purple_account_get_username(account)); | |
1700 const char *pass = purple_connection_get_password(gc); | |
1701 char *enc_pass; | |
1702 struct yahoo_data *yd = gc->proto_data; | |
1703 | |
1704 PurpleCipher *md5_cipher; | |
1705 PurpleCipherContext *md5_ctx; | |
1706 guchar md5_digest[16]; | |
1707 | |
1708 PurpleCipher *sha1_cipher; | |
1709 PurpleCipherContext *sha1_ctx1; | |
1710 PurpleCipherContext *sha1_ctx2; | |
1711 | |
1712 char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ"; | |
1713 char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop"; | |
1714 | |
1715 char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5"; | |
1716 char *operand_lookup = "+|&%/*^-"; | |
1717 char *delimit_lookup = ",;"; | |
1718 | |
1719 char *password_hash = (char *)g_malloc(25); | |
1720 char *crypt_hash = (char *)g_malloc(25); | |
1721 char *crypt_result = NULL; | |
1722 | |
1723 unsigned char pass_hash_xor1[64]; | |
1724 unsigned char pass_hash_xor2[64]; | |
1725 unsigned char crypt_hash_xor1[64]; | |
1726 unsigned char crypt_hash_xor2[64]; | |
1727 char resp_6[100]; | |
1728 char resp_96[100]; | |
1729 | |
1730 unsigned char digest1[20]; | |
1731 unsigned char digest2[20]; | |
1732 unsigned char comparison_src[20]; | |
1733 unsigned char magic_key_char[4]; | |
1734 const char *magic_ptr; | |
1735 | |
1736 unsigned int magic[64]; | |
1737 unsigned int magic_work = 0; | |
1738 unsigned int magic_4 = 0; | |
1739 | |
1740 int x; | |
1741 int y; | |
1742 int cnt = 0; | |
1743 int magic_cnt = 0; | |
1744 int magic_len; | |
1745 | |
1746 memset(password_hash, 0, 25); | |
1747 memset(crypt_hash, 0, 25); | |
1748 memset(&pass_hash_xor1, 0, 64); | |
1749 memset(&pass_hash_xor2, 0, 64); | |
1750 memset(&crypt_hash_xor1, 0, 64); | |
1751 memset(&crypt_hash_xor2, 0, 64); | |
1752 memset(&digest1, 0, 20); | |
1753 memset(&digest2, 0, 20); | |
1754 memset(&magic, 0, 64); | |
1755 memset(&resp_6, 0, 100); | |
1756 memset(&resp_96, 0, 100); | |
1757 memset(&magic_key_char, 0, 4); | |
1758 memset(&comparison_src, 0, 20); | |
1759 | 1579 |
1760 md5_cipher = purple_ciphers_find_cipher("md5"); | 1580 md5_cipher = purple_ciphers_find_cipher("md5"); |
1761 md5_ctx = purple_cipher_context_new(md5_cipher, NULL); | 1581 md5_ctx = purple_cipher_context_new(md5_cipher, NULL); |
1762 | 1582 purple_cipher_context_append(md5_ctx,(guchar *)crypt, strlen(crypt)); |
1763 sha1_cipher = purple_ciphers_find_cipher("sha1"); | 1583 purple_cipher_context_digest(md5_ctx, sizeof(md5_digest),md5_digest, NULL); |
1764 sha1_ctx1 = purple_cipher_context_new(sha1_cipher, NULL); | 1584 |
1765 sha1_ctx2 = purple_cipher_context_new(sha1_cipher, NULL); | 1585 to_y64(base64_string, md5_digest, 16); |
1766 | 1586 |
1767 /* | 1587 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_WEBLOGIN, yd->session_id); |
1768 * Magic: Phase 1. Generate what seems to be a 30 byte value (could change if base64 | 1588 if(yd->jp) { |
1769 * ends up differently? I don't remember and I'm tired, so use a 64 byte buffer. | 1589 yahoo_packet_hash(pkt, "ssssssss", |
1770 */ | 1590 1, name, |
1771 | 1591 0, name, |
1772 magic_ptr = seed; | 1592 277, yd->cookie_y, |
1773 | 1593 278, yd->cookie_t, |
1774 while (*magic_ptr != '\0') { | 1594 307, base64_string, |
1775 char *loc; | 1595 2, name, |
1776 | 1596 2, "1", |
1777 /* Ignore parentheses. */ | 1597 135, YAHOOJP_CLIENT_VERSION); |
1778 | 1598 } else { |
1779 if (*magic_ptr == '(' || *magic_ptr == ')') { | 1599 yahoo_packet_hash(pkt, "sssssssss", |
1780 magic_ptr++; | 1600 1, name, |
1781 continue; | 1601 0, name, |
1782 } | 1602 277, yd->cookie_y, |
1783 | 1603 278, yd->cookie_t, |
1784 /* Characters and digits verify against the challenge lookup. */ | 1604 307, base64_string, |
1785 | 1605 244, YAHOO_CLIENT_VERSION_ID, |
1786 if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) { | 1606 2, name, |
1787 loc = strchr(challenge_lookup, *magic_ptr); | 1607 2, "1", |
1788 if (!loc) { | 1608 135, YAHOO_CLIENT_VERSION); |
1789 /* SME XXX Error - disconnect here */ | 1609 } |
1610 if (yd->picture_checksum) | |
1611 yahoo_packet_hash_int(pkt, 192, yd->picture_checksum); | |
1612 yahoo_packet_send_and_free(pkt, yd); | |
1613 | |
1614 purple_cipher_context_destroy(md5_ctx); | |
1615 g_free(crypt); | |
1616 } | |
1617 | |
1618 static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *url_data2, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message) | |
1619 { | |
1620 struct yahoo_auth_data *auth_data = user_data; | |
1621 PurpleConnection *gc = auth_data->gc; | |
1622 struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; | |
1623 gchar **split_data = NULL; | |
1624 int totalelements; | |
1625 int response_no; | |
1626 char *crumb = NULL; | |
1627 char *error_reason = NULL; | |
1628 char *crypt = NULL; | |
1629 gboolean try_login_on_error = FALSE; | |
1630 | |
1631 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n"); | |
1632 | |
1633 g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); | |
1634 | |
1635 if (error_message != NULL) { | |
1636 purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message); | |
1637 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); | |
1638 g_free(auth_data->seed); | |
1639 g_free(auth_data); | |
1640 return; | |
1641 } | |
1642 else if (len > 0 && ret_data && *ret_data) { | |
1643 split_data = g_strsplit(ret_data, "\r\n", -1); | |
1644 totalelements = g_strv_length(split_data); | |
1645 if(totalelements >= 5) { | |
1646 response_no = strtol(*(split_data+1), NULL, 10); | |
1647 crumb = g_strdup(*(split_data+2)+6); | |
1648 yd->cookie_y = g_strdup(*(split_data+3)+2); | |
1649 yd->cookie_t = g_strdup(*(split_data+4)+2); | |
1650 } | |
1651 else | |
1652 response_no = -1; | |
1653 | |
1654 g_strfreev(split_data); | |
1655 | |
1656 if(response_no != 0) { | |
1657 /* Some error in the login process */ | |
1658 PurpleConnectionError error; | |
1659 switch(response_no) { | |
1660 case -1: | |
1661 /* Some error in the received stream */ | |
1662 error_reason = g_strdup(_("Error in the received data")); | |
1663 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; | |
1664 break; | |
1665 case 100: | |
1666 /* Unknown error */ | |
1667 error_reason = g_strdup(_("Unknown error")); | |
1668 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; | |
1669 break; | |
1670 default: | |
1671 /* if we have everything we need, why not try to login irrespective of response */ | |
1672 if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) { | |
1673 try_login_on_error = TRUE; | |
1674 break; | |
1675 } | |
1676 error_reason = g_strdup(_("Unknown error")); | |
1677 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; | |
1678 break; | |
1790 } | 1679 } |
1791 | 1680 if(error_reason) { |
1792 /* Get offset into lookup table and shl 3. */ | 1681 purple_debug_error("yahoo","Authentication error: %s", error_reason); |
1793 | 1682 purple_connection_error_reason(gc, error, error_reason); |
1794 magic_work = loc - challenge_lookup; | 1683 g_free(error_reason); |
1795 magic_work <<= 3; | |
1796 | |
1797 magic_ptr++; | |
1798 continue; | |
1799 } else { | |
1800 unsigned int local_store; | |
1801 | |
1802 loc = strchr(operand_lookup, *magic_ptr); | |
1803 if (!loc) { | |
1804 /* SME XXX Disconnect */ | |
1805 } | 1684 } |
1806 | 1685 } |
1807 local_store = loc - operand_lookup; | 1686 if((response_no == 0) || try_login_on_error) { |
1808 | 1687 crypt = g_strconcat(crumb, auth_data->seed, NULL); |
1809 /* Oops; how did this happen? */ | 1688 yahoo_auth16_stage3(gc, crypt); |
1810 | 1689 g_free(crumb); |
1811 if (magic_cnt >= 64) | 1690 } |
1812 break; | 1691 } |
1813 | 1692 g_free(auth_data->seed); |
1814 magic[magic_cnt++] = magic_work | local_store; | 1693 g_free(auth_data); |
1815 magic_ptr++; | 1694 } |
1816 continue; | 1695 |
1817 } | 1696 static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message) |
1697 { | |
1698 struct yahoo_auth_data *auth_data = user_data; | |
1699 PurpleConnection *gc = auth_data->gc; | |
1700 gchar **split_data = NULL; | |
1701 int response_no; | |
1702 int totalelements; | |
1703 char *error_reason = NULL; | |
1704 PurpleUtilFetchUrlData *url_data2 = NULL; | |
1705 char *token = NULL; | |
1706 | |
1707 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n"); | |
1708 | |
1709 g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); | |
1710 | |
1711 if (error_message != NULL) { | |
1712 purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message); | |
1713 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); | |
1714 g_free(auth_data->seed); | |
1715 g_free(auth_data); | |
1716 return; | |
1717 } | |
1718 else if (len > 0 && ret_data && *ret_data) { | |
1719 split_data = g_strsplit(ret_data, "\r\n", -1); | |
1720 totalelements = g_strv_length(split_data); | |
1721 | |
1722 if(totalelements >= 5) { | |
1723 response_no = strtol(*(split_data+1), NULL, 10); | |
1724 token = g_strdup(*(split_data+2)+6); | |
1725 } | |
1726 else | |
1727 response_no = -1; | |
1728 | |
1729 g_strfreev(split_data); | |
1730 | |
1731 if(response_no != 0) { | |
1732 /* Some error in the login process */ | |
1733 PurpleConnectionError error; | |
1734 switch(response_no) { | |
1735 case -1: | |
1736 /* Some error in the received stream */ | |
1737 error_reason = g_strdup(_("Error in the received data")); | |
1738 error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; | |
1739 break; | |
1740 case 1212: | |
1741 /* Password incorrect */ | |
1742 error_reason = g_strdup(_("Incorrect Password")); | |
1743 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; | |
1744 break; | |
1745 case 1213: | |
1746 /* security lock from too many failed login attempts */ | |
1747 error_reason = g_strdup(_("Login locked: Too many failed login attempts")); | |
1748 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; | |
1749 break; | |
1750 case 1235: | |
1751 /* the username does not exist */ | |
1752 error_reason = g_strdup(_("Username does not exist")); | |
1753 error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME; | |
1754 break; | |
1755 case 1236: | |
1756 /* indicates a lock of some description */ | |
1757 error_reason = g_strdup(_("Login locked: See the debug log")); | |
1758 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; | |
1759 break; | |
1760 case 100: | |
1761 /* username or password missing */ | |
1762 error_reason = g_strdup(_("Username or password missing")); | |
1763 error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; | |
1764 break; | |
1765 default: | |
1766 /* Unknown error! */ | |
1767 error_reason = g_strdup(_("Unkown error")); | |
1768 error = PURPLE_CONNECTION_ERROR_OTHER_ERROR; | |
1769 break; | |
1818 } | 1770 } |
1819 | 1771 purple_debug_error("yahoo","Authentication error: %s", error_reason); |
1820 magic_len = magic_cnt; | 1772 purple_connection_error_reason(gc, error, error_reason); |
1821 magic_cnt = 0; | 1773 g_free(error_reason); |
1822 | 1774 g_free(auth_data->seed); |
1823 /* Magic: Phase 2. Take generated magic value and sprinkle fairy | 1775 g_free(auth_data); |
1824 * dust on the values. | 1776 } |
1825 */ | 1777 else { |
1826 | 1778 /* OK to login, correct information provided */ |
1827 for (magic_cnt = magic_len - 2; magic_cnt >= 0; magic_cnt--) { | 1779 char *url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s",token); |
1828 unsigned char byte1; | 1780 url_data2 = purple_util_fetch_url_request(url, TRUE, "Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, NULL, FALSE, yahoo_auth16_stage2, auth_data); |
1829 unsigned char byte2; | 1781 g_free(url); |
1830 | 1782 g_free(token); |
1831 /* Bad. Abort. */ | 1783 } |
1832 | 1784 } |
1833 if ((magic_cnt + 1 > magic_len) || (magic_cnt > magic_len)) | 1785 } |
1834 break; | 1786 |
1835 | 1787 static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed) |
1836 byte1 = magic[magic_cnt]; | 1788 { |
1837 byte2 = magic[magic_cnt+1]; | 1789 PurpleUtilFetchUrlData *url_data; |
1838 | 1790 struct yahoo_auth_data *auth_data = NULL; |
1839 byte1 *= 0xcd; | 1791 char *url = NULL; |
1840 byte1 ^= byte2; | 1792 |
1841 | 1793 purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1\n"); |
1842 magic[magic_cnt+1] = byte1; | 1794 |
1843 } | 1795 if(!purple_ssl_is_supported()) { |
1844 | 1796 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("Server requires TLS/SSL for login. No TLS/SSL support found.")); |
1845 /* | 1797 return; |
1846 * Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic | 1798 } |
1847 * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key | 1799 |
1848 * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets | 1800 auth_data = g_new0(struct yahoo_auth_data, 1); |
1849 * into particular functions we'll later call to potentially alter the magic key. | 1801 auth_data->gc = gc; |
1850 * | 1802 auth_data->seed = g_strdup(seed); |
1851 * %-) | 1803 |
1852 */ | 1804 url = g_strdup_printf("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); |
1853 | 1805 |
1854 magic_cnt = 1; | 1806 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); |
1855 x = 0; | 1807 g_free(url); |
1856 | |
1857 do { | |
1858 unsigned int bl = 0; | |
1859 unsigned int cl = magic[magic_cnt++]; | |
1860 | |
1861 if (magic_cnt >= magic_len) | |
1862 break; | |
1863 | |
1864 if (cl > 0x7F) { | |
1865 if (cl < 0xe0) | |
1866 bl = cl = (cl & 0x1f) << 6; | |
1867 else { | |
1868 bl = magic[magic_cnt++]; | |
1869 cl = (cl & 0x0f) << 6; | |
1870 bl = ((bl & 0x3f) + cl) << 6; | |
1871 } | |
1872 | |
1873 cl = magic[magic_cnt++]; | |
1874 bl = (cl & 0x3f) + bl; | |
1875 } else | |
1876 bl = cl; | |
1877 | |
1878 comparison_src[x++] = (bl & 0xff00) >> 8; | |
1879 comparison_src[x++] = bl & 0xff; | |
1880 } while (x < 20); | |
1881 | |
1882 /* First four bytes are magic key. */ | |
1883 memcpy(&magic_key_char[0], comparison_src, 4); | |
1884 magic_4 = magic_key_char[0] | (magic_key_char[1] << 8) | | |
1885 (magic_key_char[2] << 16) | (magic_key_char[3] << 24); | |
1886 | |
1887 /* | |
1888 * Magic: Phase 4. Determine what function to use later by getting outside/inside | |
1889 * loop values until we match our previous buffer. | |
1890 */ | |
1891 for (x = 0; x < 65535; x++) { | |
1892 int leave = 0; | |
1893 | |
1894 for (y = 0; y < 5; y++) { | |
1895 unsigned char test[3]; | |
1896 | |
1897 /* Calculate buffer. */ | |
1898 test[0] = x; | |
1899 test[1] = x >> 8; | |
1900 test[2] = y; | |
1901 | |
1902 purple_cipher_context_reset(md5_ctx, NULL); | |
1903 purple_cipher_context_append(md5_ctx, magic_key_char, 4); | |
1904 purple_cipher_context_append(md5_ctx, test, 3); | |
1905 purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), | |
1906 md5_digest, NULL); | |
1907 | |
1908 if (!memcmp(md5_digest, comparison_src+4, 16)) { | |
1909 leave = 1; | |
1910 break; | |
1911 } | |
1912 } | |
1913 | |
1914 if (leave == 1) | |
1915 break; | |
1916 } | |
1917 | |
1918 /* If y != 0, we need some help. */ | |
1919 if (y != 0) { | |
1920 unsigned int updated_key; | |
1921 | |
1922 /* Update magic stuff. | |
1923 * Call it twice because Yahoo's encryption is super bad ass. | |
1924 */ | |
1925 updated_key = yahoo_auth_finalCountdown(magic_4, 0x60, y, x); | |
1926 updated_key = yahoo_auth_finalCountdown(updated_key, 0x60, y, x); | |
1927 | |
1928 magic_key_char[0] = updated_key & 0xff; | |
1929 magic_key_char[1] = (updated_key >> 8) & 0xff; | |
1930 magic_key_char[2] = (updated_key >> 16) & 0xff; | |
1931 magic_key_char[3] = (updated_key >> 24) & 0xff; | |
1932 } | |
1933 | |
1934 enc_pass = yahoo_string_encode(gc, pass, NULL); | |
1935 | |
1936 /* Get password and crypt hashes as per usual. */ | |
1937 purple_cipher_context_reset(md5_ctx, NULL); | |
1938 purple_cipher_context_append(md5_ctx, (const guchar *)enc_pass, strlen(enc_pass)); | |
1939 purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), | |
1940 md5_digest, NULL); | |
1941 to_y64(password_hash, md5_digest, 16); | |
1942 | |
1943 crypt_result = yahoo_crypt(enc_pass, "$1$_2S43d5f$"); | |
1944 | |
1945 g_free(enc_pass); | |
1946 enc_pass = NULL; | |
1947 | |
1948 purple_cipher_context_reset(md5_ctx, NULL); | |
1949 purple_cipher_context_append(md5_ctx, (const guchar *)crypt_result, strlen(crypt_result)); | |
1950 purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), | |
1951 md5_digest, NULL); | |
1952 to_y64(crypt_hash, md5_digest, 16); | |
1953 | |
1954 /* Our first authentication response is based off of the password hash. */ | |
1955 for (x = 0; x < (int)strlen(password_hash); x++) | |
1956 pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36; | |
1957 | |
1958 if (cnt < 64) | |
1959 memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt); | |
1960 | |
1961 cnt = 0; | |
1962 | |
1963 for (x = 0; x < (int)strlen(password_hash); x++) | |
1964 pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c; | |
1965 | |
1966 if (cnt < 64) | |
1967 memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt); | |
1968 | |
1969 /* | |
1970 * The first context gets the password hash XORed with 0x36 plus a magic value | |
1971 * which we previously extrapolated from our challenge. | |
1972 */ | |
1973 | |
1974 purple_cipher_context_append(sha1_ctx1, pass_hash_xor1, 64); | |
1975 if (y >= 3) | |
1976 purple_cipher_context_set_option(sha1_ctx1, "sizeLo", GINT_TO_POINTER(0x1ff)); | |
1977 purple_cipher_context_append(sha1_ctx1, magic_key_char, 4); | |
1978 purple_cipher_context_digest(sha1_ctx1, sizeof(digest1), digest1, NULL); | |
1979 | |
1980 /* | |
1981 * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest | |
1982 * of the first context. | |
1983 */ | |
1984 | |
1985 purple_cipher_context_append(sha1_ctx2, pass_hash_xor2, 64); | |
1986 purple_cipher_context_append(sha1_ctx2, digest1, 20); | |
1987 purple_cipher_context_digest(sha1_ctx2, sizeof(digest2), digest2, NULL); | |
1988 | |
1989 /* | |
1990 * Now that we have digest2, use it to fetch characters from an alphabet to construct | |
1991 * our first authentication response. | |
1992 */ | |
1993 | |
1994 for (x = 0; x < 20; x += 2) { | |
1995 unsigned int val = 0; | |
1996 unsigned int lookup = 0; | |
1997 | |
1998 char byte[6]; | |
1999 | |
2000 memset(&byte, 0, 6); | |
2001 | |
2002 /* First two bytes of digest stuffed together. */ | |
2003 | |
2004 val = digest2[x]; | |
2005 val <<= 8; | |
2006 val += digest2[x+1]; | |
2007 | |
2008 lookup = (val >> 0x0b); | |
2009 lookup &= 0x1f; | |
2010 if (lookup >= strlen(alphabet1)) | |
2011 break; | |
2012 sprintf(byte, "%c", alphabet1[lookup]); | |
2013 strcat(resp_6, byte); | |
2014 strcat(resp_6, "="); | |
2015 | |
2016 lookup = (val >> 0x06); | |
2017 lookup &= 0x1f; | |
2018 if (lookup >= strlen(alphabet2)) | |
2019 break; | |
2020 sprintf(byte, "%c", alphabet2[lookup]); | |
2021 strcat(resp_6, byte); | |
2022 | |
2023 lookup = (val >> 0x01); | |
2024 lookup &= 0x1f; | |
2025 if (lookup >= strlen(alphabet2)) | |
2026 break; | |
2027 sprintf(byte, "%c", alphabet2[lookup]); | |
2028 strcat(resp_6, byte); | |
2029 | |
2030 lookup = (val & 0x01); | |
2031 if (lookup >= strlen(delimit_lookup)) | |
2032 break; | |
2033 sprintf(byte, "%c", delimit_lookup[lookup]); | |
2034 strcat(resp_6, byte); | |
2035 } | |
2036 | |
2037 /* Our second authentication response is based off of the crypto hash. */ | |
2038 | |
2039 cnt = 0; | |
2040 memset(&digest1, 0, 20); | |
2041 memset(&digest2, 0, 20); | |
2042 | |
2043 for (x = 0; x < (int)strlen(crypt_hash); x++) | |
2044 crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36; | |
2045 | |
2046 if (cnt < 64) | |
2047 memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt); | |
2048 | |
2049 cnt = 0; | |
2050 | |
2051 for (x = 0; x < (int)strlen(crypt_hash); x++) | |
2052 crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c; | |
2053 | |
2054 if (cnt < 64) | |
2055 memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt); | |
2056 | |
2057 purple_cipher_context_reset(sha1_ctx1, NULL); | |
2058 purple_cipher_context_reset(sha1_ctx2, NULL); | |
2059 | |
2060 /* | |
2061 * The first context gets the password hash XORed with 0x36 plus a magic value | |
2062 * which we previously extrapolated from our challenge. | |
2063 */ | |
2064 | |
2065 purple_cipher_context_append(sha1_ctx1, crypt_hash_xor1, 64); | |
2066 if (y >= 3) { | |
2067 purple_cipher_context_set_option(sha1_ctx1, "sizeLo", | |
2068 GINT_TO_POINTER(0x1ff)); | |
2069 } | |
2070 purple_cipher_context_append(sha1_ctx1, magic_key_char, 4); | |
2071 purple_cipher_context_digest(sha1_ctx1, sizeof(digest1), digest1, NULL); | |
2072 | |
2073 /* | |
2074 * The second context gets the password hash XORed with 0x5c plus the SHA-1 digest | |
2075 * of the first context. | |
2076 */ | |
2077 | |
2078 purple_cipher_context_append(sha1_ctx2, crypt_hash_xor2, 64); | |
2079 purple_cipher_context_append(sha1_ctx2, digest1, 20); | |
2080 purple_cipher_context_digest(sha1_ctx2, sizeof(digest2), digest2, NULL); | |
2081 | |
2082 /* | |
2083 * Now that we have digest2, use it to fetch characters from an alphabet to construct | |
2084 * our first authentication response. | |
2085 */ | |
2086 | |
2087 for (x = 0; x < 20; x += 2) { | |
2088 unsigned int val = 0; | |
2089 unsigned int lookup = 0; | |
2090 | |
2091 char byte[6]; | |
2092 | |
2093 memset(&byte, 0, 6); | |
2094 | |
2095 /* First two bytes of digest stuffed together. */ | |
2096 | |
2097 val = digest2[x]; | |
2098 val <<= 8; | |
2099 val += digest2[x+1]; | |
2100 | |
2101 lookup = (val >> 0x0b); | |
2102 lookup &= 0x1f; | |
2103 if (lookup >= strlen(alphabet1)) | |
2104 break; | |
2105 sprintf(byte, "%c", alphabet1[lookup]); | |
2106 strcat(resp_96, byte); | |
2107 strcat(resp_96, "="); | |
2108 | |
2109 lookup = (val >> 0x06); | |
2110 lookup &= 0x1f; | |
2111 if (lookup >= strlen(alphabet2)) | |
2112 break; | |
2113 sprintf(byte, "%c", alphabet2[lookup]); | |
2114 strcat(resp_96, byte); | |
2115 | |
2116 lookup = (val >> 0x01); | |
2117 lookup &= 0x1f; | |
2118 if (lookup >= strlen(alphabet2)) | |
2119 break; | |
2120 sprintf(byte, "%c", alphabet2[lookup]); | |
2121 strcat(resp_96, byte); | |
2122 | |
2123 lookup = (val & 0x01); | |
2124 if (lookup >= strlen(delimit_lookup)) | |
2125 break; | |
2126 sprintf(byte, "%c", delimit_lookup[lookup]); | |
2127 strcat(resp_96, byte); | |
2128 } | |
2129 purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status); | |
2130 pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, 0); | |
2131 | |
2132 if(yd->jp) { | |
2133 yahoo_packet_hash(pack, "sssss", | |
2134 0, name, | |
2135 6, resp_6, | |
2136 96, resp_96, | |
2137 1, name, | |
2138 135, YAHOOJP_CLIENT_VERSION); | |
2139 } else { | |
2140 yahoo_packet_hash(pack, "ssssss", | |
2141 0, name, | |
2142 6, resp_6, | |
2143 96, resp_96, | |
2144 1, name, | |
2145 244, YAHOO_CLIENT_VERSION_ID, | |
2146 135, YAHOO_CLIENT_VERSION); | |
2147 } | |
2148 | |
2149 if (yd->picture_checksum) | |
2150 yahoo_packet_hash_int(pack, 192, yd->picture_checksum); | |
2151 | |
2152 yahoo_packet_send_and_free(pack, yd); | |
2153 | |
2154 purple_cipher_context_destroy(md5_ctx); | |
2155 purple_cipher_context_destroy(sha1_ctx1); | |
2156 purple_cipher_context_destroy(sha1_ctx2); | |
2157 | |
2158 g_free(password_hash); | |
2159 g_free(crypt_hash); | |
2160 } | 1808 } |
2161 | 1809 |
2162 static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt) | 1810 static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt) |
2163 { | 1811 { |
2164 char *seed = NULL; | 1812 char *seed = NULL; |
2179 } | 1827 } |
2180 | 1828 |
2181 if (seed) { | 1829 if (seed) { |
2182 switch (m) { | 1830 switch (m) { |
2183 case 0: | 1831 case 0: |
2184 yahoo_process_auth_old(gc, seed); | 1832 /* used to be for really old auth routine, dont support now */ |
2185 break; | |
2186 case 1: | 1833 case 1: |
2187 case 2: /* This case seems to work, could probably use testing */ | 1834 case 2: /* Yahoo ver 16 authentication */ |
2188 yahoo_process_auth_new(gc, seed); | 1835 yahoo_auth16_stage1(gc, seed); |
2189 break; | 1836 break; |
2190 default: | 1837 default: |
2191 { | 1838 { |
2192 GHashTable *ui_info = purple_core_get_ui_info(); | 1839 GHashTable *ui_info = purple_core_get_ui_info(); |
2193 | 1840 |
2196 "to successfully sign on to Yahoo. Check %s for updates."), | 1843 "to successfully sign on to Yahoo. Check %s for updates."), |
2197 ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); | 1844 ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); |
2198 purple_notify_error(gc, "", _("Failed Yahoo! Authentication"), | 1845 purple_notify_error(gc, "", _("Failed Yahoo! Authentication"), |
2199 buf); | 1846 buf); |
2200 g_free(buf); | 1847 g_free(buf); |
2201 yahoo_process_auth_new(gc, seed); /* Can't hurt to try it anyway. */ | 1848 yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */ |
2202 break; | 1849 break; |
2203 } | 1850 } |
2204 } | 1851 } |
2205 } | 1852 } |
2206 } | 1853 } |