From b5b3e5c975bff56579f8b85d69a6dfceffa64b05 Mon Sep 17 00:00:00 2001 From: Pritha Srivastava Date: Wed, 27 Jul 2016 10:54:22 +0530 Subject: [PATCH] rgw: Rework of s3 v2 local authentication code. The v2 local auth code has been reworked based on the new new authentication infrastructure. Signed-off-by: Pritha Srivastava --- src/rgw/rgw_rest_s3.cc | 273 ++++++++++++++++++++++++++--------------- src/rgw/rgw_rest_s3.h | 106 +++++++++++++--- src/rgw/rgw_user.cc | 1 + 3 files changed, 261 insertions(+), 119 deletions(-) diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index eb20db812d7..e464ddb002d 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -1714,8 +1714,8 @@ int RGWPostObj_ObjStore_S3::get_policy() op_ret = rgw_get_user_info_by_access_key(store, s3_access_key, user_info); if (op_ret < 0) { S3AuthFactory aplfact(store, s->account_name); - RGWGetPolicyLDAPTokenExtractor token_extr(s3_access_key); - RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact); + RGWGetPolicyV2Extractor extr(s3_access_key, received_signature_str); + RGWLDAPAuthEngine ldap(s->cct, store, extr, &aplfact); // try external authenticators if (store->ctx()->_conf->rgw_s3_auth_use_keystone && store->ctx()->_conf->rgw_keystone_url.empty()) @@ -1754,13 +1754,22 @@ int RGWPostObj_ObjStore_S3::get_policy() s->perm_mask = RGW_PERM_FULL_CONTROL; } } else if (ldap.is_applicable()) { - auto applier = ldap.authenticate(); - if (! applier) { + try { + auto applier = ldap.authenticate(); + if (! applier) { + return -EACCES; + } + + try { + applier->load_acct_info(*s->user); + s->perm_mask = applier->get_perm_mask(); + applier->modify_request_state(s); + s->auth_identity = std::move(applier); + } catch (int err) { + return -EACCES; + } + } catch (int err) { return -EACCES; - } else { - applier->load_acct_info(*s->user); - s->perm_mask = applier->get_perm_mask(); - s->auth_identity = std::move(applier); } } else { return -EACCES; @@ -3949,91 +3958,57 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s) } S3AuthFactory aplfact(store, s->account_name); - RGWLDAPTokenExtractor token_extr(s); - RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact); + RGWS3V2Extractor extr(s); + RGWLDAPAuthEngine ldap(s->cct, store, extr, &aplfact); if (ldap.is_applicable()) { - auto applier = ldap.authenticate(); - if (!applier) { - external_auth_result = -EACCES; - } else { - applier->load_acct_info(*s->user); - s->perm_mask = applier->get_perm_mask(); - s->auth_identity = std::move(applier); - external_auth_result = 0; + try { + auto applier = ldap.authenticate(); + if (! applier) { + external_auth_result = -EACCES; + } else { + try { + applier->load_acct_info(*s->user); + s->perm_mask = applier->get_perm_mask(); + applier->modify_request_state(s); + s->auth_identity = std::move(applier); + external_auth_result = 0; + } catch (int err) { + ldout(s->cct, 5) << "applier threw err=" << err << dendl; + external_auth_result = err; + } + } + } catch (int err) { + ldout(s->cct, 5) << "ldap auth engine threw err=" << err << dendl; + external_auth_result = err; } } - /* keystone failed (or not enabled); check if we want to use rados backend */ - if (!store->ctx()->_conf->rgw_s3_auth_use_rados - && external_auth_result < 0) - return external_auth_result; - /* now try rados backend, but only if keystone did not succeed */ if (external_auth_result < 0) { - /* get the user info */ - if (rgw_get_user_info_by_access_key(store, auth_id, *(s->user)) < 0) { - dout(5) << "error reading user info, uid=" << auth_id - << " can't authenticate" << dendl; - return external_auth_result; - } + RGWS3V2LocalAuthEngine localauth(s, store, extr, &aplfact); - /* now verify signature */ - string auth_hdr; - if (!rgw_create_s3_canonical_header(s->info, &s->header_time, auth_hdr, - qsr)) { - dout(10) << "failed to create auth header\n" << auth_hdr << dendl; - return -EPERM; - } - dout(10) << "auth_hdr:\n" << auth_hdr << dendl; - - time_t req_sec = s->header_time.sec(); - if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 || - req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) { - dout(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; - dout(0) << "NOTICE: request time skew too big now=" << utime_t(now, 0) - << " req_time=" << s->header_time - << dendl; - return -ERR_REQUEST_TIME_SKEWED; - } - - map::iterator iter = - s->user->access_keys.find(auth_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; + if (! localauth.is_applicable()) { + return external_auth_result; + } + try { + auto applier = localauth.authenticate(); + if (! applier) { + return external_auth_result; + } + try { + applier->load_acct_info(*s->user); + s->perm_mask = applier->get_perm_mask(); + applier->modify_request_state(s); + s->auth_identity = std::move(applier); + } catch (int err) { + ldout(s->cct, 5) << "applier threw err=" << err << dendl; + return err; + } + } catch (int err) { + ldout(s->cct, 5) << "local auth engine threw err=" << err << dendl; + return err; } - RGWSubUser& subuser = uiter->second; - s->perm_mask = subuser.perm_mask; - } else - s->perm_mask = RGW_PERM_FULL_CONTROL; - - string digest; - int ret = rgw_get_s3_header_digest(auth_hdr, k.key, digest); - if (ret < 0) { - return -EPERM; - } - - dout(15) << "calculated digest=" << digest << dendl; - dout(15) << "auth_sign=" << auth_sign << dendl; - dout(15) << "compare=" << auth_sign.compare(digest) << dendl; - - if (auth_sign != digest) { - return -ERR_SIGNATURE_NO_MATCH; - } - if (s->user->system) { s->system_request = true; dout(20) << "system request" << dendl; @@ -4042,7 +4017,7 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s) RGWUserInfo effective_user; if (!effective_uid.empty()) { rgw_user euid(effective_uid); - ret = rgw_get_user_info_by_uid(store, euid, effective_user); + int ret = rgw_get_user_info_by_uid(store, euid, effective_user); if (ret < 0) { ldout(s->cct, 0) << "User lookup failed!" << dendl; return -EACCES; @@ -4329,9 +4304,9 @@ std::mutex RGWLDAPAuthEngine::mtx; void RGWLDAPAuthEngine::init(CephContext* const cct) { - if (!ldh) { + if (! ldh) { std::lock_guard lck(mtx); - if (!ldh) { + if (! ldh) { const string& ldap_uri = cct->_conf->rgw_ldap_uri; const string& ldap_binddn = cct->_conf->rgw_ldap_binddn; const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn; @@ -4368,34 +4343,34 @@ RGWLDAPAuthEngine::get_creds_info(const rgw::RGWToken& token) const noexcept bool RGWLDAPAuthEngine::is_applicable() const noexcept { - if (!RGWTokenBasedAuthEngine::is_applicable()) { + if (! RGWS3V2AuthEngine::is_applicable()) { return false; } - if (!cct->_conf->rgw_s3_auth_use_ldap || + if (! cct->_conf->rgw_s3_auth_use_ldap || cct->_conf->rgw_ldap_uri.empty()) { return false; } - if(!base64_token.valid()) { - return false; - } - return true; } RGWAuthApplier::aplptr_t RGWLDAPAuthEngine::authenticate() const { + if(! base64_token.valid()) { + return nullptr; + } + //TODO: Uncomment, when we have a migration plan in place. //Check if a user of type other than 'ldap' is already present, if yes, then //return error. - RGWUserInfo user_info; + /*RGWUserInfo user_info; user_info.user_id = base64_token.id; if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) { if (user_info.type != TYPE_LDAP) { ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl; return nullptr; } - } + }*/ if (ldh->auth(base64_token.id, base64_token.key) != 0) { return nullptr; @@ -4404,17 +4379,111 @@ RGWAuthApplier::aplptr_t RGWLDAPAuthEngine::authenticate() const return apl_factory->create_apl_remote(cct, get_acl_strategy(), get_creds_info(base64_token)); } -std::string RGWLDAPTokenExtractor::get_token() const +void RGWS3V2Extractor::get_auth_keys(std::string& access_key_id, + std::string& signature, + std::string& expires, + bool& qsr) const { - string token = ""; - if (!s->http_auth || !(*s->http_auth)) { - token = s->info.args.get("AWSAccessKeyId"); + qsr = false; + if (! s->http_auth || !(*s->http_auth)) { + access_key_id = s->info.args.get("AWSAccessKeyId"); + signature = s->info.args.get("Signature"); + expires = s->info.args.get("Expires"); + qsr = true; } else { string auth_str(s->http_auth + 4); int pos = auth_str.rfind(':'); - if (pos >=0 ) { - token = auth_str.substr(0, pos); + if (pos >= 0) { + access_key_id = auth_str.substr(0, pos); + signature = auth_str.substr(pos + 1); + } + } +} + +bool RGWS3V2LocalAuthEngine::is_applicable() const noexcept +{ + if (! RGWS3V2AuthEngine::is_applicable()) { + return false; + } + if (! s->cct->_conf->rgw_s3_auth_use_rados) { + return false; + } + + return true; +} + +RGWAuthApplier::aplptr_t RGWS3V2LocalAuthEngine::authenticate() const +{ + if (access_key_id.empty() || signature.empty()) { + ldout(s->cct, 5) << "access_key_id or signature is empty" << dendl; + 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 */ + string access_key = access_key_id; + if (rgw_get_user_info_by_access_key(store, access_key, *(s->user)) < 0) { + ldout(s->cct, 5) << "error reading user info, uid=" << access_key_id + << " can't authenticate" << dendl; + return nullptr; + } + //TODO: Uncomment, when we have a migration plan in place. + /*else { + if (s->user->type != TYPE_RGW) { + ldout(cct, 10) << "ERROR: User id of type: " << s->user->type << " is present" << dendl; + throw -EPERM; } + }*/ + + /* now verify signature */ + string auth_hdr; + if (! rgw_create_s3_canonical_header(s->info, &s->header_time, auth_hdr, + qsr)) { + ldout(s->cct, 10) << "failed to create auth header\n" << auth_hdr << dendl; + throw -EPERM; + } + ldout(s->cct, 10) << "auth_hdr:\n" << auth_hdr << dendl; + + time_t req_sec = s->header_time.sec(); + if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 || + req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) { + ldout(s->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(s->cct, 0) << "NOTICE: request time skew too big now=" << utime_t(now, 0) + << " req_time=" << s->header_time + << dendl; + throw -ERR_REQUEST_TIME_SKEWED; + } + + map::iterator iter = s->user->access_keys.find(access_key_id); + if (iter == s->user->access_keys.end()) { + ldout(s->cct, 0) << "ERROR: access key not encoded in user info" << dendl; + throw -EPERM; + } + RGWAccessKey& k = iter->second; + + string digest; + int ret = rgw_get_s3_header_digest(auth_hdr, k.key, digest); + if (ret < 0) { + throw -EPERM; } - return token; + + ldout(s->cct, 15) << "calculated digest=" << digest << dendl; + ldout(s->cct, 15) << "auth_sign=" << signature << dendl; + ldout(s->cct, 15) << "compare=" << signature.compare(digest) << dendl; + + if (signature != digest) { + throw -ERR_SIGNATURE_NO_MATCH; + } + + return apl_factory->create_apl_local(cct, *(s->user), k.subuser); } diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index 80e58f2892f..81d71240989 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -645,7 +645,48 @@ static inline int valid_s3_bucket_name(const string& name, bool relaxed=false) return 0; } -class RGWLDAPAuthEngine: RGWTokenBasedAuthEngine +class RGWS3V2AuthEngine : public RGWAuthEngine { +protected: + std::string access_key_id; + std::string signature; + std::string expires; + bool qsr; + +public: + class Extractor { + public: + virtual ~Extractor() {}; + virtual void get_auth_keys(std::string& access_key_id, + std::string& signature, + std::string& expires, + bool& qsr) const = 0; + }; + + RGWS3V2AuthEngine(CephContext* const cct, const Extractor& extr) + : RGWAuthEngine(cct) { + extr.get_auth_keys(access_key_id, signature, expires, qsr); + } + + bool is_applicable() const noexcept override { + return ! (access_key_id.empty() && signature.empty()); + } +}; + +class RGWS3V2Extractor : public RGWS3V2AuthEngine::Extractor { +protected: + const req_state* const s; + +public: + RGWS3V2Extractor(const req_state * const s) + : s(s) {} + + void get_auth_keys(std::string& access_key_id, + std::string& signature, + std::string& expires, + bool& qsr) const override; +}; + +class RGWLDAPAuthEngine: RGWS3V2AuthEngine { static rgw::LDAPHelper* ldh; static std::mutex mtx; @@ -665,11 +706,11 @@ public: RGWRados* const store, Extractor &ex, const RGWRemoteAuthApplier::Factory * const apl_factory) - : RGWTokenBasedAuthEngine(cct, ex), + : RGWS3V2AuthEngine(cct, ex), store(store), apl_factory(apl_factory) { init(cct); - base64_token = rgw::from_base64(token); + base64_token = rgw::from_base64(access_key_id); } const char* get_name() const noexcept override { return "RGWLDAPAuthEngine"; @@ -678,42 +719,65 @@ public: RGWAuthApplier::aplptr_t authenticate() const override; }; -class RGWLDAPTokenExtractor : public RGWTokenBasedAuthEngine::Extractor { +class RGWS3V2LocalAuthEngine: RGWS3V2AuthEngine +{ protected: - const req_state * const s; + req_state* const s; + RGWRados* const store; + const RGWLocalAuthApplier::Factory* const apl_factory; public: - RGWLDAPTokenExtractor(const req_state * const s) - : s(s) { + RGWS3V2LocalAuthEngine(req_state* const s, + RGWRados* const store, + const Extractor& extr, + const RGWLocalAuthApplier::Factory * const apl_factory) + : RGWS3V2AuthEngine(s->cct, extr), + s(s), + store(store), + apl_factory(apl_factory) { + } + + const char* get_name() const noexcept override { + return "RGWS3V2LocalAuthEngine"; } - std::string get_token() const override; + bool is_applicable() const noexcept override; + RGWAuthApplier::aplptr_t authenticate() const override; }; -class RGWGetPolicyLDAPTokenExtractor : public RGWTokenBasedAuthEngine::Extractor { +class RGWGetPolicyV2Extractor:public RGWS3V2AuthEngine::Extractor { +private: std::string access_key_id; + std::string signature; + public: - RGWGetPolicyLDAPTokenExtractor(std::string access_key_id) { - access_key_id = std::move(access_key_id); + RGWGetPolicyV2Extractor(std::string access_key_id, std::string signature) { + access_key_id = std::move(access_key_id), + signature = std::move(signature); } - std::string get_token() const { - return access_key_id; + void get_auth_keys(std::string& access_key_id, + std::string& signature, + std::string& expires, + bool& qsr) const override { + access_key_id = this->access_key_id; + signature = this->signature; } }; -class S3AuthFactory : public RGWRemoteAuthApplier::Factory { +class S3AuthFactory : public RGWRemoteAuthApplier::Factory, + public RGWLocalAuthApplier::Factory { typedef RGWAuthApplier::aplptr_t aplptr_t; - RGWRados * const store; + RGWRados* const store; const std::string acct_override; public: - S3AuthFactory(RGWRados * const store, + S3AuthFactory(RGWRados* const store, const std::string& acct_override) : store(store), acct_override(acct_override) { } - aplptr_t create_apl_remote(CephContext * const cct, + aplptr_t create_apl_remote(CephContext* const cct, RGWRemoteAuthApplier::acl_strategy_t&& acl_alg, const RGWRemoteAuthApplier::AuthInfo info ) const override { @@ -722,5 +786,13 @@ public: RGWRemoteAuthApplier(cct, store, std::move(acl_alg), info), store, acct_override)); } + aplptr_t create_apl_local(CephContext* const cct, + const RGWUserInfo& user_info, + const std::string& subuser) const override { + return aplptr_t( + new RGWThirdPartyAccountAuthApplier( + RGWLocalAuthApplier(cct, user_info, subuser), + store, acct_override)); + } }; #endif /* CEPH_RGW_REST_S3_H */ diff --git a/src/rgw/rgw_user.cc b/src/rgw/rgw_user.cc index 5aa2b7d74a9..d4104c45b24 100644 --- a/src/rgw/rgw_user.cc +++ b/src/rgw/rgw_user.cc @@ -1917,6 +1917,7 @@ int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg) user_id = uid; user_info.user_id = user_id; user_info.display_name = display_name; + user_info.type = TYPE_RGW; if (!user_email.empty()) user_info.user_email = user_email; -- 2.39.5