From: Robin H. Johnson Date: Sun, 15 Dec 2013 20:26:19 +0000 (-0800) Subject: rgw: Fix CORS allow-headers validation X-Git-Tag: v0.67.6~22 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=585e0e7eec1bbee60fe352166b593d53476003f8;p=ceph.git rgw: Fix CORS allow-headers validation This fix is needed because Ceph presently validates CORS headers in a case-sensitive manner. Keeps a local cache of lowercased allowed headers to avoid converting the allowed headers to lowercase each time. CORS 6.2.6: If any of the header field-names is not a ASCII case-insensitive match for any of the values in list of headers do not set any additional headers and terminate this set of steps. Signed-off-by: Robin H. Johnson Reviewed-by: Yehuda Sadeh (cherry picked from commit 31b60bfd9347a386ff12b4e4f1812d664bcfff01) --- diff --git a/src/rgw/rgw_cors.cc b/src/rgw/rgw_cors.cc index 4be83605b502..a120a6866455 100644 --- a/src/rgw/rgw_cors.cc +++ b/src/rgw/rgw_cors.cc @@ -51,6 +51,33 @@ void RGWCORSRule::erase_origin_if_present(string& origin, bool *rule_empty) { } } +/* + * make attrs look-like-this + * does not convert underscores or dashes + * + * Per CORS specification, section 3: + * === + * "Converting a string to ASCII lowercase" means replacing all characters in the + * range U+0041 LATIN CAPITAL LETTER A to U+005A LATIN CAPITAL LETTER Z with + * the corresponding characters in the range U+0061 LATIN SMALL LETTER A to + * U+007A LATIN SMALL LETTER Z). + * === + * + * @todo When UTF-8 is allowed in HTTP headers, this function will need to change + */ +string lowercase_http_attr(const string& orig) +{ + const char *s = orig.c_str(); + char buf[orig.size() + 1]; + buf[orig.size()] = '\0'; + + for (size_t i = 0; i < orig.size(); ++i, ++s) { + buf[i] = tolower(*s); + } + return string(buf); +} + + static bool is_string_in_set(set& s, string h) { if ((s.find("*") != s.end()) || (s.find(h) != s.end())) { @@ -96,7 +123,13 @@ bool RGWCORSRule::is_origin_present(const char *o) { bool RGWCORSRule::is_header_allowed(const char *h, size_t len) { string hdr(h, len); - return is_string_in_set(allowed_hdrs, hdr); + if(lowercase_allowed_hdrs.empty()) { + set::iterator iter; + for (iter = allowed_hdrs.begin(); iter != allowed_hdrs.end(); ++iter) { + lowercase_allowed_hdrs.insert(lowercase_http_attr(*iter)); + } + } + return is_string_in_set(lowercase_allowed_hdrs, lowercase_http_attr(hdr)); } void RGWCORSRule::format_exp_headers(string& s) { diff --git a/src/rgw/rgw_cors.h b/src/rgw/rgw_cors.h index 1e0ec3bc7ece..124ebf92a7f2 100644 --- a/src/rgw/rgw_cors.h +++ b/src/rgw/rgw_cors.h @@ -41,7 +41,8 @@ protected: uint32_t max_age; uint8_t allowed_methods; std::string id; - std::set allowed_hdrs; + std::set allowed_hdrs; /* If you change this, you need to discard lowercase_allowed_hdrs */ + std::set lowercase_allowed_hdrs; /* Not built until needed in RGWCORSRule::is_header_allowed */ std::set allowed_origins; std::list exposable_hdrs; diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 72ff5214f435..25af50fe2386 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -470,6 +470,11 @@ int RGWOp::read_bucket_cors() return 0; } +/** CORS 6.2.6. + * If any of the header field-names is not a ASCII case-insensitive match for + * any of the values in list of headers do not set any additional headers and + * terminate this set of steps. + * */ static void get_cors_response_headers(RGWCORSRule *rule, const char *req_hdrs, string& hdrs, string& exp_hdrs, unsigned *max_age) { if (req_hdrs) { list hl; @@ -487,12 +492,20 @@ static void get_cors_response_headers(RGWCORSRule *rule, const char *req_hdrs, s *max_age = rule->get_max_age(); } +/** + * Generate the CORS header response + * + * This is described in the CORS standard, section 6.2. + */ bool RGWOp::generate_cors_headers(string& origin, string& method, string& headers, string& exp_headers, unsigned *max_age) { + /* CORS 6.2.1. */ const char *orig = s->info.env->get("HTTP_ORIGIN"); if (!orig) { return false; } + + /* Custom: */ origin = orig; int ret = read_bucket_cors(); if (ret < 0) { @@ -504,10 +517,12 @@ bool RGWOp::generate_cors_headers(string& origin, string& method, string& header return false; } + /* CORS 6.2.2. */ RGWCORSRule *rule = bucket_cors.host_name_rule(orig); if (!rule) return false; + /* CORS 6.2.3. */ const char *req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD"); if (!req_meth) { req_meth = s->info.method; @@ -515,13 +530,15 @@ bool RGWOp::generate_cors_headers(string& origin, string& method, string& header if (req_meth) method = req_meth; - + /* CORS 6.2.5. */ if (!validate_cors_rule_method(rule, req_meth)) { return false; } + /* CORS 6.2.4. */ const char *req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS"); + /* CORS 6.2.6. */ get_cors_response_headers(rule, req_hdrs, headers, exp_headers, max_age); return true;