From 493cc5d1241693f3ea52f4d7f3a194d9e0ec1905 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 18 May 2016 17:21:28 -0700 Subject: [PATCH 1/5] rgw: check for aws4 headers size where needed Fixes: #15940 Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_rest_s3.cc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 2358955559e..1da507bee28 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3282,8 +3282,15 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) using_qs = false; s->aws4_auth->credential = s->http_auth; +#define AWS4_HMAC_SHA256_STR "AWS4-HMAC-SHA256" +#define CREDENTIALS_PREFIX_LEN (sizeof(AWS4_HMAC_SHA256_STR) - 1) + ssize_t min_len = CREDENTIALS_PREFIX_LEN + 1; + if (s->aws4_auth->credential.length() < min_len) { + ldout(store->ctx(), 10) << "credentials string is too short" << dendl; + return -EINVAL; + } - s->aws4_auth->credential = s->aws4_auth->credential.substr(17, s->aws4_auth->credential.length()); + s->aws4_auth->credential = s->aws4_auth->credential.substr(min_len, s->aws4_auth->credential.length()); pos = s->aws4_auth->credential.find("Credential"); if (pos == std::string::npos) { @@ -3302,7 +3309,7 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) s->aws4_auth->signedheaders = s->http_auth; - s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(17, s->aws4_auth->signedheaders.length()); + s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(min_len, s->aws4_auth->signedheaders.length()); pos = s->aws4_auth->signedheaders.find("SignedHeaders"); if (pos == std::string::npos) { @@ -3332,7 +3339,12 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) s->aws4_auth->signature = s->http_auth; - s->aws4_auth->signature = s->aws4_auth->signature.substr(17, s->aws4_auth->signature.length()); + if (s->aws4_auth->signature.size() < min_len) { + ldout(store->ctx(), 10) << "signature string is too short" << dendl; + return -EINVAL; + } + + s->aws4_auth->signature = s->aws4_auth->signature.substr(min_len, s->aws4_auth->signature.length()); pos = s->aws4_auth->signature.find("Signature"); if (pos == std::string::npos) { From 310f5bdf56a9deb09347aadc158da25750fb6735 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 19 May 2016 11:30:44 -0700 Subject: [PATCH 2/5] rgw: use correct method to get current epoch Fixes: #15939 Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_rest_s3.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 1da507bee28..56c74a729f4 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3210,8 +3210,8 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) string::size_type pos; bool using_qs; - time_t now, now_req=0; - time(&now); + uint64_t now_req = 0; + uint64_t now = ceph_clock_now(s->cct); /* v4 requires rados auth */ if (!store->ctx()->_conf->rgw_s3_auth_use_rados) { @@ -3250,7 +3250,7 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) return -EPERM; } /* handle expiration in epoch time */ - now_req = mktime(&date_t); + now_req = (uint64_t)timegm(&date_t); if (now >= now_req + exp) { dout(10) << "NOTICE: now = " << now << ", now_req = " << now_req << ", exp = " << exp << dendl; return -EPERM; @@ -3284,7 +3284,7 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) s->aws4_auth->credential = s->http_auth; #define AWS4_HMAC_SHA256_STR "AWS4-HMAC-SHA256" #define CREDENTIALS_PREFIX_LEN (sizeof(AWS4_HMAC_SHA256_STR) - 1) - ssize_t min_len = CREDENTIALS_PREFIX_LEN + 1; + uint64_t min_len = CREDENTIALS_PREFIX_LEN + 1; if (s->aws4_auth->credential.length() < min_len) { ldout(store->ctx(), 10) << "credentials string is too short" << dendl; return -EINVAL; From 033888bbd0e4d8d81358bf61a099276dddb5692b Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 19 May 2016 12:52:54 -0700 Subject: [PATCH 3/5] rgw: don't add port to aws4 canonical string if using default port Fixes: #15939 When either port 80 is used, or if it's a secure connection and port 443 is used, and when going through the presigned url auth, don't add the port to the signed string. Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_rest_s3.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 56c74a729f4..463b4b10603 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3491,7 +3491,8 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) map canonical_hdrs_map; istringstream sh(s->aws4_auth->signedheaders); string token; - string port = s->info.env->get("SERVER_PORT"); + string port = s->info.env->get("SERVER_PORT", ""); + string secure_port = s->info.env->get("SERVER_PORT_SECURE", ""); while (getline(sh, token, ';')) { string token_env = "HTTP_" + token; @@ -3517,8 +3518,13 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) } } string token_value = string(t); - if (using_qs && (token == "host")) - token_value = token_value + ":" + port; + if (using_qs && (token == "host")) { + if (!port.empty() && port != "80") { + token_value = token_value + ":" + port; + } else if (!secure_port.empty() && secure_port != "443") { + token_value = token_value + ":" + secure_port; + } + } canonical_hdrs_map[token] = rgw_trim_whitespace(token_value); } From e3618c87026b5ced8ef81adbcafc7f9b34f2d48d Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 19 May 2016 15:02:21 -0700 Subject: [PATCH 4/5] rgw: rework aws4 header parsing Fixes: #15940 Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_rest_s3.cc | 92 ++++++++++++------------------------------ 1 file changed, 26 insertions(+), 66 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 463b4b10603..1605a04dd32 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3202,6 +3202,8 @@ static void aws4_uri_encode(const string& src, string& dst) } } +static std::array aws4_presigned_required_keys = { "Credential", "SignedHeaders", "Signature" }; + /* * handle v4 signatures (rados auth only) */ @@ -3281,84 +3283,42 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) /* ------------------------- handle Credential header */ using_qs = false; - s->aws4_auth->credential = s->http_auth; + + string auth_str = s->http_auth; + #define AWS4_HMAC_SHA256_STR "AWS4-HMAC-SHA256" #define CREDENTIALS_PREFIX_LEN (sizeof(AWS4_HMAC_SHA256_STR) - 1) uint64_t min_len = CREDENTIALS_PREFIX_LEN + 1; - if (s->aws4_auth->credential.length() < min_len) { + if (auth_str.length() < min_len) { ldout(store->ctx(), 10) << "credentials string is too short" << dendl; return -EINVAL; } - s->aws4_auth->credential = s->aws4_auth->credential.substr(min_len, s->aws4_auth->credential.length()); + list auth_list; + get_str_list(auth_str.substr(min_len), ",", auth_list); - pos = s->aws4_auth->credential.find("Credential"); - if (pos == std::string::npos) { - return -EINVAL; + map kv; + + for (string& s : auth_list) { + string key, val; + int ret = parse_key_value(s, key, val); + if (ret < 0) { + ldout(store->ctx(), 10) << "NOTICE: failed to parse auth header (s=" << s << ")" << dendl; + return -EINVAL; + } + kv[key] = val; } - s->aws4_auth->credential = s->aws4_auth->credential.substr(pos, s->aws4_auth->credential.find(",")); - - s->aws4_auth->credential = s->aws4_auth->credential.substr(pos + 1, s->aws4_auth->credential.length()); - - pos = s->aws4_auth->credential.find("="); - - s->aws4_auth->credential = s->aws4_auth->credential.substr(pos + 1, s->aws4_auth->credential.length()); - - /* ------------------------- handle SignedHeaders header */ - - s->aws4_auth->signedheaders = s->http_auth; - - s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(min_len, s->aws4_auth->signedheaders.length()); - - pos = s->aws4_auth->signedheaders.find("SignedHeaders"); - if (pos == std::string::npos) { - return -EINVAL; + for (string& k : aws4_presigned_required_keys) { + if (kv.find(k) == kv.end()) { + ldout(store->ctx(), 10) << "NOTICE: auth header missing key: " << k << dendl; + return -EINVAL; + } } - s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(pos, s->aws4_auth->signedheaders.length()); - - pos = s->aws4_auth->signedheaders.find(","); - if (pos == std::string::npos) { - return -EINVAL; - } - - s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(0, pos); - - pos = s->aws4_auth->signedheaders.find("="); - if (pos == std::string::npos) { - return -EINVAL; - } - - s->aws4_auth->signedheaders = s->aws4_auth->signedheaders.substr(pos + 1, s->aws4_auth->signedheaders.length()); - - /* host;user-agent;x-amz-content-sha256;x-amz-date */ - dout(10) << "v4 signedheaders format = " << s->aws4_auth->signedheaders << dendl; - - /* ------------------------- handle Signature header */ - - s->aws4_auth->signature = s->http_auth; - - if (s->aws4_auth->signature.size() < min_len) { - ldout(store->ctx(), 10) << "signature string is too short" << dendl; - return -EINVAL; - } - - s->aws4_auth->signature = s->aws4_auth->signature.substr(min_len, s->aws4_auth->signature.length()); - - pos = s->aws4_auth->signature.find("Signature"); - if (pos == std::string::npos) { - return -EINVAL; - } - - s->aws4_auth->signature = s->aws4_auth->signature.substr(pos, s->aws4_auth->signature.length()); - - pos = s->aws4_auth->signature.find("="); - if (pos == std::string::npos) { - return -EINVAL; - } - - s->aws4_auth->signature = s->aws4_auth->signature.substr(pos + 1, s->aws4_auth->signature.length()); + s->aws4_auth->credential = kv["Credential"]; + s->aws4_auth->signedheaders = kv["SignedHeaders"]; + s->aws4_auth->signature = kv["Signature"]; /* sig hex str */ dout(10) << "v4 signature format = " << s->aws4_auth->signature << dendl; From f8f1f217314c32cf65ac1fa4e8e0132b501ee184 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 1 Jun 2016 04:24:34 -0700 Subject: [PATCH 5/5] rgw: reduce string copy As suggested by Casey Bodley. Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_rest_s3.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 1605a04dd32..3843ee102da 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3306,7 +3306,7 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) ldout(store->ctx(), 10) << "NOTICE: failed to parse auth header (s=" << s << ")" << dendl; return -EINVAL; } - kv[key] = val; + kv[key] = std::move(val); } for (string& k : aws4_presigned_required_keys) { @@ -3316,9 +3316,9 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) } } - s->aws4_auth->credential = kv["Credential"]; - s->aws4_auth->signedheaders = kv["SignedHeaders"]; - s->aws4_auth->signature = kv["Signature"]; + s->aws4_auth->credential = std::move(kv["Credential"]); + s->aws4_auth->signedheaders = std::move(kv["SignedHeaders"]); + s->aws4_auth->signature = std::move(kv["Signature"]); /* sig hex str */ dout(10) << "v4 signature format = " << s->aws4_auth->signature << dendl;