9ae3a8
From cb2dd196bc247db6a40ea2847f86b63e9a923351 Mon Sep 17 00:00:00 2001
9ae3a8
From: Richard Jones <rjones@redhat.com>
9ae3a8
Date: Thu, 11 Jun 2015 11:40:21 +0200
9ae3a8
Subject: [PATCH 21/30] curl: refuse to open URL from HTTP server without range
9ae3a8
 support
9ae3a8
9ae3a8
Message-id: <1434022828-13037-15-git-send-email-rjones@redhat.com>
9ae3a8
Patchwork-id: 65849
9ae3a8
O-Subject: [RHEL-7.2 qemu-kvm v3 PATCH 14/21] curl: refuse to open URL from HTTP server without range support
9ae3a8
Bugzilla: 1226684
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
9ae3a8
From: Fam Zheng <famz@redhat.com>
9ae3a8
9ae3a8
CURL driver requests partial data from server on guest IO req. For HTTP
9ae3a8
and HTTPS, it uses "Range: ***" in requests, and this will not work if
9ae3a8
server not accepting range. This patch does this check when open.
9ae3a8
9ae3a8
 * Removed curl_size_cb, which is not used: On one hand it's registered to
9ae3a8
   libcurl as CURLOPT_WRITEFUNCTION, instead of CURLOPT_HEADERFUNCTION,
9ae3a8
   which will get called with *data*, not *header*. On the other hand the
9ae3a8
   s->len is assigned unconditionally later.
9ae3a8
9ae3a8
   In this gone function, the sscanf for "Content-Length: %zd", on
9ae3a8
   (void *)ptr, which is not guaranteed to be zero-terminated, is
9ae3a8
   potentially a security bug. So this patch fixes it as a side-effect. The
9ae3a8
   bug is reported as: https://bugs.launchpad.net/qemu/+bug/1188943
9ae3a8
   (Note the bug is marked "private" so you might not be able to see it)
9ae3a8
9ae3a8
 * Introduced curl_header_cb, which is used to parse header and mark the
9ae3a8
   server as accepting range if "Accept-Ranges: bytes" line is seen from
9ae3a8
   response header. If protocol is HTTP or HTTPS, but server response has
9ae3a8
   no not this support, refuse to open this URL.
9ae3a8
9ae3a8
Note that python builtin module SimpleHTTPServer is an example of not
9ae3a8
supporting range, if you need to test this driver, get a better server
9ae3a8
or use internet URLs.
9ae3a8
9ae3a8
Signed-off-by: Fam Zheng <famz@redhat.com>
9ae3a8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
9ae3a8
Upstream-status: 3494d650273e619606c6cb2c38aa9b8b7bed98e2
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/curl.c | 24 ++++++++++++++++++------
9ae3a8
 1 file changed, 18 insertions(+), 6 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/curl.c b/block/curl.c
9ae3a8
index 67ea05f..e97682f 100644
9ae3a8
--- a/block/curl.c
9ae3a8
+++ b/block/curl.c
9ae3a8
@@ -106,6 +106,7 @@ typedef struct BDRVCURLState {
9ae3a8
     CURLState states[CURL_NUM_STATES];
9ae3a8
     char *url;
9ae3a8
     size_t readahead_size;
9ae3a8
+    bool accept_range;
9ae3a8
 } BDRVCURLState;
9ae3a8
 
9ae3a8
 static void curl_clean_state(CURLState *s);
9ae3a8
@@ -157,14 +158,15 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
9ae3a8
     return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
9ae3a8
+static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
9ae3a8
 {
9ae3a8
-    CURLState *s = ((CURLState*)opaque);
9ae3a8
+    BDRVCURLState *s = opaque;
9ae3a8
     size_t realsize = size * nmemb;
9ae3a8
-    size_t fsize;
9ae3a8
+    const char *accept_line = "Accept-Ranges: bytes";
9ae3a8
 
9ae3a8
-    if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
9ae3a8
-        s->s->len = fsize;
9ae3a8
+    if (realsize >= strlen(accept_line)
9ae3a8
+        && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) {
9ae3a8
+        s->accept_range = true;
9ae3a8
     }
9ae3a8
 
9ae3a8
     return realsize;
9ae3a8
@@ -483,8 +485,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
 
9ae3a8
     // Get file size
9ae3a8
 
9ae3a8
+    s->accept_range = false;
9ae3a8
     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
9ae3a8
-    curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
9ae3a8
+    curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION,
9ae3a8
+                     curl_header_cb);
9ae3a8
+    curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s);
9ae3a8
     if (curl_easy_perform(state->curl))
9ae3a8
         goto out;
9ae3a8
     curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
9ae3a8
@@ -494,6 +499,13 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
         s->len = (size_t)d;
9ae3a8
     else if(!s->len)
9ae3a8
         goto out;
9ae3a8
+    if ((!strncasecmp(s->url, "http://", strlen("http://"))
9ae3a8
+        || !strncasecmp(s->url, "https://", strlen("https://")))
9ae3a8
+        && !s->accept_range) {
9ae3a8
+        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
9ae3a8
+                "Server does not support 'range' (byte ranges).");
9ae3a8
+        goto out;
9ae3a8
+    }
9ae3a8
     DPRINTF("CURL: Size = %zd\n", s->len);
9ae3a8
 
9ae3a8
     curl_clean_state(state);
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8