From 51adf772719bdd68a9288490f218d1dc7d99c91a Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Wed, 25 Sep 2024 12:26:10 +0200 Subject: [PATCH] rgw: handle http options CORS with v2 auth If we get a HTTP OPTIONS request for a presigned URL that contains credentials we need to compute the signature using the method given in the access-control-request-method http header. This is the same as performed for v4 auth in [1]. [1] https://github.com/ceph/ceph/pull/52673 Signed-off-by: Tobias Urdin --- src/rgw/rgw_auth_s3.cc | 61 +++++++++++++++++++------------------- src/rgw/rgw_auth_s3.h | 9 +++--- src/rgw/rgw_rest_client.cc | 2 +- src/rgw/rgw_rest_s3.cc | 4 +-- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/rgw/rgw_auth_s3.cc b/src/rgw/rgw_auth_s3.cc index 412f4bf759a0d..4fe1e39d0a8d0 100644 --- a/src/rgw/rgw_auth_s3.cc +++ b/src/rgw/rgw_auth_s3.cc @@ -191,6 +191,7 @@ static inline void get_v2_qs_map(const req_info& info, * compute a request's signature */ bool rgw_create_s3_canonical_header(const DoutPrefixProvider *dpp, + RGWOpType op_type, const req_info& info, utime_t* const header_time, std::string& dest, @@ -253,7 +254,8 @@ bool rgw_create_s3_canonical_header(const DoutPrefixProvider *dpp, request_uri = info.effective_uri; } - rgw_create_s3_canonical_header(dpp, info.method, content_md5, content_type, + auto method = rgw::auth::s3::get_canonical_method(dpp, op_type, info); + rgw_create_s3_canonical_header(dpp, method.c_str(), content_md5, content_type, date.c_str(), meta_map, qs_map, request_uri.c_str(), sub_resources, dest); return true; @@ -704,35 +706,6 @@ std::string gen_v4_canonical_qs(const req_info& info, bool is_non_s3_op) return canonical_qs; } -std::string get_v4_canonical_method(const req_state* s) -{ - /* If this is a OPTIONS request we need to compute the v4 signature for the - * intended HTTP method and not the OPTIONS request itself. */ - if (s->op_type == RGW_OP_OPTIONS_CORS) { - const char *cors_method = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD"); - - if (cors_method) { - /* Validate request method passed in access-control-request-method is valid. */ - auto cors_flags = get_cors_method_flags(cors_method); - if (!cors_flags) { - ldpp_dout(s, 1) << "invalid access-control-request-method header = " - << cors_method << dendl; - throw -EINVAL; - } - - ldpp_dout(s, 10) << "canonical req method = " << cors_method - << ", due to access-control-request-method header" << dendl; - return cors_method; - } else { - ldpp_dout(s, 1) << "invalid http options req missing " - << "access-control-request-method header" << dendl; - throw -EINVAL; - } - } - - return s->info.method; -} - boost::optional get_v4_canonical_headers(const req_info& info, const std::string_view& signedheaders, @@ -1740,4 +1713,32 @@ AWSv4ComplSingle::create(const req_state* const s, return std::make_shared(s); } +std::string get_canonical_method(const DoutPrefixProvider *dpp, RGWOpType op_type, const req_info& info) +{ + /* If this is a OPTIONS request we need to compute the v4 signature for the + * intended HTTP method and not the OPTIONS request itself. */ + if (op_type == RGW_OP_OPTIONS_CORS) { + const char *cors_method = info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD"); + + if (cors_method) { + /* Validate request method passed in access-control-request-method is valid. */ + auto cors_flags = get_cors_method_flags(cors_method); + if (!cors_flags) { + ldpp_dout(dpp, 1) << "invalid access-control-request-method header = " + << cors_method << dendl; + throw -EINVAL; + } + + ldpp_dout(dpp, 10) << "canonical req method = " << cors_method + << ", due to access-control-request-method header" << dendl; + return cors_method; + } else { + ldpp_dout(dpp, 1) << "invalid http options req missing " + << "access-control-request-method header" << dendl; + throw -EINVAL; + } + } + + return info.method; +} } // namespace rgw::auth::s3 diff --git a/src/rgw/rgw_auth_s3.h b/src/rgw/rgw_auth_s3.h index e1fe5163f025d..2f7fd2d75985c 100644 --- a/src/rgw/rgw_auth_s3.h +++ b/src/rgw/rgw_auth_s3.h @@ -500,16 +500,17 @@ void rgw_create_s3_canonical_header( const std::map& sub_resources, std::string& dest_str); bool rgw_create_s3_canonical_header(const DoutPrefixProvider *dpp, + RGWOpType op_type, const req_info& info, utime_t *header_time, /* out */ std::string& dest, /* out */ bool qsr); static inline std::tuple -rgw_create_s3_canonical_header(const DoutPrefixProvider *dpp, const req_info& info, const bool qsr) { +rgw_create_s3_canonical_header(const DoutPrefixProvider *dpp, RGWOpType op_type, const req_info& info, const bool qsr) { std::string dest; utime_t header_time; - const bool ok = rgw_create_s3_canonical_header(dpp, info, &header_time, dest, qsr); + const bool ok = rgw_create_s3_canonical_header(dpp, op_type, info, &header_time, dest, qsr); return std::make_tuple(ok, dest, header_time); } @@ -704,8 +705,6 @@ std::string get_v4_canonical_qs(const req_info& info, bool using_qs); std::string gen_v4_canonical_qs(const req_info& info, bool is_non_s3_op); -std::string get_v4_canonical_method(const req_state* s); - boost::optional get_v4_canonical_headers(const req_info& info, const std::string_view& signedheaders, @@ -745,6 +744,8 @@ extern AWSEngine::VersionAbstractor::server_signature_t get_v2_signature(CephContext*, const std::string& secret_key, const AWSEngine::VersionAbstractor::string_to_sign_t& string_to_sign); + +std::string get_canonical_method(const DoutPrefixProvider *dpp, RGWOpType op_type, const req_info& info); } /* namespace s3 */ } /* namespace auth */ } /* namespace rgw */ diff --git a/src/rgw/rgw_rest_client.cc b/src/rgw/rgw_rest_client.cc index 45b5e3076f494..c16064a61c2b3 100644 --- a/src/rgw/rgw_rest_client.cc +++ b/src/rgw/rgw_rest_client.cc @@ -209,7 +209,7 @@ static int sign_request_v2(const DoutPrefixProvider *dpp, const RGWAccessKey& ke } string canonical_header; - if (!rgw_create_s3_canonical_header(dpp, info, NULL, canonical_header, false)) { + if (!rgw_create_s3_canonical_header(dpp, RGW_OP_UNKNOWN, info, NULL, canonical_header, false)) { ldpp_dout(dpp, 0) << "failed to create canonical s3 header" << dendl; return -EINVAL; } diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 4a50baf1cb201..3d87df040d409 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -5845,7 +5845,7 @@ AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s, auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs); /* Craft canonical method. */ - auto canonical_method = rgw::auth::s3::get_v4_canonical_method(s); + auto canonical_method = rgw::auth::s3::get_canonical_method(s, s->op_type, s->info); /* Craft canonical request. */ auto canonical_req_hash = \ @@ -6109,7 +6109,7 @@ AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */ std::string string_to_sign; utime_t header_time; - if (! rgw_create_s3_canonical_header(s, s->info, &header_time, string_to_sign, + if (! rgw_create_s3_canonical_header(s, s->op_type, s->info, &header_time, string_to_sign, qsr)) { ldpp_dout(s, 10) << "failed to create the canonized auth header\n" << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl; -- 2.39.5