Blame SOURCES/0084-journal-remote-verify-entry-length-from-header.patch

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