|
|
a3e2b5 |
From ad18012c46724aa097f37015a8036a4343206efe Mon Sep 17 00:00:00 2001
|
|
|
a3e2b5 |
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
|
|
a3e2b5 |
Date: Fri, 7 Dec 2018 12:47:14 +0100
|
|
|
a3e2b5 |
Subject: [PATCH] journal-remote: verify entry length from header
|
|
|
a3e2b5 |
MIME-Version: 1.0
|
|
|
a3e2b5 |
Content-Type: text/plain; charset=UTF-8
|
|
|
a3e2b5 |
Content-Transfer-Encoding: 8bit
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Calling mhd_respond(), which ulimately calls MHD_queue_response() is
|
|
|
a3e2b5 |
ineffective at point, becuase MHD_queue_response() immediately returns
|
|
|
a3e2b5 |
MHD_NO signifying an error, because the connection is in state
|
|
|
a3e2b5 |
MHD_CONNECTION_CONTINUE_SENT.
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
As Christian Grothoff kindly explained:
|
|
|
a3e2b5 |
> You are likely calling MHD_queue_repsonse() too late: once you are
|
|
|
a3e2b5 |
> receiving upload_data, HTTP forces you to process it all. At this time,
|
|
|
a3e2b5 |
> MHD has already sent "100 continue" and cannot take it back (hence you
|
|
|
a3e2b5 |
> get MHD_NO!).
|
|
|
a3e2b5 |
>
|
|
|
a3e2b5 |
> In your request handler, the first time when you are called for a
|
|
|
a3e2b5 |
> connection (and when hence *upload_data_size == 0 and upload_data ==
|
|
|
a3e2b5 |
> NULL) you must check the content-length header and react (with
|
|
|
a3e2b5 |
> MHD_queue_response) based on this (to prevent MHD from automatically
|
|
|
a3e2b5 |
> generating 100 continue).
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
If we ever encounter this kind of error, print a warning and immediately
|
|
|
a3e2b5 |
abort the connection. (The alternative would be to keep reading the data,
|
|
|
a3e2b5 |
but ignore it, and return an error after we get to the end of data.
|
|
|
a3e2b5 |
That is possible, but of course puts additional load on both the
|
|
|
a3e2b5 |
sender and reciever, and doesn't seem important enough just to return
|
|
|
a3e2b5 |
a good error message.)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Note that sending of the error does not work (the connection is always aborted
|
|
|
a3e2b5 |
when MHD_queue_response is used with MHD_RESPMEM_MUST_FREE, as in this case)
|
|
|
a3e2b5 |
with libµhttpd 0.59, but works with 0.61:
|
|
|
a3e2b5 |
https://src.fedoraproject.org/rpms/libmicrohttpd/pull-request/1
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
(cherry-picked from commit 7fdb237f5473cb8fc2129e57e8a0039526dcb4fd)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Related: #1664977
|
|
|
a3e2b5 |
---
|
|
|
a3e2b5 |
src/journal-remote/journal-remote-main.c | 34 +++++++++++++++++-------
|
|
|
a3e2b5 |
1 file changed, 24 insertions(+), 10 deletions(-)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c
|
|
|
a3e2b5 |
index 8fda9d1499..e9b3702e8a 100644
|
|
|
a3e2b5 |
--- a/src/journal-remote/journal-remote-main.c
|
|
|
a3e2b5 |
+++ b/src/journal-remote/journal-remote-main.c
|
|
|
a3e2b5 |
@@ -210,16 +210,14 @@ static int process_http_upload(
|
|
|
a3e2b5 |
journal_remote_server_global->seal);
|
|
|
a3e2b5 |
if (r == -EAGAIN)
|
|
|
a3e2b5 |
break;
|
|
|
a3e2b5 |
- else if (r < 0) {
|
|
|
a3e2b5 |
- log_warning("Failed to process data for connection %p", connection);
|
|
|
a3e2b5 |
+ if (r < 0) {
|
|
|
a3e2b5 |
if (r == -E2BIG)
|
|
|
a3e2b5 |
- return mhd_respondf(connection,
|
|
|
a3e2b5 |
- r, MHD_HTTP_PAYLOAD_TOO_LARGE,
|
|
|
a3e2b5 |
- "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
|
|
|
a3e2b5 |
+ log_warning_errno(r, "Entry is too above maximum of %u, aborting connection %p.",
|
|
|
a3e2b5 |
+ DATA_SIZE_MAX, connection);
|
|
|
a3e2b5 |
else
|
|
|
a3e2b5 |
- return mhd_respondf(connection,
|
|
|
a3e2b5 |
- r, MHD_HTTP_UNPROCESSABLE_ENTITY,
|
|
|
a3e2b5 |
- "Processing failed: %m.");
|
|
|
a3e2b5 |
+ log_warning_errno(r, "Failed to process data, aborting connection %p: %m",
|
|
|
a3e2b5 |
+ connection);
|
|
|
a3e2b5 |
+ return MHD_NO;
|
|
|
a3e2b5 |
}
|
|
|
a3e2b5 |
}
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
@@ -253,6 +251,7 @@ static int request_handler(
|
|
|
a3e2b5 |
const char *header;
|
|
|
a3e2b5 |
int r, code, fd;
|
|
|
a3e2b5 |
_cleanup_free_ char *hostname = NULL;
|
|
|
a3e2b5 |
+ size_t len;
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
assert(connection);
|
|
|
a3e2b5 |
assert(connection_cls);
|
|
|
a3e2b5 |
@@ -272,12 +271,27 @@ static int request_handler(
|
|
|
a3e2b5 |
if (!streq(url, "/upload"))
|
|
|
a3e2b5 |
return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
- header = MHD_lookup_connection_value(connection,
|
|
|
a3e2b5 |
- MHD_HEADER_KIND, "Content-Type");
|
|
|
a3e2b5 |
+ header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type");
|
|
|
a3e2b5 |
if (!header || !streq(header, "application/vnd.fdo.journal"))
|
|
|
a3e2b5 |
return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
|
|
|
a3e2b5 |
"Content-Type: application/vnd.fdo.journal is required.");
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
+ header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length");
|
|
|
a3e2b5 |
+ if (!header)
|
|
|
a3e2b5 |
+ return mhd_respond(connection, MHD_HTTP_LENGTH_REQUIRED,
|
|
|
a3e2b5 |
+ "Content-Length header is required.");
|
|
|
a3e2b5 |
+ r = safe_atozu(header, &len;;
|
|
|
a3e2b5 |
+ if (r < 0)
|
|
|
a3e2b5 |
+ return mhd_respondf(connection, r, MHD_HTTP_LENGTH_REQUIRED,
|
|
|
a3e2b5 |
+ "Content-Length: %s cannot be parsed: %m", header);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (len > ENTRY_SIZE_MAX)
|
|
|
a3e2b5 |
+ /* When serialized, an entry of maximum size might be slightly larger,
|
|
|
a3e2b5 |
+ * so this does not correspond exactly to the limit in journald. Oh well.
|
|
|
a3e2b5 |
+ */
|
|
|
a3e2b5 |
+ return mhd_respondf(connection, 0, MHD_HTTP_PAYLOAD_TOO_LARGE,
|
|
|
a3e2b5 |
+ "Payload larger than maximum size of %u bytes", ENTRY_SIZE_MAX);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
{
|
|
|
a3e2b5 |
const union MHD_ConnectionInfo *ci;
|
|
|
a3e2b5 |
|