// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #include #include #include #include "common/errno.h" #include "rgw_common.h" #include "rgw_auth.h" #include "rgw_quota.h" #include "rgw_user.h" #include "rgw_http_client.h" #include "rgw_iam_managed_policy.h" #include "rgw_keystone.h" #include "rgw_sal.h" #include "rgw_log.h" #include "include/function2.hpp" #include "include/str_list.h" #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rgw using namespace std; namespace rgw { namespace auth { // match a principal by path/name[:subuser] static bool match_principal(std::string_view path, std::string_view name, std::string_view subuser, std::string_view expected) { // leading / was already matched by ":user/" in parse_principal() if (!path.empty()) { path.remove_prefix(1); } // match user path if (!expected.starts_with(path)) { return false; } expected.remove_prefix(path.size()); // match user by id if (!expected.starts_with(name)) { return false; } expected.remove_prefix(name.size()); if (expected.empty()) { // exact match return true; } // try to match name:subuser if (!expected.starts_with(":")) { return false; } expected.remove_prefix(1); if (expected.empty()) { return false; } return (expected == "*" || expected == subuser); } static bool match_owner(const rgw_owner& owner, const rgw_user& uid, const std::optional& account) { return std::visit(fu2::overload( [&uid] (const rgw_user& u) { return u == uid; }, [&account] (const rgw_account_id& a) { return account && a == account->id; }), owner); } static bool match_account_or_tenant(const std::optional& account, std::string_view tenant, std::string_view expected) { return (account && account->id == expected) || (tenant == expected); } static void load_inline_policy(CephContext* cct, const bufferlist& bl, const string* tenant, std::vector& policies) { map policy_map; using ceph::decode; decode(policy_map, bl); for (const auto& [name, policy] : policy_map) { policies.emplace_back(cct, tenant, policy, false); } } static void load_managed_policy(CephContext* cct, const bufferlist& bl, std::vector& policies) { rgw::IAM::ManagedPolicies policy_set; using ceph::decode; decode(policy_set, bl); for (const auto& arn : policy_set.arns) { if (auto p = rgw::IAM::get_managed_policy(cct, arn); p) { policies.push_back(std::move(*p)); } } } static int load_group_policies(const DoutPrefixProvider* dpp, optional_yield y, rgw::sal::Driver* driver, const std::string* tenant, std::string_view group_id, std::vector& policies) { RGWGroupInfo info; rgw::sal::Attrs attrs; RGWObjVersionTracker objv; int r = driver->load_group_by_id(dpp, y, group_id, info, attrs, objv); if (r < 0) { return r; } CephContext* cct = dpp->get_cct(); if (auto i = attrs.find(RGW_ATTR_IAM_POLICY); i != attrs.end()) { load_inline_policy(cct, i->second, tenant, policies); } if (auto i = attrs.find(RGW_ATTR_MANAGED_POLICY); i != attrs.end()) { load_managed_policy(cct, i->second, policies); } return 0; } int load_account_and_policies(const DoutPrefixProvider* dpp, optional_yield y, sal::Driver* driver, const RGWUserInfo& info, const sal::Attrs& attrs, std::optional& account, std::vector& policies) { if (!info.account_id.empty()) { account.emplace(); rgw::sal::Attrs attrs; // ignored RGWObjVersionTracker objv; // ignored int r = driver->load_account_by_id(dpp, y, info.account_id, *account, attrs, objv); if (r < 0) { ldpp_dout(dpp, 1) << "ERROR: failed to load account " << info.account_id << " for user " << info.user_id << ": " << cpp_strerror(r) << dendl; return r; } } // non-account identity policy is restricted to the current tenant const std::string* policy_tenant = info.account_id.empty() ? &info.user_id.tenant : nullptr; // load user policies from user attrs CephContext* cct = dpp->get_cct(); if (auto bl = attrs.find(RGW_ATTR_USER_POLICY); bl != attrs.end()) { load_inline_policy(cct, bl->second, policy_tenant, policies); } if (auto bl = attrs.find(RGW_ATTR_MANAGED_POLICY); bl != attrs.end()) { load_managed_policy(cct, bl->second, policies); } // load each group and its policies for (const auto& id : info.group_ids) { int r = load_group_policies(dpp, y, driver, policy_tenant, id, policies); if (r == -ENOENT) { // in multisite, metadata sync may race to replicate the user before its // group. ignore ENOENT here so we don't reject all the user's requests // in the meantime ldpp_dout(dpp, 1) << "WARNING: skipping nonexistent group id " << id << " for user " << info.user_id << ": " << cpp_strerror(r) << dendl; } else if (r < 0) { ldpp_dout(dpp, 1) << "ERROR: failed to load group id " << id << " for user " << info.user_id << ": " << cpp_strerror(r) << dendl; return r; } } return 0; } static auto transform_old_authinfo(const RGWUserInfo& user, std::optional account, std::vector policies) -> std::unique_ptr { /* This class is not intended for public use. Should be removed altogether * with this function after moving all our APIs to the new authentication * infrastructure. */ class DummyIdentityApplier : public rgw::auth::IdentityApplier { /* For this particular case it's OK to use rgw_user structure to convey * the identity info as this was the policy for doing that before the * new auth. */ const rgw_user id; const std::string display_name; const std::string path; const bool is_admin; const uint32_t type; const std::optional account; const std::vector policies; public: DummyIdentityApplier(const RGWUserInfo& user, std::optional account, std::vector policies) : id(user.user_id), display_name(user.display_name), path(user.path), is_admin(user.admin), type(user.type), account(std::move(account)), policies(std::move(policies)) {} ACLOwner get_aclowner() const { ACLOwner owner; if (account) { owner.id = account->id; owner.display_name = account->name; } else { owner.id = id; owner.display_name = display_name; } return owner; } uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override { return rgw_perms_from_aclspec_default_strategy(id.to_str(), aclspec, dpp); } bool is_admin_of(const rgw_owner& o) const override { return is_admin; } bool is_owner_of(const rgw_owner& o) const override { return match_owner(o, id, account); } bool is_identity(const Principal& p) const override { if (p.is_wildcard()) { return true; } else if (p.is_account()) { return match_account_or_tenant(account, id.tenant, p.get_account()); } else if (p.is_user()) { std::string_view no_subuser; // account users can match both account- and tenant-based arns if (account && p.get_account() == account->id) { return match_principal(path, display_name, no_subuser, p.get_id()); } else { return p.get_account() == id.tenant && match_principal(path, id.id, no_subuser, p.get_id()); } } return false; } uint32_t get_perm_mask() const override { return RGW_PERM_FULL_CONTROL; } uint32_t get_identity_type() const override { return type; } string get_acct_name() const override { return {}; } string get_subuser() const override { return {}; } const std::string& get_tenant() const override { return id.tenant; } const std::optional& get_account() const override { return account; } void write_ops_log_entry(rgw_log_entry& entry) const override { if (account) { entry.account_id = account->id; } } void to_str(std::ostream& out) const override { out << "RGWDummyIdentityApplier(auth_id=" << id << ", is_admin=" << is_admin << ")"; } void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override { // noop, this user info was passed in on construction } void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const { // copy our identity policies into req_state s->iam_identity_policies.insert(s->iam_identity_policies.end(), policies.begin(), policies.end()); } }; return std::make_unique( user, std::move(account), std::move(policies)); } auto transform_old_authinfo(const DoutPrefixProvider* dpp, optional_yield y, sal::Driver* driver, sal::User* user, std::vector* policies_) -> tl::expected, int> { const RGWUserInfo& info = user->get_info(); const sal::Attrs& attrs = user->get_attrs(); std::optional account; std::vector policies; int r = load_account_and_policies(dpp, y, driver, info, attrs, account, policies); if (r < 0) { return tl::unexpected(r); } if (policies_) { // return policies to caller if requested *policies_ = policies; } return transform_old_authinfo(info, std::move(account), std::move(policies)); } } /* namespace auth */ } /* namespace rgw */ uint32_t rgw_perms_from_aclspec_default_strategy( const std::string& uid, const rgw::auth::Identity::aclspec_t& aclspec, const DoutPrefixProvider *dpp) { ldpp_dout(dpp, 5) << "Searching permissions for uid=" << uid << dendl; const auto iter = aclspec.find(uid); if (std::end(aclspec) != iter) { ldpp_dout(dpp, 5) << "Found permission: " << iter->second << dendl; return iter->second; } ldpp_dout(dpp, 5) << "Permissions for user not found" << dendl; return 0; } static inline const std::string make_spec_item(const std::string& tenant, const std::string& id) { return tenant + ":" + id; } static inline std::pair strategy_handle_rejected(rgw::auth::Engine::result_t&& engine_result, const rgw::auth::Strategy::Control policy, rgw::auth::Engine::result_t&& strategy_result) { using Control = rgw::auth::Strategy::Control; switch (policy) { case Control::REQUISITE: /* Don't try next. */ return std::make_pair(false, std::move(engine_result)); case Control::SUFFICIENT: /* Don't try next. */ return std::make_pair(false, std::move(engine_result)); case Control::FALLBACK: /* Don't try next. */ return std::make_pair(false, std::move(strategy_result)); default: /* Huh, memory corruption? */ ceph_abort(); } } static inline std::pair strategy_handle_denied(rgw::auth::Engine::result_t&& engine_result, const rgw::auth::Strategy::Control policy, rgw::auth::Engine::result_t&& strategy_result) { using Control = rgw::auth::Strategy::Control; switch (policy) { case Control::REQUISITE: /* Don't try next. */ return std::make_pair(false, std::move(engine_result)); case Control::SUFFICIENT: /* Just try next. */ return std::make_pair(true, std::move(engine_result)); case Control::FALLBACK: return std::make_pair(true, std::move(strategy_result)); default: /* Huh, memory corruption? */ ceph_abort(); } } static inline std::pair strategy_handle_granted(rgw::auth::Engine::result_t&& engine_result, const rgw::auth::Strategy::Control policy, rgw::auth::Engine::result_t&& strategy_result) { using Control = rgw::auth::Strategy::Control; switch (policy) { case Control::REQUISITE: /* Try next. */ return std::make_pair(true, std::move(engine_result)); case Control::SUFFICIENT: /* Don't try next. */ return std::make_pair(false, std::move(engine_result)); case Control::FALLBACK: /* Don't try next. */ return std::make_pair(false, std::move(engine_result)); default: /* Huh, memory corruption? */ ceph_abort(); } } rgw::auth::Engine::result_t rgw::auth::Strategy::authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const { result_t strategy_result = result_t::deny(); for (const stack_item_t& kv : auth_stack) { const rgw::auth::Engine& engine = kv.first; const auto& policy = kv.second; ldpp_dout(dpp, 20) << get_name() << ": trying " << engine.get_name() << dendl; result_t engine_result = result_t::deny(); try { engine_result = engine.authenticate(dpp, s, y); } catch (const int err) { engine_result = result_t::deny(err); } bool try_next = true; switch (engine_result.get_status()) { case result_t::Status::REJECTED: { ldpp_dout(dpp, 20) << engine.get_name() << " rejected with reason=" << engine_result.get_reason() << dendl; std::tie(try_next, strategy_result) = \ strategy_handle_rejected(std::move(engine_result), policy, std::move(strategy_result)); break; } case result_t::Status::DENIED: { ldpp_dout(dpp, 20) << engine.get_name() << " denied with reason=" << engine_result.get_reason() << dendl; std::tie(try_next, strategy_result) = \ strategy_handle_denied(std::move(engine_result), policy, std::move(strategy_result)); break; } case result_t::Status::GRANTED: { ldpp_dout(dpp, 20) << engine.get_name() << " granted access" << dendl; std::tie(try_next, strategy_result) = \ strategy_handle_granted(std::move(engine_result), policy, std::move(strategy_result)); break; } default: { ceph_abort(); } } if (! try_next) { break; } } return strategy_result; //NOLINT(bugprone-use-after-move) } int rgw::auth::Strategy::apply(const DoutPrefixProvider *dpp, const rgw::auth::Strategy& auth_strategy, req_state* const s, optional_yield y) noexcept { try { auto result = auth_strategy.authenticate(dpp, s, y); if (result.get_status() != decltype(result)::Status::GRANTED) { /* Access denied is acknowledged by returning a std::unique_ptr with * nullptr inside. */ ldpp_dout(dpp, 5) << "Failed the auth strategy, reason=" << result.get_reason() << dendl; // Special handling for expired pre-signed URL if (result.get_reason() == -ERR_PRESIGNED_URL_EXPIRED) { result = result_t::deny(-EPERM); set_req_state_err(s, -EPERM, "The pre-signed URL has expired"); } // Special handling for disabled presigned URL if (result.get_reason() == -ERR_PRESIGNED_URL_DISABLED) { result = result_t::deny(-EPERM); set_req_state_err(s, -EPERM, "Presigned URLs are disabled by admin"); } return result.get_reason(); } try { rgw::auth::IdentityApplier::aplptr_t applier = result.get_applier(); rgw::auth::Completer::cmplptr_t completer = result.get_completer(); /* Account used by a given RGWOp is decoupled from identity employed * in the authorization phase (RGWOp::verify_permissions). */ applier->load_acct_info(dpp, s->user->get_info()); s->perm_mask = applier->get_perm_mask(); /* This is the single place where we pass req_state as a pointer * to non-const and thus its modification is allowed. In the time * of writing only RGWTempURLEngine needed that feature. */ applier->modify_request_state(dpp, s); if (completer) { completer->modify_request_state(dpp, s); } s->auth.identity = std::move(applier); s->auth.completer = std::move(completer); /* Populate the owner info. */ s->owner = s->auth.identity->get_aclowner(); return 0; } catch (const int err) { ldpp_dout(dpp, 5) << "applier throwed err=" << err << dendl; return err; } catch (const std::exception& e) { ldpp_dout(dpp, 5) << "applier throwed unexpected err: " << e.what() << dendl; return -EPERM; } } catch (const int err) { ldpp_dout(dpp, 5) << "auth engine throwed err=" << err << dendl; return err; } catch (const std::exception& e) { ldpp_dout(dpp, 5) << "auth engine throwed unexpected err: " << e.what() << dendl; } /* We never should be here. */ return -EPERM; } void rgw::auth::Strategy::add_engine(const Control ctrl_flag, const Engine& engine) noexcept { auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag)); } ACLOwner rgw::auth::WebIdentityApplier::get_aclowner() const { ACLOwner owner; if (account) { owner.id = account->id; owner.display_name = account->name; } else { owner.id = rgw_user{role_tenant, sub, "oidc"}; owner.display_name = user_name; } return owner; } bool rgw::auth::WebIdentityApplier::is_owner_of(const rgw_owner& o) const { return match_owner(o, rgw_user{role_tenant, sub, "oidc"}, account); } void rgw::auth::WebIdentityApplier::to_str(std::ostream& out) const { out << "rgw::auth::WebIdentityApplier(sub =" << sub << ", user_name=" << user_name << ", role_id=" << role_id << ", provider_id =" << iss << ")"; } void rgw::auth::WebIdentityApplier::write_ops_log_entry(rgw_log_entry& entry) const { if (account) { entry.account_id = account->id; } entry.role_id = role_id; } string rgw::auth::WebIdentityApplier::get_idp_url() const { string idp_url = this->iss; idp_url = url_remove_prefix(idp_url); return idp_url; } void rgw::auth::WebIdentityApplier::create_account(const DoutPrefixProvider* dpp, const rgw_user& acct_user, const string& display_name, RGWUserInfo& user_info) const /* out */ { std::unique_ptr user = driver->get_user(acct_user); user->get_info().display_name = display_name; user->get_info().type = TYPE_WEB; user->get_info().max_buckets = cct->_conf.get_val("rgw_user_max_buckets"); rgw_apply_default_bucket_quota(user->get_info().quota.bucket_quota, cct->_conf); rgw_apply_default_user_quota(user->get_info().quota.user_quota, cct->_conf); int ret = user->store_user(dpp, null_yield, true); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user=" << user << " ret=" << ret << dendl; throw ret; } user_info = user->get_info(); } void rgw::auth::WebIdentityApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const { rgw_user federated_user; federated_user.id = this->sub; federated_user.tenant = role_tenant; federated_user.ns = "oidc"; if (account) { // we don't need shadow users for account roles because bucket ownership, // quota, and stats are tracked by the account instead of the user user_info.user_id = std::move(federated_user); user_info.display_name = user_name; user_info.type = TYPE_WEB; return; } std::unique_ptr user = driver->get_user(federated_user); //Check in oidc namespace if (user->load_user(dpp, null_yield) >= 0) { /* Succeeded. */ user_info = user->get_info(); return; } user->clear_ns(); //Check for old users which wouldn't have been created in oidc namespace if (user->load_user(dpp, null_yield) >= 0) { /* Succeeded. */ user_info = user->get_info(); return; } //Check if user_id.buckets already exists, may have been from the time, when shadow users didnt exist RGWStorageStats stats; ceph::real_time last_synced; ceph::real_time last_updated; int ret = driver->load_stats(dpp, null_yield, federated_user, stats, last_synced, last_updated); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "ERROR: reading stats for the user returned error " << ret << dendl; return; } if (ret == -ENOENT) { /* in case of ENOENT, which means user doesnt have buckets */ //In this case user will be created in oidc namespace ldpp_dout(dpp, 5) << "NOTICE: incoming user has no buckets " << federated_user << dendl; federated_user.ns = "oidc"; } else { //User already has buckets associated, hence wont be created in oidc namespace. ldpp_dout(dpp, 5) << "NOTICE: incoming user already has buckets associated " << federated_user << ", won't be created in oidc namespace"<< dendl; federated_user.ns = ""; } ldpp_dout(dpp, 0) << "NOTICE: couldn't map oidc federated user " << federated_user << dendl; create_account(dpp, federated_user, this->user_name, user_info); } void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const { s->info.args.append("sub", this->sub); s->info.args.append("aud", this->aud); s->info.args.append("provider_id", this->iss); s->info.args.append("client_id", this->client_id); string condition; string idp_url = get_idp_url(); for (auto& claim : token_claims) { if (claim.first == "aud") { condition.clear(); condition = idp_url + ":app_id"; s->env.emplace(condition, claim.second); } condition.clear(); condition = idp_url + ":" + claim.first; s->env.emplace(condition, claim.second); } if (principal_tags) { constexpr size_t KEY_SIZE = 128, VAL_SIZE = 256; std::set> p_tags = principal_tags.get(); for (auto& it : p_tags) { string key = it.first; string val = it.second; if (key.find("aws:") == 0 || val.find("aws:") == 0) { ldpp_dout(dpp, 0) << "ERROR: Tag/Value can't start with aws:, hence skipping it" << dendl; continue; } if (key.size() > KEY_SIZE || val.size() > VAL_SIZE) { ldpp_dout(dpp, 0) << "ERROR: Invalid tag/value size, hence skipping it" << dendl; continue; } std::string p_key = "aws:PrincipalTag/"; p_key.append(key); s->principal_tags.emplace_back(std::make_pair(p_key, val)); ldpp_dout(dpp, 10) << "Principal Tag Key: " << p_key << " Value: " << val << dendl; std::string e_key = "aws:RequestTag/"; e_key.append(key); s->env.emplace(e_key, val); ldpp_dout(dpp, 10) << "RGW Env Tag Key: " << e_key << " Value: " << val << dendl; s->env.emplace("aws:TagKeys", key); ldpp_dout(dpp, 10) << "aws:TagKeys: " << key << dendl; if (s->principal_tags.size() == 50) { ldpp_dout(dpp, 0) << "ERROR: Number of tag/value pairs exceeding 50, hence skipping the rest" << dendl; break; } } } if (role_tags) { for (auto& it : role_tags.get()) { std::string p_key = "aws:PrincipalTag/"; p_key.append(it.first); s->principal_tags.emplace_back(std::make_pair(p_key, it.second)); ldpp_dout(dpp, 10) << "Principal Tag Key: " << p_key << " Value: " << it.second << dendl; std::string e_key = "iam:ResourceTag/"; e_key.append(it.first); s->env.emplace(e_key, it.second); ldpp_dout(dpp, 10) << "RGW Env Tag Key: " << e_key << " Value: " << it.second << dendl; } } } bool rgw::auth::WebIdentityApplier::is_identity(const Principal& p) const { return p.is_oidc_provider() && p.get_idp_url() == get_idp_url(); } const std::string rgw::auth::RemoteApplier::AuthInfo::NO_SUBUSER; const std::string rgw::auth::RemoteApplier::AuthInfo::NO_ACCESS_KEY; /* rgw::auth::RemoteAuthApplier */ ACLOwner rgw::auth::RemoteApplier::get_aclowner() const { ACLOwner owner; if (account) { owner.id = account->id; owner.display_name = account->name; } else { owner.id = owner_acct_user; owner.display_name = info.acct_name; } return owner; } uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const { uint32_t perm = 0; /* For backward compatibility with ACLOwner. */ perm |= rgw_perms_from_aclspec_default_strategy(info.acct_user.to_str(), aclspec, dpp); /* We also need to cover cases where rgw_keystone_implicit_tenants * was enabled. */ if (info.acct_user.tenant.empty()) { const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); perm |= rgw_perms_from_aclspec_default_strategy(tenanted_acct_user.to_str(), aclspec, dpp); } /* Now it's a time for invoking additional strategy that was supplied by * a specific auth engine. */ if (extra_acl_strategy) { perm |= extra_acl_strategy(aclspec); } ldpp_dout(dpp, 20) << "from ACL got perm=" << perm << dendl; return perm; } bool rgw::auth::RemoteApplier::is_admin_of(const rgw_owner& o) const { return info.is_admin; } bool rgw::auth::RemoteApplier::is_owner_of(const rgw_owner& o) const { auto* uid = std::get_if(&o); if (!uid) { return false; } if (info.acct_user.tenant.empty()) { const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); if (tenanted_acct_user == *uid) { return true; } } return info.acct_user == *uid; } bool rgw::auth::RemoteApplier::is_identity(const Principal& p) const { // We also need to cover cases where rgw_keystone_implicit_tenants // was enabled. std::string_view tenant = info.acct_user.tenant.empty() ? info.acct_user.id : info.acct_user.tenant; if (p.is_wildcard()) { return true; } else if (p.is_account()) { return p.get_account() == tenant; } else if (p.is_user()) { return p.get_id() == info.acct_user.id && p.get_account() == tenant; } return false; } void rgw::auth::RemoteApplier::to_str(std::ostream& out) const { out << "rgw::auth::RemoteApplier(acct_user=" << owner_acct_user << ", acct_name=" << info.acct_name << ", perm_mask=" << info.perm_mask << ", is_admin=" << info.is_admin << ")"; } void rgw::auth::ImplicitTenants::recompute_value(const ConfigProxy& c) { std::string s = c.get_val("rgw_keystone_implicit_tenants"); int v = 0; if (boost::iequals(s, "both") || boost::iequals(s, "true") || boost::iequals(s, "1")) { v = IMPLICIT_TENANTS_S3|IMPLICIT_TENANTS_SWIFT; } else if (boost::iequals(s, "0") || boost::iequals(s, "none") || boost::iequals(s, "false")) { v = 0; } else if (boost::iequals(s, "s3")) { v = IMPLICIT_TENANTS_S3; } else if (boost::iequals(s, "swift")) { v = IMPLICIT_TENANTS_SWIFT; } else { /* "" (and anything else) */ v = IMPLICIT_TENANTS_BAD; // assert(0); } saved = v; } const char **rgw::auth::ImplicitTenants::get_tracked_conf_keys() const { static const char *keys[] = { "rgw_keystone_implicit_tenants", nullptr }; return keys; } void rgw::auth::ImplicitTenants::handle_conf_change(const ConfigProxy& c, const std::set &changed) { if (changed.count("rgw_keystone_implicit_tenants")) { recompute_value(c); } } void rgw::auth::RemoteApplier::create_account(const DoutPrefixProvider* dpp, const rgw_user& acct_user, bool implicit_tenant, RGWUserInfo& user_info) const /* out */ { owner_acct_user = acct_user; /* An upper layer may enforce creating new accounts within their own * tenants. */ if (owner_acct_user.tenant.empty() && implicit_tenant) { owner_acct_user.tenant = owner_acct_user.id; } std::unique_ptr user = driver->get_user(owner_acct_user); user->get_info().display_name = info.acct_name; if (info.acct_type) { //ldap/keystone for s3 users user->get_info().type = info.acct_type; } user->get_info().max_buckets = cct->_conf.get_val("rgw_user_max_buckets"); rgw_apply_default_bucket_quota(user->get_info().quota.bucket_quota, cct->_conf); rgw_apply_default_user_quota(user->get_info().quota.user_quota, cct->_conf); user_info = user->get_info(); int ret = user->store_user(dpp, null_yield, true); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user=" << user << " ret=" << ret << dendl; throw ret; } } void rgw::auth::RemoteApplier::write_ops_log_entry(rgw_log_entry& entry) const { entry.access_key_id = info.access_key_id; entry.subuser = info.subuser; if (account) { entry.account_id = account->id; } } /* TODO(rzarzynski): we need to handle display_name changes. */ void rgw::auth::RemoteApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ { /* It's supposed that RGWRemoteAuthApplier tries to load account info * that belongs to the authenticated identity. Another policy may be * applied by using a RGWThirdPartyAccountAuthApplier decorator. */ const rgw_user& acct_user = info.acct_user; auto implicit_value = implicit_tenant_context.get_value(); bool implicit_tenant = implicit_value.implicit_tenants_for_(implicit_tenant_bit); bool split_mode = implicit_value.is_split_mode(); std::unique_ptr user; /* Normally, empty "tenant" field of acct_user means the authenticated * identity has the legacy, global tenant. However, due to inclusion * of multi-tenancy, we got some special compatibility kludge for remote * backends like Keystone. * If the global tenant is the requested one, we try the same tenant as * the user name first. If that RGWUserInfo exists, we use it. This way, * migrated OpenStack users can get their namespaced containers and nobody's * the wiser. * If that fails, we look up in the requested (possibly empty) tenant. * If that fails too, we create the account within the global or separated * namespace depending on rgw_keystone_implicit_tenants. * For compatibility with previous versions of ceph, it is possible * to enable implicit_tenants for only s3 or only swift. * in this mode ("split_mode"), we must constrain the id lookups to * only use the identifier space that would be used if the id were * to be created. */ if (split_mode && !implicit_tenant) ; /* suppress lookup for id used by "other" protocol */ else if (acct_user.tenant.empty()) { rgw_user tenanted_uid(acct_user.id, acct_user.id); user = driver->get_user(tenanted_uid); if (user->load_user(dpp, null_yield) >= 0) { /* Succeeded. */ (void) load_account_and_policies(dpp, null_yield, driver, user->get_info(), user->get_attrs(), account, policies); user_info = std::move(user->get_info()); owner_acct_user = std::move(tenanted_uid); return; } } user = driver->get_user(acct_user); if (split_mode && implicit_tenant) ; /* suppress lookup for id used by "other" protocol */ else if (user->load_user(dpp, null_yield) >= 0) { /* Succeeded. */ (void) load_account_and_policies(dpp, null_yield, driver, user->get_info(), user->get_attrs(), account, policies); user_info = std::move(user->get_info()); owner_acct_user = acct_user; return; } ldpp_dout(dpp, 0) << "NOTICE: couldn't map swift user " << acct_user << dendl; create_account(dpp, acct_user, implicit_tenant, user_info); /* Succeeded if we are here (create_account() hasn't throwed). */ } void rgw::auth::RemoteApplier::modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const { // copy our identity policies into req_state s->iam_identity_policies.insert(s->iam_identity_policies.end(), policies.begin(), policies.end()); } /* rgw::auth::LocalApplier */ /* static declaration */ const std::string rgw::auth::LocalApplier::NO_SUBUSER; const std::string rgw::auth::LocalApplier::NO_ACCESS_KEY; ACLOwner rgw::auth::LocalApplier::get_aclowner() const { ACLOwner owner; if (account) { owner.id = account->id; owner.display_name = account->name; } else { owner.id = user_info.user_id; owner.display_name = user_info.display_name; } return owner; } uint32_t rgw::auth::LocalApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const { // match acl grants to the specific user id uint32_t mask = rgw_perms_from_aclspec_default_strategy( user_info.user_id.to_str(), aclspec, dpp); if (account) { // account users also match acl grants to the account id. in aws, grantees // ONLY refer to accounts. but we continue to match user grants to preserve // access when moving legacy users into new accounts mask |= rgw_perms_from_aclspec_default_strategy(account->id, aclspec, dpp); } return mask; } bool rgw::auth::LocalApplier::is_admin_of(const rgw_owner& o) const { return user_info.admin || user_info.system; } bool rgw::auth::LocalApplier::is_owner_of(const rgw_owner& o) const { return match_owner(o, user_info.user_id, account); } bool rgw::auth::LocalApplier::is_identity(const Principal& p) const { if (p.is_wildcard()) { return true; } else if (p.is_account()) { return match_account_or_tenant(account, user_info.user_id.tenant, p.get_account()); } else if (p.is_user()) { // account users can match both account- and tenant-based arns if (account && p.get_account() == account->id) { return match_principal(user_info.path, user_info.display_name, subuser, p.get_id()); } else { return p.get_account() == user_info.user_id.tenant && match_principal(user_info.path, user_info.user_id.id, subuser, p.get_id()); } } return false; } void rgw::auth::LocalApplier::to_str(std::ostream& out) const { out << "rgw::auth::LocalApplier(acct_user=" << user_info.user_id << ", acct_name=" << user_info.display_name << ", subuser=" << subuser << ", perm_mask=" << get_perm_mask() << ", is_admin=" << static_cast(user_info.admin) << ")"; } uint32_t rgw::auth::LocalApplier::get_perm_mask(const std::string& subuser_name, const RGWUserInfo &uinfo) const { if (! subuser_name.empty() && subuser_name != NO_SUBUSER) { const auto iter = uinfo.subusers.find(subuser_name); if (iter != std::end(uinfo.subusers)) { return iter->second.perm_mask; } else { /* Subuser specified but not found. */ return RGW_PERM_NONE; } } else { /* Due to backward compatibility. */ return RGW_PERM_FULL_CONTROL; } } void rgw::auth::LocalApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ { /* Load the account that belongs to the authenticated identity. An extra call * to RADOS may be safely skipped in this case. */ user_info = this->user_info; } void rgw::auth::LocalApplier::modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const { // copy our identity policies into req_state s->iam_identity_policies.insert(s->iam_identity_policies.end(), policies.begin(), policies.end()); } void rgw::auth::LocalApplier::write_ops_log_entry(rgw_log_entry& entry) const { entry.access_key_id = access_key_id; entry.subuser = subuser; if (account) { entry.account_id = account->id; } } ACLOwner rgw::auth::RoleApplier::get_aclowner() const { ACLOwner owner; if (role.account) { owner.id = role.account->id; owner.display_name = role.account->name; } else { owner.id = token_attrs.user_id; owner.display_name = role.name; } return owner; } bool rgw::auth::RoleApplier::is_owner_of(const rgw_owner& o) const { return match_owner(o, token_attrs.user_id, role.account); } void rgw::auth::RoleApplier::to_str(std::ostream& out) const { out << "rgw::auth::RoleApplier(role name =" << role.name; for (auto& policy: role.inline_policies) { out << ", role policy =" << policy; } for (std::string_view arn : role.managed_policies) { if (auto p = arn.find('/'); p != arn.npos) { out << ", managed policy =" << arn.substr(p + 1); } else { out << ", managed policy =" << arn; } } out << ", token policy =" << token_attrs.token_policy; out << ")"; } bool rgw::auth::RoleApplier::is_identity(const Principal& p) const { if (p.is_wildcard()) { return true; } else if (p.is_account()) { return match_account_or_tenant(role.account, role.tenant, p.get_account()); } else if (p.is_role()) { std::string_view no_subuser; // account roles can match both account- and tenant-based arns return match_account_or_tenant(role.account, role.tenant, p.get_account()) && match_principal(role.path, role.name, no_subuser, p.get_id()); } else if (p.is_assumed_role()) { string role_session = role.name + "/" + token_attrs.role_session_name; //role/role-session return p.get_account() == role.tenant && p.get_role_session() == role_session; } else { string oidc_id; if (token_attrs.user_id.ns.empty()) { oidc_id = token_attrs.user_id.id; } else { oidc_id = token_attrs.user_id.ns + "$" + token_attrs.user_id.id; } return p.get_id() == oidc_id && p.get_account() == token_attrs.user_id.tenant; } return false; } void rgw::auth::RoleApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ { /* Load the user id */ user_info.user_id = this->token_attrs.user_id; } void rgw::auth::RoleApplier::write_ops_log_entry(rgw_log_entry& entry) const { if (role.account) { entry.account_id = role.account->id; } entry.role_id = role.id; } void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const { // non-account identity policy is restricted to the current tenant const std::string* policy_tenant = role.account ? nullptr : &role.tenant; for (const auto& policy : role.inline_policies) { try { const rgw::IAM::Policy p(s->cct, policy_tenant, policy, false); s->iam_identity_policies.push_back(std::move(p)); } catch (rgw::IAM::PolicyParseException& e) { //Control shouldn't reach here as the policy has already been //verified earlier ldpp_dout(dpp, 20) << "failed to parse role policy: " << e.what() << dendl; } } for (const auto& arn : role.managed_policies) { try { if (auto p = rgw::IAM::get_managed_policy(s->cct, arn); p) { s->iam_identity_policies.push_back(std::move(*p)); } } catch (rgw::IAM::PolicyParseException& e) { //Control shouldn't reach here as the policy has already been //verified earlier ldpp_dout(dpp, 20) << "failed to parse role policy: " << e.what() << dendl; } } if (!this->token_attrs.token_policy.empty()) { try { string policy = this->token_attrs.token_policy; const rgw::IAM::Policy p(s->cct, policy_tenant, policy, false); s->session_policies.push_back(std::move(p)); } catch (rgw::IAM::PolicyParseException& e) { //Control shouldn't reach here as the policy has already been //verified earlier ldpp_dout(dpp, 20) << "failed to parse token policy: " << e.what() << dendl; } } string condition = "aws:userid"; string value = role.id + ":" + token_attrs.role_session_name; s->env.emplace(condition, value); s->env.emplace("aws:TokenIssueTime", token_attrs.token_issued_at); for (auto& m : token_attrs.principal_tags) { s->env.emplace(m.first, m.second); ldpp_dout(dpp, 10) << "Principal Tag Key: " << m.first << " Value: " << m.second << dendl; std::size_t pos = m.first.find('/'); string key = m.first.substr(pos + 1); s->env.emplace("aws:TagKeys", key); ldpp_dout(dpp, 10) << "aws:TagKeys: " << key << dendl; } s->token_claims.emplace_back("sts"); s->token_claims.emplace_back("role_name:" + role.tenant + "$" + role.name); s->token_claims.emplace_back("role_session:" + token_attrs.role_session_name); for (auto& it : token_attrs.token_claims) { s->token_claims.emplace_back(it); } } rgw::auth::Engine::result_t rgw::auth::AnonymousEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const { if (! is_applicable(s)) { return result_t::deny(-EPERM); } else { RGWUserInfo user_info; rgw_get_anon_user(user_info); auto apl = \ apl_factory->create_apl_local(cct, s, user_info, std::nullopt, {}, rgw::auth::LocalApplier::NO_SUBUSER, std::nullopt, rgw::auth::LocalApplier::NO_ACCESS_KEY); return result_t::grant(std::move(apl)); } }