Mercurial > pidgin.yaz
comparison libpurple/protocols/jabber/auth.c @ 17227:61c5d8737f01
Added more robust parsing of the xmpp digest challenge and add validation that some required challenge fields are present. Fixes #1024
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Tue, 22 May 2007 05:22:36 +0000 |
parents | b713af8ae274 |
children | 5033139b3ead |
comparison
equal
deleted
inserted
replaced
17226:618a3748ff64 | 17227:61c5d8737f01 |
---|---|
610 jabber_iq_set_callback(iq, auth_old_cb, NULL); | 610 jabber_iq_set_callback(iq, auth_old_cb, NULL); |
611 | 611 |
612 jabber_iq_send(iq); | 612 jabber_iq_send(iq); |
613 } | 613 } |
614 | 614 |
615 /* Parts of this algorithm are inspired by stuff in libgsasl */ | |
615 static GHashTable* parse_challenge(const char *challenge) | 616 static GHashTable* parse_challenge(const char *challenge) |
616 { | 617 { |
618 const char *token_start, *val_start, *val_end, *cur; | |
617 GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, | 619 GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, |
618 g_free, g_free); | 620 g_free, g_free); |
619 char **pairs; | 621 |
620 int i; | 622 cur = challenge; |
621 | 623 while(*cur != '\0') { |
622 pairs = g_strsplit(challenge, ",", -1); | 624 /* Find the end of the token */ |
623 | 625 gboolean in_quotes = FALSE; |
624 for(i=0; pairs[i]; i++) { | 626 char *name, *value = NULL; |
625 char **keyval = g_strsplit(pairs[i], "=", 2); | 627 token_start = cur; |
626 if(keyval[0] && keyval[1]) { | 628 while(*cur != '\0' && (in_quotes || (!in_quotes && *cur != ','))) { |
627 if(keyval[1][0] == '"' && keyval[1][strlen(keyval[1])-1] == '"') | 629 if (*cur == '"') |
628 g_hash_table_replace(ret, g_strdup(keyval[0]), g_strndup(keyval[1]+1, strlen(keyval[1])-2)); | 630 in_quotes = !in_quotes; |
629 else | 631 cur++; |
630 g_hash_table_replace(ret, g_strdup(keyval[0]), g_strdup(keyval[1])); | 632 } |
631 } | 633 |
632 g_strfreev(keyval); | 634 /* Find start of value. */ |
633 } | 635 val_start = strchr(token_start, '='); |
634 | 636 if (val_start == NULL || val_start > cur) |
635 g_strfreev(pairs); | 637 val_start = cur; |
638 | |
639 if (token_start != val_start) { | |
640 name = g_strndup(token_start, val_start - token_start); | |
641 | |
642 if (val_start != cur) { | |
643 val_start++; | |
644 while (val_start != cur && (*val_start == ' ' || *val_start == '\t' | |
645 || *val_start == '\r' || *val_start == '\n' | |
646 || *val_start == '"')) | |
647 val_start++; | |
648 | |
649 val_end = cur; | |
650 while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t' | |
651 || *val_end == '\r' || *val_start == '\n' | |
652 || *val_end == '"')) | |
653 val_end--; | |
654 | |
655 if (val_start != val_end) | |
656 value = g_strndup(val_start, val_end - val_start + 1); | |
657 } | |
658 | |
659 g_hash_table_replace(ret, name, value); | |
660 } | |
661 | |
662 /* Find the start of the next token, if there is one */ | |
663 if (*cur != '\0') { | |
664 cur++; | |
665 while (*cur == ' ' || *cur == ',' || *cur == '\t' | |
666 || *cur == '\r' || *cur == '\n') | |
667 cur++; | |
668 } | |
669 } | |
636 | 670 |
637 return ret; | 671 return ret; |
638 } | 672 } |
639 | 673 |
640 static char * | 674 static char * |
736 } | 770 } |
737 g_free(js->expected_rspauth); | 771 g_free(js->expected_rspauth); |
738 } else { | 772 } else { |
739 /* assemble a response, and send it */ | 773 /* assemble a response, and send it */ |
740 /* see RFC 2831 */ | 774 /* see RFC 2831 */ |
741 GString *response = g_string_new(""); | |
742 char *a2; | |
743 char *auth_resp; | |
744 char *buf; | |
745 char *cnonce; | |
746 char *realm; | 775 char *realm; |
747 char *nonce; | 776 char *nonce; |
777 | |
778 /* Make sure the auth string contains everything that should be there. | |
779 This isn't everything in RFC2831, but it is what we need. */ | |
780 | |
781 nonce = g_hash_table_lookup(parts, "nonce"); | |
748 | 782 |
749 /* we're actually supposed to prompt the user for a realm if | 783 /* we're actually supposed to prompt the user for a realm if |
750 * the server doesn't send one, but that really complicates things, | 784 * the server doesn't send one, but that really complicates things, |
751 * so i'm not gonna worry about it until is poses a problem to | 785 * so i'm not gonna worry about it until is poses a problem to |
752 * someone, or I get really bored */ | 786 * someone, or I get really bored */ |
753 realm = g_hash_table_lookup(parts, "realm"); | 787 realm = g_hash_table_lookup(parts, "realm"); |
754 if(!realm) | 788 if(!realm) |
755 realm = js->user->domain; | 789 realm = js->user->domain; |
756 | 790 |
757 cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), | 791 if (nonce == NULL || realm == NULL) |
758 g_random_int()); | 792 purple_connection_error(js->gc, _("Invalid challenge from server")); |
759 nonce = g_hash_table_lookup(parts, "nonce"); | 793 else { |
760 | 794 GString *response = g_string_new(""); |
761 | 795 char *a2; |
762 a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); | 796 char *auth_resp; |
763 auth_resp = generate_response_value(js->user, | 797 char *buf; |
764 purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); | 798 char *cnonce; |
765 g_free(a2); | 799 |
766 | 800 cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), |
767 a2 = g_strdup_printf(":xmpp/%s", realm); | 801 g_random_int()); |
768 js->expected_rspauth = generate_response_value(js->user, | 802 |
769 purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); | 803 a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); |
770 g_free(a2); | 804 auth_resp = generate_response_value(js->user, |
771 | 805 purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); |
772 | 806 g_free(a2); |
773 g_string_append_printf(response, "username=\"%s\"", js->user->node); | 807 |
774 g_string_append_printf(response, ",realm=\"%s\"", realm); | 808 a2 = g_strdup_printf(":xmpp/%s", realm); |
775 g_string_append_printf(response, ",nonce=\"%s\"", nonce); | 809 js->expected_rspauth = generate_response_value(js->user, |
776 g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); | 810 purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); |
777 g_string_append_printf(response, ",nc=00000001"); | 811 g_free(a2); |
778 g_string_append_printf(response, ",qop=auth"); | 812 |
779 g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); | 813 g_string_append_printf(response, "username=\"%s\"", js->user->node); |
780 g_string_append_printf(response, ",response=%s", auth_resp); | 814 g_string_append_printf(response, ",realm=\"%s\"", realm); |
781 g_string_append_printf(response, ",charset=utf-8"); | 815 g_string_append_printf(response, ",nonce=\"%s\"", nonce); |
782 | 816 g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); |
783 g_free(auth_resp); | 817 g_string_append_printf(response, ",nc=00000001"); |
784 g_free(cnonce); | 818 g_string_append_printf(response, ",qop=auth"); |
785 | 819 g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); |
786 enc_out = purple_base64_encode((guchar *)response->str, response->len); | 820 g_string_append_printf(response, ",response=%s", auth_resp); |
787 | 821 g_string_append_printf(response, ",charset=utf-8"); |
788 purple_debug(PURPLE_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); | 822 |
789 | 823 g_free(auth_resp); |
790 buf = g_strdup_printf("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>", enc_out); | 824 g_free(cnonce); |
791 | 825 |
792 jabber_send_raw(js, buf, -1); | 826 enc_out = purple_base64_encode((guchar *)response->str, response->len); |
793 | 827 |
794 g_free(buf); | 828 purple_debug(PURPLE_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); |
795 | 829 |
796 g_free(enc_out); | 830 buf = g_strdup_printf("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>", enc_out); |
797 | 831 |
798 g_string_free(response, TRUE); | 832 jabber_send_raw(js, buf, -1); |
833 | |
834 g_free(buf); | |
835 | |
836 g_free(enc_out); | |
837 | |
838 g_string_free(response, TRUE); | |
839 } | |
799 } | 840 } |
800 | 841 |
801 g_free(enc_in); | 842 g_free(enc_in); |
802 g_free(dec_in); | 843 g_free(dec_in); |
803 g_hash_table_destroy(parts); | 844 g_hash_table_destroy(parts); |