]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_auth.cc
53649433ab6ab3823784efd600d70e5a5c05a882
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
6 #include "rgw_common.h"
10 #include "rgw_http_client.h"
11 #include "rgw_keystone.h"
14 #include "include/str_list.h"
16 #define dout_context g_ceph_context
17 #define dout_subsys ceph_subsys_rgw
23 std::unique_ptr
<rgw::auth::Identity
>
24 transform_old_authinfo(CephContext
* const cct
,
25 const rgw_user
& auth_id
,
30 /* This class is not intended for public use. Should be removed altogether
31 * with this function after moving all our APIs to the new authentication
33 class DummyIdentityApplier
: public rgw::auth::Identity
{
34 CephContext
* const cct
;
36 /* For this particular case it's OK to use rgw_user structure to convey
37 * the identity info as this was the policy for doing that before the
44 DummyIdentityApplier(CephContext
* const cct
,
45 const rgw_user
& auth_id
,
56 uint32_t get_perms_from_aclspec(const DoutPrefixProvider
* dpp
, const aclspec_t
& aclspec
) const override
{
57 return rgw_perms_from_aclspec_default_strategy(id
, aclspec
);
60 bool is_admin_of(const rgw_user
& acct_id
) const override
{
64 bool is_owner_of(const rgw_user
& acct_id
) const override
{
68 bool is_identity(const idset_t
& ids
) const override
{
70 if (p
.is_wildcard()) {
72 } else if (p
.is_tenant() && p
.get_tenant() == id
.tenant
) {
74 } else if (p
.is_user() &&
75 (p
.get_tenant() == id
.tenant
) &&
76 (p
.get_id() == id
.id
)) {
83 uint32_t get_perm_mask() const override
{
87 uint32_t get_identity_type() const override
{
91 string
get_acct_name() const override
{
95 string
get_subuser() const override
{
99 void to_str(std::ostream
& out
) const override
{
100 out
<< "RGWDummyIdentityApplier(auth_id=" << id
101 << ", perm_mask=" << perm_mask
102 << ", is_admin=" << is_admin
<< ")";
106 return std::unique_ptr
<rgw::auth::Identity
>(
107 new DummyIdentityApplier(cct
,
114 std::unique_ptr
<rgw::auth::Identity
>
115 transform_old_authinfo(const req_state
* const s
)
117 return transform_old_authinfo(s
->cct
,
120 /* System user has admin permissions by default - it's supposed to pass
121 * through any security check. */
123 s
->user
->get_type());
126 } /* namespace auth */
127 } /* namespace rgw */
130 uint32_t rgw_perms_from_aclspec_default_strategy(
132 const rgw::auth::Identity::aclspec_t
& aclspec
)
134 dout(5) << "Searching permissions for uid=" << uid
<< dendl
;
136 const auto iter
= aclspec
.find(uid
.to_str());
137 if (std::end(aclspec
) != iter
) {
138 dout(5) << "Found permission: " << iter
->second
<< dendl
;
142 dout(5) << "Permissions for user not found" << dendl
;
147 static inline const std::string
make_spec_item(const std::string
& tenant
,
148 const std::string
& id
)
150 return tenant
+ ":" + id
;
154 static inline std::pair
<bool, rgw::auth::Engine::result_t
>
155 strategy_handle_rejected(rgw::auth::Engine::result_t
&& engine_result
,
156 const rgw::auth::Strategy::Control policy
,
157 rgw::auth::Engine::result_t
&& strategy_result
)
159 using Control
= rgw::auth::Strategy::Control
;
161 case Control::REQUISITE
:
162 /* Don't try next. */
163 return std::make_pair(false, std::move(engine_result
));
165 case Control::SUFFICIENT
:
166 /* Don't try next. */
167 return std::make_pair(false, std::move(engine_result
));
169 case Control::FALLBACK
:
170 /* Don't try next. */
171 return std::make_pair(false, std::move(strategy_result
));
174 /* Huh, memory corruption? */
179 static inline std::pair
<bool, rgw::auth::Engine::result_t
>
180 strategy_handle_denied(rgw::auth::Engine::result_t
&& engine_result
,
181 const rgw::auth::Strategy::Control policy
,
182 rgw::auth::Engine::result_t
&& strategy_result
)
184 using Control
= rgw::auth::Strategy::Control
;
186 case Control::REQUISITE
:
187 /* Don't try next. */
188 return std::make_pair(false, std::move(engine_result
));
190 case Control::SUFFICIENT
:
192 return std::make_pair(true, std::move(engine_result
));
194 case Control::FALLBACK
:
195 return std::make_pair(true, std::move(strategy_result
));
198 /* Huh, memory corruption? */
203 static inline std::pair
<bool, rgw::auth::Engine::result_t
>
204 strategy_handle_granted(rgw::auth::Engine::result_t
&& engine_result
,
205 const rgw::auth::Strategy::Control policy
,
206 rgw::auth::Engine::result_t
&& strategy_result
)
208 using Control
= rgw::auth::Strategy::Control
;
210 case Control::REQUISITE
:
212 return std::make_pair(true, std::move(engine_result
));
214 case Control::SUFFICIENT
:
215 /* Don't try next. */
216 return std::make_pair(false, std::move(engine_result
));
218 case Control::FALLBACK
:
219 /* Don't try next. */
220 return std::make_pair(false, std::move(engine_result
));
223 /* Huh, memory corruption? */
228 rgw::auth::Engine::result_t
229 rgw::auth::Strategy::authenticate(const DoutPrefixProvider
* dpp
, const req_state
* const s
) const
231 result_t strategy_result
= result_t::deny();
233 for (const stack_item_t
& kv
: auth_stack
) {
234 const rgw::auth::Engine
& engine
= kv
.first
;
235 const auto& policy
= kv
.second
;
237 ldpp_dout(dpp
, 20) << get_name() << ": trying " << engine
.get_name() << dendl
;
239 result_t engine_result
= result_t::deny();
241 engine_result
= engine
.authenticate(dpp
, s
);
242 } catch (const int err
) {
243 engine_result
= result_t::deny(err
);
246 bool try_next
= true;
247 switch (engine_result
.get_status()) {
248 case result_t::Status::REJECTED
: {
249 ldpp_dout(dpp
, 20) << engine
.get_name() << " rejected with reason="
250 << engine_result
.get_reason() << dendl
;
252 std::tie(try_next
, strategy_result
) = \
253 strategy_handle_rejected(std::move(engine_result
), policy
,
254 std::move(strategy_result
));
257 case result_t::Status::DENIED
: {
258 ldpp_dout(dpp
, 20) << engine
.get_name() << " denied with reason="
259 << engine_result
.get_reason() << dendl
;
261 std::tie(try_next
, strategy_result
) = \
262 strategy_handle_denied(std::move(engine_result
), policy
,
263 std::move(strategy_result
));
266 case result_t::Status::GRANTED
: {
267 ldpp_dout(dpp
, 20) << engine
.get_name() << " granted access" << dendl
;
269 std::tie(try_next
, strategy_result
) = \
270 strategy_handle_granted(std::move(engine_result
), policy
,
271 std::move(strategy_result
));
284 return strategy_result
;
288 rgw::auth::Strategy::apply(const DoutPrefixProvider
*dpp
, const rgw::auth::Strategy
& auth_strategy
,
289 req_state
* const s
) noexcept
292 auto result
= auth_strategy
.authenticate(dpp
, s
);
293 if (result
.get_status() != decltype(result
)::Status::GRANTED
) {
294 /* Access denied is acknowledged by returning a std::unique_ptr with
296 ldpp_dout(dpp
, 5) << "Failed the auth strategy, reason="
297 << result
.get_reason() << dendl
;
298 return result
.get_reason();
302 rgw::auth::IdentityApplier::aplptr_t applier
= result
.get_applier();
303 rgw::auth::Completer::cmplptr_t completer
= result
.get_completer();
305 /* Account used by a given RGWOp is decoupled from identity employed
306 * in the authorization phase (RGWOp::verify_permissions). */
307 applier
->load_acct_info(dpp
, s
->user
->get_info());
308 s
->perm_mask
= applier
->get_perm_mask();
310 /* This is the single place where we pass req_state as a pointer
311 * to non-const and thus its modification is allowed. In the time
312 * of writing only RGWTempURLEngine needed that feature. */
313 applier
->modify_request_state(dpp
, s
);
315 completer
->modify_request_state(dpp
, s
);
318 s
->auth
.identity
= std::move(applier
);
319 s
->auth
.completer
= std::move(completer
);
322 } catch (const int err
) {
323 ldpp_dout(dpp
, 5) << "applier throwed err=" << err
<< dendl
;
326 } catch (const int err
) {
327 ldpp_dout(dpp
, 5) << "auth engine throwed err=" << err
<< dendl
;
331 /* We never should be here. */
336 rgw::auth::Strategy::add_engine(const Control ctrl_flag
,
337 const Engine
& engine
) noexcept
339 auth_stack
.push_back(std::make_pair(std::cref(engine
), ctrl_flag
));
342 void rgw::auth::WebIdentityApplier::to_str(std::ostream
& out
) const
344 out
<< "rgw::auth::WebIdentityApplier(sub =" << token_claims
.sub
345 << ", user_name=" << token_claims
.user_name
346 << ", aud =" << token_claims
.aud
347 << ", provider_id =" << token_claims
.iss
<< ")";
350 string
rgw::auth::WebIdentityApplier::get_idp_url() const
352 string idp_url
= token_claims
.iss
;
353 auto pos
= idp_url
.find("http://");
354 if (pos
== std::string::npos
) {
355 pos
= idp_url
.find("https://");
356 if (pos
!= std::string::npos
) {
357 idp_url
.erase(pos
, 8);
360 idp_url
.erase(pos
, 7);
365 void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider
*dpp
, req_state
* s
) const
367 s
->info
.args
.append("sub", token_claims
.sub
);
368 s
->info
.args
.append("aud", token_claims
.aud
);
369 s
->info
.args
.append("provider_id", token_claims
.iss
);
371 string idp_url
= get_idp_url();
372 string condition
= idp_url
+ ":app_id";
373 s
->env
.emplace(condition
, token_claims
.aud
);
376 bool rgw::auth::WebIdentityApplier::is_identity(const idset_t
& ids
) const
378 if (ids
.size() > 1) {
382 for (auto id
: ids
) {
383 string idp_url
= get_idp_url();
384 if (id
.is_oidc_provider() && id
.get_idp_url() == idp_url
) {
391 /* rgw::auth::RemoteAuthApplier */
392 uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const DoutPrefixProvider
* dpp
, const aclspec_t
& aclspec
) const
396 /* For backward compatibility with ACLOwner. */
397 perm
|= rgw_perms_from_aclspec_default_strategy(info
.acct_user
,
400 /* We also need to cover cases where rgw_keystone_implicit_tenants
402 if (info
.acct_user
.tenant
.empty()) {
403 const rgw_user
tenanted_acct_user(info
.acct_user
.id
, info
.acct_user
.id
);
405 perm
|= rgw_perms_from_aclspec_default_strategy(tenanted_acct_user
,
409 /* Now it's a time for invoking additional strategy that was supplied by
410 * a specific auth engine. */
411 if (extra_acl_strategy
) {
412 perm
|= extra_acl_strategy(aclspec
);
415 ldpp_dout(dpp
, 20) << "from ACL got perm=" << perm
<< dendl
;
419 bool rgw::auth::RemoteApplier::is_admin_of(const rgw_user
& uid
) const
421 return info
.is_admin
;
424 bool rgw::auth::RemoteApplier::is_owner_of(const rgw_user
& uid
) const
426 if (info
.acct_user
.tenant
.empty()) {
427 const rgw_user
tenanted_acct_user(info
.acct_user
.id
, info
.acct_user
.id
);
429 if (tenanted_acct_user
== uid
) {
434 return info
.acct_user
== uid
;
437 bool rgw::auth::RemoteApplier::is_identity(const idset_t
& ids
) const {
438 for (auto& id
: ids
) {
439 if (id
.is_wildcard()) {
442 // We also need to cover cases where rgw_keystone_implicit_tenants
444 } else if (id
.is_tenant() &&
445 (info
.acct_user
.tenant
.empty() ?
447 info
.acct_user
.tenant
) == id
.get_tenant()) {
449 } else if (id
.is_user() &&
450 info
.acct_user
.id
== id
.get_id() &&
451 (info
.acct_user
.tenant
.empty() ?
453 info
.acct_user
.tenant
) == id
.get_tenant()) {
460 void rgw::auth::RemoteApplier::to_str(std::ostream
& out
) const
462 out
<< "rgw::auth::RemoteApplier(acct_user=" << info
.acct_user
463 << ", acct_name=" << info
.acct_name
464 << ", perm_mask=" << info
.perm_mask
465 << ", is_admin=" << info
.is_admin
<< ")";
468 void rgw::auth::ImplicitTenants::recompute_value(const ConfigProxy
& c
)
470 std::string s
= c
.get_val
<std::string
>("rgw_keystone_implicit_tenants");
472 if (boost::iequals(s
, "both")
473 || boost::iequals(s
, "true")
474 || boost::iequals(s
, "1")) {
475 v
= IMPLICIT_TENANTS_S3
|IMPLICIT_TENANTS_SWIFT
;
476 } else if (boost::iequals(s
, "0")
477 || boost::iequals(s
, "none")
478 || boost::iequals(s
, "false")) {
480 } else if (boost::iequals(s
, "s3")) {
481 v
= IMPLICIT_TENANTS_S3
;
482 } else if (boost::iequals(s
, "swift")) {
483 v
= IMPLICIT_TENANTS_SWIFT
;
484 } else { /* "" (and anything else) */
485 v
= IMPLICIT_TENANTS_BAD
;
491 const char **rgw::auth::ImplicitTenants::get_tracked_conf_keys() const
493 static const char *keys
[] = {
494 "rgw_keystone_implicit_tenants",
499 void rgw::auth::ImplicitTenants::handle_conf_change(const ConfigProxy
& c
,
500 const std::set
<std::string
> &changed
)
502 if (changed
.count("rgw_keystone_implicit_tenants")) {
507 void rgw::auth::RemoteApplier::create_account(const DoutPrefixProvider
* dpp
,
508 const rgw_user
& acct_user
,
509 bool implicit_tenant
,
510 RGWUserInfo
& user_info
) const /* out */
512 rgw_user new_acct_user
= acct_user
;
514 if (info
.acct_type
) {
515 //ldap/keystone for s3 users
516 user_info
.type
= info
.acct_type
;
519 /* An upper layer may enforce creating new accounts within their own
521 if (new_acct_user
.tenant
.empty() && implicit_tenant
) {
522 new_acct_user
.tenant
= new_acct_user
.id
;
525 user_info
.user_id
= new_acct_user
;
526 user_info
.display_name
= info
.acct_name
;
528 user_info
.max_buckets
=
529 cct
->_conf
.get_val
<int64_t>("rgw_user_max_buckets");
530 rgw_apply_default_bucket_quota(user_info
.bucket_quota
, cct
->_conf
);
531 rgw_apply_default_user_quota(user_info
.user_quota
, cct
->_conf
);
533 int ret
= ctl
->user
->store_info(user_info
, null_yield
,
534 RGWUserCtl::PutParams().set_exclusive(true));
536 ldpp_dout(dpp
, 0) << "ERROR: failed to store new user info: user="
537 << user_info
.user_id
<< " ret=" << ret
<< dendl
;
542 /* TODO(rzarzynski): we need to handle display_name changes. */
543 void rgw::auth::RemoteApplier::load_acct_info(const DoutPrefixProvider
* dpp
, RGWUserInfo
& user_info
) const /* out */
545 /* It's supposed that RGWRemoteAuthApplier tries to load account info
546 * that belongs to the authenticated identity. Another policy may be
547 * applied by using a RGWThirdPartyAccountAuthApplier decorator. */
548 const rgw_user
& acct_user
= info
.acct_user
;
549 auto implicit_value
= implicit_tenant_context
.get_value();
550 bool implicit_tenant
= implicit_value
.implicit_tenants_for_(implicit_tenant_bit
);
551 bool split_mode
= implicit_value
.is_split_mode();
553 /* Normally, empty "tenant" field of acct_user means the authenticated
554 * identity has the legacy, global tenant. However, due to inclusion
555 * of multi-tenancy, we got some special compatibility kludge for remote
556 * backends like Keystone.
557 * If the global tenant is the requested one, we try the same tenant as
558 * the user name first. If that RGWUserInfo exists, we use it. This way,
559 * migrated OpenStack users can get their namespaced containers and nobody's
561 * If that fails, we look up in the requested (possibly empty) tenant.
562 * If that fails too, we create the account within the global or separated
563 * namespace depending on rgw_keystone_implicit_tenants.
564 * For compatibility with previous versions of ceph, it is possible
565 * to enable implicit_tenants for only s3 or only swift.
566 * in this mode ("split_mode"), we must constrain the id lookups to
567 * only use the identifier space that would be used if the id were
570 if (split_mode
&& !implicit_tenant
)
571 ; /* suppress lookup for id used by "other" protocol */
572 else if (acct_user
.tenant
.empty()) {
573 const rgw_user
tenanted_uid(acct_user
.id
, acct_user
.id
);
575 if (ctl
->user
->get_info_by_uid(tenanted_uid
, &user_info
, null_yield
) >= 0) {
581 if (split_mode
&& implicit_tenant
)
582 ; /* suppress lookup for id used by "other" protocol */
583 else if (ctl
->user
->get_info_by_uid(acct_user
, &user_info
, null_yield
) >= 0) {
588 ldpp_dout(dpp
, 0) << "NOTICE: couldn't map swift user " << acct_user
<< dendl
;
589 create_account(dpp
, acct_user
, implicit_tenant
, user_info
);
591 /* Succeeded if we are here (create_account() hasn't throwed). */
594 /* rgw::auth::LocalApplier */
595 /* static declaration */
596 const std::string
rgw::auth::LocalApplier::NO_SUBUSER
;
598 uint32_t rgw::auth::LocalApplier::get_perms_from_aclspec(const DoutPrefixProvider
* dpp
, const aclspec_t
& aclspec
) const
600 return rgw_perms_from_aclspec_default_strategy(user_info
.user_id
, aclspec
);
603 bool rgw::auth::LocalApplier::is_admin_of(const rgw_user
& uid
) const
605 return user_info
.admin
|| user_info
.system
;
608 bool rgw::auth::LocalApplier::is_owner_of(const rgw_user
& uid
) const
610 return uid
== user_info
.user_id
;
613 bool rgw::auth::LocalApplier::is_identity(const idset_t
& ids
) const {
614 for (auto& id
: ids
) {
615 if (id
.is_wildcard()) {
617 } else if (id
.is_tenant() &&
618 id
.get_tenant() == user_info
.user_id
.tenant
) {
620 } else if (id
.is_user() &&
621 (id
.get_tenant() == user_info
.user_id
.tenant
)) {
622 if (id
.get_id() == user_info
.user_id
.id
) {
625 std::string wildcard_subuser
= user_info
.user_id
.id
;
626 wildcard_subuser
.append(":*");
627 if (wildcard_subuser
== id
.get_id()) {
629 } else if (subuser
!= NO_SUBUSER
) {
630 std::string user
= user_info
.user_id
.id
;
632 user
.append(subuser
);
633 if (user
== id
.get_id()) {
642 void rgw::auth::LocalApplier::to_str(std::ostream
& out
) const {
643 out
<< "rgw::auth::LocalApplier(acct_user=" << user_info
.user_id
644 << ", acct_name=" << user_info
.display_name
645 << ", subuser=" << subuser
646 << ", perm_mask=" << get_perm_mask()
647 << ", is_admin=" << static_cast<bool>(user_info
.admin
) << ")";
650 uint32_t rgw::auth::LocalApplier::get_perm_mask(const std::string
& subuser_name
,
651 const RGWUserInfo
&uinfo
) const
653 if (! subuser_name
.empty() && subuser_name
!= NO_SUBUSER
) {
654 const auto iter
= uinfo
.subusers
.find(subuser_name
);
656 if (iter
!= std::end(uinfo
.subusers
)) {
657 return iter
->second
.perm_mask
;
659 /* Subuser specified but not found. */
660 return RGW_PERM_NONE
;
663 /* Due to backward compatibility. */
664 return RGW_PERM_FULL_CONTROL
;
668 void rgw::auth::LocalApplier::load_acct_info(const DoutPrefixProvider
* dpp
, RGWUserInfo
& user_info
) const /* out */
670 /* Load the account that belongs to the authenticated identity. An extra call
671 * to RADOS may be safely skipped in this case. */
672 user_info
= this->user_info
;
675 void rgw::auth::RoleApplier::to_str(std::ostream
& out
) const {
676 out
<< "rgw::auth::LocalApplier(role name =" << role_name
;
677 for (auto policy
: role_policies
) {
678 out
<< ", role policy =" << policy
;
683 bool rgw::auth::RoleApplier::is_identity(const idset_t
& ids
) const {
684 for (auto& p
: ids
) {
686 string tenant
= p
.get_tenant();
687 if (tenant
.empty()) {
690 name
= tenant
+ "$" + p
.get_id();
692 if (p
.is_wildcard()) {
694 } else if (p
.is_role() && name
== role_name
) {
701 void rgw::auth::RoleApplier::load_acct_info(const DoutPrefixProvider
* dpp
, RGWUserInfo
& user_info
) const /* out */
703 /* Load the user id */
704 user_info
.user_id
= this->user_id
;
707 void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider
*dpp
, req_state
* s
) const
709 for (auto it
: role_policies
) {
711 bufferlist bl
= bufferlist::static_from_string(it
);
712 const rgw::IAM::Policy
p(s
->cct
, s
->user
->get_tenant(), bl
);
713 s
->iam_user_policies
.push_back(std::move(p
));
714 } catch (rgw::IAM::PolicyParseException
& e
) {
715 //Control shouldn't reach here as the policy has already been
717 ldpp_dout(dpp
, 20) << "failed to parse policy: " << e
.what() << dendl
;
722 rgw::auth::Engine::result_t
723 rgw::auth::AnonymousEngine::authenticate(const DoutPrefixProvider
* dpp
, const req_state
* const s
) const
725 if (! is_applicable(s
)) {
726 return result_t::deny(-EPERM
);
728 RGWUserInfo user_info
;
729 rgw_get_anon_user(user_info
);
732 apl_factory
->create_apl_local(cct
, s
, user_info
,
733 rgw::auth::LocalApplier::NO_SUBUSER
,
735 return result_t::grant(std::move(apl
));