diff --git a/src/rgw/rgw_auth.cc b/src/rgw/rgw_auth.cc index b5771021b80655..fb265281213c37 100644 --- a/src/rgw/rgw_auth.cc +++ b/src/rgw/rgw_auth.cc @@ -149,6 +149,11 @@ void RGWRemoteAuthApplier::create_account(const rgw_user& acct_user, { rgw_user new_acct_user = acct_user; + if (info.acct_type) { + //ldap/keystone for s3 users + user_info.type = info.acct_type; + } + /* Administrator may enforce creating new accounts within their own tenants. * The config parameter name is kept due to legacy. */ if (new_acct_user.tenant.empty() && g_conf->rgw_keystone_implicit_tenants) { diff --git a/src/rgw/rgw_auth.h b/src/rgw/rgw_auth.h index efcd654425fe1c..221198ebab6588 100644 --- a/src/rgw/rgw_auth.h +++ b/src/rgw/rgw_auth.h @@ -117,16 +117,19 @@ class RGWRemoteAuthApplier : public RGWAuthApplier { const std::string acct_name; const uint32_t perm_mask; const bool is_admin; + const uint32_t acct_type; public: AuthInfo(const rgw_user& acct_user, const std::string& acct_name, const uint32_t perm_mask, - const bool is_admin) + const bool is_admin, + const uint32_t acct_type=0) : acct_user(acct_user), acct_name(acct_name), perm_mask(perm_mask), - is_admin(is_admin) { + is_admin(is_admin), + acct_type(acct_type) { } }; diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index f669ea43cc2797..23559c8819c554 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -527,6 +527,14 @@ void encode_json(const char *name, const RGWUserCaps& val, Formatter *f); void decode_json_obj(obj_version& v, JSONObj *obj); +enum RGWUserSourceType +{ + TYPE_NONE=0, + TYPE_RGW=1, + TYPE_KEYSTONE=2, + TYPE_LDAP=3 +}; + struct RGWUserInfo { uint64_t auid; @@ -547,6 +555,7 @@ struct RGWUserInfo RGWQuotaInfo bucket_quota; map temp_url_keys; RGWQuotaInfo user_quota; + uint32_t type; RGWUserInfo() : auid(0), @@ -554,7 +563,8 @@ struct RGWUserInfo max_buckets(RGW_DEFAULT_MAX_BUCKETS), op_mask(RGW_OP_TYPE_ALL), admin(0), - system(0) { + system(0), + type(TYPE_NONE) { } RGWAccessKey* get_key0() { @@ -565,7 +575,7 @@ struct RGWUserInfo } void encode(bufferlist& bl) const { - ENCODE_START(18, 9, bl); + ENCODE_START(19, 9, bl); ::encode(auid, bl); string access_key; string secret_key; @@ -605,10 +615,11 @@ struct RGWUserInfo ::encode(user_quota, bl); ::encode(user_id.tenant, bl); ::encode(admin, bl); + ::encode(type, bl); ENCODE_FINISH(bl); } void decode(bufferlist::iterator& bl) { - DECODE_START_LEGACY_COMPAT_LEN_32(18, 9, 9, bl); + DECODE_START_LEGACY_COMPAT_LEN_32(19, 9, 9, bl); if (struct_v >= 2) ::decode(auid, bl); else auid = CEPH_AUTH_UID_DEFAULT; string access_key; @@ -678,6 +689,9 @@ struct RGWUserInfo if (struct_v >= 18) { ::decode(admin, bl); } + if (struct_v >= 19) { + ::decode(type, bl); + } DECODE_FINISH(bl); } void dump(Formatter *f) const; @@ -727,7 +741,7 @@ struct rgw_bucket { } void encode(bufferlist& bl) const { - ENCODE_START(8, 3, bl); + ENCODE_START(9, 3, bl); ::encode(name, bl); ::encode(data_pool, bl); ::encode(marker, bl); @@ -738,7 +752,7 @@ struct rgw_bucket { ENCODE_FINISH(bl); } void decode(bufferlist::iterator& bl) { - DECODE_START_LEGACY_COMPAT_LEN(8, 3, 3, bl); + DECODE_START_LEGACY_COMPAT_LEN(9, 3, 3, bl); ::decode(name, bl); ::decode(data_pool, bl); if (struct_v >= 2) { diff --git a/src/rgw/rgw_json_enc.cc b/src/rgw/rgw_json_enc.cc index 78be0a13f84181..10191fa2987495 100644 --- a/src/rgw/rgw_json_enc.cc +++ b/src/rgw/rgw_json_enc.cc @@ -427,6 +427,7 @@ void RGWUserInfo::dump(Formatter *f) const encode_json("bucket_quota", bucket_quota, f); encode_json("user_quota", user_quota, f); encode_json("temp_url_keys", temp_url_keys, f); + encode_json("type", type, f); } @@ -484,6 +485,7 @@ void RGWUserInfo::decode_json(JSONObj *obj) JSONDecoder::decode_json("bucket_quota", bucket_quota, obj); JSONDecoder::decode_json("user_quota", user_quota, obj); JSONDecoder::decode_json("temp_url_keys", temp_url_keys, obj); + JSONDecoder::decode_json("type", type, obj); } void RGWQuotaInfo::dump(Formatter *f) const diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 23267199c78bdf..345c0a39eada16 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -1705,11 +1705,13 @@ int RGWPostObj_ObjStore_S3::get_policy() err_msg = "Missing signature"; return -EINVAL; } - RGWUserInfo user_info; 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); + RGWLDAPTokenExtractor token_extr(s); + RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact); // try external authenticators if (store->ctx()->_conf->rgw_s3_auth_use_keystone && store->ctx()->_conf->rgw_keystone_url.empty()) @@ -1747,53 +1749,15 @@ int RGWPostObj_ObjStore_S3::get_policy() } s->perm_mask = RGW_PERM_FULL_CONTROL; } - } else if (store->ctx()->_conf->rgw_s3_auth_use_ldap && - (! store->ctx()->_conf->rgw_ldap_uri.empty())) { - - ldout(store->ctx(), 15) - << __func__ << " LDAP auth uri=" - << store->ctx()->_conf->rgw_ldap_uri - << dendl; - - RGWToken token{from_base64(s3_access_key)}; - if (! token.valid()) - return -EACCES; - - rgw::LDAPHelper *ldh = RGW_Auth_S3::get_ldap_ctx(store); - if (unlikely(!ldh)) { - ldout(store->ctx(), 0) - << __func__ << " RGW_Auth_S3::get_ldap_ctx() failed" - << dendl; - return -EACCES; - } - - ldout(store->ctx(), 10) - << __func__ << " try LDAP auth uri=" - << store->ctx()->_conf->rgw_ldap_uri - << " token.id=" << token.id - << dendl; - - if (ldh->auth(token.id, token.key) != 0) - return -EACCES; - - /* ok, succeeded */ - user_info.user_id = token.id; - user_info.display_name = token.id; // cn? - - /* create local account, if none exists */ - if (rgw_get_user_info_by_uid(store, user_info.user_id, - user_info) < 0) { - int ret = rgw_store_user_info(store, user_info, nullptr, nullptr, - real_time(), true); - if (ret < 0) { - ldout(store->ctx(), 10) - << "NOTICE: failed to store new user's info: ret=" << ret - << dendl; - } - } - - /* set request perms */ - s->perm_mask = RGW_PERM_FULL_CONTROL; + } else if (ldap.is_applicable()) { + auto applier = ldap.authenticate(); + if (! applier) { + 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; } @@ -3085,27 +3049,6 @@ int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s, return RGWHandler_REST::init(store, s, cio); } -/* RGW_Auth_S3 static members */ -std::mutex RGW_Auth_S3::mtx; -rgw::LDAPHelper* RGW_Auth_S3::ldh; - -/* static */ -void RGW_Auth_S3::init_impl(RGWRados* store) -{ - const string& ldap_uri = store->ctx()->_conf->rgw_ldap_uri; - const string& ldap_binddn = store->ctx()->_conf->rgw_ldap_binddn; - const string& ldap_searchdn = store->ctx()->_conf->rgw_ldap_searchdn; - const string& ldap_dnattr = - store->ctx()->_conf->rgw_ldap_dnattr; - std::string ldap_bindpw = parse_rgw_ldap_bindpw(store->ctx()); - - ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw, - ldap_searchdn, ldap_dnattr); - - ldh->init(); - ldh->bind(); -} - /* * Try to validate S3 auth against keystone s3token interface */ @@ -3936,52 +3879,23 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s) } } - if ((external_auth_result < 0) && - (store->ctx()->_conf->rgw_s3_auth_use_ldap) && - (! store->ctx()->_conf->rgw_ldap_uri.empty())) { + S3AuthFactory aplfact(store, s->account_name); + RGWLDAPTokenExtractor token_extr(s); + RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact); - RGW_Auth_S3::init(store); - - ldout(store->ctx(), 15) - << __func__ << " LDAP auth uri=" - << store->ctx()->_conf->rgw_ldap_uri - << dendl; - - RGWToken token{from_base64(auth_id)}; - - if (! token.valid()) + if (! ldap.is_applicable()) { + external_auth_result = -EACCES; + } else { + auto applier = ldap.authenticate(); + if (!applier) { external_auth_result = -EACCES; - else { - ldout(store->ctx(), 10) - << __func__ << " try LDAP auth uri=" - << store->ctx()->_conf->rgw_ldap_uri - << " token.id=" << token.id - << dendl; - - if (ldh->auth(token.id, token.key) != 0) - external_auth_result = -EACCES; - else { - /* ok, succeeded */ - external_auth_result = 0; - - /* create local account, if none exists */ - s->user->user_id = token.id; - s->user->display_name = token.id; // cn? - int ret = rgw_get_user_info_by_uid(store, s->user->user_id, *(s->user)); - if (ret < 0) { - ret = rgw_store_user_info(store, *(s->user), nullptr, nullptr, - real_time(), true); - if (ret < 0) { - dout(10) << "NOTICE: failed to store new user's info: ret=" << ret - << dendl; - } - } - - /* set request perms */ - s->perm_mask = RGW_PERM_FULL_CONTROL; - } /* success */ - } /* token */ - } /* ldap */ + } else { + applier->load_acct_info(*s->user); + s->perm_mask = applier->get_perm_mask(); + s->auth_identity = std::move(applier); + external_auth_result = 0; + } + } /* keystone failed (or not enabled); check if we want to use rados backend */ if (!store->ctx()->_conf->rgw_s3_auth_use_rados @@ -3997,7 +3911,7 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s) return external_auth_result; } - /* now verify signature */ + /* now verify signature */ string auth_hdr; if (!rgw_create_s3_canonical_header(s->info, &s->header_time, auth_hdr, qsr)) { @@ -4342,3 +4256,98 @@ RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data) op->set_get_data(get_data); return op; } + +rgw::LDAPHelper* RGWLDAPAuthEngine::ldh = nullptr; +std::mutex RGWLDAPAuthEngine::mtx; + +void RGWLDAPAuthEngine::init(CephContext* const cct) +{ + if (!ldh) { + std::lock_guard lck(mtx); + 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; + const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr; + std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct); + + ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw, + ldap_searchdn, ldap_dnattr); + + ldh->init(); + ldh->bind(); + } + } +} + +RGWRemoteAuthApplier::acl_strategy_t RGWLDAPAuthEngine::get_acl_strategy() const +{ + //This is based on the assumption that the default acl strategy in + // get_perms_from_aclspec, will take care. Extra acl spec is not required. + return nullptr; +} + +RGWRemoteAuthApplier::AuthInfo +RGWLDAPAuthEngine::get_creds_info(const rgw::RGWToken& token) const noexcept +{ + return RGWRemoteAuthApplier::AuthInfo { + rgw_user(token.id), + token.id, + RGW_PERM_FULL_CONTROL, + true, + TYPE_LDAP + }; +} + +bool RGWLDAPAuthEngine::is_applicable() const noexcept +{ + if (!RGWTokenBasedAuthEngine::is_applicable()) { + return false; + } + + 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 +{ + //Check if a user of type other than 'ldap' is already present, if yes, then + //return error. + 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; + } + + return apl_factory->create_apl_remote(cct, get_acl_strategy(), get_creds_info(base64_token)); +} + +std::string RGWLDAPTokenExtractor::get_token() const +{ + string token = ""; + if (!s->http_auth || !(*s->http_auth)) { + token = s->info.args.get("AWSAccessKeyId"); + } else { + string auth_str(s->http_auth + 4); + int pos = auth_str.rfind(':'); + if (pos >=0 ) { + token = auth_str.substr(0, pos); + } + } + return token; +} diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index cecc14c97656d3..9945d9ee805d3a 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -16,6 +16,12 @@ #include "rgw_rest_conn.h" #include "rgw_ldap.h" +#include "rgw_token.h" +#include "include/assert.h" + +#include "rgw_auth.h" +#include "rgw_auth_decoimpl.h" + #define RGW_AUTH_GRACE_MINS 15 void rgw_get_errno_s3(struct rgw_http_errors *e, int err_no); @@ -417,9 +423,6 @@ class RGW_Auth_S3_Keystone_ValidateToken : public RGWHTTPClient { class RGW_Auth_S3 { private: - static std::mutex mtx; - static rgw::LDAPHelper* ldh; - 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, @@ -428,22 +431,6 @@ 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); - - static inline void init(RGWRados* store) { - if (! ldh) { - std::lock_guard lck(mtx); - if (! ldh) { - init_impl(store); - } - } - } - - static inline rgw::LDAPHelper* get_ldap_ctx(RGWRados* store) { - init(store); - return ldh; - } - - static void init_impl(RGWRados* store); }; class RGWHandler_Auth_S3 : public RGWHandler_REST { @@ -627,4 +614,70 @@ static inline int valid_s3_bucket_name(const string& name, bool relaxed=false) return 0; } +class RGWLDAPAuthEngine: RGWTokenBasedAuthEngine +{ + static rgw::LDAPHelper* ldh; + static std::mutex mtx; + rgw::RGWToken base64_token; + + static void init(CephContext* const cct); + +protected: + RGWRados* const store; + const RGWRemoteAuthApplier::Factory * const apl_factory; + + RGWRemoteAuthApplier::acl_strategy_t get_acl_strategy() const; + RGWRemoteAuthApplier::AuthInfo get_creds_info(const rgw::RGWToken& token) const noexcept; + +public: + RGWLDAPAuthEngine(CephContext* const cct, + RGWRados* const store, + Extractor &ex, + const RGWRemoteAuthApplier::Factory * const apl_factory) + : RGWTokenBasedAuthEngine(cct, ex), + store(store), + apl_factory(apl_factory) { + init(cct); + base64_token = rgw::from_base64(token); + } + const char* get_name() const noexcept override { + return "RGWLDAPAuthEngine"; + } + bool is_applicable() const noexcept override; + RGWAuthApplier::aplptr_t authenticate() const override; +}; + +class RGWLDAPTokenExtractor : public RGWTokenBasedAuthEngine::Extractor { +protected: + const req_state * const s; + +public: + RGWLDAPTokenExtractor(const req_state * const s) + : s(s) { + } + std::string get_token() const override; +}; + +class S3AuthFactory : public RGWRemoteAuthApplier::Factory { + typedef RGWAuthApplier::aplptr_t aplptr_t; + RGWRados * const store; + const std::string acct_override; + +public: + S3AuthFactory(RGWRados * const store, + const std::string& acct_override) + : store(store), + acct_override(acct_override) { + } + + aplptr_t create_apl_remote(CephContext * const cct, + RGWRemoteAuthApplier::acl_strategy_t&& acl_alg, + const RGWRemoteAuthApplier::AuthInfo info + ) const override { + return aplptr_t( + new RGWThirdPartyAccountAuthApplier( + RGWRemoteAuthApplier(cct, store, std::move(acl_alg), info), + store, acct_override)); + } +}; #endif /* CEPH_RGW_REST_S3_H */ diff --git a/src/rgw/rgw_token.h b/src/rgw/rgw_token.h index 7dce9a9ef3cebb..972c57448db2d7 100644 --- a/src/rgw/rgw_token.h +++ b/src/rgw/rgw_token.h @@ -71,7 +71,7 @@ namespace rgw { virtual uint32_t version() const { return 1; }; - bool valid() { + bool valid() const{ return ((type != TOKEN_NONE) && (! id.empty()) && (! key.empty()));