diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-10-05 19:23:26 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-10-05 19:23:26 +0200 |
commit | 0db81a9248b12abc74f153ebd642441d0f9c3e58 (patch) | |
tree | 01406d248d24a802445824ffda019200ce203ab2 | |
parent | bc8e12c8379d7bf1d99bf67260bcefadd77852a8 (diff) | |
download | libmicrohttpd-0db81a9248b12abc74f153ebd642441d0f9c3e58.tar.gz libmicrohttpd-0db81a9248b12abc74f153ebd642441d0f9c3e58.zip |
fix #5411
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | doc/libmicrohttpd.texi | 15 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 79 | ||||
-rw-r--r-- | src/microhttpd/response.c | 21 |
4 files changed, 90 insertions, 35 deletions
@@ -1,3 +1,13 @@ | |||
1 | Fri Oct 5 18:44:45 CEST 2018 | ||
2 | MHD_add_response_header() now prevents applications from | ||
3 | setting a "Transfer-Encoding" header to values other than | ||
4 | "identity" or "chunked" as other transfer encodings are | ||
5 | not supported by MHD. (Note that usually MHD will pick the | ||
6 | transfer encoding correctly automatically, but applications | ||
7 | can use the header to force a particular behavior.) | ||
8 | Fixing #5411 (never set Content-length if Transfer-Encoding | ||
9 | is given). -CG | ||
10 | |||
1 | Sat Jul 14 11:42:15 CEST 2018 | 11 | Sat Jul 14 11:42:15 CEST 2018 |
2 | Add MHD_OPTION_GNUTLS_PSK_CRED_HANDLER to allow use of PSK with | 12 | Add MHD_OPTION_GNUTLS_PSK_CRED_HANDLER to allow use of PSK with |
3 | TLS connections. -CG/TM | 13 | TLS connections. -CG/TM |
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi index d2886a25..b61c94ff 100644 --- a/doc/libmicrohttpd.texi +++ b/doc/libmicrohttpd.texi | |||
@@ -874,9 +874,9 @@ or higher. | |||
874 | @cindex TLS | 874 | @cindex TLS |
875 | @cindex PSK | 875 | @cindex PSK |
876 | Use pre-shared key for TLS credentials. | 876 | Use pre-shared key for TLS credentials. |
877 | Pass a pointer to callback of type | 877 | Pass a pointer to callback of type |
878 | @code{MHD_PskServerCredentialsCallback} and a closure. | 878 | @code{MHD_PskServerCredentialsCallback} and a closure. |
879 | The function will be called to | 879 | The function will be called to |
880 | retrieve the shared key for a given username. | 880 | retrieve the shared key for a given username. |
881 | 881 | ||
882 | @item MHD_OPTION_DIGEST_AUTH_RANDOM | 882 | @item MHD_OPTION_DIGEST_AUTH_RANDOM |
@@ -2050,6 +2050,17 @@ duplicated into memory blocks embedded in @var{response}. | |||
2050 | Notice that the strings must not hold newlines, carriage returns or tab | 2050 | Notice that the strings must not hold newlines, carriage returns or tab |
2051 | chars. | 2051 | chars. |
2052 | 2052 | ||
2053 | MHD_add_response_header() prevents applications from setting a | ||
2054 | ``Transfer-Encoding'' header to values other than ``identity'' or | ||
2055 | ``chunked'' as other transfer encodings are not supported by MHD. Note | ||
2056 | that usually MHD will pick the transfer encoding correctly | ||
2057 | automatically, but applications can use the header to force a | ||
2058 | particular behavior. | ||
2059 | |||
2060 | MHD_add_response_header() also prevents applications from setting a | ||
2061 | ``Content-Length'' header. MHD will automatically set a correct | ||
2062 | ``Content-Length'' header if it is possible and allowed. | ||
2063 | |||
2053 | Return @code{MHD_NO} on error (i.e. invalid header or content format or | 2064 | Return @code{MHD_NO} on error (i.e. invalid header or content format or |
2054 | memory allocation error). | 2065 | memory allocation error). |
2055 | @end deftypefun | 2066 | @end deftypefun |
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 1778c59b..60dc5eb4 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -1404,6 +1404,7 @@ try_grow_read_buffer (struct MHD_Connection *connection) | |||
1404 | static int | 1404 | static int |
1405 | build_header_response (struct MHD_Connection *connection) | 1405 | build_header_response (struct MHD_Connection *connection) |
1406 | { | 1406 | { |
1407 | struct MHD_Response *response = connection->response; | ||
1407 | size_t size; | 1408 | size_t size; |
1408 | size_t off; | 1409 | size_t off; |
1409 | struct MHD_HTTP_Header *pos; | 1410 | struct MHD_HTTP_Header *pos; |
@@ -1420,11 +1421,11 @@ build_header_response (struct MHD_Connection *connection) | |||
1420 | bool response_has_close; | 1421 | bool response_has_close; |
1421 | bool response_has_keepalive; | 1422 | bool response_has_keepalive; |
1422 | const char *have_encoding; | 1423 | const char *have_encoding; |
1423 | const char *have_content_length; | ||
1424 | int must_add_close; | 1424 | int must_add_close; |
1425 | int must_add_chunked_encoding; | 1425 | int must_add_chunked_encoding; |
1426 | int must_add_keep_alive; | 1426 | int must_add_keep_alive; |
1427 | int must_add_content_length; | 1427 | int must_add_content_length; |
1428 | int may_add_content_length; | ||
1428 | 1429 | ||
1429 | mhd_assert (NULL != connection->version); | 1430 | mhd_assert (NULL != connection->version); |
1430 | if (0 == connection->version[0]) | 1431 | if (0 == connection->version[0]) |
@@ -1458,7 +1459,7 @@ build_header_response (struct MHD_Connection *connection) | |||
1458 | size = off + 2; /* +2 for extra "\r\n" at the end */ | 1459 | size = off + 2; /* +2 for extra "\r\n" at the end */ |
1459 | kind = MHD_HEADER_KIND; | 1460 | kind = MHD_HEADER_KIND; |
1460 | if ( (0 == (connection->daemon->options & MHD_USE_SUPPRESS_DATE_NO_CLOCK)) && | 1461 | if ( (0 == (connection->daemon->options & MHD_USE_SUPPRESS_DATE_NO_CLOCK)) && |
1461 | (NULL == MHD_get_response_header (connection->response, | 1462 | (NULL == MHD_get_response_header (response, |
1462 | MHD_HTTP_HEADER_DATE)) ) | 1463 | MHD_HTTP_HEADER_DATE)) ) |
1463 | get_date_string (date, | 1464 | get_date_string (date, |
1464 | sizeof (date)); | 1465 | sizeof (date)); |
@@ -1486,30 +1487,35 @@ build_header_response (struct MHD_Connection *connection) | |||
1486 | switch (connection->state) | 1487 | switch (connection->state) |
1487 | { | 1488 | { |
1488 | case MHD_CONNECTION_FOOTERS_RECEIVED: | 1489 | case MHD_CONNECTION_FOOTERS_RECEIVED: |
1489 | response_has_close = MHD_check_response_header_s_token_ci (connection->response, | 1490 | response_has_close = MHD_check_response_header_s_token_ci (response, |
1490 | MHD_HTTP_HEADER_CONNECTION, | 1491 | MHD_HTTP_HEADER_CONNECTION, |
1491 | "close"); | 1492 | "close"); |
1492 | response_has_keepalive = MHD_check_response_header_s_token_ci (connection->response, | 1493 | response_has_keepalive = MHD_check_response_header_s_token_ci (response, |
1493 | MHD_HTTP_HEADER_CONNECTION, | 1494 | MHD_HTTP_HEADER_CONNECTION, |
1494 | "Keep-Alive"); | 1495 | "Keep-Alive"); |
1495 | client_requested_close = MHD_lookup_header_s_token_ci (connection, | 1496 | client_requested_close = MHD_lookup_header_s_token_ci (connection, |
1496 | MHD_HTTP_HEADER_CONNECTION, | 1497 | MHD_HTTP_HEADER_CONNECTION, |
1497 | "close"); | 1498 | "close"); |
1498 | 1499 | ||
1499 | if (0 != (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY)) | 1500 | if (0 != (response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY)) |
1500 | connection->keepalive = MHD_CONN_MUST_CLOSE; | 1501 | connection->keepalive = MHD_CONN_MUST_CLOSE; |
1501 | #ifdef UPGRADE_SUPPORT | 1502 | #ifdef UPGRADE_SUPPORT |
1502 | else if (NULL != connection->response->upgrade_handler) | 1503 | else if (NULL != response->upgrade_handler) |
1503 | /* If this connection will not be "upgraded", it must be closed. */ | 1504 | /* If this connection will not be "upgraded", it must be closed. */ |
1504 | connection->keepalive = MHD_CONN_MUST_CLOSE; | 1505 | connection->keepalive = MHD_CONN_MUST_CLOSE; |
1505 | #endif /* UPGRADE_SUPPORT */ | 1506 | #endif /* UPGRADE_SUPPORT */ |
1506 | 1507 | ||
1507 | /* now analyze chunked encoding situation */ | 1508 | /* now analyze chunked encoding situation */ |
1508 | connection->have_chunked_upload = false; | 1509 | connection->have_chunked_upload = false; |
1509 | 1510 | have_encoding = MHD_get_response_header (response, | |
1510 | if ( (MHD_SIZE_UNKNOWN == connection->response->total_size) && | 1511 | MHD_HTTP_HEADER_TRANSFER_ENCODING); |
1512 | if (NULL == have_encoding) | ||
1513 | may_add_content_length = MHD_YES; | ||
1514 | else | ||
1515 | may_add_content_length = MHD_NO; /* RFC 7230, Section 3.3.2 forbids header */ | ||
1516 | if ( (MHD_SIZE_UNKNOWN == response->total_size) && | ||
1511 | #ifdef UPGRADE_SUPPORT | 1517 | #ifdef UPGRADE_SUPPORT |
1512 | (NULL == connection->response->upgrade_handler) && | 1518 | (NULL == response->upgrade_handler) && |
1513 | #endif /* UPGRADE_SUPPORT */ | 1519 | #endif /* UPGRADE_SUPPORT */ |
1514 | (! response_has_close) && | 1520 | (! response_has_close) && |
1515 | (! client_requested_close) ) | 1521 | (! client_requested_close) ) |
@@ -1523,22 +1529,23 @@ build_header_response (struct MHD_Connection *connection) | |||
1523 | (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1, | 1529 | (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1, |
1524 | connection->version) ) ) | 1530 | connection->version) ) ) |
1525 | { | 1531 | { |
1526 | have_encoding = MHD_get_response_header (connection->response, | ||
1527 | MHD_HTTP_HEADER_TRANSFER_ENCODING); | ||
1528 | if (NULL == have_encoding) | 1532 | if (NULL == have_encoding) |
1529 | { | 1533 | { |
1530 | must_add_chunked_encoding = MHD_YES; | 1534 | must_add_chunked_encoding = MHD_YES; |
1531 | connection->have_chunked_upload = true; | 1535 | connection->have_chunked_upload = true; |
1532 | } | 1536 | } |
1533 | else if (MHD_str_equal_caseless_ (have_encoding, | ||
1534 | "identity")) | ||
1535 | { | ||
1536 | /* application forced identity encoding, can't do 'chunked' */ | ||
1537 | must_add_close = MHD_YES; | ||
1538 | } | ||
1539 | else | 1537 | else |
1540 | { | 1538 | { |
1541 | connection->have_chunked_upload = true; | 1539 | if (MHD_str_equal_caseless_ (have_encoding, |
1540 | "identity")) | ||
1541 | { | ||
1542 | /* application forced identity encoding, can't do 'chunked' */ | ||
1543 | must_add_close = MHD_YES; | ||
1544 | } | ||
1545 | else | ||
1546 | { | ||
1547 | connection->have_chunked_upload = true; | ||
1548 | } | ||
1542 | } | 1549 | } |
1543 | } | 1550 | } |
1544 | else | 1551 | else |
@@ -1556,23 +1563,29 @@ build_header_response (struct MHD_Connection *connection) | |||
1556 | (MHD_CONN_MUST_CLOSE == connection->keepalive)) && | 1563 | (MHD_CONN_MUST_CLOSE == connection->keepalive)) && |
1557 | (! response_has_close) && | 1564 | (! response_has_close) && |
1558 | #ifdef UPGRADE_SUPPORT | 1565 | #ifdef UPGRADE_SUPPORT |
1559 | (NULL == connection->response->upgrade_handler) && | 1566 | (NULL == response->upgrade_handler) && |
1560 | #endif /* UPGRADE_SUPPORT */ | 1567 | #endif /* UPGRADE_SUPPORT */ |
1561 | (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) ) | 1568 | (0 == (response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) ) |
1562 | must_add_close = MHD_YES; | 1569 | must_add_close = MHD_YES; |
1563 | 1570 | ||
1564 | /* check if we should add a 'content length' header */ | 1571 | /* check if we must add 'close' header because we cannot add content-length |
1565 | have_content_length = MHD_get_response_header (connection->response, | 1572 | because it is forbidden AND we don't have a 'chunked' encoding */ |
1566 | MHD_HTTP_HEADER_CONTENT_LENGTH); | 1573 | if ( (! may_add_content_length) && |
1567 | 1574 | (! connection->have_chunked_upload) && | |
1568 | /* MHD_HTTP_NO_CONTENT, MHD_HTTP_NOT_MODIFIED and 1xx-status | 1575 | (! response_has_close) ) |
1576 | must_add_close = MHD_YES; | ||
1577 | /* #MHD_HTTP_NO_CONTENT, #MHD_HTTP_NOT_MODIFIED and 1xx-status | ||
1569 | codes SHOULD NOT have a Content-Length according to spec; | 1578 | codes SHOULD NOT have a Content-Length according to spec; |
1570 | also chunked encoding / unknown length or CONNECT... */ | 1579 | also chunked encoding / unknown length or CONNECT... */ |
1571 | if ( (MHD_SIZE_UNKNOWN != connection->response->total_size) && | 1580 | if ( (MHD_SIZE_UNKNOWN != response->total_size) && |
1572 | (MHD_HTTP_NO_CONTENT != rc) && | 1581 | (MHD_HTTP_NO_CONTENT != rc) && |
1573 | (MHD_HTTP_NOT_MODIFIED != rc) && | 1582 | (MHD_HTTP_NOT_MODIFIED != rc) && |
1574 | (MHD_HTTP_OK <= rc) && | 1583 | (MHD_HTTP_OK <= rc) && |
1575 | (NULL == have_content_length) && | 1584 | (NULL == /* this should always succeed due to check in |
1585 | MHD_add_response_header() */ | ||
1586 | MHD_get_response_header (response, | ||
1587 | MHD_HTTP_HEADER_CONTENT_LENGTH)) && | ||
1588 | (may_add_content_length) && | ||
1576 | ( (NULL == connection->method) || | 1589 | ( (NULL == connection->method) || |
1577 | (! MHD_str_equal_caseless_ (connection->method, | 1590 | (! MHD_str_equal_caseless_ (connection->method, |
1578 | MHD_HTTP_METHOD_CONNECT)) ) ) | 1591 | MHD_HTTP_METHOD_CONNECT)) ) ) |
@@ -1596,7 +1609,7 @@ build_header_response (struct MHD_Connection *connection) | |||
1596 | = MHD_snprintf_ (content_length_buf, | 1609 | = MHD_snprintf_ (content_length_buf, |
1597 | sizeof (content_length_buf), | 1610 | sizeof (content_length_buf), |
1598 | MHD_HTTP_HEADER_CONTENT_LENGTH ": " MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n", | 1611 | MHD_HTTP_HEADER_CONTENT_LENGTH ": " MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n", |
1599 | (MHD_UNSIGNED_LONG_LONG) connection->response->total_size); | 1612 | (MHD_UNSIGNED_LONG_LONG) response->total_size); |
1600 | must_add_content_length = MHD_YES; | 1613 | must_add_content_length = MHD_YES; |
1601 | } | 1614 | } |
1602 | 1615 | ||
@@ -1606,7 +1619,7 @@ build_header_response (struct MHD_Connection *connection) | |||
1606 | (MHD_NO == must_add_close) && | 1619 | (MHD_NO == must_add_close) && |
1607 | (MHD_CONN_MUST_CLOSE != connection->keepalive) && | 1620 | (MHD_CONN_MUST_CLOSE != connection->keepalive) && |
1608 | #ifdef UPGRADE_SUPPORT | 1621 | #ifdef UPGRADE_SUPPORT |
1609 | (NULL == connection->response->upgrade_handler) && | 1622 | (NULL == response->upgrade_handler) && |
1610 | #endif /* UPGRADE_SUPPORT */ | 1623 | #endif /* UPGRADE_SUPPORT */ |
1611 | (MHD_YES == keepalive_possible (connection)) ) | 1624 | (MHD_YES == keepalive_possible (connection)) ) |
1612 | must_add_keep_alive = MHD_YES; | 1625 | must_add_keep_alive = MHD_YES; |
@@ -1638,7 +1651,7 @@ build_header_response (struct MHD_Connection *connection) | |||
1638 | mhd_assert (! (must_add_close && must_add_keep_alive) ); | 1651 | mhd_assert (! (must_add_close && must_add_keep_alive) ); |
1639 | mhd_assert (! (must_add_chunked_encoding && must_add_content_length) ); | 1652 | mhd_assert (! (must_add_chunked_encoding && must_add_content_length) ); |
1640 | 1653 | ||
1641 | for (pos = connection->response->first_header; NULL != pos; pos = pos->next) | 1654 | for (pos = response->first_header; NULL != pos; pos = pos->next) |
1642 | { | 1655 | { |
1643 | /* TODO: add proper support for excluding "Keep-Alive" token. */ | 1656 | /* TODO: add proper support for excluding "Keep-Alive" token. */ |
1644 | if ( (pos->kind == kind) && | 1657 | if ( (pos->kind == kind) && |
@@ -1700,7 +1713,7 @@ build_header_response (struct MHD_Connection *connection) | |||
1700 | content_length_len); | 1713 | content_length_len); |
1701 | off += content_length_len; | 1714 | off += content_length_len; |
1702 | } | 1715 | } |
1703 | for (pos = connection->response->first_header; NULL != pos; pos = pos->next) | 1716 | for (pos = response->first_header; NULL != pos; pos = pos->next) |
1704 | { | 1717 | { |
1705 | /* TODO: add proper support for excluding "Keep-Alive" token. */ | 1718 | /* TODO: add proper support for excluding "Keep-Alive" token. */ |
1706 | if ( (pos->kind == kind) && | 1719 | if ( (pos->kind == kind) && |
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index 9de4843d..d7835c20 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c | |||
@@ -119,6 +119,27 @@ MHD_add_response_header (struct MHD_Response *response, | |||
119 | const char *header, | 119 | const char *header, |
120 | const char *content) | 120 | const char *content) |
121 | { | 121 | { |
122 | if ( (MHD_str_equal_caseless_ (header, | ||
123 | MHD_HTTP_HEADER_TRANSFER_ENCODING)) && | ||
124 | (! MHD_str_equal_caseless_ (content, | ||
125 | "identity")) && | ||
126 | (! MHD_str_equal_caseless_ (content, | ||
127 | "chunked")) ) | ||
128 | { | ||
129 | /* Setting transfer encodings other than "identity" or | ||
130 | "chunked" is not allowed. Note that MHD will set the | ||
131 | correct transfer encoding if required automatically. */ | ||
132 | /* NOTE: for compressed bodies, use the "Content-encoding" header */ | ||
133 | return MHD_NO; | ||
134 | } | ||
135 | if (MHD_str_equal_caseless_ (header, | ||
136 | MHD_HTTP_HEADER_CONTENT_LENGTH)) | ||
137 | { | ||
138 | /* MHD will set Content-length if allowed and possible, | ||
139 | reject attempt by application */ | ||
140 | return MHD_NO; | ||
141 | } | ||
142 | |||
122 | return add_response_entry (response, | 143 | return add_response_entry (response, |
123 | MHD_HEADER_KIND, | 144 | MHD_HEADER_KIND, |
124 | header, | 145 | header, |