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 }