aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-05-29 10:20:53 +0000
committerChristian Grothoff <christian@grothoff.org>2015-05-29 10:20:53 +0000
commitf2f5b555500c4f965c330fcd6e5e6c428d21bcf0 (patch)
treebf77543636a9866e5d586f2c4c21f438cc8ed123
parentdd80b6137d38a9e01c37daea18e11542ec2edd05 (diff)
downloadlibmicrohttpd-f2f5b555500c4f965c330fcd6e5e6c428d21bcf0.tar.gz
libmicrohttpd-f2f5b555500c4f965c330fcd6e5e6c428d21bcf0.zip
fix digest authentication with escaped urls, as reported on mailinglist
-rw-r--r--ChangeLog4
-rw-r--r--src/microhttpd/digestauth.c207
-rw-r--r--src/testcurl/test_digestauth.c77
3 files changed, 149 insertions, 139 deletions
diff --git a/ChangeLog b/ChangeLog
index 57dd440f..2555527e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
1Fri May 29 12:23:01 CEST 2015
2 Fixing digest authentication when used in combination
3 with escaped characters in URLs. -CG/AW
4
1Wed May 13 11:49:09 CEST 2015 5Wed May 13 11:49:09 CEST 2015
2 Releasing libmicrohttpd 0.9.42. -CG 6 Releasing libmicrohttpd 0.9.42. -CG
3 7
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 9c3fe8c5..4cc7f61b 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of libmicrohttpd 2 This file is part of libmicrohttpd
3 Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff 3 Copyright (C) 2010, 2011, 2012, 2015 Daniel Pittman and Christian Grothoff
4 4
5 This library is free software; you can redistribute it and/or 5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public 6 modify it under the terms of the GNU Lesser General Public
@@ -472,8 +472,8 @@ test_header (struct MHD_Connection *connection,
472 * 472 *
473 * @param connection connections with headers to compare against 473 * @param connection connections with headers to compare against
474 * @param args argument URI string (after "?" in URI) 474 * @param args argument URI string (after "?" in URI)
475 * @return MHD_YES if the arguments match, 475 * @return #MHD_YES if the arguments match,
476 * MHD_NO if not 476 * #MHD_NO if not
477 */ 477 */
478static int 478static int
479check_argument_match (struct MHD_Connection *connection, 479check_argument_match (struct MHD_Connection *connection,
@@ -632,10 +632,83 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
632 header value. */ 632 header value. */
633 return MHD_NO; 633 return MHD_NO;
634 } 634 }
635 /* 8 = 4 hexadecimal numbers for the timestamp */
636 nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16);
637 t = (uint32_t) MHD_monotonic_time();
638 /*
639 * First level vetting for the nonce validity: if the timestamp
640 * attached to the nonce exceeds `nonce_timeout', then the nonce is
641 * invalid.
642 */
643 if ( (t > nonce_time + nonce_timeout) ||
644 (nonce_time + nonce_timeout < nonce_time) )
645 {
646 /* too old */
647 return MHD_INVALID_NONCE;
648 }
649
650 calculate_nonce (nonce_time,
651 connection->method,
652 connection->daemon->digest_auth_random,
653 connection->daemon->digest_auth_rand_size,
654 connection->url,
655 realm,
656 noncehashexp);
657 /*
658 * Second level vetting for the nonce validity
659 * if the timestamp attached to the nonce is valid
660 * and possibly fabricated (in case of an attack)
661 * the attacker must also know the random seed to be
662 * able to generate a "sane" nonce, which if he does
663 * not, the nonce fabrication process going to be
664 * very hard to achieve.
665 */
666
667 if (0 != strcmp (nonce, noncehashexp))
668 {
669 return MHD_INVALID_NONCE;
670 }
671 if ( (0 == lookup_sub_value (cnonce,
672 sizeof (cnonce),
673 header, "cnonce")) ||
674 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
675 ( (0 != strcmp (qop, "auth")) &&
676 (0 != strcmp (qop, "")) ) ||
677 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
678 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
679 {
680#if HAVE_MESSAGES
681 MHD_DLOG (connection->daemon,
682 "Authentication failed, invalid format.\n");
683#endif
684 return MHD_NO;
685 }
686 nci = strtoul (nc, &end, 16);
687 if ( ('\0' != *end) ||
688 ( (LONG_MAX == nci) &&
689 (ERANGE == errno) ) )
690 {
691#if HAVE_MESSAGES
692 MHD_DLOG (connection->daemon,
693 "Authentication failed, invalid format.\n");
694#endif
695 return MHD_NO; /* invalid nonce format */
696 }
697 /*
698 * Checking if that combination of nonce and nc is sound
699 * and not a replay attack attempt. Also adds the nonce
700 * to the nonce-nc map if it does not exist there.
701 */
702
703 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
704 {
705 return MHD_NO;
706 }
707
635 { 708 {
636 char *uri; 709 char *uri;
637 710
638 uri = malloc(left + 1); 711 uri = malloc (left + 1);
639 if (NULL == uri) 712 if (NULL == uri)
640 { 713 {
641#if HAVE_MESSAGES 714#if HAVE_MESSAGES
@@ -648,24 +721,31 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
648 left + 1, 721 left + 1,
649 header, "uri")) 722 header, "uri"))
650 { 723 {
651 free(uri); 724 free (uri);
652 return MHD_NO; 725 return MHD_NO;
653 } 726 }
654 727
655 /* 8 = 4 hexadecimal numbers for the timestamp */ 728 digest_calc_ha1("md5",
656 nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16); 729 username,
657 t = (uint32_t) MHD_monotonic_time(); 730 realm,
658 /* 731 password,
659 * First level vetting for the nonce validity: if the timestamp 732 nonce,
660 * attached to the nonce exceeds `nonce_timeout', then the nonce is 733 cnonce,
661 * invalid. 734 ha1);
662 */ 735 digest_calc_response (ha1,
663 if ( (t > nonce_time + nonce_timeout) || 736 nonce,
664 (nonce_time + nonce_timeout < nonce_time) ) 737 nc,
665 { 738 cnonce,
666 free(uri); 739 qop,
667 return MHD_INVALID_NONCE; 740 connection->method,
668 } 741 uri,
742 hentity,
743 respexp);
744
745 /* Need to unescape URI before comparing with connection->url */
746 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
747 connection,
748 uri);
669 if (0 != strncmp (uri, 749 if (0 != strncmp (uri,
670 connection->url, 750 connection->url,
671 strlen (connection->url))) 751 strlen (connection->url)))
@@ -674,9 +754,10 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
674 MHD_DLOG (connection->daemon, 754 MHD_DLOG (connection->daemon,
675 "Authentication failed, URI does not match.\n"); 755 "Authentication failed, URI does not match.\n");
676#endif 756#endif
677 free(uri); 757 free (uri);
678 return MHD_NO; 758 return MHD_NO;
679 } 759 }
760
680 { 761 {
681 const char *args = strchr (uri, '?'); 762 const char *args = strchr (uri, '?');
682 763
@@ -692,89 +773,11 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
692 MHD_DLOG (connection->daemon, 773 MHD_DLOG (connection->daemon,
693 "Authentication failed, arguments do not match.\n"); 774 "Authentication failed, arguments do not match.\n");
694#endif 775#endif
695 free(uri); 776 free (uri);
696 return MHD_NO; 777 return MHD_NO;
697 } 778 }
698 } 779 }
699 calculate_nonce (nonce_time, 780 free (uri);
700 connection->method,
701 connection->daemon->digest_auth_random,
702 connection->daemon->digest_auth_rand_size,
703 connection->url,
704 realm,
705 noncehashexp);
706 /*
707 * Second level vetting for the nonce validity
708 * if the timestamp attached to the nonce is valid
709 * and possibly fabricated (in case of an attack)
710 * the attacker must also know the random seed to be
711 * able to generate a "sane" nonce, which if he does
712 * not, the nonce fabrication process going to be
713 * very hard to achieve.
714 */
715
716 if (0 != strcmp (nonce, noncehashexp))
717 {
718 free(uri);
719 return MHD_INVALID_NONCE;
720 }
721 if ( (0 == lookup_sub_value (cnonce,
722 sizeof (cnonce),
723 header, "cnonce")) ||
724 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
725 ( (0 != strcmp (qop, "auth")) &&
726 (0 != strcmp (qop, "")) ) ||
727 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
728 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
729 {
730#if HAVE_MESSAGES
731 MHD_DLOG (connection->daemon,
732 "Authentication failed, invalid format.\n");
733#endif
734 free(uri);
735 return MHD_NO;
736 }
737 nci = strtoul (nc, &end, 16);
738 if ( ('\0' != *end) ||
739 ( (LONG_MAX == nci) &&
740 (ERANGE == errno) ) )
741 {
742#if HAVE_MESSAGES
743 MHD_DLOG (connection->daemon,
744 "Authentication failed, invalid format.\n");
745#endif
746 free(uri);
747 return MHD_NO; /* invalid nonce format */
748 }
749 /*
750 * Checking if that combination of nonce and nc is sound
751 * and not a replay attack attempt. Also adds the nonce
752 * to the nonce-nc map if it does not exist there.
753 */
754
755 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
756 {
757 free(uri);
758 return MHD_NO;
759 }
760
761 digest_calc_ha1("md5",
762 username,
763 realm,
764 password,
765 nonce,
766 cnonce,
767 ha1);
768 digest_calc_response (ha1,
769 nonce,
770 nc,
771 cnonce,
772 qop,
773 connection->method,
774 uri,
775 hentity,
776 respexp);
777 free(uri);
778 return (0 == strcmp(response, respexp)) 781 return (0 == strcmp(response, respexp))
779 ? MHD_YES 782 ? MHD_YES
780 : MHD_NO; 783 : MHD_NO;
@@ -835,7 +838,7 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
835 : ""); 838 : "");
836 { 839 {
837 char *header; 840 char *header;
838 841
839 header = malloc(hlen + 1); 842 header = malloc(hlen + 1);
840 if (NULL == header) 843 if (NULL == header)
841 { 844 {
diff --git a/src/testcurl/test_digestauth.c b/src/testcurl/test_digestauth.c
index 255e086a..fd579196 100644
--- a/src/testcurl/test_digestauth.c
+++ b/src/testcurl/test_digestauth.c
@@ -73,7 +73,8 @@ ahc_echo (void *cls,
73 const char *url, 73 const char *url,
74 const char *method, 74 const char *method,
75 const char *version, 75 const char *version,
76 const char *upload_data, size_t *upload_data_size, 76 const char *upload_data,
77 size_t *upload_data_size,
77 void **unused) 78 void **unused)
78{ 79{
79 struct MHD_Response *response; 80 struct MHD_Response *response;
@@ -82,44 +83,47 @@ ahc_echo (void *cls,
82 const char *realm = "test@example.com"; 83 const char *realm = "test@example.com";
83 int ret; 84 int ret;
84 85
85 username = MHD_digest_auth_get_username(connection); 86 username = MHD_digest_auth_get_username (connection);
86 if ( (username == NULL) || 87 if ( (username == NULL) ||
87 (0 != strcmp (username, "testuser")) ) 88 (0 != strcmp (username, "testuser")) )
88 { 89 {
89 response = MHD_create_response_from_buffer(strlen (DENIED), 90 response = MHD_create_response_from_buffer (strlen (DENIED),
90 DENIED, 91 DENIED,
91 MHD_RESPMEM_PERSISTENT); 92 MHD_RESPMEM_PERSISTENT);
92 ret = MHD_queue_auth_fail_response(connection, realm, 93 ret = MHD_queue_auth_fail_response(connection, realm,
93 MY_OPAQUE, 94 MY_OPAQUE,
94 response, 95 response,
95 MHD_NO); 96 MHD_NO);
96 MHD_destroy_response(response); 97 MHD_destroy_response(response);
97 return ret; 98 return ret;
98 } 99 }
99 ret = MHD_digest_auth_check(connection, realm, 100 ret = MHD_digest_auth_check(connection, realm,
100 username, 101 username,
101 password, 102 password,
102 300); 103 300);
103 free(username); 104 free(username);
104 if ( (ret == MHD_INVALID_NONCE) || 105 if ( (ret == MHD_INVALID_NONCE) ||
105 (ret == MHD_NO) ) 106 (ret == MHD_NO) )
106 { 107 {
107 response = MHD_create_response_from_buffer(strlen (DENIED), 108 response = MHD_create_response_from_buffer(strlen (DENIED),
108 DENIED, 109 DENIED,
109 MHD_RESPMEM_PERSISTENT); 110 MHD_RESPMEM_PERSISTENT);
110 if (NULL == response) 111 if (NULL == response)
111 return MHD_NO; 112 return MHD_NO;
112 ret = MHD_queue_auth_fail_response(connection, realm, 113 ret = MHD_queue_auth_fail_response(connection, realm,
113 MY_OPAQUE, 114 MY_OPAQUE,
114 response, 115 response,
115 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO); 116 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
116 MHD_destroy_response(response); 117 MHD_destroy_response(response);
117 return ret; 118 return ret;
118 } 119 }
119 response = MHD_create_response_from_buffer(strlen(PAGE), PAGE, 120 response = MHD_create_response_from_buffer (strlen(PAGE),
120 MHD_RESPMEM_PERSISTENT); 121 PAGE,
121 ret = MHD_queue_response(connection, MHD_HTTP_OK, response); 122 MHD_RESPMEM_PERSISTENT);
122 MHD_destroy_response(response); 123 ret = MHD_queue_response (connection,
124 MHD_HTTP_OK,
125 response);
126 MHD_destroy_response (response);
123 return ret; 127 return ret;
124} 128}
125 129
@@ -144,24 +148,24 @@ testDigestAuth ()
144 fd = open("/dev/urandom", O_RDONLY); 148 fd = open("/dev/urandom", O_RDONLY);
145 if (-1 == fd) 149 if (-1 == fd)
146 { 150 {
147 fprintf(stderr, "Failed to open `%s': %s\n", 151 fprintf(stderr, "Failed to open `%s': %s\n",
148 "/dev/urandom", 152 "/dev/urandom",
149 strerror(errno)); 153 strerror(errno));
150 return 1; 154 return 1;
151 } 155 }
152 while (off < 8) 156 while (off < 8)
153 { 157 {
154 len = read(fd, rnd, 8); 158 len = read(fd, rnd, 8);
155 if (len == -1) 159 if (len == -1)
156 { 160 {
157 fprintf(stderr, "Failed to read `%s': %s\n", 161 fprintf(stderr, "Failed to read `%s': %s\n",
158 "/dev/urandom", 162 "/dev/urandom",
159 strerror(errno)); 163 strerror(errno));
160 (void) close(fd); 164 (void) close(fd);
161 return 1; 165 return 1;
162 } 166 }
163 off += len; 167 off += len;
164 } 168 }
165 (void) close(fd); 169 (void) close(fd);
166#else 170#else
167 { 171 {
@@ -193,7 +197,7 @@ testDigestAuth ()
193 if (d == NULL) 197 if (d == NULL)
194 return 1; 198 return 1;
195 c = curl_easy_init (); 199 c = curl_easy_init ();
196 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1337/"); 200 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1337/bar%20 foo?a=bü%20");
197 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); 201 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
198 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 202 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
199 curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 203 curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
@@ -225,7 +229,6 @@ testDigestAuth ()
225} 229}
226 230
227 231
228
229int 232int
230main (int argc, char *const *argv) 233main (int argc, char *const *argv)
231{ 234{