From e5f4ea2f7d8a57eb5e85d3af07cc6dab38bcc3b3 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Thu, 12 Jan 2017 01:25:15 +0100 Subject: [PATCH] rgw: unify handling S3's ::authorize_v2 and ::get_policy methods. Signed-off-by: Radoslaw Zarzynski --- src/rgw/rgw_auth_keystone.cc | 50 +--------- src/rgw/rgw_auth_keystone.h | 10 +- src/rgw/rgw_auth_s3.h | 16 ++++ src/rgw/rgw_rest_s3.cc | 180 +++++++++++++++++++---------------- src/rgw/rgw_rest_s3.h | 69 +++++++------- src/rgw/rgw_user.cc | 13 ++- src/rgw/rgw_user.h | 7 +- 7 files changed, 169 insertions(+), 176 deletions(-) diff --git a/src/rgw/rgw_auth_keystone.cc b/src/rgw/rgw_auth_keystone.cc index a7d3048d6ec9f..b333be9f7e2cd 100644 --- a/src/rgw/rgw_auth_keystone.cc +++ b/src/rgw/rgw_auth_keystone.cc @@ -363,34 +363,6 @@ EC2Engine::get_from_keystone(const std::string& access_key_id, return std::move(token_envelope); } - -bool EC2Engine::is_time_skew_ok(const utime_t& header_time, - const bool qsr) const -{ - /* Check for time skew first. */ - const time_t req_sec = header_time.sec(); - time_t now; - time(&now); - - if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 || - req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) { - ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now - << "; now - RGW_AUTH_GRACE_MINS=" - << now - RGW_AUTH_GRACE_MINS * 60 - << "; now + RGW_AUTH_GRACE_MINS=" - << now + RGW_AUTH_GRACE_MINS * 60 - << dendl; - - ldout(cct, 0) << "NOTICE: request time skew too big now=" - << utime_t(now, 0) - << " req_time=" << header_time - << dendl; - return false; - } else { - return true; - } -} - EC2Engine::acl_strategy_t EC2Engine::get_acl_strategy(const EC2Engine::token_envelope_t&) const { @@ -429,11 +401,9 @@ EC2Engine::get_creds_info(const EC2Engine::token_envelope_t& token, }; } -rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, +rgw::auth::Engine::result_t EC2Engine::authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, /* Passthorugh only! */ const req_state* s) const { @@ -452,16 +422,6 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id, std::vector admin; } accepted_roles(cct); - bool is_ok; - std::string string_to_sign; - utime_t header_time; - std::tie(is_ok, string_to_sign, header_time) = \ - rgw_create_s3_canonical_header(info, qsr); - if (! is_ok) { - dout(10) << "failed to create auth header\n" << string_to_sign << dendl; - throw -EPERM; - } - const auto t = get_from_keystone(access_key_id, string_to_sign, signature); /* Verify expiration. */ @@ -472,10 +432,6 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id, return std::make_pair(nullptr, nullptr); } - if (! is_time_skew_ok(header_time, qsr)) { - throw -ERR_REQUEST_TIME_SKEWED; - } - /* check if we have a valid role */ bool found = false; for (const auto& role : accepted_roles.plain) { diff --git a/src/rgw/rgw_auth_keystone.h b/src/rgw/rgw_auth_keystone.h index 3307b4865d880..c3b00e47f2383 100644 --- a/src/rgw/rgw_auth_keystone.h +++ b/src/rgw/rgw_auth_keystone.h @@ -80,16 +80,12 @@ class EC2Engine : public rgw::auth::s3::Version2ndEngine { auth_info_t get_creds_info(const token_envelope_t& token, const std::vector& admin_roles ) const noexcept; - bool is_time_skew_ok(const utime_t& header_time, - const bool qsr) const; token_envelope_t get_from_keystone(const std::string& access_key_id, const std::string& string_to_sign, const std::string& signature) const; - result_t authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, + result_t authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* s) const override; public: EC2Engine(CephContext* const cct, diff --git a/src/rgw/rgw_auth_s3.h b/src/rgw/rgw_auth_s3.h index 99f5d015b5f51..a50229cb3670b 100644 --- a/src/rgw/rgw_auth_s3.h +++ b/src/rgw/rgw_auth_s3.h @@ -94,6 +94,7 @@ public: AWSv2AuthStrategy(CephContext* const cct, RGWRados* const store) : store(store), + extractor(cct), external_engines(cct, store, &extractor), local_engine(cct, store, extractor, static_cast(this)) { @@ -103,6 +104,21 @@ public: } } + /* FIXME(rzarzynski): hack for S3's browsers upload. */ + AWSv2AuthStrategy(CephContext* const cct, + RGWRados* const store, + Version2ndEngine::Extractor* const external_extractor) + : store(store), + extractor(cct), + external_engines(cct, store, external_extractor), + local_engine(cct, store, *external_extractor, + static_cast(this)) { + add_engine(Control::SUFFICIENT, external_engines); + if (cct->_conf->rgw_s3_auth_use_rados) { + add_engine(Control::SUFFICIENT, local_engine); + } + } + const char* get_name() const noexcept override { return "rgw::auth::s3::AWSv2AuthStrategy"; } diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index b86f36a0b49a6..ea4d1f34b0c2d 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -1798,6 +1798,12 @@ int RGWPostObj_ObjStore_S3::get_params() return 0; } +static std::string to_string(ceph::bufferlist& bl) +{ + return std::string(bl.c_str(), + static_cast(bl.length())); +} + int RGWPostObj_ObjStore_S3::get_policy() { bufferlist encoded_policy; @@ -1818,11 +1824,13 @@ int RGWPostObj_ObjStore_S3::get_policy() return -EINVAL; } - rgw::auth::s3::RGWGetPolicyV2Extractor extr(s3_access_key, received_signature_str); + rgw::auth::s3::RGWGetPolicyV2Extractor extr(s3_access_key, + received_signature_str, + to_string(encoded_policy)); /* FIXME: this is a makeshift solution. The browser upload authenication will be * handled by an instance of rgw::auth::Completer spawned in Handler's authorize() * method. Thus creating a strategy per request won't be necessary. */ - const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store); + const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store, &extr); try { auto result = strategy.authenticate(s); auto& applier = result.first; @@ -1847,7 +1855,8 @@ int RGWPostObj_ObjStore_S3::get_policy() } ldout(s->cct, 0) << "Successful Signature Verification!" << dendl; - bufferlist decoded_policy; + + ceph::bufferlist decoded_policy; try { decoded_policy.decode_base64(encoded_policy); } catch (buffer::error& err) { @@ -1857,9 +1866,9 @@ int RGWPostObj_ObjStore_S3::get_policy() } decoded_policy.append('\0'); // NULL terminate - ldout(s->cct, 0) << "POST policy: " << decoded_policy.c_str() << dendl; + int r = post_policy.from_json(decoded_policy, err_msg); if (r < 0) { if (err_msg.empty()) { @@ -3884,30 +3893,6 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s, bool force_b */ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s) { - bool qsr = false; - string auth_id; - string auth_sign; - - time_t now; - time(&now); - - if (!s->http_auth || !(*s->http_auth)) { - auth_id = s->info.args.get("AWSAccessKeyId"); - auth_sign = s->info.args.get("Signature"); - string date = s->info.args.get("Expires"); - time_t exp = atoll(date.c_str()); - if (now >= exp) - return -EPERM; - qsr = true; - } else { - string auth_str(s->http_auth + 4); - int pos = auth_str.rfind(':'); - if (pos < 0) - return -EINVAL; - auth_id = auth_str.substr(0, pos); - auth_sign = auth_str.substr(pos + 1); - } - /* TODO(rzarzynski): this will be moved to the S3 handlers -- in exactly * way like we currently have in the case of Swift API. */ static const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store); @@ -4211,28 +4196,88 @@ namespace rgw { namespace auth { namespace s3 { +bool rgw::auth::s3::RGWS3V2Extractor::is_time_skew_ok(const utime_t& header_time, + const bool qsr) const +{ + /* Check for time skew first. */ + const time_t req_sec = header_time.sec(); + time_t now; + time(&now); + + if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 || + req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) { + ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now + << "; now - RGW_AUTH_GRACE_MINS=" + << now - RGW_AUTH_GRACE_MINS * 60 + << "; now + RGW_AUTH_GRACE_MINS=" + << now + RGW_AUTH_GRACE_MINS * 60 + << dendl; + + ldout(cct, 0) << "NOTICE: request time skew too big now=" + << utime_t(now, 0) + << " req_time=" << header_time + << dendl; + return false; + } else { + return true; + } +} + std::tuple + Version2ndEngine::Extractor::string_to_sign_t> rgw::auth::s3::RGWS3V2Extractor::get_auth_data(const req_state* const s) const { + std::string access_key_id; + std::string signature; + bool qsr = false; + if (! s->http_auth || s->http_auth[0] == '\0') { - return std::make_tuple(s->info.args.get("AWSAccessKeyId"), - s->info.args.get("Signature"), - s->info.args.get("Expires"), - true); + /* Credentials are provided in query string. We also need to verify + * the "Expires" parameter now. */ + access_key_id = s->info.args.get("AWSAccessKeyId"); + signature = s->info.args.get("Signature"); + qsr = true; + + std::string expires = s->info.args.get("Expires"); + if (! expires.empty()) { + const time_t exp = atoll(expires.c_str()); + time_t now; + time(&now); + + if (now >= exp) { + throw -EPERM; + } + } } else { + /* The "Authorization" HTTP header is being used. */ const std::string auth_str(s->http_auth + strlen("AWS ")); const size_t pos = auth_str.rfind(':'); if (pos != std::string::npos) { - return std::make_tuple(auth_str.substr(0, pos), - auth_str.substr(pos + 1), - std::string(), false); + access_key_id = auth_str.substr(0, pos); + signature = auth_str.substr(pos + 1); } } - return std::make_tuple("", "", "", false); + /* 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->info, &header_time, string_to_sign, + qsr)) { + ldout(cct, 10) << "failed to create the canonized auth header\n" + << string_to_sign << dendl; + throw -EPERM; + } + + ldout(cct, 10) << "string_to_sign:\n" << string_to_sign << dendl; + + if (! is_time_skew_ok(header_time, qsr)) { + throw -ERR_REQUEST_TIME_SKEWED; + } + + return std::make_tuple(std::move(access_key_id), + std::move(signature), + std::move(string_to_sign)); } } /* namespace s3 */ @@ -4289,11 +4334,9 @@ rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noex } rgw::auth::Engine::result_t -rgw::auth::s3::LDAPEngine::authenticate(const std::string access_key_id, - const std::string signature, - const std::string expires, - const bool qsr, - const req_info& /* unused */, +rgw::auth::s3::LDAPEngine::authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* const s) const { /* boost filters and/or string_ref may throw on invalid input */ @@ -4332,11 +4375,9 @@ rgw::auth::s3::LDAPEngine::authenticate(const std::string access_key_id, /* LocalVersion2ndEngine */ rgw::auth::Engine::result_t -rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, +rgw::auth::s3::LocalVersion2ndEngine::authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* const s) const { if (access_key_id.empty() || signature.empty()) { @@ -4344,14 +4385,6 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id, throw -EINVAL; } - time_t now; - time(&now); - if (! expires.empty()) { - time_t exp = atoll(expires.c_str()); - if (now >= exp) { - throw -EPERM; - } - } /* get the user info */ RGWUserInfo user_info; if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) { @@ -4367,44 +4400,23 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id, } }*/ - /* now verify signature */ - string auth_hdr; - utime_t header_time; - if (! rgw_create_s3_canonical_header(info, &header_time, auth_hdr, - qsr)) { - ldout(cct, 10) << "failed to create auth header\n" << auth_hdr << dendl; - throw -EPERM; - } - ldout(cct, 10) << "auth_hdr:\n" << auth_hdr << dendl; - time_t req_sec = header_time.sec(); - if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 || - req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) { - ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now - << "; now - RGW_AUTH_GRACE_MINS=" << now - RGW_AUTH_GRACE_MINS * 60 - << "; now + RGW_AUTH_GRACE_MINS=" << now + RGW_AUTH_GRACE_MINS * 60 - << dendl; - ldout(cct, 0) << "NOTICE: request time skew too big now=" << utime_t(now, 0) - << " req_time=" << header_time - << dendl; - throw -ERR_REQUEST_TIME_SKEWED; - } - - map::iterator iter = user_info.access_keys.find(access_key_id); - if (iter == user_info.access_keys.end()) { + const auto iter = user_info.access_keys.find(access_key_id); + if (iter == std::end(user_info.access_keys)) { ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl; throw -EPERM; } - RGWAccessKey& k = iter->second; + const RGWAccessKey& k = iter->second; - string digest; - int ret = rgw_get_s3_header_digest(auth_hdr, k.key, digest); + std::string digest; + int ret = rgw_get_s3_header_digest(string_to_sign, k.key, digest); if (ret < 0) { throw -EPERM; } + ldout(cct, 15) << "string_to_sign=" << string_to_sign << dendl; ldout(cct, 15) << "calculated digest=" << digest << dendl; - ldout(cct, 15) << "auth_sign=" << signature << dendl; + ldout(cct, 15) << "auth signature=" << signature << dendl; ldout(cct, 15) << "compare=" << signature.compare(digest) << dendl; if (signature != digest) { diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index edc93966d3991..7450dfe3d34ea 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -624,13 +624,12 @@ public: using access_key_id_t = std::string; using signature_t = std::string; - using expires_t = std::string; - using qsr_t = bool; + using string_to_sign_t = std::string; virtual std::tuple get_auth_data(const req_state* s) const = 0; + string_to_sign_t> + get_auth_data(const req_state* s) const = 0; }; protected: @@ -644,38 +643,44 @@ protected: using result_t = rgw::auth::Engine::result_t; - virtual result_t authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, + virtual result_t authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* s) const = 0; public: result_t authenticate(const req_state* const s) const final { std::string access_key_id; std::string signature; - std::string expires; - bool qsr; + std::string string_to_sign; - std::tie(access_key_id, signature, expires, qsr) = \ + /* Small reminder: an extractor is allowed to throw! */ + std::tie(access_key_id, signature, string_to_sign) = \ extractor.get_auth_data(s); if (access_key_id.empty() || signature.empty()) { return std::make_pair(nullptr, nullptr); } else { - return authenticate(std::move(access_key_id), std::move(signature), - std::move(expires), qsr, s->info, s); + return authenticate(access_key_id, signature, string_to_sign, s); } } }; class RGWS3V2Extractor : public Version2ndEngine::Extractor { + CephContext* const cct; + + bool is_time_skew_ok(const utime_t& header_time, + const bool qsr) const; + public: + RGWS3V2Extractor(CephContext* const cct) + : cct(cct) { + } + std::tuple get_auth_data(const req_state* s) const override; + string_to_sign_t> + get_auth_data(const req_state* s) const override; }; @@ -683,18 +688,22 @@ class RGWGetPolicyV2Extractor : public Version2ndEngine::Extractor { private: std::string access_key_id; std::string signature; + std::string string_to_sign; public: - RGWGetPolicyV2Extractor(std::string access_key_id, std::string signature) + RGWGetPolicyV2Extractor(std::string access_key_id, + std::string signature, + std::string string_to_sign) : access_key_id(std::move(access_key_id)), - signature(std::move(signature)) {} + signature(std::move(signature)), + string_to_sign(std::move(string_to_sign)) { + } std::tuple get_auth_data(const req_state* s) const override { - // FIXME - return std::make_tuple(this->access_key_id, this->signature, "", false); + string_to_sign_t> + get_auth_data(const req_state* s) const override { + return std::make_tuple(access_key_id, signature, string_to_sign); } }; @@ -716,11 +725,9 @@ protected: acl_strategy_t get_acl_strategy() const; auth_info_t get_creds_info(const rgw::RGWToken& token) const noexcept; - result_t authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, + result_t authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* s) const override; public: LDAPEngine(CephContext* const cct, @@ -745,11 +752,9 @@ class LocalVersion2ndEngine : public Version2ndEngine { RGWRados* const store; const rgw::auth::LocalApplier::Factory* const apl_factory; - result_t authenticate(std::string access_key_id, - std::string signature, - std::string expires, - bool qsr, - const req_info& info, + result_t authenticate(const std::string& access_key_id, + const std::string& signature, + const std::string& string_to_sign, const req_state* s) const override; public: LocalVersion2ndEngine(CephContext* const cct, diff --git a/src/rgw/rgw_user.cc b/src/rgw/rgw_user.cc index 162e1b7189c19..dc143d033585e 100644 --- a/src/rgw/rgw_user.cc +++ b/src/rgw/rgw_user.cc @@ -338,10 +338,15 @@ extern int rgw_get_user_info_by_swift(RGWRados * const store, * Given an access key, finds the user info associated with it. * returns: 0 on success, -ERR# on failure (including nonexistence) */ -extern int rgw_get_user_info_by_access_key(RGWRados *store, string& access_key, RGWUserInfo& info, - RGWObjVersionTracker *objv_tracker, real_time *pmtime) -{ - return rgw_get_user_info_from_index(store, access_key, store->get_zone_params().user_keys_pool, info, objv_tracker, pmtime); +extern int rgw_get_user_info_by_access_key(RGWRados* store, + const std::string& access_key, + RGWUserInfo& info, + RGWObjVersionTracker* objv_tracker, + real_time *pmtime) +{ + return rgw_get_user_info_from_index(store, access_key, + store->get_zone_params().user_keys_pool, + info, objv_tracker, pmtime); } int rgw_get_user_attrs_by_uid(RGWRados *store, diff --git a/src/rgw/rgw_user.h b/src/rgw/rgw_user.h index 832b665530174..50cc1b62909f2 100644 --- a/src/rgw/rgw_user.h +++ b/src/rgw/rgw_user.h @@ -97,8 +97,11 @@ extern int rgw_get_user_info_by_swift(RGWRados *store, * Given an access key, finds the user info associated with it. * returns: 0 on success, -ERR# on failure (including nonexistence) */ -extern int rgw_get_user_info_by_access_key(RGWRados *store, string& access_key, RGWUserInfo& info, - RGWObjVersionTracker *objv_tracker = NULL, real_time *pmtime = NULL); +extern int rgw_get_user_info_by_access_key(RGWRados* store, + const std::string& access_key, + RGWUserInfo& info, + RGWObjVersionTracker* objv_tracker = nullptr, + real_time* pmtime = nullptr); /** * Get all the custom metadata stored for user specified in @user_id * and put it into @attrs. -- 2.39.5