From: Javier M. Mellid Date: Fri, 12 Jun 2015 13:04:23 +0000 (+0200) Subject: rgw: AWS4 authentication minimal support X-Git-Tag: v10.1.0~351^2^2~27 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=9a0de484d80b1ea9671166d3d094f619e9008cda;p=ceph.git rgw: AWS4 authentication minimal support Amazon S3 supports Signature Version 4. This patch contains the minimal implementation supporting AWS4 in RGW. It implements AWS4 authentication on http methods without body content and vars not shipping in the request query string. Fixes: #10333 Signed-off-by: Javier M. Mellid --- diff --git a/src/common/ceph_crypto.cc b/src/common/ceph_crypto.cc index de5a03b790ab..6da3232b1dc4 100644 --- a/src/common/ceph_crypto.cc +++ b/src/common/ceph_crypto.cc @@ -36,6 +36,10 @@ ceph::crypto::HMACSHA1::~HMACSHA1() { } +ceph::crypto::HMACSHA256::~HMACSHA256() +{ +} + #elif defined(USE_NSS) // for SECMOD_RestartModules() @@ -85,7 +89,7 @@ void ceph::crypto::shutdown() pthread_mutex_unlock(&crypto_init_mutex); } -ceph::crypto::HMACSHA1::~HMACSHA1() +ceph::crypto::HMAC::~HMAC() { PK11_DestroyContext(ctx, PR_TRUE); PK11_FreeSymKey(symkey); diff --git a/src/common/ceph_crypto.h b/src/common/ceph_crypto.h index 4ef8821f836b..689128fd1c22 100644 --- a/src/common/ceph_crypto.h +++ b/src/common/ceph_crypto.h @@ -6,6 +6,7 @@ #define CEPH_CRYPTO_MD5_DIGESTSIZE 16 #define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20 #define CEPH_CRYPTO_SHA1_DIGESTSIZE 20 +#define CEPH_CRYPTO_HMACSHA256_DIGESTSIZE 32 #define CEPH_CRYPTO_SHA256_DIGESTSIZE 32 #ifdef USE_CRYPTOPP @@ -36,6 +37,15 @@ namespace ceph { } ~HMACSHA1(); }; + + class HMACSHA256: public CryptoPP::HMAC { + public: + HMACSHA256 (const byte *key, size_t length) + : CryptoPP::HMAC(key, length) + { + } + ~HMACSHA256(); + }; } } #elif defined(USE_NSS) @@ -107,31 +117,33 @@ namespace ceph { SHA256 () : Digest(SEC_OID_SHA256, CEPH_CRYPTO_SHA256_DIGESTSIZE) { } }; - class HMACSHA1 { + class HMAC { private: PK11SlotInfo *slot; PK11SymKey *symkey; PK11Context *ctx; + unsigned int digest_size; public: - HMACSHA1 (const byte *key, size_t length) { - slot = PK11_GetBestSlot(CKM_SHA_1_HMAC, NULL); + HMAC (CK_MECHANISM_TYPE cktype, unsigned int digestsize, const byte *key, size_t length) { + digest_size = digestsize; + slot = PK11_GetBestSlot(cktype, NULL); assert(slot); SECItem keyItem; keyItem.type = siBuffer; keyItem.data = (unsigned char*)key; keyItem.len = length; - symkey = PK11_ImportSymKey(slot, CKM_SHA_1_HMAC, PK11_OriginUnwrap, + symkey = PK11_ImportSymKey(slot, cktype, PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL); assert(symkey); SECItem param; param.type = siBuffer; param.data = NULL; param.len = 0; - ctx = PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, CKA_SIGN, symkey, ¶m); + ctx = PK11_CreateContextBySymKey(cktype, CKA_SIGN, symkey, ¶m); assert(ctx); Restart(); } - ~HMACSHA1 (); + ~HMAC (); void Restart() { SECStatus s; s = PK11_DigestBegin(ctx); @@ -145,12 +157,22 @@ namespace ceph { void Final (byte *digest) { SECStatus s; unsigned int dummy; - s = PK11_DigestFinal(ctx, digest, &dummy, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE); + s = PK11_DigestFinal(ctx, digest, &dummy, digest_size); assert(s == SECSuccess); - assert(dummy == CEPH_CRYPTO_HMACSHA1_DIGESTSIZE); + assert(dummy == digest_size); Restart(); } }; + + class HMACSHA1 : public HMAC { + public: + HMACSHA1 (const byte *key, size_t length) : HMAC(CKM_SHA_1_HMAC, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, key, length) { } + }; + + class HMACSHA256 : public HMAC { + public: + HMACSHA256 (const byte *key, size_t length) : HMAC(CKM_SHA256_HMAC, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, key, length) { } + }; } } diff --git a/src/common/utf8.c b/src/common/utf8.c index 5bf0a5be8508..3bc77c32cded 100644 --- a/src/common/utf8.c +++ b/src/common/utf8.c @@ -16,9 +16,6 @@ #include #include -#define MAX_UTF8_SZ 6 -#define INVALID_UTF8_CHAR 0xfffffffful - static int high_bits_set(int c) { int ret = 0; diff --git a/src/common/utf8.h b/src/common/utf8.h index 28649effaec4..83efe6fd6dad 100644 --- a/src/common/utf8.h +++ b/src/common/utf8.h @@ -15,6 +15,9 @@ #ifndef CEPH_COMMON_UTF8_H #define CEPH_COMMON_UTF8_H +#define MAX_UTF8_SZ 6 +#define INVALID_UTF8_CHAR 0xfffffffful + #ifdef __cplusplus extern "C" { #endif diff --git a/src/rgw/rgw_auth_s3.cc b/src/rgw/rgw_auth_s3.cc index b372630cb3e2..c132a35482c0 100644 --- a/src/rgw/rgw_auth_s3.cc +++ b/src/rgw/rgw_auth_s3.cc @@ -2,6 +2,7 @@ // vim: ts=8 sw=2 smarttab #include "common/armor.h" +#include "common/utf8.h" #include "rgw_common.h" #define dout_subsys ceph_subsys_rgw @@ -147,6 +148,11 @@ 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) +{ + calc_hash_sha256(str, dest); +} + static inline bool is_base64_for_content_md5(unsigned char c) { return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '=')); } @@ -214,3 +220,182 @@ bool rgw_create_s3_canonical_header(req_info& info, utime_t *header_time, string return true; } + +/* + * assemble canonical request for signature version 4 + */ +void rgw_assemble_s3_v4_canonical_request(const char *method, const char *canonical_uri, const char *canonical_qs, + const char *canonical_hdrs, const char *signed_hdrs, const char *request_payload_hash, + string& dest_str) +{ + string dest; + + if (method) + dest = method; + dest.append("\n"); + + if (canonical_uri) { + dest.append(canonical_uri); + } + dest.append("\n"); + + if (canonical_qs) + dest.append(canonical_qs); + dest.append("\n"); + + if (canonical_hdrs) + dest.append(canonical_hdrs); + dest.append("\n"); + + if (signed_hdrs) + dest.append(signed_hdrs); + dest.append("\n"); + + if (request_payload_hash) + dest.append(request_payload_hash); + + dest_str = dest; +} + +/* + * create canonical request for signature version 4 + */ +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, + string& canonical_req, string& canonical_req_hash) +{ + string request_payload_hash; + + rgw_hash_s3_string_sha256(request_payload, 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); + + dout(10) << "canonical request = " << canonical_req << dendl; + dout(10) << "canonical request hash = " << canonical_req_hash << dendl; +} + +/* + * assemble string to sign for signature version 4 + */ +void rgw_assemble_s3_v4_string_to_sign(const char *algorithm, const char *request_date, + const char *credential_scope, const char *hashed_qr, string& dest_str) +{ + string dest; + + if (algorithm) + dest = algorithm; + dest.append("\n"); + + if (request_date) + dest.append(request_date); + dest.append("\n"); + + if (credential_scope) + dest.append(credential_scope); + dest.append("\n"); + + if (hashed_qr) + dest.append(hashed_qr); + + dest_str = dest; +} + +/* + * create string to sign for signature version 4 + */ +void rgw_create_s3_v4_string_to_sign(const string& algorithm, const string& request_date, + const string& credential_scope, const string& hashed_qr, + string& string_to_sign) { + + rgw_assemble_s3_v4_string_to_sign(algorithm.c_str(), request_date.c_str(), + credential_scope.c_str(), hashed_qr.c_str(), string_to_sign); + + dout(10) << "string to sign = " << string_to_sign << dendl; +} + +/* + * calculate the AWS signature version 4 + */ +int rgw_calculate_s3_v4_aws_signature(struct req_state *s, + const string& access_key_id, const string &date, const string& region, + const string& service, const string& string_to_sign, string& signature) { + + map::iterator iter = s->user.access_keys.find(access_key_id); + if (iter == s->user.access_keys.end()) { + dout(10) << "ERROR: access key not encoded in user info" << dendl; + return -EPERM; + } + + RGWAccessKey& k = iter->second; + + string secret_key = "AWS4" + k.key; + + char secret_k[secret_key.size() * MAX_UTF8_SZ]; + + size_t n = 0; + + for (size_t i = 0; i < secret_key.size(); i++) { + n += encode_utf8(secret_key[i], (unsigned char *) (secret_k + n)); + } + + string secret_key_utf8_k(secret_k, n); + + /* date */ + + char date_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + calc_hmac_sha256(secret_key_utf8_k.c_str(), secret_key_utf8_k.size(), + date.c_str(), date.size(), date_k); + + char aux[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1]; + buf_to_hex((unsigned char *) date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux); + + dout(10) << "date_k = " << string(aux) << dendl; + + /* region */ + + char region_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + calc_hmac_sha256(date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, region.c_str(), region.size(), region_k); + + buf_to_hex((unsigned char *) region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux); + + dout(10) << "region_k = " << string(aux) << dendl; + + /* service */ + + char service_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + calc_hmac_sha256(region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, service.c_str(), service.size(), service_k); + + buf_to_hex((unsigned char *) service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux); + + dout(10) << "service_k = " << string(aux) << dendl; + + /* aws4_request */ + + char signing_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + calc_hmac_sha256(service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, "aws4_request", 12, signing_k); + + buf_to_hex((unsigned char *) signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux); + + dout(10) << "signing_k = " << string(aux) << dendl; + + /* new signature */ + + char signature_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + calc_hmac_sha256(signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, string_to_sign.c_str(), string_to_sign.size(), signature_k); + + buf_to_hex((unsigned char *) signature_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux); + + dout(10) << "signature_k = " << string(aux) << dendl; + + signature = string(aux); + + dout(10) << "new signature = " << signature << dendl; + + return 0; +} diff --git a/src/rgw/rgw_auth_s3.h b/src/rgw/rgw_auth_s3.h index 3a7da098c6a6..b08fb560a1a5 100644 --- a/src/rgw/rgw_auth_s3.h +++ b/src/rgw/rgw_auth_s3.h @@ -12,7 +12,11 @@ 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_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, + string& canonical_req, string& canonical_req_hash); +void rgw_create_s3_v4_string_to_sign(const string& algorithm, const string& request_date, const string& credential_scope, const string& hashed_qr, string& string_to_sign); +int rgw_calculate_s3_v4_aws_signature(struct req_state *s, const string& access_key_id, const string &date, const string& region, const string& service, const string& string_to_sign, string& signature); #endif diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 80b6c1bd4ace..2de86c54117f 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -419,6 +419,38 @@ void calc_hmac_sha1(const char *key, int key_len, hmac.Final((unsigned char *)dest); } +/* + * 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) +{ + char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + + HMACSHA256 hmac((const unsigned char *)key, key_len); + hmac.Update((const unsigned char *)msg, msg_len); + hmac.Final((unsigned char *)hash_sha256); + + memcpy(dest, hash_sha256, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE); +} + +/* + * calculate the sha256 hash value of a given msg + */ +void calc_hash_sha256(const string& msg, string& dest) +{ + char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE]; + + SHA256 hash; + hash.Update((const unsigned char *)msg.c_str(), msg.size()); + 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); + + dest = 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 e444f735c442..948c2797af19 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -1712,9 +1712,12 @@ extern bool verify_object_permission(struct req_state *s, int perm); extern bool url_decode(const string& src_str, string& dest_str, bool in_query = false); extern void url_encode(const string& src, string& dst); +/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */ extern void calc_hmac_sha1(const char *key, int key_len, const char *msg, int msg_len, char *dest); -/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */ +/* 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 string& msg, string& dest); extern int rgw_parse_op_type_list(const string& str, uint32_t *perm); diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index dd22378415af..41b7e7cfd67e 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -2634,6 +2634,365 @@ static void init_anon_user(struct req_state *s) s->perm_mask = RGW_PERM_FULL_CONTROL; } +/* + * handle v4 signatures (rados auth only) + */ +int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s) +{ + string::size_type pos; + string algorithm; + string date; + string credential; + string signedheaders; + string signature; + string access_key_id; + string credential_scope; + + /* v4 requires rados auth */ + if (!store->ctx()->_conf->rgw_s3_auth_use_rados) { + return -EPERM; + } + + if ((!s->http_auth) || !(*s->http_auth)) { + + /* auth ships with req params ... */ + + /* look for required params */ + + algorithm = s->info.args.get("X-Amz-Algorithm"); + if (algorithm.size() == 0) { + return -EPERM; + } + credential = s->info.args.get("X-Amz-Credential"); + if (credential.size() == 0) { + return -EPERM; + } + date = s->info.args.get("X-Amz-Date"); + if (date.size() == 0) { + return -EPERM; + } + signedheaders = s->info.args.get("X-Amz-SignedHeaders"); + if (signedheaders.size() == 0) { + return -EPERM; + } + + signature = s->info.args.get("X-Amz-Signature"); + if (signature.size() == 0) { + return -EPERM; + } + + } else { + + /* auth ships in headers ... */ + + /* valid algorithm? */ + + if (strncmp(s->http_auth, "AWS4-HMAC-SHA256", 16)) { + return -EPERM; + } + + algorithm = "AWS4-HMAC-SHA256"; + + /* ------------------------- handle Credential header */ + + credential = s->http_auth; + + credential = credential.substr(17, credential.length()); + + pos = credential.find("Credential"); + if (pos == std::string::npos) { + return -EINVAL; + } + + credential = credential.substr(pos, credential.find(",")); + + credential = credential.substr(pos + 1, credential.length()); + + pos = credential.find("="); + + credential = credential.substr(pos + 1, credential.length()); + + /* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */ + dout(10) << "v4 credential format = " << credential << dendl; + + if (std::count(credential.begin(), credential.end(), '/') != 4) { + return -EINVAL; + } + + /* credential must end with 'aws4_request' */ + if (credential.find("aws4_request") == std::string::npos) { + return -EINVAL; + } + + /* grab access key id */ + + pos = credential.find("/"); + access_key_id = credential.substr(0, pos); + + dout(10) << "access key id = " << access_key_id << dendl; + + /* grab credential scope */ + + credential_scope = credential.substr(pos + 1, credential.length()); + + dout(10) << "credential scope = " << credential_scope << dendl; + + /* ------------------------- handle SignedHeaders header */ + + signedheaders = s->http_auth; + + signedheaders = signedheaders.substr(17, signedheaders.length()); + + pos = signedheaders.find("SignedHeaders"); + if (pos == std::string::npos) { + return -EINVAL; + } + + signedheaders = signedheaders.substr(pos, signedheaders.length()); + + pos = signedheaders.find(","); + if (pos == std::string::npos) { + return -EINVAL; + } + + signedheaders = signedheaders.substr(0, pos); + + pos = signedheaders.find("="); + if (pos == std::string::npos) { + return -EINVAL; + } + + signedheaders = signedheaders.substr(pos + 1, signedheaders.length()); + + /* host;user-agent;x-amz-content-sha256;x-amz-date */ + dout(10) << "v4 signedheaders format = " << signedheaders << dendl; + + /* ------------------------- handle Signature header */ + + signature = s->http_auth; + + signature = signature.substr(17, signature.length()); + + pos = signature.find("Signature"); + if (pos == std::string::npos) { + return -EINVAL; + } + + signature = signature.substr(pos, signature.length()); + + pos = signature.find("="); + if (pos == std::string::npos) { + return -EINVAL; + } + + signature = signature.substr(pos + 1, signature.length()); + + /* sig hex str */ + dout(10) << "v4 signature format = " << signature << dendl; + + /* ------------------------- handle x-amz-date header */ + + /* grab date */ + + date = s->info.env->get("HTTP_X_AMZ_DATE"); + if (date.empty()) { + return -EINVAL; + } + + } + + /* 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 + << " can't authenticate" << dendl; + return -ERR_INVALID_ACCESS_KEY; + } + + /* + * create a canonical request + * + * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + */ + + /* craft canonical uri */ + + /* here code should normalize via rfc3986 but S3 does **NOT** do path normalization + * 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; + + if (canonical_uri.empty()) { + canonical_uri = "/"; + } + + /* craft canonical query string */ + + string canonical_qs = s->info.request_params; + + if (!canonical_qs.empty()) { + + /* TODO: implement step 3 in + * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html */ + + /* handle case when query string exists */ + } + + /* craft canonical headers */ + + map canonical_hdrs_map; + istringstream sh(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(), '-', '_'); + const char *t = s->info.env->get(token_env.c_str()); + if (!t) + 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"); + } + + dout(10) << "canonical headers format = " << canonical_hdrs << dendl; + + /* craft signed headers */ + + string signed_hdrs = signedheaders; + + /* TODO: craft request payload */ + + /* from rfc2616 - 4.3 Message Body + * + * "The presence of a message-body in a request is signaled by the inclusion of a + * Content-Length or Transfer-Encoding header field in the request's message-headers." + */ + + string request_payload; + + if ((s->content_length > 0) || s->info.env->get("HTTP_TRANSFER_ENCODING")) { + + /* TODO: read body in request_payload */ + + } + + /* 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, 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", date, 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 = 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); + + string new_signature; + + 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; + } + + /* verify signature */ + + dout(10) << "----------------------------- Verifying signatures" << dendl; + dout(10) << "Signature = " << signature << dendl; + dout(10) << "New Signature = " << new_signature << dendl; + dout(10) << "-----------------------------" << 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); + if (iter == s->user.access_keys.end()) { + dout(0) << "ERROR: access key not encoded in user info" << dendl; + return -EPERM; + } + + RGWAccessKey& k = iter->second; + + if (!k.subuser.empty()) { + map::iterator uiter = s->user.subusers.find(k.subuser); + if (uiter == s->user.subusers.end()) { + dout(0) << "NOTICE: could not find subuser: " << k.subuser << dendl; + return -EPERM; + } + RGWSubUser& subuser = uiter->second; + s->perm_mask = subuser.perm_mask; + } else { + s->perm_mask = RGW_PERM_FULL_CONTROL; + } + + if (s->user.system) { + s->system_request = true; + dout(20) << "system request" << dendl; + s->info.args.set_system(); + string effective_uid = s->info.args.get(RGW_SYS_PARAM_PREFIX "uid"); + RGWUserInfo effective_user; + if (!effective_uid.empty()) { + int ret = rgw_get_user_info_by_uid(store, effective_uid, effective_user); + if (ret < 0) { + ldout(s->cct, 0) << "User lookup failed!" << dendl; + return -ENOENT; + } + s->user = effective_user; + } + } + + // populate the owner info + s->owner.set_id(s->user.user_id); + s->owner.set_name(s->user.display_name); + + return 0; +} + /* * verify that a signed request comes from the keyholder * by checking the signature against our locally-computed version @@ -2644,6 +3003,9 @@ int RGW_Auth_S3::authorize(RGWRados *store, struct req_state *s) string auth_id; string auth_sign; + if (!authorize_v4(store, s)) + return 0; + time_t now; time(&now); diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index ada556290f77..c264f34b09b1 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -386,6 +386,8 @@ public: class RGW_Auth_S3 { public: static int authorize(RGWRados *store, struct req_state *s); +private: + static int authorize_v4(RGWRados *store, struct req_state *s); }; class RGWHandler_Auth_S3 : public RGWHandler_ObjStore {