]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
7c673cae FG |
3 | |
4 | #include <array> | |
f91f0fd5 | 5 | #include <string> |
7c673cae FG |
6 | |
7 | #include "rgw_common.h" | |
8 | #include "rgw_auth.h" | |
f64942e4 | 9 | #include "rgw_quota.h" |
7c673cae FG |
10 | #include "rgw_user.h" |
11 | #include "rgw_http_client.h" | |
12 | #include "rgw_keystone.h" | |
9f95a23c | 13 | #include "rgw_sal.h" |
2a845540 | 14 | #include "rgw_log.h" |
7c673cae FG |
15 | |
16 | #include "include/str_list.h" | |
17 | ||
18 | #define dout_context g_ceph_context | |
19 | #define dout_subsys ceph_subsys_rgw | |
20 | ||
20effc67 | 21 | using namespace std; |
7c673cae FG |
22 | |
23 | namespace rgw { | |
24 | namespace auth { | |
25 | ||
26 | std::unique_ptr<rgw::auth::Identity> | |
9f95a23c TL |
27 | transform_old_authinfo(CephContext* const cct, |
28 | const rgw_user& auth_id, | |
29 | const int perm_mask, | |
30 | const bool is_admin, | |
31 | const uint32_t type) | |
7c673cae FG |
32 | { |
33 | /* This class is not intended for public use. Should be removed altogether | |
34 | * with this function after moving all our APIs to the new authentication | |
35 | * infrastructure. */ | |
36 | class DummyIdentityApplier : public rgw::auth::Identity { | |
37 | CephContext* const cct; | |
38 | ||
39 | /* For this particular case it's OK to use rgw_user structure to convey | |
40 | * the identity info as this was the policy for doing that before the | |
41 | * new auth. */ | |
42 | const rgw_user id; | |
43 | const int perm_mask; | |
44 | const bool is_admin; | |
11fdf7f2 | 45 | const uint32_t type; |
7c673cae FG |
46 | public: |
47 | DummyIdentityApplier(CephContext* const cct, | |
48 | const rgw_user& auth_id, | |
49 | const int perm_mask, | |
11fdf7f2 TL |
50 | const bool is_admin, |
51 | const uint32_t type) | |
7c673cae FG |
52 | : cct(cct), |
53 | id(auth_id), | |
54 | perm_mask(perm_mask), | |
11fdf7f2 TL |
55 | is_admin(is_admin), |
56 | type(type) { | |
7c673cae FG |
57 | } |
58 | ||
11fdf7f2 | 59 | uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override { |
b3b6e05e | 60 | return rgw_perms_from_aclspec_default_strategy(id, aclspec, dpp); |
7c673cae FG |
61 | } |
62 | ||
63 | bool is_admin_of(const rgw_user& acct_id) const override { | |
64 | return is_admin; | |
65 | } | |
66 | ||
67 | bool is_owner_of(const rgw_user& acct_id) const override { | |
68 | return id == acct_id; | |
69 | } | |
70 | ||
31f18b77 FG |
71 | bool is_identity(const idset_t& ids) const override { |
72 | for (auto& p : ids) { | |
73 | if (p.is_wildcard()) { | |
74 | return true; | |
75 | } else if (p.is_tenant() && p.get_tenant() == id.tenant) { | |
76 | return true; | |
77 | } else if (p.is_user() && | |
78 | (p.get_tenant() == id.tenant) && | |
79 | (p.get_id() == id.id)) { | |
80 | return true; | |
81 | } | |
82 | } | |
83 | return false; | |
84 | } | |
85 | ||
7c673cae FG |
86 | uint32_t get_perm_mask() const override { |
87 | return perm_mask; | |
88 | } | |
89 | ||
11fdf7f2 TL |
90 | uint32_t get_identity_type() const override { |
91 | return type; | |
92 | } | |
93 | ||
94 | string get_acct_name() const override { | |
95 | return {}; | |
96 | } | |
97 | ||
f6b5b4d7 TL |
98 | string get_subuser() const override { |
99 | return {}; | |
100 | } | |
101 | ||
7c673cae FG |
102 | void to_str(std::ostream& out) const override { |
103 | out << "RGWDummyIdentityApplier(auth_id=" << id | |
104 | << ", perm_mask=" << perm_mask | |
105 | << ", is_admin=" << is_admin << ")"; | |
106 | } | |
107 | }; | |
108 | ||
109 | return std::unique_ptr<rgw::auth::Identity>( | |
9f95a23c TL |
110 | new DummyIdentityApplier(cct, |
111 | auth_id, | |
112 | perm_mask, | |
113 | is_admin, | |
114 | type)); | |
115 | } | |
116 | ||
117 | std::unique_ptr<rgw::auth::Identity> | |
118 | transform_old_authinfo(const req_state* const s) | |
119 | { | |
120 | return transform_old_authinfo(s->cct, | |
121 | s->user->get_id(), | |
122 | s->perm_mask, | |
7c673cae FG |
123 | /* System user has admin permissions by default - it's supposed to pass |
124 | * through any security check. */ | |
9f95a23c TL |
125 | s->system_request, |
126 | s->user->get_type()); | |
7c673cae FG |
127 | } |
128 | ||
129 | } /* namespace auth */ | |
130 | } /* namespace rgw */ | |
131 | ||
132 | ||
133 | uint32_t rgw_perms_from_aclspec_default_strategy( | |
134 | const rgw_user& uid, | |
b3b6e05e TL |
135 | const rgw::auth::Identity::aclspec_t& aclspec, |
136 | const DoutPrefixProvider *dpp) | |
7c673cae | 137 | { |
b3b6e05e | 138 | ldpp_dout(dpp, 5) << "Searching permissions for uid=" << uid << dendl; |
7c673cae FG |
139 | |
140 | const auto iter = aclspec.find(uid.to_str()); | |
141 | if (std::end(aclspec) != iter) { | |
b3b6e05e | 142 | ldpp_dout(dpp, 5) << "Found permission: " << iter->second << dendl; |
7c673cae FG |
143 | return iter->second; |
144 | } | |
145 | ||
b3b6e05e | 146 | ldpp_dout(dpp, 5) << "Permissions for user not found" << dendl; |
7c673cae FG |
147 | return 0; |
148 | } | |
149 | ||
150 | ||
151 | static inline const std::string make_spec_item(const std::string& tenant, | |
152 | const std::string& id) | |
153 | { | |
154 | return tenant + ":" + id; | |
155 | } | |
156 | ||
157 | ||
158 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
159 | strategy_handle_rejected(rgw::auth::Engine::result_t&& engine_result, | |
160 | const rgw::auth::Strategy::Control policy, | |
161 | rgw::auth::Engine::result_t&& strategy_result) | |
162 | { | |
163 | using Control = rgw::auth::Strategy::Control; | |
164 | switch (policy) { | |
165 | case Control::REQUISITE: | |
166 | /* Don't try next. */ | |
167 | return std::make_pair(false, std::move(engine_result)); | |
168 | ||
169 | case Control::SUFFICIENT: | |
170 | /* Don't try next. */ | |
171 | return std::make_pair(false, std::move(engine_result)); | |
172 | ||
173 | case Control::FALLBACK: | |
174 | /* Don't try next. */ | |
175 | return std::make_pair(false, std::move(strategy_result)); | |
176 | ||
177 | default: | |
178 | /* Huh, memory corruption? */ | |
11fdf7f2 | 179 | ceph_abort(); |
7c673cae FG |
180 | } |
181 | } | |
182 | ||
183 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
184 | strategy_handle_denied(rgw::auth::Engine::result_t&& engine_result, | |
185 | const rgw::auth::Strategy::Control policy, | |
186 | rgw::auth::Engine::result_t&& strategy_result) | |
187 | { | |
188 | using Control = rgw::auth::Strategy::Control; | |
189 | switch (policy) { | |
190 | case Control::REQUISITE: | |
191 | /* Don't try next. */ | |
192 | return std::make_pair(false, std::move(engine_result)); | |
193 | ||
194 | case Control::SUFFICIENT: | |
195 | /* Just try next. */ | |
196 | return std::make_pair(true, std::move(engine_result)); | |
197 | ||
198 | case Control::FALLBACK: | |
199 | return std::make_pair(true, std::move(strategy_result)); | |
200 | ||
201 | default: | |
202 | /* Huh, memory corruption? */ | |
11fdf7f2 | 203 | ceph_abort(); |
7c673cae FG |
204 | } |
205 | } | |
206 | ||
207 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
208 | strategy_handle_granted(rgw::auth::Engine::result_t&& engine_result, | |
209 | const rgw::auth::Strategy::Control policy, | |
210 | rgw::auth::Engine::result_t&& strategy_result) | |
211 | { | |
212 | using Control = rgw::auth::Strategy::Control; | |
213 | switch (policy) { | |
214 | case Control::REQUISITE: | |
215 | /* Try next. */ | |
216 | return std::make_pair(true, std::move(engine_result)); | |
217 | ||
218 | case Control::SUFFICIENT: | |
219 | /* Don't try next. */ | |
220 | return std::make_pair(false, std::move(engine_result)); | |
221 | ||
222 | case Control::FALLBACK: | |
223 | /* Don't try next. */ | |
224 | return std::make_pair(false, std::move(engine_result)); | |
225 | ||
226 | default: | |
227 | /* Huh, memory corruption? */ | |
11fdf7f2 | 228 | ceph_abort(); |
7c673cae FG |
229 | } |
230 | } | |
231 | ||
232 | rgw::auth::Engine::result_t | |
f67539c2 | 233 | rgw::auth::Strategy::authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const |
7c673cae FG |
234 | { |
235 | result_t strategy_result = result_t::deny(); | |
236 | ||
237 | for (const stack_item_t& kv : auth_stack) { | |
238 | const rgw::auth::Engine& engine = kv.first; | |
239 | const auto& policy = kv.second; | |
240 | ||
11fdf7f2 | 241 | ldpp_dout(dpp, 20) << get_name() << ": trying " << engine.get_name() << dendl; |
7c673cae FG |
242 | |
243 | result_t engine_result = result_t::deny(); | |
244 | try { | |
f67539c2 | 245 | engine_result = engine.authenticate(dpp, s, y); |
7c673cae FG |
246 | } catch (const int err) { |
247 | engine_result = result_t::deny(err); | |
248 | } | |
249 | ||
250 | bool try_next = true; | |
251 | switch (engine_result.get_status()) { | |
252 | case result_t::Status::REJECTED: { | |
11fdf7f2 | 253 | ldpp_dout(dpp, 20) << engine.get_name() << " rejected with reason=" |
7c673cae FG |
254 | << engine_result.get_reason() << dendl; |
255 | ||
256 | std::tie(try_next, strategy_result) = \ | |
257 | strategy_handle_rejected(std::move(engine_result), policy, | |
258 | std::move(strategy_result)); | |
259 | break; | |
260 | } | |
261 | case result_t::Status::DENIED: { | |
11fdf7f2 | 262 | ldpp_dout(dpp, 20) << engine.get_name() << " denied with reason=" |
7c673cae FG |
263 | << engine_result.get_reason() << dendl; |
264 | ||
265 | std::tie(try_next, strategy_result) = \ | |
266 | strategy_handle_denied(std::move(engine_result), policy, | |
267 | std::move(strategy_result)); | |
268 | break; | |
269 | } | |
270 | case result_t::Status::GRANTED: { | |
11fdf7f2 | 271 | ldpp_dout(dpp, 20) << engine.get_name() << " granted access" << dendl; |
7c673cae FG |
272 | |
273 | std::tie(try_next, strategy_result) = \ | |
274 | strategy_handle_granted(std::move(engine_result), policy, | |
275 | std::move(strategy_result)); | |
276 | break; | |
277 | } | |
278 | default: { | |
11fdf7f2 | 279 | ceph_abort(); |
7c673cae FG |
280 | } |
281 | } | |
282 | ||
283 | if (! try_next) { | |
284 | break; | |
285 | } | |
286 | } | |
287 | ||
288 | return strategy_result; | |
289 | } | |
290 | ||
31f18b77 | 291 | int |
11fdf7f2 | 292 | rgw::auth::Strategy::apply(const DoutPrefixProvider *dpp, const rgw::auth::Strategy& auth_strategy, |
f67539c2 | 293 | req_state* const s, optional_yield y) noexcept |
31f18b77 FG |
294 | { |
295 | try { | |
f67539c2 | 296 | auto result = auth_strategy.authenticate(dpp, s, y); |
31f18b77 FG |
297 | if (result.get_status() != decltype(result)::Status::GRANTED) { |
298 | /* Access denied is acknowledged by returning a std::unique_ptr with | |
299 | * nullptr inside. */ | |
11fdf7f2 | 300 | ldpp_dout(dpp, 5) << "Failed the auth strategy, reason=" |
31f18b77 FG |
301 | << result.get_reason() << dendl; |
302 | return result.get_reason(); | |
303 | } | |
304 | ||
305 | try { | |
306 | rgw::auth::IdentityApplier::aplptr_t applier = result.get_applier(); | |
307 | rgw::auth::Completer::cmplptr_t completer = result.get_completer(); | |
308 | ||
309 | /* Account used by a given RGWOp is decoupled from identity employed | |
310 | * in the authorization phase (RGWOp::verify_permissions). */ | |
9f95a23c | 311 | applier->load_acct_info(dpp, s->user->get_info()); |
31f18b77 FG |
312 | s->perm_mask = applier->get_perm_mask(); |
313 | ||
11fdf7f2 | 314 | /* This is the single place where we pass req_state as a pointer |
31f18b77 FG |
315 | * to non-const and thus its modification is allowed. In the time |
316 | * of writing only RGWTempURLEngine needed that feature. */ | |
11fdf7f2 | 317 | applier->modify_request_state(dpp, s); |
31f18b77 | 318 | if (completer) { |
11fdf7f2 | 319 | completer->modify_request_state(dpp, s); |
31f18b77 FG |
320 | } |
321 | ||
322 | s->auth.identity = std::move(applier); | |
323 | s->auth.completer = std::move(completer); | |
324 | ||
325 | return 0; | |
326 | } catch (const int err) { | |
11fdf7f2 | 327 | ldpp_dout(dpp, 5) << "applier throwed err=" << err << dendl; |
31f18b77 | 328 | return err; |
f67539c2 TL |
329 | } catch (const std::exception& e) { |
330 | ldpp_dout(dpp, 5) << "applier throwed unexpected err: " << e.what() | |
331 | << dendl; | |
332 | return -EPERM; | |
31f18b77 FG |
333 | } |
334 | } catch (const int err) { | |
11fdf7f2 | 335 | ldpp_dout(dpp, 5) << "auth engine throwed err=" << err << dendl; |
31f18b77 | 336 | return err; |
f67539c2 TL |
337 | } catch (const std::exception& e) { |
338 | ldpp_dout(dpp, 5) << "auth engine throwed unexpected err: " << e.what() | |
339 | << dendl; | |
31f18b77 FG |
340 | } |
341 | ||
342 | /* We never should be here. */ | |
343 | return -EPERM; | |
344 | } | |
345 | ||
7c673cae FG |
346 | void |
347 | rgw::auth::Strategy::add_engine(const Control ctrl_flag, | |
348 | const Engine& engine) noexcept | |
349 | { | |
350 | auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag)); | |
351 | } | |
352 | ||
11fdf7f2 TL |
353 | void rgw::auth::WebIdentityApplier::to_str(std::ostream& out) const |
354 | { | |
20effc67 TL |
355 | out << "rgw::auth::WebIdentityApplier(sub =" << sub |
356 | << ", user_name=" << user_name | |
357 | << ", provider_id =" << iss << ")"; | |
11fdf7f2 TL |
358 | } |
359 | ||
360 | string rgw::auth::WebIdentityApplier::get_idp_url() const | |
361 | { | |
20effc67 | 362 | string idp_url = this->iss; |
f91f0fd5 | 363 | idp_url = url_remove_prefix(idp_url); |
11fdf7f2 TL |
364 | return idp_url; |
365 | } | |
366 | ||
f67539c2 TL |
367 | void rgw::auth::WebIdentityApplier::create_account(const DoutPrefixProvider* dpp, |
368 | const rgw_user& acct_user, | |
369 | const string& display_name, | |
370 | RGWUserInfo& user_info) const /* out */ | |
371 | { | |
1e59de90 | 372 | std::unique_ptr<rgw::sal::User> user = driver->get_user(acct_user); |
20effc67 TL |
373 | user->get_info().display_name = display_name; |
374 | user->get_info().type = TYPE_WEB; | |
375 | user->get_info().max_buckets = | |
f67539c2 | 376 | cct->_conf.get_val<int64_t>("rgw_user_max_buckets"); |
1e59de90 TL |
377 | rgw_apply_default_bucket_quota(user->get_info().quota.bucket_quota, cct->_conf); |
378 | rgw_apply_default_user_quota(user->get_info().quota.user_quota, cct->_conf); | |
f67539c2 | 379 | |
20effc67 | 380 | int ret = user->store_user(dpp, null_yield, true); |
f67539c2 TL |
381 | if (ret < 0) { |
382 | ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user=" | |
20effc67 | 383 | << user << " ret=" << ret << dendl; |
f67539c2 TL |
384 | throw ret; |
385 | } | |
20effc67 | 386 | user_info = user->get_info(); |
f67539c2 TL |
387 | } |
388 | ||
389 | void rgw::auth::WebIdentityApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const { | |
390 | rgw_user federated_user; | |
20effc67 | 391 | federated_user.id = this->sub; |
f67539c2 TL |
392 | federated_user.tenant = role_tenant; |
393 | federated_user.ns = "oidc"; | |
394 | ||
1e59de90 | 395 | std::unique_ptr<rgw::sal::User> user = driver->get_user(federated_user); |
20effc67 | 396 | |
f67539c2 | 397 | //Check in oidc namespace |
20effc67 | 398 | if (user->load_user(dpp, null_yield) >= 0) { |
f67539c2 | 399 | /* Succeeded. */ |
20effc67 | 400 | user_info = user->get_info(); |
f67539c2 TL |
401 | return; |
402 | } | |
403 | ||
20effc67 | 404 | user->clear_ns(); |
f67539c2 | 405 | //Check for old users which wouldn't have been created in oidc namespace |
20effc67 | 406 | if (user->load_user(dpp, null_yield) >= 0) { |
f67539c2 | 407 | /* Succeeded. */ |
20effc67 | 408 | user_info = user->get_info(); |
f67539c2 TL |
409 | return; |
410 | } | |
411 | ||
412 | //Check if user_id.buckets already exists, may have been from the time, when shadow users didnt exist | |
413 | RGWStorageStats stats; | |
20effc67 | 414 | int ret = user->read_stats(dpp, null_yield, &stats); |
f67539c2 TL |
415 | if (ret < 0 && ret != -ENOENT) { |
416 | ldpp_dout(dpp, 0) << "ERROR: reading stats for the user returned error " << ret << dendl; | |
417 | return; | |
418 | } | |
419 | if (ret == -ENOENT) { /* in case of ENOENT, which means user doesnt have buckets */ | |
420 | //In this case user will be created in oidc namespace | |
421 | ldpp_dout(dpp, 5) << "NOTICE: incoming user has no buckets " << federated_user << dendl; | |
422 | federated_user.ns = "oidc"; | |
423 | } else { | |
424 | //User already has buckets associated, hence wont be created in oidc namespace. | |
425 | ldpp_dout(dpp, 5) << "NOTICE: incoming user already has buckets associated " << federated_user << ", won't be created in oidc namespace"<< dendl; | |
426 | federated_user.ns = ""; | |
427 | } | |
428 | ||
429 | ldpp_dout(dpp, 0) << "NOTICE: couldn't map oidc federated user " << federated_user << dendl; | |
20effc67 | 430 | create_account(dpp, federated_user, this->user_name, user_info); |
f67539c2 TL |
431 | } |
432 | ||
11fdf7f2 TL |
433 | void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const |
434 | { | |
20effc67 TL |
435 | s->info.args.append("sub", this->sub); |
436 | s->info.args.append("aud", this->aud); | |
437 | s->info.args.append("provider_id", this->iss); | |
438 | s->info.args.append("client_id", this->client_id); | |
11fdf7f2 | 439 | |
20effc67 | 440 | string condition; |
11fdf7f2 | 441 | string idp_url = get_idp_url(); |
20effc67 TL |
442 | for (auto& claim : token_claims) { |
443 | if (claim.first == "aud") { | |
444 | condition.clear(); | |
445 | condition = idp_url + ":app_id"; | |
446 | s->env.emplace(condition, claim.second); | |
447 | } | |
448 | condition.clear(); | |
449 | condition = idp_url + ":" + claim.first; | |
450 | s->env.emplace(condition, claim.second); | |
451 | } | |
452 | ||
453 | if (principal_tags) { | |
454 | constexpr size_t KEY_SIZE = 128, VAL_SIZE = 256; | |
455 | std::set<std::pair<string, string>> p_tags = principal_tags.get(); | |
456 | for (auto& it : p_tags) { | |
457 | string key = it.first; | |
458 | string val = it.second; | |
459 | if (key.find("aws:") == 0 || val.find("aws:") == 0) { | |
460 | ldpp_dout(dpp, 0) << "ERROR: Tag/Value can't start with aws:, hence skipping it" << dendl; | |
461 | continue; | |
462 | } | |
463 | if (key.size() > KEY_SIZE || val.size() > VAL_SIZE) { | |
464 | ldpp_dout(dpp, 0) << "ERROR: Invalid tag/value size, hence skipping it" << dendl; | |
465 | continue; | |
466 | } | |
467 | std::string p_key = "aws:PrincipalTag/"; | |
468 | p_key.append(key); | |
469 | s->principal_tags.emplace_back(std::make_pair(p_key, val)); | |
470 | ldpp_dout(dpp, 10) << "Principal Tag Key: " << p_key << " Value: " << val << dendl; | |
471 | ||
472 | std::string e_key = "aws:RequestTag/"; | |
473 | e_key.append(key); | |
474 | s->env.emplace(e_key, val); | |
475 | ldpp_dout(dpp, 10) << "RGW Env Tag Key: " << e_key << " Value: " << val << dendl; | |
f91f0fd5 | 476 | |
20effc67 TL |
477 | s->env.emplace("aws:TagKeys", key); |
478 | ldpp_dout(dpp, 10) << "aws:TagKeys: " << key << dendl; | |
f91f0fd5 | 479 | |
20effc67 TL |
480 | if (s->principal_tags.size() == 50) { |
481 | ldpp_dout(dpp, 0) << "ERROR: Number of tag/value pairs exceeding 50, hence skipping the rest" << dendl; | |
482 | break; | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | if (role_tags) { | |
488 | for (auto& it : role_tags.get()) { | |
489 | std::string p_key = "aws:PrincipalTag/"; | |
490 | p_key.append(it.first); | |
491 | s->principal_tags.emplace_back(std::make_pair(p_key, it.second)); | |
492 | ldpp_dout(dpp, 10) << "Principal Tag Key: " << p_key << " Value: " << it.second << dendl; | |
493 | ||
494 | std::string e_key = "iam:ResourceTag/"; | |
495 | e_key.append(it.first); | |
496 | s->env.emplace(e_key, it.second); | |
497 | ldpp_dout(dpp, 10) << "RGW Env Tag Key: " << e_key << " Value: " << it.second << dendl; | |
498 | } | |
499 | } | |
11fdf7f2 TL |
500 | } |
501 | ||
502 | bool rgw::auth::WebIdentityApplier::is_identity(const idset_t& ids) const | |
503 | { | |
504 | if (ids.size() > 1) { | |
505 | return false; | |
506 | } | |
507 | ||
508 | for (auto id : ids) { | |
509 | string idp_url = get_idp_url(); | |
510 | if (id.is_oidc_provider() && id.get_idp_url() == idp_url) { | |
511 | return true; | |
512 | } | |
513 | } | |
514 | return false; | |
515 | } | |
7c673cae | 516 | |
2a845540 TL |
517 | const std::string rgw::auth::RemoteApplier::AuthInfo::NO_SUBUSER; |
518 | const std::string rgw::auth::RemoteApplier::AuthInfo::NO_ACCESS_KEY; | |
519 | ||
7c673cae | 520 | /* rgw::auth::RemoteAuthApplier */ |
11fdf7f2 | 521 | uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const |
7c673cae FG |
522 | { |
523 | uint32_t perm = 0; | |
524 | ||
525 | /* For backward compatibility with ACLOwner. */ | |
526 | perm |= rgw_perms_from_aclspec_default_strategy(info.acct_user, | |
b3b6e05e | 527 | aclspec, dpp); |
7c673cae FG |
528 | |
529 | /* We also need to cover cases where rgw_keystone_implicit_tenants | |
530 | * was enabled. */ | |
531 | if (info.acct_user.tenant.empty()) { | |
532 | const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); | |
533 | ||
534 | perm |= rgw_perms_from_aclspec_default_strategy(tenanted_acct_user, | |
b3b6e05e | 535 | aclspec, dpp); |
7c673cae FG |
536 | } |
537 | ||
538 | /* Now it's a time for invoking additional strategy that was supplied by | |
539 | * a specific auth engine. */ | |
540 | if (extra_acl_strategy) { | |
541 | perm |= extra_acl_strategy(aclspec); | |
542 | } | |
543 | ||
11fdf7f2 | 544 | ldpp_dout(dpp, 20) << "from ACL got perm=" << perm << dendl; |
7c673cae FG |
545 | return perm; |
546 | } | |
547 | ||
548 | bool rgw::auth::RemoteApplier::is_admin_of(const rgw_user& uid) const | |
549 | { | |
550 | return info.is_admin; | |
551 | } | |
552 | ||
553 | bool rgw::auth::RemoteApplier::is_owner_of(const rgw_user& uid) const | |
554 | { | |
555 | if (info.acct_user.tenant.empty()) { | |
556 | const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); | |
557 | ||
558 | if (tenanted_acct_user == uid) { | |
559 | return true; | |
560 | } | |
561 | } | |
562 | ||
563 | return info.acct_user == uid; | |
564 | } | |
565 | ||
31f18b77 FG |
566 | bool rgw::auth::RemoteApplier::is_identity(const idset_t& ids) const { |
567 | for (auto& id : ids) { | |
568 | if (id.is_wildcard()) { | |
569 | return true; | |
570 | ||
571 | // We also need to cover cases where rgw_keystone_implicit_tenants | |
572 | // was enabled. */ | |
573 | } else if (id.is_tenant() && | |
574 | (info.acct_user.tenant.empty() ? | |
575 | info.acct_user.id : | |
576 | info.acct_user.tenant) == id.get_tenant()) { | |
577 | return true; | |
578 | } else if (id.is_user() && | |
579 | info.acct_user.id == id.get_id() && | |
580 | (info.acct_user.tenant.empty() ? | |
581 | info.acct_user.id : | |
582 | info.acct_user.tenant) == id.get_tenant()) { | |
583 | return true; | |
584 | } | |
585 | } | |
586 | return false; | |
587 | } | |
588 | ||
7c673cae FG |
589 | void rgw::auth::RemoteApplier::to_str(std::ostream& out) const |
590 | { | |
591 | out << "rgw::auth::RemoteApplier(acct_user=" << info.acct_user | |
592 | << ", acct_name=" << info.acct_name | |
593 | << ", perm_mask=" << info.perm_mask | |
594 | << ", is_admin=" << info.is_admin << ")"; | |
595 | } | |
596 | ||
9f95a23c TL |
597 | void rgw::auth::ImplicitTenants::recompute_value(const ConfigProxy& c) |
598 | { | |
599 | std::string s = c.get_val<std::string>("rgw_keystone_implicit_tenants"); | |
600 | int v = 0; | |
601 | if (boost::iequals(s, "both") | |
602 | || boost::iequals(s, "true") | |
603 | || boost::iequals(s, "1")) { | |
604 | v = IMPLICIT_TENANTS_S3|IMPLICIT_TENANTS_SWIFT; | |
605 | } else if (boost::iequals(s, "0") | |
606 | || boost::iequals(s, "none") | |
607 | || boost::iequals(s, "false")) { | |
608 | v = 0; | |
609 | } else if (boost::iequals(s, "s3")) { | |
610 | v = IMPLICIT_TENANTS_S3; | |
611 | } else if (boost::iequals(s, "swift")) { | |
612 | v = IMPLICIT_TENANTS_SWIFT; | |
613 | } else { /* "" (and anything else) */ | |
614 | v = IMPLICIT_TENANTS_BAD; | |
615 | // assert(0); | |
616 | } | |
617 | saved = v; | |
618 | } | |
619 | ||
620 | const char **rgw::auth::ImplicitTenants::get_tracked_conf_keys() const | |
621 | { | |
622 | static const char *keys[] = { | |
623 | "rgw_keystone_implicit_tenants", | |
624 | nullptr }; | |
625 | return keys; | |
626 | } | |
627 | ||
628 | void rgw::auth::ImplicitTenants::handle_conf_change(const ConfigProxy& c, | |
629 | const std::set <std::string> &changed) | |
630 | { | |
631 | if (changed.count("rgw_keystone_implicit_tenants")) { | |
632 | recompute_value(c); | |
633 | } | |
634 | } | |
635 | ||
11fdf7f2 TL |
636 | void rgw::auth::RemoteApplier::create_account(const DoutPrefixProvider* dpp, |
637 | const rgw_user& acct_user, | |
9f95a23c | 638 | bool implicit_tenant, |
7c673cae FG |
639 | RGWUserInfo& user_info) const /* out */ |
640 | { | |
641 | rgw_user new_acct_user = acct_user; | |
642 | ||
7c673cae FG |
643 | /* An upper layer may enforce creating new accounts within their own |
644 | * tenants. */ | |
9f95a23c | 645 | if (new_acct_user.tenant.empty() && implicit_tenant) { |
7c673cae FG |
646 | new_acct_user.tenant = new_acct_user.id; |
647 | } | |
648 | ||
1e59de90 | 649 | std::unique_ptr<rgw::sal::User> user = driver->get_user(new_acct_user); |
20effc67 TL |
650 | user->get_info().display_name = info.acct_name; |
651 | if (info.acct_type) { | |
652 | //ldap/keystone for s3 users | |
653 | user->get_info().type = info.acct_type; | |
654 | } | |
655 | user->get_info().max_buckets = | |
f6b5b4d7 | 656 | cct->_conf.get_val<int64_t>("rgw_user_max_buckets"); |
1e59de90 TL |
657 | rgw_apply_default_bucket_quota(user->get_info().quota.bucket_quota, cct->_conf); |
658 | rgw_apply_default_user_quota(user->get_info().quota.user_quota, cct->_conf); | |
20effc67 | 659 | user_info = user->get_info(); |
f64942e4 | 660 | |
20effc67 | 661 | int ret = user->store_user(dpp, null_yield, true); |
7c673cae | 662 | if (ret < 0) { |
11fdf7f2 | 663 | ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user=" |
20effc67 | 664 | << user << " ret=" << ret << dendl; |
7c673cae FG |
665 | throw ret; |
666 | } | |
667 | } | |
668 | ||
2a845540 TL |
669 | void rgw::auth::RemoteApplier::write_ops_log_entry(rgw_log_entry& entry) const |
670 | { | |
671 | entry.access_key_id = info.access_key_id; | |
672 | entry.subuser = info.subuser; | |
673 | } | |
674 | ||
7c673cae | 675 | /* TODO(rzarzynski): we need to handle display_name changes. */ |
11fdf7f2 | 676 | void rgw::auth::RemoteApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ |
7c673cae FG |
677 | { |
678 | /* It's supposed that RGWRemoteAuthApplier tries to load account info | |
679 | * that belongs to the authenticated identity. Another policy may be | |
680 | * applied by using a RGWThirdPartyAccountAuthApplier decorator. */ | |
681 | const rgw_user& acct_user = info.acct_user; | |
9f95a23c TL |
682 | auto implicit_value = implicit_tenant_context.get_value(); |
683 | bool implicit_tenant = implicit_value.implicit_tenants_for_(implicit_tenant_bit); | |
684 | bool split_mode = implicit_value.is_split_mode(); | |
20effc67 | 685 | std::unique_ptr<rgw::sal::User> user; |
7c673cae FG |
686 | |
687 | /* Normally, empty "tenant" field of acct_user means the authenticated | |
688 | * identity has the legacy, global tenant. However, due to inclusion | |
689 | * of multi-tenancy, we got some special compatibility kludge for remote | |
690 | * backends like Keystone. | |
691 | * If the global tenant is the requested one, we try the same tenant as | |
692 | * the user name first. If that RGWUserInfo exists, we use it. This way, | |
693 | * migrated OpenStack users can get their namespaced containers and nobody's | |
694 | * the wiser. | |
695 | * If that fails, we look up in the requested (possibly empty) tenant. | |
696 | * If that fails too, we create the account within the global or separated | |
9f95a23c TL |
697 | * namespace depending on rgw_keystone_implicit_tenants. |
698 | * For compatibility with previous versions of ceph, it is possible | |
699 | * to enable implicit_tenants for only s3 or only swift. | |
700 | * in this mode ("split_mode"), we must constrain the id lookups to | |
701 | * only use the identifier space that would be used if the id were | |
702 | * to be created. */ | |
703 | ||
704 | if (split_mode && !implicit_tenant) | |
705 | ; /* suppress lookup for id used by "other" protocol */ | |
706 | else if (acct_user.tenant.empty()) { | |
7c673cae | 707 | const rgw_user tenanted_uid(acct_user.id, acct_user.id); |
1e59de90 | 708 | user = driver->get_user(tenanted_uid); |
7c673cae | 709 | |
20effc67 | 710 | if (user->load_user(dpp, null_yield) >= 0) { |
7c673cae | 711 | /* Succeeded. */ |
20effc67 | 712 | user_info = user->get_info(); |
7c673cae FG |
713 | return; |
714 | } | |
715 | } | |
716 | ||
1e59de90 | 717 | user = driver->get_user(acct_user); |
20effc67 | 718 | |
9f95a23c TL |
719 | if (split_mode && implicit_tenant) |
720 | ; /* suppress lookup for id used by "other" protocol */ | |
20effc67 TL |
721 | else if (user->load_user(dpp, null_yield) >= 0) { |
722 | /* Succeeded. */ | |
723 | user_info = user->get_info(); | |
724 | return; | |
7c673cae FG |
725 | } |
726 | ||
9f95a23c TL |
727 | ldpp_dout(dpp, 0) << "NOTICE: couldn't map swift user " << acct_user << dendl; |
728 | create_account(dpp, acct_user, implicit_tenant, user_info); | |
729 | ||
7c673cae FG |
730 | /* Succeeded if we are here (create_account() hasn't throwed). */ |
731 | } | |
732 | ||
7c673cae FG |
733 | /* rgw::auth::LocalApplier */ |
734 | /* static declaration */ | |
735 | const std::string rgw::auth::LocalApplier::NO_SUBUSER; | |
2a845540 | 736 | const std::string rgw::auth::LocalApplier::NO_ACCESS_KEY; |
7c673cae | 737 | |
11fdf7f2 | 738 | uint32_t rgw::auth::LocalApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const |
7c673cae | 739 | { |
b3b6e05e | 740 | return rgw_perms_from_aclspec_default_strategy(user_info.user_id, aclspec, dpp); |
7c673cae FG |
741 | } |
742 | ||
743 | bool rgw::auth::LocalApplier::is_admin_of(const rgw_user& uid) const | |
744 | { | |
745 | return user_info.admin || user_info.system; | |
746 | } | |
747 | ||
748 | bool rgw::auth::LocalApplier::is_owner_of(const rgw_user& uid) const | |
749 | { | |
750 | return uid == user_info.user_id; | |
751 | } | |
752 | ||
31f18b77 FG |
753 | bool rgw::auth::LocalApplier::is_identity(const idset_t& ids) const { |
754 | for (auto& id : ids) { | |
755 | if (id.is_wildcard()) { | |
756 | return true; | |
757 | } else if (id.is_tenant() && | |
758 | id.get_tenant() == user_info.user_id.tenant) { | |
759 | return true; | |
760 | } else if (id.is_user() && | |
9f95a23c TL |
761 | (id.get_tenant() == user_info.user_id.tenant)) { |
762 | if (id.get_id() == user_info.user_id.id) { | |
763 | return true; | |
764 | } | |
f6b5b4d7 TL |
765 | std::string wildcard_subuser = user_info.user_id.id; |
766 | wildcard_subuser.append(":*"); | |
767 | if (wildcard_subuser == id.get_id()) { | |
768 | return true; | |
769 | } else if (subuser != NO_SUBUSER) { | |
9f95a23c TL |
770 | std::string user = user_info.user_id.id; |
771 | user.append(":"); | |
772 | user.append(subuser); | |
773 | if (user == id.get_id()) { | |
774 | return true; | |
775 | } | |
776 | } | |
31f18b77 FG |
777 | } |
778 | } | |
779 | return false; | |
780 | } | |
781 | ||
782 | void rgw::auth::LocalApplier::to_str(std::ostream& out) const { | |
7c673cae FG |
783 | out << "rgw::auth::LocalApplier(acct_user=" << user_info.user_id |
784 | << ", acct_name=" << user_info.display_name | |
785 | << ", subuser=" << subuser | |
786 | << ", perm_mask=" << get_perm_mask() | |
787 | << ", is_admin=" << static_cast<bool>(user_info.admin) << ")"; | |
788 | } | |
789 | ||
790 | uint32_t rgw::auth::LocalApplier::get_perm_mask(const std::string& subuser_name, | |
791 | const RGWUserInfo &uinfo) const | |
792 | { | |
793 | if (! subuser_name.empty() && subuser_name != NO_SUBUSER) { | |
794 | const auto iter = uinfo.subusers.find(subuser_name); | |
795 | ||
796 | if (iter != std::end(uinfo.subusers)) { | |
797 | return iter->second.perm_mask; | |
798 | } else { | |
799 | /* Subuser specified but not found. */ | |
800 | return RGW_PERM_NONE; | |
801 | } | |
802 | } else { | |
803 | /* Due to backward compatibility. */ | |
804 | return RGW_PERM_FULL_CONTROL; | |
805 | } | |
806 | } | |
807 | ||
11fdf7f2 | 808 | void rgw::auth::LocalApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ |
7c673cae FG |
809 | { |
810 | /* Load the account that belongs to the authenticated identity. An extra call | |
811 | * to RADOS may be safely skipped in this case. */ | |
812 | user_info = this->user_info; | |
813 | } | |
814 | ||
2a845540 TL |
815 | void rgw::auth::LocalApplier::write_ops_log_entry(rgw_log_entry& entry) const |
816 | { | |
817 | entry.access_key_id = access_key_id; | |
818 | entry.subuser = subuser; | |
819 | } | |
820 | ||
11fdf7f2 | 821 | void rgw::auth::RoleApplier::to_str(std::ostream& out) const { |
20effc67 | 822 | out << "rgw::auth::RoleApplier(role name =" << role.name; |
f91f0fd5 | 823 | for (auto& policy: role.role_policies) { |
11fdf7f2 TL |
824 | out << ", role policy =" << policy; |
825 | } | |
20effc67 | 826 | out << ", token policy =" << token_attrs.token_policy; |
11fdf7f2 TL |
827 | out << ")"; |
828 | } | |
829 | ||
830 | bool rgw::auth::RoleApplier::is_identity(const idset_t& ids) const { | |
831 | for (auto& p : ids) { | |
11fdf7f2 TL |
832 | if (p.is_wildcard()) { |
833 | return true; | |
f91f0fd5 TL |
834 | } else if (p.is_role()) { |
835 | string name = p.get_id(); | |
836 | string tenant = p.get_tenant(); | |
837 | if (name == role.name && tenant == role.tenant) { | |
838 | return true; | |
839 | } | |
840 | } else if (p.is_assumed_role()) { | |
841 | string tenant = p.get_tenant(); | |
20effc67 | 842 | string role_session = role.name + "/" + token_attrs.role_session_name; //role/role-session |
f91f0fd5 TL |
843 | if (role.tenant == tenant && role_session == p.get_role_session()) { |
844 | return true; | |
845 | } | |
846 | } else { | |
847 | string id = p.get_id(); | |
f67539c2 TL |
848 | string tenant = p.get_tenant(); |
849 | string oidc_id; | |
20effc67 TL |
850 | if (token_attrs.user_id.ns.empty()) { |
851 | oidc_id = token_attrs.user_id.id; | |
f67539c2 | 852 | } else { |
20effc67 | 853 | oidc_id = token_attrs.user_id.ns + "$" + token_attrs.user_id.id; |
f67539c2 | 854 | } |
20effc67 | 855 | if (oidc_id == id && token_attrs.user_id.tenant == tenant) { |
f91f0fd5 TL |
856 | return true; |
857 | } | |
11fdf7f2 TL |
858 | } |
859 | } | |
860 | return false; | |
861 | } | |
862 | ||
863 | void rgw::auth::RoleApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const /* out */ | |
864 | { | |
865 | /* Load the user id */ | |
20effc67 | 866 | user_info.user_id = this->token_attrs.user_id; |
11fdf7f2 TL |
867 | } |
868 | ||
869 | void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const | |
870 | { | |
f91f0fd5 | 871 | for (auto it: role.role_policies) { |
11fdf7f2 TL |
872 | try { |
873 | bufferlist bl = bufferlist::static_from_string(it); | |
1e59de90 | 874 | const rgw::IAM::Policy p(s->cct, role.tenant, bl, false); |
11fdf7f2 TL |
875 | s->iam_user_policies.push_back(std::move(p)); |
876 | } catch (rgw::IAM::PolicyParseException& e) { | |
877 | //Control shouldn't reach here as the policy has already been | |
878 | //verified earlier | |
f91f0fd5 | 879 | ldpp_dout(dpp, 20) << "failed to parse role policy: " << e.what() << dendl; |
11fdf7f2 TL |
880 | } |
881 | } | |
f91f0fd5 | 882 | |
20effc67 | 883 | if (!this->token_attrs.token_policy.empty()) { |
522d829b | 884 | try { |
20effc67 | 885 | string policy = this->token_attrs.token_policy; |
522d829b | 886 | bufferlist bl = bufferlist::static_from_string(policy); |
1e59de90 | 887 | const rgw::IAM::Policy p(s->cct, role.tenant, bl, false); |
522d829b TL |
888 | s->session_policies.push_back(std::move(p)); |
889 | } catch (rgw::IAM::PolicyParseException& e) { | |
890 | //Control shouldn't reach here as the policy has already been | |
891 | //verified earlier | |
892 | ldpp_dout(dpp, 20) << "failed to parse token policy: " << e.what() << dendl; | |
893 | } | |
f91f0fd5 TL |
894 | } |
895 | ||
896 | string condition = "aws:userid"; | |
20effc67 | 897 | string value = role.id + ":" + token_attrs.role_session_name; |
f91f0fd5 | 898 | s->env.emplace(condition, value); |
adb31ebb | 899 | |
20effc67 TL |
900 | s->env.emplace("aws:TokenIssueTime", token_attrs.token_issued_at); |
901 | ||
902 | for (auto& m : token_attrs.principal_tags) { | |
903 | s->env.emplace(m.first, m.second); | |
904 | ldpp_dout(dpp, 10) << "Principal Tag Key: " << m.first << " Value: " << m.second << dendl; | |
905 | std::size_t pos = m.first.find('/'); | |
906 | string key = m.first.substr(pos + 1); | |
907 | s->env.emplace("aws:TagKeys", key); | |
908 | ldpp_dout(dpp, 10) << "aws:TagKeys: " << key << dendl; | |
909 | } | |
f67539c2 | 910 | |
adb31ebb | 911 | s->token_claims.emplace_back("sts"); |
20effc67 TL |
912 | s->token_claims.emplace_back("role_name:" + role.tenant + "$" + role.name); |
913 | s->token_claims.emplace_back("role_session:" + token_attrs.role_session_name); | |
914 | for (auto& it : token_attrs.token_claims) { | |
adb31ebb TL |
915 | s->token_claims.emplace_back(it); |
916 | } | |
11fdf7f2 | 917 | } |
7c673cae FG |
918 | |
919 | rgw::auth::Engine::result_t | |
f67539c2 | 920 | rgw::auth::AnonymousEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const |
7c673cae FG |
921 | { |
922 | if (! is_applicable(s)) { | |
31f18b77 | 923 | return result_t::deny(-EPERM); |
7c673cae FG |
924 | } else { |
925 | RGWUserInfo user_info; | |
926 | rgw_get_anon_user(user_info); | |
927 | ||
d2e6a577 FG |
928 | auto apl = \ |
929 | apl_factory->create_apl_local(cct, s, user_info, | |
11fdf7f2 | 930 | rgw::auth::LocalApplier::NO_SUBUSER, |
2a845540 | 931 | std::nullopt, rgw::auth::LocalApplier::NO_ACCESS_KEY); |
7c673cae FG |
932 | return result_t::grant(std::move(apl)); |
933 | } | |
934 | } |