diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-01-19 16:40:57 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-01-19 16:40:57 +0000 |
commit | b54d68ff219a354920cf650bbb5418c5c2af5b66 (patch) | |
tree | e47a810a7bb1c81a2edbcbc14b1b6311d1d28c7f | |
parent | 24fe454f1044082ea0f9f457fd7570387d79e50a (diff) | |
download | libmicrohttpd-b54d68ff219a354920cf650bbb5418c5c2af5b66.tar.gz libmicrohttpd-b54d68ff219a354920cf650bbb5418c5c2af5b66.zip |
properly fixing #2059, keeping the check that the uri from the nonce generation is exactly the same as the primary uri we got from the HTTP request
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | src/daemon/connection.c | 11 | ||||
-rw-r--r-- | src/daemon/digestauth.c | 165 | ||||
-rw-r--r-- | src/include/microhttpd.h | 2 | ||||
-rw-r--r-- | src/testcurl/Makefile.am | 8 |
5 files changed, 175 insertions, 15 deletions
@@ -1,3 +1,7 @@ | |||
1 | Thu Jan 19 13:31:27 CET 2012 | ||
2 | Fixing digest authentication for GET requests with URI arguments | ||
3 | (#2059). -CG | ||
4 | |||
1 | Sat Jan 7 17:30:48 CET 2012 | 5 | Sat Jan 7 17:30:48 CET 2012 |
2 | Digest authentication expects nonce count in base 16, not base 10 | 6 | Digest authentication expects nonce count in base 16, not base 10 |
3 | (#2061). -tclaveirole | 7 | (#2061). -tclaveirole |
diff --git a/src/daemon/connection.c b/src/daemon/connection.c index 70e1ad91..7d973283 100644 --- a/src/daemon/connection.c +++ b/src/daemon/connection.c | |||
@@ -1066,6 +1066,12 @@ connection_add_header (struct MHD_Connection *connection, | |||
1066 | } | 1066 | } |
1067 | 1067 | ||
1068 | /** | 1068 | /** |
1069 | * Parse and unescape the arguments given by the client as part | ||
1070 | * of the HTTP request URI. | ||
1071 | * | ||
1072 | * @param kind header kind to use for adding to the connection | ||
1073 | * @param connection connection to add headers to | ||
1074 | * @param args argument URI string (after "?" in URI) | ||
1069 | * @return MHD_NO on failure (out of memory), MHD_YES for success | 1075 | * @return MHD_NO on failure (out of memory), MHD_YES for success |
1070 | */ | 1076 | */ |
1071 | static int | 1077 | static int |
@@ -1111,6 +1117,7 @@ parse_arguments (enum MHD_ValueKind kind, | |||
1111 | return MHD_YES; | 1117 | return MHD_YES; |
1112 | } | 1118 | } |
1113 | 1119 | ||
1120 | |||
1114 | /** | 1121 | /** |
1115 | * Parse the cookie header (see RFC 2109). | 1122 | * Parse the cookie header (see RFC 2109). |
1116 | * | 1123 | * |
@@ -1238,7 +1245,7 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line) | |||
1238 | connection->daemon->uri_log_callback (connection->daemon-> | 1245 | connection->daemon->uri_log_callback (connection->daemon-> |
1239 | uri_log_callback_cls, uri); | 1246 | uri_log_callback_cls, uri); |
1240 | args = strstr (uri, "?"); | 1247 | args = strstr (uri, "?"); |
1241 | if (args != NULL) | 1248 | if (NULL != args) |
1242 | { | 1249 | { |
1243 | args[0] = '\0'; | 1250 | args[0] = '\0'; |
1244 | args++; | 1251 | args++; |
@@ -1248,7 +1255,7 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line) | |||
1248 | connection, | 1255 | connection, |
1249 | uri); | 1256 | uri); |
1250 | connection->url = uri; | 1257 | connection->url = uri; |
1251 | if (httpVersion == NULL) | 1258 | if (NULL == httpVersion) |
1252 | connection->version = ""; | 1259 | connection->version = ""; |
1253 | else | 1260 | else |
1254 | connection->version = httpVersion; | 1261 | connection->version = httpVersion; |
diff --git a/src/daemon/digestauth.c b/src/daemon/digestauth.c index 472811e8..1e976bb6 100644 --- a/src/daemon/digestauth.c +++ b/src/daemon/digestauth.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of libmicrohttpd | 2 | This file is part of libmicrohttpd |
3 | (C) 2010 Daniel Pittman and Christian Grothoff | 3 | (C) 2010, 2011, 2012 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 |
@@ -387,7 +387,7 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection) | |||
387 | * @param method HTTP method | 387 | * @param method HTTP method |
388 | * @param rnd A pointer to a character array for the random seed | 388 | * @param rnd A pointer to a character array for the random seed |
389 | * @param rnd_size The size of the random seed array | 389 | * @param rnd_size The size of the random seed array |
390 | * @param uri HTTP URI | 390 | * @param uri HTTP URI (in MHD, without the arguments ("?k=v") |
391 | * @param realm A string of characters that describes the realm of auth. | 391 | * @param realm A string of characters that describes the realm of auth. |
392 | * @param nonce A pointer to a character array for the nonce to put in | 392 | * @param nonce A pointer to a character array for the nonce to put in |
393 | */ | 393 | */ |
@@ -428,6 +428,114 @@ calculate_nonce (uint32_t nonce_time, | |||
428 | 428 | ||
429 | 429 | ||
430 | /** | 430 | /** |
431 | * Test if the given key-value pair is in the headers for the | ||
432 | * given connection. | ||
433 | * | ||
434 | * @param connection the connection | ||
435 | * @param key the key | ||
436 | * @param value the value, can be NULL | ||
437 | * @return MHD_YES if the key-value pair is in the headers, | ||
438 | * MHD_NO if not | ||
439 | */ | ||
440 | static int | ||
441 | test_header (struct MHD_Connection *connection, | ||
442 | const char *key, | ||
443 | const char *value) | ||
444 | { | ||
445 | struct MHD_HTTP_Header *pos; | ||
446 | |||
447 | for (pos = connection->headers_received; NULL != pos; pos = pos->next) | ||
448 | { | ||
449 | if (MHD_GET_ARGUMENT_KIND != pos->kind) | ||
450 | continue; | ||
451 | if (0 != strcmp (key, pos->header)) | ||
452 | continue; | ||
453 | if ( (NULL == value) && (NULL == pos->value)) | ||
454 | return MHD_YES; | ||
455 | if ( (NULL == value) || (NULL == pos->value)) | ||
456 | continue; | ||
457 | if (0 != strcmp (value, pos->value)) | ||
458 | continue; | ||
459 | return MHD_YES; | ||
460 | } | ||
461 | return MHD_NO; | ||
462 | } | ||
463 | |||
464 | |||
465 | /** | ||
466 | * Check that the arguments given by the client as part | ||
467 | * of the authentication header match the arguments we | ||
468 | * got as part of the HTTP request URI. | ||
469 | * | ||
470 | * @param connection connections with headers to compare against | ||
471 | * @param args argument URI string (after "?" in URI) | ||
472 | * @return MHD_YES if the arguments match, | ||
473 | * MHD_NO if not | ||
474 | */ | ||
475 | static int | ||
476 | check_argument_match (struct MHD_Connection *connection, | ||
477 | const char *args) | ||
478 | { | ||
479 | struct MHD_HTTP_Header *pos; | ||
480 | size_t slen = strlen (args) + 1; | ||
481 | char argb[slen]; | ||
482 | char *argp; | ||
483 | char *equals; | ||
484 | char *amper; | ||
485 | unsigned int num_headers; | ||
486 | |||
487 | num_headers = 0; | ||
488 | memcpy (argb, args, slen); | ||
489 | argp = argb; | ||
490 | while ( (argp != NULL) && | ||
491 | (argp[0] != '\0') ) | ||
492 | { | ||
493 | equals = strstr (argp, "="); | ||
494 | if (equals == NULL) | ||
495 | { | ||
496 | /* add with 'value' NULL */ | ||
497 | connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, | ||
498 | connection, | ||
499 | argp); | ||
500 | if (MHD_YES != test_header (connection, argp, NULL)) | ||
501 | return MHD_NO; | ||
502 | num_headers++; | ||
503 | break; | ||
504 | } | ||
505 | equals[0] = '\0'; | ||
506 | equals++; | ||
507 | amper = strstr (equals, "&"); | ||
508 | if (amper != NULL) | ||
509 | { | ||
510 | amper[0] = '\0'; | ||
511 | amper++; | ||
512 | } | ||
513 | connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, | ||
514 | connection, | ||
515 | argp); | ||
516 | connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, | ||
517 | connection, | ||
518 | equals); | ||
519 | if (! test_header (connection, argp, equals)) | ||
520 | return MHD_NO; | ||
521 | num_headers++; | ||
522 | argp = amper; | ||
523 | } | ||
524 | |||
525 | /* also check that the number of headers matches */ | ||
526 | for (pos = connection->headers_received; NULL != pos; pos = pos->next) | ||
527 | { | ||
528 | if (MHD_GET_ARGUMENT_KIND != pos->kind) | ||
529 | continue; | ||
530 | num_headers--; | ||
531 | } | ||
532 | if (0 != num_headers) | ||
533 | return MHD_NO; | ||
534 | return MHD_YES; | ||
535 | } | ||
536 | |||
537 | |||
538 | /** | ||
431 | * Authenticates the authorization header sent by the client | 539 | * Authenticates the authorization header sent by the client |
432 | * | 540 | * |
433 | * @param connection The MHD connection structure | 541 | * @param connection The MHD connection structure |
@@ -513,16 +621,40 @@ MHD_digest_auth_check(struct MHD_Connection *connection, | |||
513 | nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16); | 621 | nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16); |
514 | t = (uint32_t) time(NULL); | 622 | t = (uint32_t) time(NULL); |
515 | /* | 623 | /* |
516 | * First level vetting for the nonce validity | 624 | * First level vetting for the nonce validity if the timestamp |
517 | * if the timestamp attached to the nonce | 625 | * attached to the nonce exceeds `nonce_timeout' then the nonce is |
518 | * exceeds `nonce_timeout' then the nonce is | ||
519 | * invalid. | 626 | * invalid. |
520 | */ | 627 | */ |
521 | if ( (t > nonce_time + nonce_timeout) || | 628 | if ( (t > nonce_time + nonce_timeout) || |
522 | (0 != strncmp (uri, | 629 | (nonce_time + nonce_timeout < nonce_time) ) |
523 | connection->url, | 630 | return MHD_INVALID_NONCE; |
524 | strlen (connection->url))) ) | 631 | if (0 != strncmp (uri, |
525 | return MHD_INVALID_NONCE; | 632 | connection->url, |
633 | strlen (connection->url))) | ||
634 | { | ||
635 | #if HAVE_MESSAGES | ||
636 | MHD_DLOG (connection->daemon, | ||
637 | "Authentication failed, URI does not match.\n"); | ||
638 | #endif | ||
639 | return MHD_NO; | ||
640 | } | ||
641 | { | ||
642 | const char *args = strstr (uri, "?"); | ||
643 | if (args == NULL) | ||
644 | args = ""; | ||
645 | else | ||
646 | args++; | ||
647 | if (MHD_YES != | ||
648 | check_argument_match (connection, | ||
649 | args) ) | ||
650 | { | ||
651 | #if HAVE_MESSAGES | ||
652 | MHD_DLOG (connection->daemon, | ||
653 | "Authentication failed, arguments do not match.\n"); | ||
654 | #endif | ||
655 | return MHD_NO; | ||
656 | } | ||
657 | } | ||
526 | calculate_nonce (nonce_time, | 658 | calculate_nonce (nonce_time, |
527 | connection->method, | 659 | connection->method, |
528 | connection->daemon->digest_auth_random, | 660 | connection->daemon->digest_auth_random, |
@@ -550,12 +682,23 @@ MHD_digest_auth_check(struct MHD_Connection *connection, | |||
550 | (0 != strcmp (qop, "")) ) || | 682 | (0 != strcmp (qop, "")) ) || |
551 | (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) || | 683 | (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) || |
552 | (0 == lookup_sub_value(response, sizeof (response), header, "response")) ) | 684 | (0 == lookup_sub_value(response, sizeof (response), header, "response")) ) |
685 | { | ||
686 | #if HAVE_MESSAGES | ||
687 | MHD_DLOG (connection->daemon, | ||
688 | "Authentication failed, invalid format.\n"); | ||
689 | #endif | ||
553 | return MHD_NO; | 690 | return MHD_NO; |
691 | } | ||
554 | nci = strtoul (nc, &end, 16); | 692 | nci = strtoul (nc, &end, 16); |
555 | if ( ('\0' != *end) || | 693 | if ( ('\0' != *end) || |
556 | ( (LONG_MAX == nci) && (errno == ERANGE) ) ) | 694 | ( (LONG_MAX == nci) && (errno == ERANGE) ) ) |
557 | return MHD_NO; /* invalid nonce */ | 695 | { |
558 | 696 | #if HAVE_MESSAGES | |
697 | MHD_DLOG (connection->daemon, | ||
698 | "Authentication failed, invalid format.\n"); | ||
699 | #endif | ||
700 | return MHD_NO; /* invalid nonce format */ | ||
701 | } | ||
559 | /* | 702 | /* |
560 | * Checking if that combination of nonce and nc is sound | 703 | * Checking if that combination of nonce and nc is sound |
561 | * and not a replay attack attempt. Also adds the nonce | 704 | * and not a replay attack attempt. Also adds the nonce |
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 4904dcb8..fd15b698 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -106,7 +106,7 @@ extern "C" | |||
106 | /** | 106 | /** |
107 | * Current version of the library. | 107 | * Current version of the library. |
108 | */ | 108 | */ |
109 | #define MHD_VERSION 0x00091101 | 109 | #define MHD_VERSION 0x00091102 |
110 | 110 | ||
111 | /** | 111 | /** |
112 | * MHD-internal return code for "YES". | 112 | * MHD-internal return code for "YES". |
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am index 74732f07..8f1563f3 100644 --- a/src/testcurl/Makefile.am +++ b/src/testcurl/Makefile.am | |||
@@ -63,7 +63,7 @@ noinst_PROGRAMS = \ | |||
63 | 63 | ||
64 | if ENABLE_DAUTH | 64 | if ENABLE_DAUTH |
65 | check_PROGRAMS += \ | 65 | check_PROGRAMS += \ |
66 | daemontest_digestauth | 66 | daemontest_digestauth daemontest_digestauth_with_arguments |
67 | endif | 67 | endif |
68 | 68 | ||
69 | TESTS = $(check_PROGRAMS) | 69 | TESTS = $(check_PROGRAMS) |
@@ -115,6 +115,12 @@ daemontest_digestauth_LDADD = \ | |||
115 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | 115 | $(top_builddir)/src/daemon/libmicrohttpd.la \ |
116 | @LIBCURL@ | 116 | @LIBCURL@ |
117 | 117 | ||
118 | daemontest_digestauth_with_arguments_SOURCES = \ | ||
119 | daemontest_digestauth_with_arguments.c | ||
120 | daemontest_digestauth_with_arguments_LDADD = \ | ||
121 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | ||
122 | @LIBCURL@ | ||
123 | |||
118 | daemontest_get_sendfile_SOURCES = \ | 124 | daemontest_get_sendfile_SOURCES = \ |
119 | daemontest_get_sendfile.c | 125 | daemontest_get_sendfile.c |
120 | daemontest_get_sendfile_LDADD = \ | 126 | daemontest_get_sendfile_LDADD = \ |