]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | ||
5 | #ifndef CEPH_RGW_AUTH_H | |
6 | #define CEPH_RGW_AUTH_H | |
7 | ||
8 | #include <functional> | |
9 | #include <ostream> | |
10 | #include <type_traits> | |
11 | #include <system_error> | |
12 | #include <utility> | |
13 | ||
14 | #include "rgw_common.h" | |
15 | #include "rgw_keystone.h" | |
16 | ||
17 | #define RGW_USER_ANON_ID "anonymous" | |
18 | ||
7c673cae FG |
19 | namespace rgw { |
20 | namespace auth { | |
21 | ||
22 | using Exception = std::system_error; | |
23 | ||
24 | ||
25 | /* Load information about identity that will be used by RGWOp to authorize | |
26 | * any operation that comes from an authenticated user. */ | |
27 | class Identity { | |
28 | public: | |
29 | typedef std::map<std::string, int> aclspec_t; | |
31f18b77 | 30 | using idset_t = boost::container::flat_set<Principal>; |
7c673cae FG |
31 | |
32 | virtual ~Identity() = default; | |
33 | ||
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. | |
37 | * | |
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; | |
42 | ||
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; | |
47 | ||
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; | |
52 | ||
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 | |
56 | * with the reason. */ | |
57 | virtual uint32_t get_perm_mask() const = 0; | |
58 | ||
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 | |
62 | * the reason. */ | |
63 | return is_owner_of(rgw_user(RGW_USER_ANON_ID)); | |
64 | } | |
65 | ||
66 | virtual void to_str(std::ostream& out) const = 0; | |
31f18b77 FG |
67 | |
68 | /* Verify whether a given identity corresponds to an identity in the | |
69 | provided set */ | |
70 | virtual bool is_identity(const idset_t& ids) const = 0; | |
7c673cae FG |
71 | }; |
72 | ||
73 | inline std::ostream& operator<<(std::ostream& out, | |
74 | const rgw::auth::Identity& id) { | |
75 | id.to_str(out); | |
76 | return out; | |
77 | } | |
78 | ||
79 | ||
80 | std::unique_ptr<Identity> transform_old_authinfo(const req_state* const s); | |
81 | ||
82 | ||
83 | /* Interface for classes applying changes to request state/RADOS store | |
84 | * imposed by a particular rgw::auth::Engine. | |
85 | * | |
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. | |
88 | * | |
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 { | |
93 | public: | |
94 | typedef std::unique_ptr<IdentityApplier> aplptr_t; | |
95 | ||
96 | virtual ~IdentityApplier() {}; | |
97 | ||
98 | /* Fill provided RGWUserInfo with information about the account that | |
99 | * RGWOp will operate on. Errors are handled solely through exceptions. | |
100 | * | |
101 | * XXX: be aware that the "account" term refers to rgw_user. The naming | |
102 | * is legacy. */ | |
103 | virtual void load_acct_info(RGWUserInfo& user_info) const = 0; /* out */ | |
104 | ||
105 | /* Apply any changes to request state. This method will be most useful for | |
31f18b77 FG |
106 | * TempURL of Swift API. */ |
107 | virtual void modify_request_state(req_state* s) const {} /* in/out */ | |
7c673cae FG |
108 | }; |
109 | ||
110 | ||
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). | |
115 | * | |
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. */ | |
127 | class Completer { | |
128 | public: | |
31f18b77 FG |
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; | |
7c673cae FG |
133 | |
134 | virtual ~Completer() = default; | |
135 | ||
136 | /* Complete the authentication process. Return boolean indicating whether | |
137 | * the completion succeeded. On error throws rgw::auth::Exception storing | |
138 | * the reason. */ | |
139 | virtual bool complete() = 0; | |
31f18b77 FG |
140 | |
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 */ | |
7c673cae FG |
144 | }; |
145 | ||
146 | ||
147 | /* Interface class for authentication backends (auth engines) in RadosGW. | |
148 | * | |
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). | |
158 | * | |
159 | * Both of them are supposed to be wrapped in Engine::AuthResult. | |
160 | * | |
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). | |
168 | * | |
169 | * An engine outlives both Applier and Completer. It's intended to live | |
170 | * since RadosGW's initialization and handle multiple requests till | |
171 | * a reconfiguration. | |
172 | * | |
173 | * Auth engine MUST NOT make any changes to req_state nor RADOS store. | |
174 | * This is solely an Applier's responsibility! | |
175 | * | |
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. */ | |
180 | class Engine { | |
181 | public: | |
182 | virtual ~Engine() = default; | |
183 | ||
184 | class AuthResult { | |
185 | struct rejection_mark_t {}; | |
186 | bool is_rejected = false; | |
187 | int reason = 0; | |
188 | ||
189 | std::pair<IdentityApplier::aplptr_t, Completer::cmplptr_t> result_pair; | |
190 | ||
191 | AuthResult(const int reason) | |
192 | : reason(reason) { | |
193 | } | |
194 | ||
195 | AuthResult(rejection_mark_t&&, const int reason) | |
196 | : is_rejected(true), | |
197 | reason(reason) { | |
198 | } | |
199 | ||
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) { | |
204 | } | |
205 | ||
206 | AuthResult(IdentityApplier::aplptr_t&& applier, | |
207 | Completer::cmplptr_t&& completer) | |
208 | : result_pair(std::move(applier), std::move(completer)) { | |
209 | } | |
210 | ||
211 | public: | |
212 | enum class Status { | |
213 | /* Engine doesn't grant the access but also doesn't reject it. */ | |
214 | DENIED, | |
215 | ||
216 | /* Engine successfully authenticated requester. */ | |
217 | GRANTED, | |
218 | ||
219 | /* Engine strictly indicates that a request should be rejected | |
220 | * without trying any further engine. */ | |
221 | REJECTED | |
222 | }; | |
223 | ||
224 | Status get_status() const { | |
225 | if (is_rejected) { | |
226 | return Status::REJECTED; | |
227 | } else if (! result_pair.first) { | |
228 | return Status::DENIED; | |
229 | } else { | |
230 | return Status::GRANTED; | |
231 | } | |
232 | } | |
233 | ||
234 | int get_reason() const { | |
235 | return reason; | |
236 | } | |
237 | ||
238 | IdentityApplier::aplptr_t get_applier() { | |
239 | return std::move(result_pair.first); | |
240 | } | |
241 | ||
242 | Completer::cmplptr_t&& get_completer() { | |
243 | return std::move(result_pair.second); | |
244 | } | |
245 | ||
246 | static AuthResult reject(const int reason = -EACCES) { | |
247 | return AuthResult(rejection_mark_t(), reason); | |
248 | } | |
249 | ||
250 | static AuthResult deny(const int reason = -EACCES) { | |
251 | return AuthResult(reason); | |
252 | } | |
253 | ||
254 | static AuthResult grant(IdentityApplier::aplptr_t&& applier) { | |
255 | return AuthResult(std::move(applier)); | |
256 | } | |
257 | ||
258 | static AuthResult grant(IdentityApplier::aplptr_t&& applier, | |
259 | Completer::cmplptr_t&& completer) { | |
260 | return AuthResult(std::move(applier), std::move(completer)); | |
261 | } | |
262 | }; | |
263 | ||
264 | using result_t = AuthResult; | |
265 | ||
266 | /* Get name of the auth engine. */ | |
267 | virtual const char* get_name() const noexcept = 0; | |
268 | ||
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 | |
274 | * interface. | |
275 | * | |
276 | * On error throws rgw::auth::Exception containing the reason. */ | |
277 | virtual result_t authenticate(const req_state* s) const = 0; | |
278 | }; | |
279 | ||
280 | ||
281 | /* Interface for extracting a token basing from data carried by req_state. */ | |
282 | class TokenExtractor { | |
283 | public: | |
284 | virtual ~TokenExtractor() = default; | |
285 | virtual std::string get_token(const req_state* s) const = 0; | |
286 | }; | |
287 | ||
288 | ||
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(). | |
294 | * | |
295 | * Each new Strategy should be exposed to StrategyRegistry for handling | |
296 | * the dynamic reconfiguration. */ | |
297 | class Strategy : public Engine { | |
298 | public: | |
299 | /* Specifiers controlling what happens when an associated engine fails. | |
300 | * The names and semantic has been borrowed mostly from libpam. */ | |
301 | enum class Control { | |
302 | /* Failure of an engine injected with the REQUISITE specifier aborts | |
303 | * the strategy's authentication process immediately. No other engine | |
304 | * will be tried. */ | |
305 | REQUISITE, | |
306 | ||
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. */ | |
311 | SUFFICIENT, | |
312 | ||
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. */ | |
317 | FALLBACK, | |
318 | }; | |
319 | ||
320 | Engine::result_t authenticate(const req_state* s) const override final; | |
321 | ||
322 | bool is_empty() const { | |
323 | return auth_stack.empty(); | |
324 | } | |
325 | ||
31f18b77 FG |
326 | static int apply(const Strategy& auth_strategy, req_state* s) noexcept; |
327 | ||
7c673cae FG |
328 | private: |
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>, | |
332 | Control>; | |
333 | std::vector<stack_item_t> auth_stack; | |
334 | ||
335 | protected: | |
336 | void add_engine(Control ctrl_flag, const Engine& engine) noexcept; | |
337 | }; | |
338 | ||
339 | ||
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, | |
343 | * | |
344 | * Each new Strategy should be exposed to it. */ | |
345 | class StrategyRegistry; | |
346 | ||
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. | |
350 | * | |
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 { | |
355 | public: | |
356 | class AuthInfo { | |
357 | friend class RemoteApplier; | |
358 | protected: | |
359 | const rgw_user acct_user; | |
360 | const std::string acct_name; | |
361 | const uint32_t perm_mask; | |
362 | const bool is_admin; | |
363 | const uint32_t acct_type; | |
364 | ||
365 | public: | |
366 | enum class acct_privilege_t { | |
367 | IS_ADMIN_ACCT, | |
368 | IS_PLAIN_ACCT | |
369 | }; | |
370 | ||
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) { | |
381 | } | |
382 | }; | |
383 | ||
384 | using aclspec_t = rgw::auth::Identity::aclspec_t; | |
385 | typedef std::function<uint32_t(const aclspec_t&)> acl_strategy_t; | |
386 | ||
387 | protected: | |
388 | CephContext* const cct; | |
389 | ||
390 | /* Read-write is intensional here due to RGWUserInfo creation process. */ | |
391 | RGWRados* const store; | |
392 | ||
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; | |
397 | ||
398 | const AuthInfo info; | |
399 | const bool implicit_tenants; | |
400 | ||
401 | virtual void create_account(const rgw_user& acct_user, | |
402 | RGWUserInfo& user_info) const; /* out */ | |
403 | ||
404 | public: | |
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) | |
410 | : cct(cct), | |
411 | store(store), | |
412 | extra_acl_strategy(std::move(extra_acl_strategy)), | |
413 | info(info), | |
414 | implicit_tenants(implicit_tenants) { | |
415 | } | |
416 | ||
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; | |
31f18b77 FG |
420 | bool is_identity(const idset_t& ids) const override; |
421 | ||
7c673cae FG |
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 */ | |
425 | ||
426 | struct Factory { | |
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, | |
432 | const req_state* s, | |
433 | acl_strategy_t&& extra_acl_strategy, | |
434 | const AuthInfo info) const = 0; | |
435 | }; | |
436 | }; | |
437 | ||
438 | ||
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; | |
445 | ||
446 | protected: | |
447 | const RGWUserInfo user_info; | |
448 | const std::string subuser; | |
449 | ||
450 | uint32_t get_perm_mask(const std::string& subuser_name, | |
451 | const RGWUserInfo &uinfo) const; | |
452 | ||
453 | public: | |
454 | static const std::string NO_SUBUSER; | |
455 | ||
456 | LocalApplier(CephContext* const cct, | |
457 | const RGWUserInfo& user_info, | |
458 | std::string subuser) | |
459 | : user_info(user_info), | |
460 | subuser(std::move(subuser)) { | |
461 | } | |
462 | ||
463 | ||
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; | |
31f18b77 | 467 | bool is_identity(const idset_t& ids) const override; |
7c673cae FG |
468 | uint32_t get_perm_mask() const override { |
469 | return get_perm_mask(subuser, user_info); | |
470 | } | |
471 | void to_str(std::ostream& out) const override; | |
472 | void load_acct_info(RGWUserInfo& user_info) const override; /* out */ | |
473 | ||
474 | struct Factory { | |
475 | virtual ~Factory() {} | |
476 | virtual aplptr_t create_apl_local(CephContext* cct, | |
477 | const req_state* s, | |
478 | const RGWUserInfo& user_info, | |
479 | const std::string& subuser) const = 0; | |
480 | }; | |
481 | }; | |
482 | ||
483 | ||
484 | /* The anonymous abstract engine. */ | |
485 | class AnonymousEngine : public Engine { | |
486 | CephContext* const cct; | |
487 | const rgw::auth::LocalApplier::Factory* const apl_factory; | |
488 | ||
489 | public: | |
490 | AnonymousEngine(CephContext* const cct, | |
491 | const rgw::auth::LocalApplier::Factory* const apl_factory) | |
492 | : cct(cct), | |
493 | apl_factory(apl_factory) { | |
494 | } | |
495 | ||
496 | const char* get_name() const noexcept override { | |
497 | return "rgw::auth::AnonymousEngine"; | |
498 | } | |
499 | ||
500 | Engine::result_t authenticate(const req_state* s) const override final; | |
501 | ||
502 | protected: | |
503 | virtual bool is_applicable(const req_state*) const noexcept { | |
504 | return true; | |
505 | } | |
506 | }; | |
507 | ||
508 | } /* namespace auth */ | |
509 | } /* namespace rgw */ | |
510 | ||
511 | ||
512 | uint32_t rgw_perms_from_aclspec_default_strategy( | |
513 | const rgw_user& uid, | |
514 | const rgw::auth::Identity::aclspec_t& aclspec); | |
515 | ||
516 | #endif /* CEPH_RGW_AUTH_H */ |