comparison src/protocols/yahoo/yahoo.c @ 3147:3805d9d344f2

[gaim-migrate @ 3163] Please test this. Yahoo changed it's authentication method and has started disallowing older clients (and third party clients using the older method) to connect to their servers. This impliments the new authentication method so that we won't get blocked from Yahoo, and Indian users (who have already been blocked) can connect again. It's not perfect--it should fail about 1 out of 16 times. It's an easy bug to find and fix, but I want to go to bed now. If you find yourself failing to connect more than this 1 of 16, please let me know, and I'll tell you how to help me debug it. Thanks. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Sun, 14 Apr 2002 07:50:48 +0000
parents 793fb2e9d53b
children 2bb8b7380814
comparison
equal deleted inserted replaced
3146:442ae0b81f08 3147:3805d9d344f2
39 #include <ctype.h> 39 #include <ctype.h>
40 #include "multi.h" 40 #include "multi.h"
41 #include "prpl.h" 41 #include "prpl.h"
42 #include "gaim.h" 42 #include "gaim.h"
43 #include "proxy.h" 43 #include "proxy.h"
44 #include "md5.h"
44 45
45 extern char *yahoo_crypt(char *, char *); 46 extern char *yahoo_crypt(char *, char *);
46 47
47 #include "pixmaps/status-away.xpm" 48 #include "pixmaps/status-away.xpm"
48 #include "pixmaps/status-here.xpm" 49 #include "pixmaps/status-here.xpm"
80 #define YAHOO_DEBUG 81 #define YAHOO_DEBUG
81 82
82 #define USEROPT_MAIL 0 83 #define USEROPT_MAIL 0
83 84
84 #define USEROPT_PAGERHOST 3 85 #define USEROPT_PAGERHOST 3
85 #define YAHOO_PAGER_HOST "cs.yahoo.com" 86 #define YAHOO_PAGER_HOST "scs.yahoo.com"
86 #define USEROPT_PAGERPORT 4 87 #define USEROPT_PAGERPORT 4
87 #define YAHOO_PAGER_PORT 5050 88 #define YAHOO_PAGER_PORT 5050
88 89
89 enum yahoo_service { /* these are easier to see in hex */ 90 enum yahoo_service { /* these are easier to see in hex */
90 YAHOO_SERVICE_LOGON = 1, 91 YAHOO_SERVICE_LOGON = 1,
120 YAHOO_SERVICE_GAMELOGON = 0x28, 121 YAHOO_SERVICE_GAMELOGON = 0x28,
121 YAHOO_SERVICE_GAMELOGOFF, 122 YAHOO_SERVICE_GAMELOGOFF,
122 YAHOO_SERVICE_GAMEMSG = 0x2a, 123 YAHOO_SERVICE_GAMEMSG = 0x2a,
123 YAHOO_SERVICE_FILETRANSFER = 0x46, 124 YAHOO_SERVICE_FILETRANSFER = 0x46,
124 YAHOO_SERVICE_NOTIFY = 0x4B, 125 YAHOO_SERVICE_NOTIFY = 0x4B,
126 YAHOO_SERVICE_AUTHRESP = 0x54,
125 YAHOO_SERVICE_LIST = 0x55, 127 YAHOO_SERVICE_LIST = 0x55,
128 YAHOO_SERVICE_AUTH = 0x57,
126 YAHOO_SERVICE_ADDBUDDY = 0x83, 129 YAHOO_SERVICE_ADDBUDDY = 0x83,
127 YAHOO_SERVICE_REMBUDDY = 0x84 130 YAHOO_SERVICE_REMBUDDY = 0x84
128 }; 131 };
129 132
130 enum yahoo_status { 133 enum yahoo_status {
390 case 1: /* we don't get the full buddy list here. */ 393 case 1: /* we don't get the full buddy list here. */
391 if (!yd->logged_in) { 394 if (!yd->logged_in) {
392 account_online(gc); 395 account_online(gc);
393 serv_finish_login(gc); 396 serv_finish_login(gc);
394 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value); 397 g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value);
395 do_import(gc, NULL);
396 yd->logged_in = TRUE; 398 yd->logged_in = TRUE;
397 399
398 /* this requests the list. i have a feeling that this is very evil */ 400 /* this requests the list. i have a feeling that this is very evil
399 newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0); 401 *
400 yahoo_send_packet(yd, newpkt); 402 * scs.yahoo.com sends you the list before this packet without it being
401 yahoo_packet_free(newpkt); 403 * requested
402 } 404 *
405 * do_import(gc, NULL);
406 * newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0);
407 * yahoo_send_packet(yd, newpkt);
408 * yahoo_packet_free(newpkt);
409 */
410
411 }
403 break; 412 break;
404 case 8: /* how many online buddies we have */ 413 case 8: /* how many online buddies we have */
405 break; 414 break;
406 case 7: /* the current buddy */ 415 case 7: /* the current buddy */
407 name = pair->value; 416 name = pair->value;
424 } 433 }
425 if (g_hash_table_lookup(yd->games, name)) 434 if (g_hash_table_lookup(yd->games, name))
426 gamestate = YAHOO_STATUS_GAME; 435 gamestate = YAHOO_STATUS_GAME;
427 if (state == YAHOO_STATUS_AVAILABLE) 436 if (state == YAHOO_STATUS_AVAILABLE)
428 serv_got_update(gc, name, 1, 0, 0, 0, gamestate, 0); 437 serv_got_update(gc, name, 1, 0, 0, 0, gamestate, 0);
429 else 438 else
430 serv_got_update(gc, name, 1, 0, 0, 0, (state << 2) | UC_UNAVAILABLE | gamestate, 0); 439 serv_got_update(gc, name, 1, 0, 0, 0, (state << 2) | UC_UNAVAILABLE | gamestate, 0);
431 if (state == YAHOO_STATUS_CUSTOM) { 440 if (state == YAHOO_STATUS_CUSTOM) {
432 gpointer val = g_hash_table_lookup(yd->hash, name); 441 gpointer val = g_hash_table_lookup(yd->hash, name);
433 if (val) { 442 if (val) {
434 g_free(val); 443 g_free(val);
468 l = l->next; 477 l = l->next;
469 478
470 if (pair->key != 87) 479 if (pair->key != 87)
471 continue; 480 continue;
472 481
482 do_import(gc, NULL);
473 lines = g_strsplit(pair->value, "\n", -1); 483 lines = g_strsplit(pair->value, "\n", -1);
474 for (tmp = lines; *tmp; tmp++) { 484 for (tmp = lines; *tmp; tmp++) {
475 split = g_strsplit(*tmp, ":", 2); 485 split = g_strsplit(*tmp, ":", 2);
476 if (!split) 486 if (!split)
477 continue; 487 continue;
664 connection_has_mail(gc, -1, from, subj, "http://mail.yahoo.com/"); 674 connection_has_mail(gc, -1, from, subj, "http://mail.yahoo.com/");
665 g_free(from); 675 g_free(from);
666 } else 676 } else
667 connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/"); 677 connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/");
668 } 678 }
679 /* This is the y64 alphabet... it's like base64, but has a . and a _ */
680 char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
681
682 /* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
683 * in util.c, but it has a bug I don't feel like finding right now ;) */
684 void to_y64(unsigned char *out, const unsigned char *in, int inlen)
685 /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
686 {
687 for (; inlen >= 3; inlen -= 3)
688 {
689 *out++ = base64digits[in[0] >> 2];
690 *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
691 *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
692 *out++ = base64digits[in[2] & 0x3f];
693 in += 3;
694 }
695 if (inlen > 0)
696 {
697 unsigned char fragment;
698
699 *out++ = base64digits[in[0] >> 2];
700 fragment = (in[0] << 4) & 0x30;
701 if (inlen > 1)
702 fragment |= in[1] >> 4;
703 *out++ = base64digits[fragment];
704 *out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
705 *out++ = '-';
706 }
707 *out = '\0';
708 }
709
710 static void yahoo_process_auth(struct gaim_connection *gc, struct yahoo_packet *pkt)
711 {
712 char *seed = NULL;
713 char *sn = NULL;
714 GSList *l = pkt->hash;
715 struct yahoo_data *yd = gc->proto_data;
716
717 while (l) {
718 struct yahoo_pair *pair = l->data;
719 if (pair->key == 94)
720 seed = pair->value;
721 if (pair->key == 1)
722 sn = pair->value;
723 l = l->next;
724 }
725
726 if (seed) {
727 struct yahoo_packet *pack;
728
729 /* So, Yahoo has stopped supporting its older clients in India, and undoubtedly
730 * will soon do so in the rest of the world.
731 *
732 * The new clients use this authentication method. I warn you in advance, it's
733 * bizzare, convoluted, inordinately complicated. It's also no more secure than
734 * crypt() was. The only purpose this scheme could serve is to prevent third
735 * part clients from connecting to their servers.
736 *
737 * Sorry, Yahoo.
738 */
739
740 md5_byte_t result[16];
741 md5_state_t ctx;
742 char *crypt_result;
743 char *password_hash = g_malloc(25);
744 char *crypt_hash = g_malloc(25);
745 char *hash_string_p = g_malloc(50 + strlen(sn));
746 char *hash_string_c = g_malloc(50 + strlen(sn));
747
748 int ordering;
749 char checksum;
750
751 char sv;
752
753 char *result6 = g_malloc(25);
754 char *result96 = g_malloc(25);
755
756 sv = seed[15];
757 checksum = sv % 16;
758
759 /* I bet there's some really cool mathematical pattern here if I looked hard enough.
760 * But, this works. */
761 switch (checksum) {
762 case 1:
763 case 6:
764 case 9:
765 case 14:
766
767 checksum = seed[9];
768 break;
769 case 3:
770 case 11:
771 checksum = seed[1];
772 break;
773 case 4:
774 case 12:
775 checksum = seed[3];
776 break;
777 case 5:
778 case 8:
779 case 13:
780 case 0:
781 checksum = seed[7];
782 break;
783 }
784 checksum = seed[checksum % 16];
785
786 ordering = sv % 8;
787
788 md5_init(&ctx);
789 md5_append(&ctx, gc->password, strlen(gc->password));
790 md5_finish(&ctx, result);
791 to_y64(password_hash, result, 16);
792
793 md5_init(&ctx);
794 crypt_result = yahoo_crypt(gc->password, "$1$_2S43d5f$");
795 md5_append(&ctx, crypt_result, strlen(crypt_result));
796 md5_finish(&ctx, result);
797 to_y64(crypt_hash, result, 16);
798
799 /* I bet there's a nice pattern here, too. */
800 switch (ordering) {
801 case 1:
802 case 6:
803 g_snprintf(hash_string_p, strlen(sn) + 50,
804 "%c%s%s%s", checksum, gc->username, seed, password_hash);
805 g_snprintf(hash_string_c, strlen(sn) + 50,
806 "%c%s%s%s", checksum, gc->username, seed, crypt_hash);
807 break;
808 case 2:
809 case 7:
810 g_snprintf(hash_string_p, strlen(sn) + 50,
811 "%c%s%s%s", checksum, seed, password_hash, gc->username);
812 g_snprintf(hash_string_c, strlen(sn) + 50,
813 "%c%s%s%s", checksum, seed, crypt_hash, gc->username);
814 break;
815 case 3:
816 g_snprintf(hash_string_p, strlen(sn) + 50,
817 "%c%s%s%s", checksum, gc->username, password_hash, seed);
818 g_snprintf(hash_string_c, strlen(sn) + 50,
819 "%c%s%s%s", checksum, gc->username, crypt_hash, seed);
820 break;
821 case 4:
822 g_snprintf(hash_string_p, strlen(sn) + 50,
823 "%c%s%s%s", checksum, gc->username, password_hash, seed);
824 g_snprintf(hash_string_c, strlen(sn) + 50,
825 "%c%s%s%s", checksum, gc->username, crypt_hash, seed);
826 break;
827 case 0:
828 case 5:
829 g_snprintf(hash_string_p, strlen(sn) + 50,
830 "%c%s%s%s", checksum, password_hash, gc->username, seed);
831 g_snprintf(hash_string_c, strlen(sn) + 50,
832 "%c%s%s%s", checksum, crypt_hash, gc->username, seed);
833 break;
834 }
835
836 debug_printf("\nPassword: %s\n", hash_string_p);
837 debug_printf("Crypt: %s\n\n", hash_string_c);
838
839 md5_init(&ctx);
840 md5_append(&ctx, hash_string_p, strlen(hash_string_c));
841 md5_finish(&ctx, result);
842 to_y64(result6, result, 16);
843
844 md5_init(&ctx);
845 md5_append(&ctx, hash_string_c, strlen(hash_string_c));
846 md5_finish(&ctx, result);
847 to_y64(result96, result, 16);
848
849 md5_init(&ctx);
850 md5_append(&ctx, gc->password, strlen(gc->password));
851 md5_finish(&ctx, result);
852 to_y64(password_hash, result, 16);
853
854 pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_AVAILABLE, 0);
855 yahoo_packet_hash(pack, 0, gc->username);
856 yahoo_packet_hash(pack, 6, result6);
857 yahoo_packet_hash(pack, 96, result96);
858 yahoo_packet_hash(pack, 1, gc->username);
859
860 yahoo_send_packet(yd, pack);
861
862 g_free(password_hash);
863 g_free(crypt_hash);
864 g_free(hash_string_p);
865 g_free(hash_string_c);
866
867 yahoo_packet_free(pack);
868 }
869 }
669 870
670 static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt) 871 static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt)
671 { 872 {
672 switch (pkt->service) 873 switch (pkt->service)
673 { 874 {
693 yahoo_process_contact(gc, pkt); 894 yahoo_process_contact(gc, pkt);
694 break; 895 break;
695 case YAHOO_SERVICE_LIST: 896 case YAHOO_SERVICE_LIST:
696 yahoo_process_list(gc, pkt); 897 yahoo_process_list(gc, pkt);
697 break; 898 break;
899 case YAHOO_SERVICE_AUTH:
900 yahoo_process_auth(gc, pkt);
901 break;
698 default: 902 default:
699 debug_printf("unhandled service 0x%02x\n", pkt->service); 903 debug_printf("unhandled service 0x%02x\n", pkt->service);
700 break; 904 break;
701 } 905 }
702 } 906 }
783 } 987 }
784 988
785 yd = gc->proto_data; 989 yd = gc->proto_data;
786 yd->fd = source; 990 yd->fd = source;
787 991
788 pkt = yahoo_packet_new(YAHOO_SERVICE_LOGON, YAHOO_STATUS_AVAILABLE, 0); 992 pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, 0);
789 993
790 yahoo_packet_hash(pkt, 0, gc->username);
791 yahoo_packet_hash(pkt, 1, gc->username); 994 yahoo_packet_hash(pkt, 1, gc->username);
792 yahoo_packet_hash(pkt, 6, yahoo_crypt(gc->password, "$1$_2S43d5f$"));
793
794 yahoo_send_packet(yd, pkt); 995 yahoo_send_packet(yd, pkt);
795 996
796 yahoo_packet_free(pkt); 997 yahoo_packet_free(pkt);
797 998
798 gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc); 999 gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc);
806 1007
807 yd->fd = -1; 1008 yd->fd = -1;
808 yd->hash = g_hash_table_new(g_str_hash, g_str_equal); 1009 yd->hash = g_hash_table_new(g_str_hash, g_str_equal);
809 yd->games = g_hash_table_new(g_str_hash, g_str_equal); 1010 yd->games = g_hash_table_new(g_str_hash, g_str_equal);
810 1011
1012 #if 0
811 if (!g_strncasecmp(user->proto_opt[USEROPT_PAGERHOST], "scs.yahoo.com", strlen("scs.yahoo.com"))) { 1013 if (!g_strncasecmp(user->proto_opt[USEROPT_PAGERHOST], "scs.yahoo.com", strlen("scs.yahoo.com"))) {
812 /* As of this morning, Yahoo is no longer supporting its server at scs.yahoo.com 1014 /* Figured out the new auth method */
813 * I don't like to edit the preferences in a prpl, but we'll keep this here
814 * for a while until everybody's happy again. -5 Feb 2002*/
815 debug_printf("Setting new Yahoo! server.\n"); 1015 debug_printf("Setting new Yahoo! server.\n");
816 g_snprintf(user->proto_opt[USEROPT_PAGERHOST], strlen("cs.yahoo.com") + 1, "cs.yahoo.com"); 1016 g_snprintf(user->proto_opt[USEROPT_PAGERHOST], strlen("cs.yahoo.com") + 1, "cs.yahoo.com");
817 save_prefs(); 1017 save_prefs();
818 } 1018 }
819 1019
1020 #endif /* 0 */
1021
820 if (proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ? 1022 if (proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ?
821 user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST, 1023 user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST,
822 user->proto_opt[USEROPT_PAGERPORT][0] ? 1024 user->proto_opt[USEROPT_PAGERPORT][0] ?
823 atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT, 1025 atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT,
824 yahoo_got_connected, gc) < 0) { 1026 yahoo_got_connected, gc) < 0) {