From 3210cd8a280029f22623084b1f52d8dd7cfd9a01 Mon Sep 17 00:00:00 2001 From: "Javier M. Mellid" Date: Mon, 21 Sep 2015 19:09:22 +0200 Subject: [PATCH] rgw: AWS4 auth support for positive content-length Handle AWS4 auth on requests with positive content-length. It splits the auth process along several steps to process the body content on the fly instead of reading the whole body in memory. After that, it completes the delayed AWS4 auth properly. Requests with content-length <= 0 are validated as usual. They don't require any kind of completion. Requests with content-length > 0 use a streaming approach together with a completion step. Fixes: #10333 Signed-off-by: Javier M. Mellid --- src/rgw/rgw_auth_s3.cc | 15 +- src/rgw/rgw_auth_s3.h | 2 +- src/rgw/rgw_client_io.cc | 11 +- src/rgw/rgw_client_io.h | 9 +- src/rgw/rgw_common.cc | 32 ++++- src/rgw/rgw_common.h | 21 +++ src/rgw/rgw_op.cc | 31 ++++- src/rgw/rgw_rest.cc | 2 +- src/rgw/rgw_rest_s3.cc | 286 +++++++++++++++++++++------------------ src/rgw/rgw_rest_s3.h | 4 + 10 files changed, 267 insertions(+), 146 deletions(-) diff --git a/src/rgw/rgw_auth_s3.cc b/src/rgw/rgw_auth_s3.cc index cd3adc41683e..e370eee2b09c 100644 --- a/src/rgw/rgw_auth_s3.cc +++ b/src/rgw/rgw_auth_s3.cc @@ -4,6 +4,7 @@ #include "common/armor.h" #include "common/utf8.h" #include "rgw_common.h" +#include "rgw_client_io.h" #define dout_subsys ceph_subsys_rgw @@ -148,9 +149,9 @@ int rgw_get_s3_header_digest(const string& auth_hdr, const string& key, string& return 0; } -void rgw_hash_s3_string_sha256(const string& str, string& dest) +void rgw_hash_s3_string_sha256(const char *data, int len, string& dest) { - calc_hash_sha256(str, dest); + calc_hash_sha256(data, len, dest); } static inline bool is_base64_for_content_md5(unsigned char c) { @@ -269,16 +270,22 @@ void rgw_create_s3_v4_canonical_request(struct req_state *s, const string& canon if (unsigned_payload) { request_payload_hash = "UNSIGNED-PAYLOAD"; } else { - rgw_hash_s3_string_sha256(request_payload.c_str(), request_payload.size(), request_payload_hash); + if (s->aws4_auth_complete) { + request_payload_hash = s->cio->grab_aws4_sha256_hash(); + } else { + rgw_hash_s3_string_sha256(request_payload.c_str(), request_payload.size(), request_payload_hash); + } } + s->aws4_auth_payload_hash = request_payload_hash; + dout(10) << "payload request hash = " << request_payload_hash << dendl; rgw_assemble_s3_v4_canonical_request(s->info.method, canonical_uri.c_str(), canonical_qs.c_str(), canonical_hdrs.c_str(), signed_hdrs.c_str(), request_payload_hash.c_str(), canonical_req); - rgw_hash_s3_string_sha256(canonical_req, canonical_req_hash); + rgw_hash_s3_string_sha256(canonical_req.c_str(), canonical_req.size(), canonical_req_hash); dout(10) << "canonical request = " << canonical_req << dendl; dout(10) << "canonical request hash = " << canonical_req_hash << dendl; diff --git a/src/rgw/rgw_auth_s3.h b/src/rgw/rgw_auth_s3.h index cb56a181d203..ed4aa010f6db 100644 --- a/src/rgw/rgw_auth_s3.h +++ b/src/rgw/rgw_auth_s3.h @@ -12,7 +12,7 @@ void rgw_create_s3_canonical_header(const char *method, const char *content_md5, string& dest_str); bool rgw_create_s3_canonical_header(req_info& info, utime_t *header_time, string& dest, bool qsr); int rgw_get_s3_header_digest(const string& auth_hdr, const string& key, string& dest); -void rgw_hash_s3_string_sha256(const string& str, string& dest); +void rgw_hash_s3_string_sha256(const char *data, int len, string& dest); void rgw_create_s3_v4_canonical_request(struct req_state *s, const string& canonical_uri, const string& canonical_qs, const string& canonical_hdrs, const string& signed_hdrs, const string& request_payload, bool unsigned_payload, string& canonical_req, string& canonical_req_hash); diff --git a/src/rgw/rgw_client_io.cc b/src/rgw/rgw_client_io.cc index b6ef745c4f27..c280b4209e7a 100644 --- a/src/rgw/rgw_client_io.cc +++ b/src/rgw/rgw_client_io.cc @@ -71,7 +71,7 @@ int RGWClientIO::write(const char *buf, int len) } -int RGWClientIO::read(char *buf, int max, int *actual) +int RGWClientIO::read(char *buf, int max, int *actual, bool hash /* = false */) { int ret = read_data(buf, max); if (ret < 0) @@ -81,6 +81,15 @@ int RGWClientIO::read(char *buf, int max, int *actual) bytes_received += *actual; + if (hash) { + calc_hash_sha256_update_stream(sha256_hash, buf, *actual); + } + return 0; } + +string RGWClientIO::grab_aws4_sha256_hash() +{ + return calc_hash_sha256_close_stream(sha256_hash); +} diff --git a/src/rgw/rgw_client_io.h b/src/rgw/rgw_client_io.h index ac610c6215f3..07c11c777f48 100644 --- a/src/rgw/rgw_client_io.h +++ b/src/rgw/rgw_client_io.h @@ -20,6 +20,7 @@ class RGWClientIO { protected: RGWEnv env; + SHA256 *sha256_hash; virtual void init_env(CephContext *cct) = 0; @@ -28,13 +29,17 @@ protected: public: virtual ~RGWClientIO() {} - RGWClientIO() : account(false), bytes_sent(0), bytes_received(0) {} + RGWClientIO() : account(false), bytes_sent(0), bytes_received(0) { + sha256_hash = calc_hash_sha256_open_stream(); + } void init(CephContext *cct); int print(const char *format, ...); int write(const char *buf, int len); virtual void flush() = 0; - int read(char *buf, int max, int *actual); + int read(char *buf, int max, int *actual, bool hash = false); + + string grab_aws4_sha256_hash(); virtual int send_status(int status, const char *status_name) = 0; virtual int send_100_continue() = 0; diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index d30fd38ed5a2..af28173c8a03 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -423,7 +423,7 @@ void calc_hmac_sha1(const char *key, int key_len, * calculate the sha256 value of a given msg and key */ void calc_hmac_sha256(const char *key, int key_len, - const char *msg, int msg_len, char *dest) + const char *msg, int msg_len, char *dest) { char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; @@ -437,12 +437,12 @@ void calc_hmac_sha256(const char *key, int key_len, /* * calculate the sha256 hash value of a given msg */ -void calc_hash_sha256(const string& msg, string& dest) +void calc_hash_sha256(const char *msg, int len, string& dest) { char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; SHA256 hash; - hash.Update((const unsigned char *)msg.c_str(), msg.size()); + hash.Update((const unsigned char *)msg, len); hash.Final((unsigned char *)hash_sha256); char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1]; @@ -451,6 +451,32 @@ void calc_hash_sha256(const string& msg, string& dest) dest = std::string(hex_str); } +using ceph::crypto::SHA256; + +SHA256* calc_hash_sha256_open_stream() +{ + return new SHA256; +} + +void calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len) +{ + hash->Update((const unsigned char *)msg, len); +} + +string calc_hash_sha256_close_stream(SHA256* hash) +{ + char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + + hash->Final((unsigned char *)hash_sha256); + + char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1]; + buf_to_hex((unsigned char *)hash_sha256, CEPH_CRYPTO_SHA256_DIGESTSIZE, hex_str); + + delete hash; + + return std::string(hex_str); +} + int gen_rand_base64(CephContext *cct, char *dest, int size) /* size should be the required string size + 1 */ { char buf[size]; diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index ed2e619a8718..48c4e292a9e0 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -1131,6 +1131,21 @@ struct req_state { string swift_user; string swift_groups; + /* aws4 auth support */ + bool aws4_auth_complete; + string aws4_auth_date; + string aws4_auth_credential; + string aws4_auth_signedheaders; + string aws4_auth_signed_hdrs; + string aws4_auth_access_key_id; + string aws4_auth_credential_scope; + string aws4_auth_canonical_uri; + string aws4_auth_canonical_qs; + string aws4_auth_canonical_hdrs; + string aws4_auth_signature; + string aws4_auth_new_signature; + string aws4_auth_payload_hash; + utime_t time; void *obj_ctx; @@ -1717,8 +1732,14 @@ extern void calc_hmac_sha1(const char *key, int key_len, const char *msg, int msg_len, char *dest); /* destination should be CEPH_CRYPTO_HMACSHA256_DIGESTSIZE bytes long */ extern void calc_hmac_sha256(const char *key, int key_len, const char *msg, int msg_len, char *dest); +extern void calc_hash_sha256(const char *msg, int len, string& dest); extern void calc_hash_sha256(const string& msg, string& dest); +using ceph::crypto::SHA256; +extern SHA256* calc_hash_sha256_open_stream(); +extern void calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len); +extern string calc_hash_sha256_close_stream(SHA256* hash); + extern int rgw_parse_op_type_list(const string& str, uint32_t *perm); #endif diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index e5a6af131be7..b5983b257075 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -25,6 +25,7 @@ #include "rgw_multi_del.h" #include "rgw_cors.h" #include "rgw_cors_s3.h" +#include "rgw_rest_s3.h" #include "rgw_client_io.h" @@ -2323,11 +2324,35 @@ void RGWPutObj::execute() perfcounter->inc(l_rgw_put_b, s->obj_size); + if (s->aws4_auth_complete) { + + /* complete aws4 auth */ + + op_ret = RGW_Auth_S3::authorize_aws4_auth_complete(store, s); + if (op_ret) { + goto done; + } + + s->aws4_auth_complete = false; + + /* verify signature */ + + if (s->aws4_auth_signature != s->aws4_auth_new_signature) { + op_ret = -ERR_SIGNATURE_NO_MATCH; + ldout(s->cct, 20) << "delayed aws4 auth failed" << dendl; + goto done; + } + + /* authorization ok */ + + dout(10) << "v4 auth ok" << dendl; + + } + op_ret = store->check_quota(s->bucket_owner.get_id(), s->bucket, - user_quota, bucket_quota, s->obj_size); + user_quota, bucket_quota, s->obj_size); if (op_ret < 0) { - ldout(s->cct, 20) << "second check_quota() returned ret=" << op_ret - << dendl; + ldout(s->cct, 20) << "second check_quota() returned op_ret=" << op_ret << dendl; goto done; } diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index ba7d429d75e6..dc8f65007a5c 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -970,7 +970,7 @@ int RGWPutObj_ObjStore::get_data(bufferlist& bl) bufferptr bp(cl); int read_len; /* cio->read() expects int * */ - int r = s->cio->read(bp.c_str(), cl, &read_len); + int r = s->cio->read(bp.c_str(), cl, &read_len, true); len = read_len; if (r < 0) return r; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 5cdd2df490f4..1f195b5925f0 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -2698,18 +2698,79 @@ int RGW_Auth_S3::authorize(RGWRados *store, struct req_state *s) return -EPERM; } +int RGW_Auth_S3::authorize_aws4_auth_complete(RGWRados *store, struct req_state *s) +{ + return authorize_v4_complete(store, s, "", false); +} + +int RGW_Auth_S3::authorize_v4_complete(RGWRados *store, struct req_state *s, string request_payload, bool unsigned_payload) +{ + size_t pos; + + /* craft canonical request */ + + string canonical_req; + string canonical_req_hash; + + rgw_create_s3_v4_canonical_request(s, s->aws4_auth_canonical_uri, s->aws4_auth_canonical_qs, + s->aws4_auth_canonical_hdrs, s->aws4_auth_signed_hdrs, request_payload, unsigned_payload, + canonical_req, canonical_req_hash); + + /* + * create a string to sign + * + * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html + */ + + string string_to_sign; + + rgw_create_s3_v4_string_to_sign("AWS4-HMAC-SHA256", s->aws4_auth_date, s->aws4_auth_credential_scope, + canonical_req_hash, string_to_sign); + + /* + * calculate the AWS signature + * + * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html + */ + + string cs_aux = s->aws4_auth_credential_scope; + + string date_cs = cs_aux; + pos = date_cs.find("/"); + date_cs = date_cs.substr(0, pos); + cs_aux = cs_aux.substr(pos + 1, cs_aux.length()); + + string region_cs = cs_aux; + pos = region_cs.find("/"); + region_cs = region_cs.substr(0, pos); + cs_aux = cs_aux.substr(pos + 1, cs_aux.length()); + + string service_cs = cs_aux; + pos = service_cs.find("/"); + service_cs = service_cs.substr(0, pos); + + int err = rgw_calculate_s3_v4_aws_signature(s, s->aws4_auth_access_key_id, date_cs, + region_cs, service_cs, string_to_sign, s->aws4_auth_new_signature); + + dout(10) << "----------------------------- Verifying signatures" << dendl; + dout(10) << "Signature = " << s->aws4_auth_signature << dendl; + dout(10) << "New Signature = " << s->aws4_auth_new_signature << dendl; + dout(10) << "-----------------------------" << dendl; + + if (err) { + return err; + } + + return 0; + +} + /* * handle v4 signatures (rados auth only) */ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) { string::size_type pos; - string date; - string credential; - string signedheaders; - string signature; - string access_key_id; - string credential_scope; bool using_qs; /* v4 requires rados auth */ @@ -2726,21 +2787,21 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) /* look for required params */ using_qs = true; - credential = s->info.args.get("X-Amz-Credential"); - if (credential.size() == 0) { + s->aws4_auth_credential = s->info.args.get("X-Amz-Credential"); + if (s->aws4_auth_credential.size() == 0) { return -EPERM; } - date = s->info.args.get("X-Amz-Date"); - if (date.size() == 0) { + s->aws4_auth_date = s->info.args.get("X-Amz-Date"); + if (s->aws4_auth_date.size() == 0) { return -EPERM; } - signedheaders = s->info.args.get("X-Amz-SignedHeaders"); - if (signedheaders.size() == 0) { + s->aws4_auth_signedheaders = s->info.args.get("X-Amz-SignedHeaders"); + if (s->aws4_auth_signedheaders.size() == 0) { return -EPERM; } - signature = s->info.args.get("X-Amz-Signature"); - if (signature.size() == 0) { + s->aws4_auth_signature = s->info.args.get("X-Amz-Signature"); + if (s->aws4_auth_signature.size() == 0) { return -EPERM; } @@ -2749,117 +2810,119 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) /* auth ships in headers ... */ /* ------------------------- handle Credential header */ + using_qs = false; - credential = s->http_auth; + s->aws4_auth_credential = s->http_auth; - credential = credential.substr(17, credential.length()); + s->aws4_auth_credential = s->aws4_auth_credential.substr(17, s->aws4_auth_credential.length()); - pos = credential.find("Credential"); + pos = s->aws4_auth_credential.find("Credential"); if (pos == std::string::npos) { return -EINVAL; } - credential = credential.substr(pos, credential.find(",")); + s->aws4_auth_credential = s->aws4_auth_credential.substr(pos, s->aws4_auth_credential.find(",")); - credential = credential.substr(pos + 1, credential.length()); + s->aws4_auth_credential = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length()); - pos = credential.find("="); + pos = s->aws4_auth_credential.find("="); - credential = credential.substr(pos + 1, credential.length()); + s->aws4_auth_credential = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length()); /* ------------------------- handle SignedHeaders header */ - signedheaders = s->http_auth; + s->aws4_auth_signedheaders = s->http_auth; - signedheaders = signedheaders.substr(17, signedheaders.length()); + s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(17, s->aws4_auth_signedheaders.length()); - pos = signedheaders.find("SignedHeaders"); + pos = s->aws4_auth_signedheaders.find("SignedHeaders"); if (pos == std::string::npos) { return -EINVAL; } - signedheaders = signedheaders.substr(pos, signedheaders.length()); + s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(pos, s->aws4_auth_signedheaders.length()); - pos = signedheaders.find(","); + pos = s->aws4_auth_signedheaders.find(","); if (pos == std::string::npos) { return -EINVAL; } - signedheaders = signedheaders.substr(0, pos); + s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(0, pos); - pos = signedheaders.find("="); + pos = s->aws4_auth_signedheaders.find("="); if (pos == std::string::npos) { return -EINVAL; } - signedheaders = signedheaders.substr(pos + 1, signedheaders.length()); + 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 = " << signedheaders << dendl; + dout(10) << "v4 signedheaders format = " << s->aws4_auth_signedheaders << dendl; /* ------------------------- handle Signature header */ - signature = s->http_auth; + s->aws4_auth_signature = s->http_auth; - signature = signature.substr(17, signature.length()); + s->aws4_auth_signature = s->aws4_auth_signature.substr(17, s->aws4_auth_signature.length()); - pos = signature.find("Signature"); + pos = s->aws4_auth_signature.find("Signature"); if (pos == std::string::npos) { return -EINVAL; } - signature = signature.substr(pos, signature.length()); + s->aws4_auth_signature = s->aws4_auth_signature.substr(pos, s->aws4_auth_signature.length()); - pos = signature.find("="); + pos = s->aws4_auth_signature.find("="); if (pos == std::string::npos) { return -EINVAL; } - signature = signature.substr(pos + 1, signature.length()); + s->aws4_auth_signature = s->aws4_auth_signature.substr(pos + 1, s->aws4_auth_signature.length()); /* sig hex str */ - dout(10) << "v4 signature format = " << signature << dendl; + dout(10) << "v4 signature format = " << s->aws4_auth_signature << dendl; /* ------------------------- handle x-amz-date header */ /* grab date */ - date = s->info.env->get("HTTP_X_AMZ_DATE"); - if (date.empty()) { + s->aws4_auth_date = s->info.env->get("HTTP_X_AMZ_DATE"); + if (s->aws4_auth_date.empty()) { + dout(10) << "error reading date via http_x_amz_date" << dendl; return -EINVAL; } } /* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */ - dout(10) << "v4 credential format = " << credential << dendl; + dout(10) << "v4 credential format = " << s->aws4_auth_credential << dendl; - if (std::count(credential.begin(), credential.end(), '/') != 4) { + if (std::count(s->aws4_auth_credential.begin(), s->aws4_auth_credential.end(), '/') != 4) { return -EINVAL; } /* credential must end with 'aws4_request' */ - if (credential.find("aws4_request") == std::string::npos) { + if (s->aws4_auth_credential.find("aws4_request") == std::string::npos) { return -EINVAL; } /* grab access key id */ - pos = credential.find("/"); - access_key_id = credential.substr(0, pos); + pos = s->aws4_auth_credential.find("/"); + s->aws4_auth_access_key_id = s->aws4_auth_credential.substr(0, pos); - dout(10) << "access key id = " << access_key_id << dendl; + dout(10) << "access key id = " << s->aws4_auth_access_key_id << dendl; /* grab credential scope */ - credential_scope = credential.substr(pos + 1, credential.length()); + s->aws4_auth_credential_scope = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length()); - dout(10) << "credential scope = " << credential_scope << dendl; + dout(10) << "credential scope = " << s->aws4_auth_credential_scope << dendl; /* grab user information */ - if (rgw_get_user_info_by_access_key(store, access_key_id, s->user) < 0) { - dout(10) << "error reading user info, uid=" << access_key_id + if (rgw_get_user_info_by_access_key(store, s->aws4_auth_access_key_id, s->user) < 0) { + dout(10) << "error reading user info, uid=" << s->aws4_auth_access_key_id << " can't authenticate" << dendl; return -ERR_INVALID_ACCESS_KEY; } @@ -2876,23 +2939,23 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) * that SigV4 typically does. this code follows the same approach that boto library * see auth.py:canonical_uri(...) */ - string canonical_uri = s->info.request_uri; + s->aws4_auth_canonical_uri = s->info.request_uri; - if (canonical_uri.empty()) { - canonical_uri = "/"; + if (s->aws4_auth_canonical_uri.empty()) { + s->aws4_auth_canonical_uri = "/"; } /* craft canonical query string */ - string canonical_qs = s->info.request_params; + s->aws4_auth_canonical_qs = s->info.request_params; - if (!canonical_qs.empty()) { + if (!s->aws4_auth_canonical_qs.empty()) { /* handle case when query string exists. Step 3 in * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html */ map canonical_qs_map; - istringstream cqs(canonical_qs); + istringstream cqs(s->aws4_auth_canonical_qs); string keyval; while (getline(cqs, keyval, '&')) { @@ -2908,16 +2971,16 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) } } - canonical_qs = ""; + s->aws4_auth_canonical_qs = ""; map::iterator last = canonical_qs_map.end(); --last; for (map::iterator it = canonical_qs_map.begin(); it != canonical_qs_map.end(); ++it) { - canonical_qs.append(it->first + "=" + it->second); + s->aws4_auth_canonical_qs.append(it->first + "=" + it->second); if (it != last) { - canonical_qs.append("&"); + s->aws4_auth_canonical_qs.append("&"); } } @@ -2926,34 +2989,40 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) /* craft canonical headers */ map canonical_hdrs_map; - istringstream sh(signedheaders); + istringstream sh(s->aws4_auth_signedheaders); string token; while (getline(sh, token, ';')) { string token_env = "HTTP_" + token; transform(token_env.begin(), token_env.end(), token_env.begin(), ::toupper); replace(token_env.begin(), token_env.end(), '-', '_'); + if (token_env == "HTTP_CONTENT_LENGTH") { + token_env = "CONTENT_LENGTH"; + } + if (token_env == "HTTP_CONTENT_TYPE") { + token_env = "CONTENT_TYPE"; + } const char *t = s->info.env->get(token_env.c_str()); - if (!t) + if (!t) { + dout(10) << "error getting env var" << dendl; return -EINVAL; + } string token_value = string(t); canonical_hdrs_map[token] = rgw_trim_whitespace(token_value); } - string canonical_hdrs; - for (map::iterator it = canonical_hdrs_map.begin(); it != canonical_hdrs_map.end(); ++it) { - canonical_hdrs.append(it->first + ":" + it->second + "\n"); + s->aws4_auth_canonical_hdrs.append(it->first + ":" + it->second + "\n"); } - dout(10) << "canonical headers format = " << canonical_hdrs << dendl; + dout(10) << "canonical headers format = " << s->aws4_auth_canonical_hdrs << dendl; /* craft signed headers */ - string signed_hdrs = signedheaders; + s->aws4_auth_signed_hdrs = s->aws4_auth_signedheaders; - /* TODO: craft request payload */ + /* handle request payload */ /* from rfc2616 - 4.3 Message Body * @@ -2961,6 +3030,8 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) * Content-Length or Transfer-Encoding header field in the request's message-headers." */ + s->aws4_auth_payload_hash = ""; + string request_payload; bool unsigned_payload = false; @@ -2972,85 +3043,38 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) /* requests lacking of body are authenticated now */ - /* craft canonical request */ - - string canonical_req; - string canonical_req_hash; - - rgw_create_s3_v4_canonical_request(s, canonical_uri, canonical_qs, - canonical_hdrs, signed_hdrs, request_payload, unsigned_payload, - canonical_req, canonical_req_hash); - - /* TODO: read body in request_payload */ - - } - - /* craft canonical request */ + /* complete aws4 auth */ - string canonical_req; - string canonical_req_hash; - - rgw_create_s3_v4_canonical_request(s, canonical_uri, canonical_qs, - canonical_hdrs, signed_hdrs, request_payload, canonical_req, - canonical_req_hash); - - /* - * create a string to sign - * - * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html - */ - - string string_to_sign; + int err = authorize_v4_complete(store, s, request_payload, unsigned_payload); + if (err) { + return err; + } - rgw_create_s3_v4_string_to_sign("AWS4-HMAC-SHA256", date, credential_scope, - canonical_req_hash, string_to_sign); + /* verify signature */ - /* - * calculate the AWS signature - * - * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html - */ + if (s->aws4_auth_signature != s->aws4_auth_new_signature) { + return -ERR_SIGNATURE_NO_MATCH; + } - string cs_aux = credential_scope; + /* authorization ok */ - string date_cs = cs_aux; - pos = date_cs.find("/"); - date_cs = date_cs.substr(0, pos); - cs_aux = cs_aux.substr(pos + 1, cs_aux.length()); + dout(10) << "v4 auth ok" << dendl; - string region_cs = cs_aux; - pos = region_cs.find("/"); - region_cs = region_cs.substr(0, pos); - cs_aux = cs_aux.substr(pos + 1, cs_aux.length()); + /* aws4 auth completed */ - string service_cs = cs_aux; - pos = service_cs.find("/"); - service_cs = service_cs.substr(0, pos); + s->aws4_auth_complete = false; - string new_signature; + } else { - int err = rgw_calculate_s3_v4_aws_signature(s, access_key_id, date_cs, - region_cs, service_cs, string_to_sign, new_signature); - if (err) { - return err; - } + /* aws4 auth not completed... delay aws4 auth */ - /* verify signature */ + s->aws4_auth_complete = true; - dout(10) << "----------------------------- Verifying signatures" << dendl; - dout(10) << "Signature = " << signature << dendl; - dout(10) << "New Signature = " << new_signature << dendl; - dout(10) << "-----------------------------" << dendl; + dout(10) << "body content detected... delaying v4 auth" << dendl; - if (signature != new_signature) { - return -ERR_SIGNATURE_NO_MATCH; } - /* authorization ok */ - - dout(10) << "v4 auth ok" << dendl; - - map::iterator iter = s->user.access_keys.find(access_key_id); + map::iterator iter = s->user.access_keys.find(s->aws4_auth_access_key_id); if (iter == s->user.access_keys.end()) { dout(0) << "ERROR: access key not encoded in user info" << dendl; return -EPERM; diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index a0fd7f0db6c7..24b75a0eb37f 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -386,9 +386,13 @@ public: class RGW_Auth_S3 { public: static int authorize(RGWRados *store, struct req_state *s); + static int authorize_aws4_auth_complete(RGWRados *store, struct req_state *s); private: static int authorize_v2(RGWRados *store, struct req_state *s); static int authorize_v4(RGWRados *store, struct req_state *s); + static int authorize_v4_complete(RGWRados *store, struct req_state *s, + string request_payload, bool unsigned_payload); + }; class RGWHandler_Auth_S3 : public RGWHandler_ObjStore { -- 2.47.3