1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 #ifndef CEPH_RGW_AUTH_H
6 #define CEPH_RGW_AUTH_H
10 #include <type_traits>
11 #include <system_error>
14 #include "rgw_common.h"
15 #include "rgw_keystone.h"
17 #define RGW_USER_ANON_ID "anonymous"
22 using Exception
= std::system_error
;
25 /* Load information about identity that will be used by RGWOp to authorize
26 * any operation that comes from an authenticated user. */
29 typedef std::map
<std::string
, int> aclspec_t
;
30 using idset_t
= boost::container::flat_set
<Principal
>;
32 virtual ~Identity() = default;
34 /* Translate the ACL provided in @aclspec into concrete permission set that
35 * can be used during the authorization phase (RGWOp::verify_permission).
36 * On error throws rgw::auth::Exception storing the reason.
38 * NOTE: an implementation is responsible for giving the real semantic to
39 * the items in @aclspec. That is, their meaning may depend on particular
40 * applier that is being used. */
41 virtual uint32_t get_perms_from_aclspec(const aclspec_t
& aclspec
) const = 0;
43 /* Verify whether a given identity *can be treated as* an admin of rgw_user
44 * (account in Swift's terminology) specified in @uid. On error throws
45 * rgw::auth::Exception storing the reason. */
46 virtual bool is_admin_of(const rgw_user
& uid
) const = 0;
48 /* Verify whether a given identity *is* the owner of the rgw_user (account
49 * in the Swift's terminology) specified in @uid. On internal error throws
50 * rgw::auth::Exception storing the reason. */
51 virtual bool is_owner_of(const rgw_user
& uid
) const = 0;
53 /* Return the permission mask that is used to narrow down the set of
54 * operations allowed for a given identity. This method reflects the idea
55 * of subuser tied to RGWUserInfo. On error throws rgw::auth::Exception
57 virtual uint32_t get_perm_mask() const = 0;
59 virtual bool is_anonymous() const final
{
60 /* If the identity owns the anonymous account (rgw_user), it's considered
61 * the anonymous identity. On error throws rgw::auth::Exception storing
63 return is_owner_of(rgw_user(RGW_USER_ANON_ID
));
66 virtual void to_str(std::ostream
& out
) const = 0;
68 /* Verify whether a given identity corresponds to an identity in the
70 virtual bool is_identity(const idset_t
& ids
) const = 0;
73 inline std::ostream
& operator<<(std::ostream
& out
,
74 const rgw::auth::Identity
& id
) {
80 std::unique_ptr
<Identity
> transform_old_authinfo(const req_state
* const s
);
83 /* Interface for classes applying changes to request state/RADOS store
84 * imposed by a particular rgw::auth::Engine.
86 * In contrast to rgw::auth::Engine, implementations of this interface
87 * are allowed to handle req_state or RGWRados in the read-write manner.
89 * It's expected that most (if not all) of implementations will also
90 * conform to rgw::auth::Identity interface to provide authorization
91 * policy (ACLs, account's ownership and entitlement). */
92 class IdentityApplier
: public Identity
{
94 typedef std::unique_ptr
<IdentityApplier
> aplptr_t
;
96 virtual ~IdentityApplier() {};
98 /* Fill provided RGWUserInfo with information about the account that
99 * RGWOp will operate on. Errors are handled solely through exceptions.
101 * XXX: be aware that the "account" term refers to rgw_user. The naming
103 virtual void load_acct_info(RGWUserInfo
& user_info
) const = 0; /* out */
105 /* Apply any changes to request state. This method will be most useful for
106 * TempURL of Swift API. */
107 virtual void modify_request_state(req_state
* s
) const {} /* in/out */
111 /* Interface class for completing the two-step authentication process.
112 * Completer provides the second step - the complete() method that should
113 * be called after Engine::authenticate() but before *committing* results
114 * of an RGWOp (or sending a response in the case of non-mutating ops).
116 * The motivation driving the interface is to address those authentication
117 * schemas that require message integrity verification *without* in-memory
118 * data buffering. Typical examples are AWS Auth v4 and the auth mechanism
119 * of browser uploads facilities both in S3 and Swift APIs (see RGWPostObj).
120 * The workflow of request from the authentication point-of-view does look
121 * like following one:
122 * A. authenticate (Engine::authenticate),
123 * B. authorize (see RGWOp::verify_permissions),
124 * C. execute-prepare (init potential data modifications),
125 * D. authenticate-complete - (Completer::complete),
126 * E. execute-commit - commit the modifications from point C. */
129 /* It's expected that Completers would tend to implement many interfaces
130 * and be used not only in req_state::auth::completer. Ref counting their
131 * instances woild be helpful. */
132 typedef std::shared_ptr
<Completer
> cmplptr_t
;
134 virtual ~Completer() = default;
136 /* Complete the authentication process. Return boolean indicating whether
137 * the completion succeeded. On error throws rgw::auth::Exception storing
139 virtual bool complete() = 0;
141 /* Apply any changes to request state. The initial use case was injecting
142 * the AWSv4 filter over rgw::io::RestfulClient in req_state. */
143 virtual void modify_request_state(req_state
* s
) = 0; /* in/out */
147 /* Interface class for authentication backends (auth engines) in RadosGW.
149 * An engine is supposed only to authenticate (not authorize!) requests
150 * basing on their req_state and - if access has been granted - provide
151 * an upper layer with:
152 * - rgw::auth::IdentityApplier to commit all changes to the request state as
153 * well as to the RADOS store (creating an account, synchronizing
154 * user-related information with external databases and so on).
155 * - rgw::auth::Completer (optionally) to finish the authentication
156 * of the request. Typical use case is verifying message integrity
157 * in AWS Auth v4 and browser uploads (RGWPostObj).
159 * Both of them are supposed to be wrapped in Engine::AuthResult.
161 * The authentication process consists of two steps:
162 * - Engine::authenticate() which should be called before *initiating*
163 * any modifications to RADOS store that are related to an operation
164 * a client wants to perform (RGWOp::execute).
165 * - Completer::complete() supposed to be called, if completer has been
166 * returned, after the authenticate() step but before *committing*
167 * those modifications or sending a response (RGWOp::complete).
169 * An engine outlives both Applier and Completer. It's intended to live
170 * since RadosGW's initialization and handle multiple requests till
173 * Auth engine MUST NOT make any changes to req_state nor RADOS store.
174 * This is solely an Applier's responsibility!
176 * Separation between authentication and global state modification has
177 * been introduced because many auth engines are orthogonal to appliers
178 * and thus they can be decoupled. Additional motivation is to clearly
179 * distinguish all portions of code modifying data structures. */
182 virtual ~Engine() = default;
185 struct rejection_mark_t
{};
186 bool is_rejected
= false;
189 std::pair
<IdentityApplier::aplptr_t
, Completer::cmplptr_t
> result_pair
;
191 AuthResult(const int reason
)
195 AuthResult(rejection_mark_t
&&, const int reason
)
200 /* Allow only the reasonable combintations - returning just Completer
201 * without accompanying IdentityApplier is strictly prohibited! */
202 AuthResult(IdentityApplier::aplptr_t
&& applier
)
203 : result_pair(std::move(applier
), nullptr) {
206 AuthResult(IdentityApplier::aplptr_t
&& applier
,
207 Completer::cmplptr_t
&& completer
)
208 : result_pair(std::move(applier
), std::move(completer
)) {
213 /* Engine doesn't grant the access but also doesn't reject it. */
216 /* Engine successfully authenticated requester. */
219 /* Engine strictly indicates that a request should be rejected
220 * without trying any further engine. */
224 Status
get_status() const {
226 return Status::REJECTED
;
227 } else if (! result_pair
.first
) {
228 return Status::DENIED
;
230 return Status::GRANTED
;
234 int get_reason() const {
238 IdentityApplier::aplptr_t
get_applier() {
239 return std::move(result_pair
.first
);
242 Completer::cmplptr_t
&& get_completer() {
243 return std::move(result_pair
.second
);
246 static AuthResult
reject(const int reason
= -EACCES
) {
247 return AuthResult(rejection_mark_t(), reason
);
250 static AuthResult
deny(const int reason
= -EACCES
) {
251 return AuthResult(reason
);
254 static AuthResult
grant(IdentityApplier::aplptr_t
&& applier
) {
255 return AuthResult(std::move(applier
));
258 static AuthResult
grant(IdentityApplier::aplptr_t
&& applier
,
259 Completer::cmplptr_t
&& completer
) {
260 return AuthResult(std::move(applier
), std::move(completer
));
264 using result_t
= AuthResult
;
266 /* Get name of the auth engine. */
267 virtual const char* get_name() const noexcept
= 0;
269 /* Throwing method for identity verification. When the check is positive
270 * an implementation should return Engine::result_t containing:
271 * - a non-null pointer to an object conforming the Applier interface.
272 * Otherwise, the authentication is treated as failed.
273 * - a (potentially null) pointer to an object conforming the Completer
276 * On error throws rgw::auth::Exception containing the reason. */
277 virtual result_t
authenticate(const req_state
* s
) const = 0;
281 /* Interface for extracting a token basing from data carried by req_state. */
282 class TokenExtractor
{
284 virtual ~TokenExtractor() = default;
285 virtual std::string
get_token(const req_state
* s
) const = 0;
289 /* Abstract class for stacking sub-engines to expose them as a single
290 * Engine. It is responsible for ordering its sub-engines and managing
291 * fall-backs between them. Derivatee is supposed to encapsulate engine
292 * instances and add them using the add_engine() method in the order it
293 * wants to be tried during the call to authenticate().
295 * Each new Strategy should be exposed to StrategyRegistry for handling
296 * the dynamic reconfiguration. */
297 class Strategy
: public Engine
{
299 /* Specifiers controlling what happens when an associated engine fails.
300 * The names and semantic has been borrowed mostly from libpam. */
302 /* Failure of an engine injected with the REQUISITE specifier aborts
303 * the strategy's authentication process immediately. No other engine
307 /* Success of an engine injected with the SUFFICIENT specifier ends
308 * strategy's authentication process successfully. However, denying
309 * doesn't abort it -- there will be fall-back to following engine
310 * if the one that failed wasn't the last one. */
313 /* Like SUFFICIENT with the exception that on failure the reason code
314 * is not overridden. Instead, it's taken directly from the last tried
315 * non-FALLBACK engine. If there was no previous non-FALLBACK engine
316 * in a Strategy, then the result_t::deny(reason = -EACCES) is used. */
320 Engine::result_t
authenticate(const req_state
* s
) const override final
;
322 bool is_empty() const {
323 return auth_stack
.empty();
326 static int apply(const Strategy
& auth_strategy
, req_state
* s
) noexcept
;
329 /* Using the reference wrapper here to explicitly point out we are not
330 * interested in storing nulls while preserving the dynamic polymorphism. */
331 using stack_item_t
= std::pair
<std::reference_wrapper
<const Engine
>,
333 std::vector
<stack_item_t
> auth_stack
;
336 void add_engine(Control ctrl_flag
, const Engine
& engine
) noexcept
;
340 /* A class aggregating the knowledge about all Strategies in RadosGW. It is
341 * responsible for handling the dynamic reconfiguration on e.g. realm update.
342 * The definition is in rgw/rgw_auth_registry.h,
344 * Each new Strategy should be exposed to it. */
345 class StrategyRegistry
;
347 /* rgw::auth::RemoteApplier targets those authentication engines which don't
348 * need to ask the RADOS store while performing the auth process. Instead,
349 * they obtain credentials from an external source like Keystone or LDAP.
351 * As the authenticated user may not have an account yet, RGWRemoteAuthApplier
352 * must be able to create it basing on data passed by an auth engine. Those
353 * data will be used to fill RGWUserInfo structure. */
354 class RemoteApplier
: public IdentityApplier
{
357 friend class RemoteApplier
;
359 const rgw_user acct_user
;
360 const std::string acct_name
;
361 const uint32_t perm_mask
;
363 const uint32_t acct_type
;
366 enum class acct_privilege_t
{
371 AuthInfo(const rgw_user
& acct_user
,
372 const std::string
& acct_name
,
373 const uint32_t perm_mask
,
374 const acct_privilege_t level
,
375 const uint32_t acct_type
=TYPE_NONE
)
376 : acct_user(acct_user
),
377 acct_name(acct_name
),
378 perm_mask(perm_mask
),
379 is_admin(acct_privilege_t::IS_ADMIN_ACCT
== level
),
380 acct_type(acct_type
) {
384 using aclspec_t
= rgw::auth::Identity::aclspec_t
;
385 typedef std::function
<uint32_t(const aclspec_t
&)> acl_strategy_t
;
388 CephContext
* const cct
;
390 /* Read-write is intensional here due to RGWUserInfo creation process. */
391 RGWRados
* const store
;
393 /* Supplemental strategy for extracting permissions from ACLs. Its results
394 * will be combined (ORed) with a default strategy that is responsible for
395 * handling backward compatibility. */
396 const acl_strategy_t extra_acl_strategy
;
399 const bool implicit_tenants
;
401 virtual void create_account(const rgw_user
& acct_user
,
402 RGWUserInfo
& user_info
) const; /* out */
405 RemoteApplier(CephContext
* const cct
,
406 RGWRados
* const store
,
407 acl_strategy_t
&& extra_acl_strategy
,
408 const AuthInfo
& info
,
409 const bool implicit_tenants
)
412 extra_acl_strategy(std::move(extra_acl_strategy
)),
414 implicit_tenants(implicit_tenants
) {
417 uint32_t get_perms_from_aclspec(const aclspec_t
& aclspec
) const override
;
418 bool is_admin_of(const rgw_user
& uid
) const override
;
419 bool is_owner_of(const rgw_user
& uid
) const override
;
420 bool is_identity(const idset_t
& ids
) const override
;
422 uint32_t get_perm_mask() const override
{ return info
.perm_mask
; }
423 void to_str(std::ostream
& out
) const override
;
424 void load_acct_info(RGWUserInfo
& user_info
) const override
; /* out */
427 virtual ~Factory() {}
428 /* Providing r-value reference here is required intensionally. Callee is
429 * thus disallowed to handle std::function in a way that could inhibit
430 * the move behaviour (like forgetting about std::moving a l-value). */
431 virtual aplptr_t
create_apl_remote(CephContext
* cct
,
433 acl_strategy_t
&& extra_acl_strategy
,
434 const AuthInfo info
) const = 0;
439 /* rgw::auth::LocalApplier targets those auth engines that base on the data
440 * enclosed in the RGWUserInfo control structure. As a side effect of doing
441 * the authentication process, they must have it loaded. Leveraging this is
442 * a way to avoid unnecessary calls to underlying RADOS store. */
443 class LocalApplier
: public IdentityApplier
{
444 using aclspec_t
= rgw::auth::Identity::aclspec_t
;
447 const RGWUserInfo user_info
;
448 const std::string subuser
;
450 uint32_t get_perm_mask(const std::string
& subuser_name
,
451 const RGWUserInfo
&uinfo
) const;
454 static const std::string NO_SUBUSER
;
456 LocalApplier(CephContext
* const cct
,
457 const RGWUserInfo
& user_info
,
459 : user_info(user_info
),
460 subuser(std::move(subuser
)) {
464 uint32_t get_perms_from_aclspec(const aclspec_t
& aclspec
) const override
;
465 bool is_admin_of(const rgw_user
& uid
) const override
;
466 bool is_owner_of(const rgw_user
& uid
) const override
;
467 bool is_identity(const idset_t
& ids
) const override
;
468 uint32_t get_perm_mask() const override
{
469 return get_perm_mask(subuser
, user_info
);
471 void to_str(std::ostream
& out
) const override
;
472 void load_acct_info(RGWUserInfo
& user_info
) const override
; /* out */
475 virtual ~Factory() {}
476 virtual aplptr_t
create_apl_local(CephContext
* cct
,
478 const RGWUserInfo
& user_info
,
479 const std::string
& subuser
) const = 0;
484 /* The anonymous abstract engine. */
485 class AnonymousEngine
: public Engine
{
486 CephContext
* const cct
;
487 const rgw::auth::LocalApplier::Factory
* const apl_factory
;
490 AnonymousEngine(CephContext
* const cct
,
491 const rgw::auth::LocalApplier::Factory
* const apl_factory
)
493 apl_factory(apl_factory
) {
496 const char* get_name() const noexcept override
{
497 return "rgw::auth::AnonymousEngine";
500 Engine::result_t
authenticate(const req_state
* s
) const override final
;
503 virtual bool is_applicable(const req_state
*) const noexcept
{
508 } /* namespace auth */
509 } /* namespace rgw */
512 uint32_t rgw_perms_from_aclspec_default_strategy(
514 const rgw::auth::Identity::aclspec_t
& aclspec
);
516 #endif /* CEPH_RGW_AUTH_H */