Skip to content

Commit

Permalink
rgw: handle S3 version 2 pre-signed urls with meta-data
Browse files Browse the repository at this point in the history
Add CanonicalizedAmzHeaders to the v2 signing document, as required
in https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html

Also lift qs metadata into x_meta_map in RGWPutObj_ObjStore_S3::get_parms(),
so they are applied to the stored object.

Fixes: https://tracker.ceph.com/issues/23470
Signed-off-by: Matt Benjamin <mbenjamin@redhat.com>
  • Loading branch information
mattbenjamin committed Oct 22, 2018
1 parent aa8c686 commit 3b0480e
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 7 deletions.
20 changes: 18 additions & 2 deletions src/rgw/rgw_auth_s3.cc
Expand Up @@ -116,6 +116,7 @@ void rgw_create_s3_canonical_header(
const char* const content_type,
const char* const date,
const std::map<std::string, std::string>& meta_map,
const std::map<std::string, std::string>& qs_map,
const char* const request_uri,
const std::map<std::string, std::string>& sub_resources,
std::string& dest_str)
Expand Down Expand Up @@ -143,6 +144,7 @@ void rgw_create_s3_canonical_header(
dest.append("\n");

dest.append(get_canon_amz_hdr(meta_map));
dest.append(get_canon_amz_hdr(qs_map));
dest.append(get_canon_resource(request_uri, sub_resources));

dest_str = dest;
Expand All @@ -152,6 +154,17 @@ static inline bool is_base64_for_content_md5(unsigned char c) {
return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '='));
}

static inline void get_v2_qs_map(const req_info& info,
std::map<std::string, std::string>& qs_map) {
const auto& params = const_cast<RGWHTTPArgs&>(info.args).get_params();
for (const auto& elt : params) {
std::string k = boost::algorithm::to_lower_copy(elt.first);
if (k.find("x-amz-meta-") == /* offset */ 0) {
add_amz_meta_header(qs_map, k, elt.second);
}
}
}

/*
* get the header authentication information required to
* compute a request's signature
Expand All @@ -175,7 +188,10 @@ bool rgw_create_s3_canonical_header(const req_info& info,
const char *content_type = info.env->get("CONTENT_TYPE");

std::string date;
std::map<std::string, std::string> qs_map;

if (qsr) {
get_v2_qs_map(info, qs_map); // handle qs metadata
date = info.args.get("Expires");
} else {
const char *str = info.env->get("HTTP_X_AMZ_DATE");
Expand Down Expand Up @@ -214,8 +230,8 @@ bool rgw_create_s3_canonical_header(const req_info& info,
}

rgw_create_s3_canonical_header(info.method, content_md5, content_type,
date.c_str(), meta_map, request_uri.c_str(),
sub_resources, dest);
date.c_str(), meta_map, qs_map,
request_uri.c_str(), sub_resources, dest);
return true;
}

Expand Down
1 change: 1 addition & 0 deletions src/rgw/rgw_auth_s3.h
Expand Up @@ -404,6 +404,7 @@ void rgw_create_s3_canonical_header(
const char *content_type,
const char *date,
const std::map<std::string, std::string>& meta_map,
const std::map<std::string, std::string>& qs_map,
const char *request_uri,
const std::map<std::string, std::string>& sub_resources,
std::string& dest_str);
Expand Down
3 changes: 0 additions & 3 deletions src/rgw/rgw_common.cc
Expand Up @@ -6,8 +6,6 @@
#include <algorithm>
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/utility/string_view.hpp>

#include "json_spirit/json_spirit.h"
#include "common/ceph_json.h"
Expand Down Expand Up @@ -405,7 +403,6 @@ struct str_len meta_prefixes[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
{NULL, 0} };


void req_info::init_meta_info(bool *found_bad_meta)
{
x_meta_map.clear();
Expand Down
20 changes: 20 additions & 0 deletions src/rgw/rgw_common.h
Expand Up @@ -18,6 +18,7 @@

#include <array>

#include <boost/algorithm/string.hpp>
#include <boost/utility/string_view.hpp>

#include "common/ceph_crypto.h"
Expand Down Expand Up @@ -2263,6 +2264,25 @@ static inline uint64_t rgw_rounded_objsize_kb(uint64_t bytes)
return ((bytes + 4095) & ~4095) / 1024;
}

/* implement combining step, S3 header canonicalization; k is a
* valid header and in lc form */
static inline void add_amz_meta_header(
std::map<std::string, std::string>& x_meta_map,
const std::string& k,
const std::string& v)
{
auto it = x_meta_map.find(k);
if (it != x_meta_map.end()) {
std::string old = it->second;
boost::algorithm::trim_right(old);
old.append(",");
old.append(v);
x_meta_map[k] = old;
} else {
x_meta_map[k] = v;
}
} /* add_amz_meta_header */

extern string rgw_string_unquote(const string& s);
extern void parse_csv_string(const string& ival, vector<string>& ovals);
extern int parse_key_value(string& in_str, string& key, string& val);
Expand Down
1 change: 1 addition & 0 deletions src/rgw/rgw_loadgen.cc
Expand Up @@ -29,6 +29,7 @@ int RGWLoadGenRequestEnv::sign(RGWAccessKey& access_key)
content_type.c_str(),
date_str.c_str(),
meta_map,
map<string, string>{},
uri.c_str(),
sub_resources,
canonical_header);
Expand Down
5 changes: 3 additions & 2 deletions src/rgw/rgw_rest_client.cc
Expand Up @@ -140,9 +140,10 @@ int RGWRESTSimpleRequest::execute(RGWAccessKey& key, const char *_method, const
string canonical_header;
map<string, string> meta_map;
map<string, string> sub_resources;

rgw_create_s3_canonical_header(method.c_str(), NULL, NULL, date_str.c_str(),
meta_map, url.c_str(), sub_resources,
canonical_header);
meta_map, meta_map, url.c_str(), sub_resources,
canonical_header);

string digest;
try {
Expand Down
19 changes: 19 additions & 0 deletions src/rgw/rgw_rest_s3.cc
Expand Up @@ -1290,6 +1290,19 @@ void RGWDeleteBucket_ObjStore_S3::send_response()
}
}

static inline void map_qs_metadata(struct req_state* s)
{
/* merge S3 valid user metadata from the query-string into
* x_meta_map, which maps them to attributes */
const auto& params = const_cast<RGWHTTPArgs&>(s->info.args).get_params();
for (const auto& elt : params) {
std::string k = boost::algorithm::to_lower_copy(elt.first);
if (k.find("x-amz-meta-") == /* offset */ 0) {
add_amz_meta_header(s->info.x_meta_map, k, elt.second);
}
}
}

int RGWPutObj_ObjStore_S3::get_params()
{
if (!s->length)
Expand All @@ -1300,6 +1313,8 @@ int RGWPutObj_ObjStore_S3::get_params()
size_t pos;
int ret;

map_qs_metadata(s);

RGWAccessControlPolicy_S3 s3policy(s->cct);
ret = create_s3_policy(s, store, s3policy, s->owner);
if (ret < 0)
Expand Down Expand Up @@ -1601,6 +1616,8 @@ int RGWPostObj_ObjStore_S3::get_params()
return op_ret;
}

map_qs_metadata(s);

ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name
<< dendl;
env.add_var("bucket", s->bucket.name);
Expand Down Expand Up @@ -2607,6 +2624,8 @@ int RGWCompleteMultipart_ObjStore_S3::get_params()
return ret;
}

map_qs_metadata(s);

return do_aws4_auth_completion();
}

Expand Down

0 comments on commit 3b0480e

Please sign in to comment.