From 8bdd10a9ea609af24d2bf38fb41e5a464c95c7a3 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Thu, 3 Jul 2025 12:01:18 -0400 Subject: [PATCH] rgw/s3: apply vhost logic to s3 only the vhost-style transformations ran in RGWREST::preprocess() before we even route the request, so applied to every REST API in radosgw vhost-style requests are specific to the S3 API, so they should only apply after being routed to RGWRESTMgr_S3 extract the vhost logic from RGWREST::proprocess() into rgw_rest_transform_s3_vhost_style(), and call that only from RGWRESTMgr_S3::get_resource_mgr_as_default() url-decoding of request_uri into decoded_uri is now duplicated in preprocess() to apply to all requests, then again after vhost-style transforms the request_uri Signed-off-by: Casey Bodley --- src/rgw/rgw_rest.cc | 32 ++++++++++++++++++++++---------- src/rgw/rgw_rest.h | 6 ++++++ src/rgw/rgw_rest_s3.cc | 14 ++++++++++++-- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index cbc5126fd332..186715b3ccee 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -2003,17 +2003,8 @@ RGWRESTMgr::~RGWRESTMgr() delete default_mgr; } -int RGWREST::preprocess(req_state *s, rgw::io::BasicClient* cio) +int rgw_rest_transform_s3_vhost_style(req_state* s) { - req_info& info = s->info; - - /* save the request uri used to hash on the client side. request_uri may suffer - modifications as part of the bucket encoding in the subdomain calling format. - request_uri_aws4 will be used under aws4 auth */ - s->info.request_uri_aws4 = s->info.request_uri; - - s->cio = cio; - // We need to know if this RGW instance is running the s3website API with a // higher priority than regular S3 API, or possibly in place of the regular // S3 API. @@ -2033,6 +2024,7 @@ int RGWREST::preprocess(req_state *s, rgw::io::BasicClient* cio) ldpp_dout(s, 10) << "rgw api priority: s3=" << api_priority_s3 << " s3website=" << api_priority_s3website << dendl; bool s3website_enabled = api_priority_s3website >= 0; + req_info& info = s->info; if (info.host.size()) { ssize_t pos; if (info.host.find('[') == 0) { @@ -2178,6 +2170,26 @@ int RGWREST::preprocess(req_state *s, rgw::io::BasicClient* cio) return -ERR_ZERO_IN_URL; } + return 0; +} + +int RGWREST::preprocess(req_state *s, rgw::io::BasicClient* cio) +{ + req_info& info = s->info; + + /* save the request uri used to hash on the client side. request_uri may suffer + modifications as part of the bucket encoding in the subdomain calling format. + request_uri_aws4 will be used under aws4 auth */ + s->info.request_uri_aws4 = s->info.request_uri; + + s->cio = cio; + + s->decoded_uri = url_decode(s->info.request_uri); + /* Validate for being free of the '\0' buried in the middle of the string. */ + if (std::strlen(s->decoded_uri.c_str()) != s->decoded_uri.length()) { + return -ERR_ZERO_IN_URL; + } + /* FastCGI specification, section 6.3 * http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6.3 * === diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index 42df336a5bde..fac9afd124f2 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -819,6 +819,12 @@ inline std::string compute_domain_uri(const req_state *s) { return uri; } +// Transform S3 virtual-host style requests to a path-style request. +// When the Host header includes the bucket name as a subdomain, prepend +// that bucket name to s->info.request_uri then re-url-decode that +// into s->decoded_uri. +int rgw_rest_transform_s3_vhost_style(req_state* s); + extern void dump_content_length(req_state *s, uint64_t len); extern void dump_etag(req_state *s, const std::string_view& etag, diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index e8b324fa777d..f17fd09abc98 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -6025,13 +6025,23 @@ RGWRESTMgr* RGWRESTMgr_S3::get_resource_mgr_as_default(req_state* s, const std::string& uri, std::string* out_uri) { + // check the Host header for virtual-host style requests, and + // rewrite the request_uri with the subdomain as the bucket name. + // this applies to s3 and s3website requests, but not s3control + int ret = rgw_rest_transform_s3_vhost_style(s); + if (ret < 0) { + return nullptr; + } + // use the updated decoded_uri for routing + const std::string& new_uri = s->decoded_uri; + // route matching requests to RGWRESTMgr_S3Website const bool in_s3website_domain = (s->prot_flags & RGW_REST_WEBSITE); if (s3website && in_s3website_domain) { - return s3website->get_resource_mgr(s, uri, out_uri); + return s3website->get_resource_mgr(s, new_uri, out_uri); } - return RGWRESTMgr::get_resource_mgr(s, uri, out_uri); + return RGWRESTMgr::get_resource_mgr(s, new_uri, out_uri); } RGWHandler_REST* RGWRESTMgr_S3::get_handler(rgw::sal::Driver* driver, -- 2.47.3