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);