]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: reject control characters in response-header actions 34504/head
authorRobin H. Johnson <rjohnson@digitalocean.com>
Fri, 27 Mar 2020 19:48:13 +0000 (20:48 +0100)
committerAbhishek Lekshmanan <abhishek@suse.com>
Wed, 8 Apr 2020 15:35:15 +0000 (17:35 +0200)
S3 GetObject permits overriding response header values, but those inputs
need to be validated to insure only characters that are valid in an HTTP
header value are present.

Credit: Initial vulnerability discovery by William Bowling (@wcbowling)
Credit: Further vulnerability discovery by Robin H. Johnson <rjohnson@digitalocean.com>
Signed-off-by: Robin H. Johnson <rjohnson@digitalocean.com>
src/rgw/rgw_rest_s3.cc

index dc49caae18d2d4860629b072aa710ca78c12fb69..459dd1dc7155953453cda22eec85ca117a11d2c4 100644 (file)
@@ -167,6 +167,15 @@ int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr
   return 0;
 }
 
+inline bool str_has_cntrl(const std::string s) {
+  return std::any_of(s.begin(), s.end(), ::iscntrl);
+}
+
+inline bool str_has_cntrl(const char* s) {
+  std::string _s(s);
+  return str_has_cntrl(_s);
+}
+
 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
                                              off_t bl_len)
 {
@@ -271,6 +280,19 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
        if (s->auth.identity->is_anonymous()) {
          return -ERR_INVALID_REQUEST;
        }
+        /* HTTP specification says no control characters should be present in
+         * header values: https://tools.ietf.org/html/rfc7230#section-3.2
+         *      field-vchar    = VCHAR / obs-text
+         *
+         * Failure to validate this permits a CRLF injection in HTTP headers,
+         * whereas S3 GetObject only permits specific headers.
+         */
+        if(str_has_cntrl(val)) {
+          /* TODO: return a more distinct error in future;
+           * stating what the problem is */
+          return -ERR_INVALID_REQUEST;
+        }
+
        if (strcmp(p->param, "response-content-type") != 0) {
          response_attrs[p->http_attr] = val;
        } else {