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 <robbat2@gentoo.org>
Reviewed-by: Yehuda Sadeh <yehuda@inktank.com>
(cherry picked from commit
31b60bfd9347a386ff12b4e4f1812d664bcfff01)
}
}
+/*
+ * 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<string>& s, string h) {
if ((s.find("*") != s.end()) ||
(s.find(h) != s.end())) {
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<string>::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) {
uint32_t max_age;
uint8_t allowed_methods;
std::string id;
- std::set<string> allowed_hdrs;
+ std::set<string> allowed_hdrs; /* If you change this, you need to discard lowercase_allowed_hdrs */
+ std::set<string> lowercase_allowed_hdrs; /* Not built until needed in RGWCORSRule::is_header_allowed */
std::set<string> allowed_origins;
std::list<string> exposable_hdrs;
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<string> hl;
*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) {
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;
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;