]>
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 | #include <array> | |
5 | ||
6 | #include "rgw_common.h" | |
7 | #include "rgw_auth.h" | |
8 | #include "rgw_user.h" | |
9 | #include "rgw_http_client.h" | |
10 | #include "rgw_keystone.h" | |
11 | ||
12 | #include "include/str_list.h" | |
13 | ||
14 | #define dout_context g_ceph_context | |
15 | #define dout_subsys ceph_subsys_rgw | |
16 | ||
17 | ||
18 | namespace rgw { | |
19 | namespace auth { | |
20 | ||
21 | std::unique_ptr<rgw::auth::Identity> | |
22 | transform_old_authinfo(const req_state* const s) | |
23 | { | |
24 | /* This class is not intended for public use. Should be removed altogether | |
25 | * with this function after moving all our APIs to the new authentication | |
26 | * infrastructure. */ | |
27 | class DummyIdentityApplier : public rgw::auth::Identity { | |
28 | CephContext* const cct; | |
29 | ||
30 | /* For this particular case it's OK to use rgw_user structure to convey | |
31 | * the identity info as this was the policy for doing that before the | |
32 | * new auth. */ | |
33 | const rgw_user id; | |
34 | const int perm_mask; | |
35 | const bool is_admin; | |
36 | public: | |
37 | DummyIdentityApplier(CephContext* const cct, | |
38 | const rgw_user& auth_id, | |
39 | const int perm_mask, | |
40 | const bool is_admin) | |
41 | : cct(cct), | |
42 | id(auth_id), | |
43 | perm_mask(perm_mask), | |
44 | is_admin(is_admin) { | |
45 | } | |
46 | ||
47 | uint32_t get_perms_from_aclspec(const aclspec_t& aclspec) const override { | |
48 | return rgw_perms_from_aclspec_default_strategy(id, aclspec); | |
49 | } | |
50 | ||
51 | bool is_admin_of(const rgw_user& acct_id) const override { | |
52 | return is_admin; | |
53 | } | |
54 | ||
55 | bool is_owner_of(const rgw_user& acct_id) const override { | |
56 | return id == acct_id; | |
57 | } | |
58 | ||
59 | uint32_t get_perm_mask() const override { | |
60 | return perm_mask; | |
61 | } | |
62 | ||
63 | void to_str(std::ostream& out) const override { | |
64 | out << "RGWDummyIdentityApplier(auth_id=" << id | |
65 | << ", perm_mask=" << perm_mask | |
66 | << ", is_admin=" << is_admin << ")"; | |
67 | } | |
68 | }; | |
69 | ||
70 | return std::unique_ptr<rgw::auth::Identity>( | |
71 | new DummyIdentityApplier(s->cct, | |
72 | s->user->user_id, | |
73 | s->perm_mask, | |
74 | /* System user has admin permissions by default - it's supposed to pass | |
75 | * through any security check. */ | |
76 | s->system_request)); | |
77 | } | |
78 | ||
79 | } /* namespace auth */ | |
80 | } /* namespace rgw */ | |
81 | ||
82 | ||
83 | uint32_t rgw_perms_from_aclspec_default_strategy( | |
84 | const rgw_user& uid, | |
85 | const rgw::auth::Identity::aclspec_t& aclspec) | |
86 | { | |
87 | dout(5) << "Searching permissions for uid=" << uid << dendl; | |
88 | ||
89 | const auto iter = aclspec.find(uid.to_str()); | |
90 | if (std::end(aclspec) != iter) { | |
91 | dout(5) << "Found permission: " << iter->second << dendl; | |
92 | return iter->second; | |
93 | } | |
94 | ||
95 | dout(5) << "Permissions for user not found" << dendl; | |
96 | return 0; | |
97 | } | |
98 | ||
99 | ||
100 | static inline const std::string make_spec_item(const std::string& tenant, | |
101 | const std::string& id) | |
102 | { | |
103 | return tenant + ":" + id; | |
104 | } | |
105 | ||
106 | ||
107 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
108 | strategy_handle_rejected(rgw::auth::Engine::result_t&& engine_result, | |
109 | const rgw::auth::Strategy::Control policy, | |
110 | rgw::auth::Engine::result_t&& strategy_result) | |
111 | { | |
112 | using Control = rgw::auth::Strategy::Control; | |
113 | switch (policy) { | |
114 | case Control::REQUISITE: | |
115 | /* Don't try next. */ | |
116 | return std::make_pair(false, std::move(engine_result)); | |
117 | ||
118 | case Control::SUFFICIENT: | |
119 | /* Don't try next. */ | |
120 | return std::make_pair(false, std::move(engine_result)); | |
121 | ||
122 | case Control::FALLBACK: | |
123 | /* Don't try next. */ | |
124 | return std::make_pair(false, std::move(strategy_result)); | |
125 | ||
126 | default: | |
127 | /* Huh, memory corruption? */ | |
128 | abort(); | |
129 | } | |
130 | } | |
131 | ||
132 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
133 | strategy_handle_denied(rgw::auth::Engine::result_t&& engine_result, | |
134 | const rgw::auth::Strategy::Control policy, | |
135 | rgw::auth::Engine::result_t&& strategy_result) | |
136 | { | |
137 | using Control = rgw::auth::Strategy::Control; | |
138 | switch (policy) { | |
139 | case Control::REQUISITE: | |
140 | /* Don't try next. */ | |
141 | return std::make_pair(false, std::move(engine_result)); | |
142 | ||
143 | case Control::SUFFICIENT: | |
144 | /* Just try next. */ | |
145 | return std::make_pair(true, std::move(engine_result)); | |
146 | ||
147 | case Control::FALLBACK: | |
148 | return std::make_pair(true, std::move(strategy_result)); | |
149 | ||
150 | default: | |
151 | /* Huh, memory corruption? */ | |
152 | abort(); | |
153 | } | |
154 | } | |
155 | ||
156 | static inline std::pair<bool, rgw::auth::Engine::result_t> | |
157 | strategy_handle_granted(rgw::auth::Engine::result_t&& engine_result, | |
158 | const rgw::auth::Strategy::Control policy, | |
159 | rgw::auth::Engine::result_t&& strategy_result) | |
160 | { | |
161 | using Control = rgw::auth::Strategy::Control; | |
162 | switch (policy) { | |
163 | case Control::REQUISITE: | |
164 | /* Try next. */ | |
165 | return std::make_pair(true, std::move(engine_result)); | |
166 | ||
167 | case Control::SUFFICIENT: | |
168 | /* Don't try next. */ | |
169 | return std::make_pair(false, std::move(engine_result)); | |
170 | ||
171 | case Control::FALLBACK: | |
172 | /* Don't try next. */ | |
173 | return std::make_pair(false, std::move(engine_result)); | |
174 | ||
175 | default: | |
176 | /* Huh, memory corruption? */ | |
177 | abort(); | |
178 | } | |
179 | } | |
180 | ||
181 | rgw::auth::Engine::result_t | |
182 | rgw::auth::Strategy::authenticate(const req_state* const s) const | |
183 | { | |
184 | result_t strategy_result = result_t::deny(); | |
185 | ||
186 | for (const stack_item_t& kv : auth_stack) { | |
187 | const rgw::auth::Engine& engine = kv.first; | |
188 | const auto& policy = kv.second; | |
189 | ||
190 | dout(20) << get_name() << ": trying " << engine.get_name() << dendl; | |
191 | ||
192 | result_t engine_result = result_t::deny(); | |
193 | try { | |
194 | engine_result = engine.authenticate(s); | |
195 | } catch (const int err) { | |
196 | engine_result = result_t::deny(err); | |
197 | } | |
198 | ||
199 | bool try_next = true; | |
200 | switch (engine_result.get_status()) { | |
201 | case result_t::Status::REJECTED: { | |
202 | dout(20) << engine.get_name() << " rejected with reason=" | |
203 | << engine_result.get_reason() << dendl; | |
204 | ||
205 | std::tie(try_next, strategy_result) = \ | |
206 | strategy_handle_rejected(std::move(engine_result), policy, | |
207 | std::move(strategy_result)); | |
208 | break; | |
209 | } | |
210 | case result_t::Status::DENIED: { | |
211 | dout(20) << engine.get_name() << " denied with reason=" | |
212 | << engine_result.get_reason() << dendl; | |
213 | ||
214 | std::tie(try_next, strategy_result) = \ | |
215 | strategy_handle_denied(std::move(engine_result), policy, | |
216 | std::move(strategy_result)); | |
217 | break; | |
218 | } | |
219 | case result_t::Status::GRANTED: { | |
220 | dout(20) << engine.get_name() << " granted access" << dendl; | |
221 | ||
222 | std::tie(try_next, strategy_result) = \ | |
223 | strategy_handle_granted(std::move(engine_result), policy, | |
224 | std::move(strategy_result)); | |
225 | break; | |
226 | } | |
227 | default: { | |
228 | abort(); | |
229 | } | |
230 | } | |
231 | ||
232 | if (! try_next) { | |
233 | break; | |
234 | } | |
235 | } | |
236 | ||
237 | return strategy_result; | |
238 | } | |
239 | ||
240 | void | |
241 | rgw::auth::Strategy::add_engine(const Control ctrl_flag, | |
242 | const Engine& engine) noexcept | |
243 | { | |
244 | auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag)); | |
245 | } | |
246 | ||
247 | ||
248 | /* rgw::auth::RemoteAuthApplier */ | |
249 | uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const aclspec_t& aclspec) const | |
250 | { | |
251 | uint32_t perm = 0; | |
252 | ||
253 | /* For backward compatibility with ACLOwner. */ | |
254 | perm |= rgw_perms_from_aclspec_default_strategy(info.acct_user, | |
255 | aclspec); | |
256 | ||
257 | /* We also need to cover cases where rgw_keystone_implicit_tenants | |
258 | * was enabled. */ | |
259 | if (info.acct_user.tenant.empty()) { | |
260 | const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); | |
261 | ||
262 | perm |= rgw_perms_from_aclspec_default_strategy(tenanted_acct_user, | |
263 | aclspec); | |
264 | } | |
265 | ||
266 | /* Now it's a time for invoking additional strategy that was supplied by | |
267 | * a specific auth engine. */ | |
268 | if (extra_acl_strategy) { | |
269 | perm |= extra_acl_strategy(aclspec); | |
270 | } | |
271 | ||
272 | ldout(cct, 20) << "from ACL got perm=" << perm << dendl; | |
273 | return perm; | |
274 | } | |
275 | ||
276 | bool rgw::auth::RemoteApplier::is_admin_of(const rgw_user& uid) const | |
277 | { | |
278 | return info.is_admin; | |
279 | } | |
280 | ||
281 | bool rgw::auth::RemoteApplier::is_owner_of(const rgw_user& uid) const | |
282 | { | |
283 | if (info.acct_user.tenant.empty()) { | |
284 | const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); | |
285 | ||
286 | if (tenanted_acct_user == uid) { | |
287 | return true; | |
288 | } | |
289 | } | |
290 | ||
291 | return info.acct_user == uid; | |
292 | } | |
293 | ||
294 | void rgw::auth::RemoteApplier::to_str(std::ostream& out) const | |
295 | { | |
296 | out << "rgw::auth::RemoteApplier(acct_user=" << info.acct_user | |
297 | << ", acct_name=" << info.acct_name | |
298 | << ", perm_mask=" << info.perm_mask | |
299 | << ", is_admin=" << info.is_admin << ")"; | |
300 | } | |
301 | ||
302 | void rgw::auth::RemoteApplier::create_account(const rgw_user& acct_user, | |
303 | RGWUserInfo& user_info) const /* out */ | |
304 | { | |
305 | rgw_user new_acct_user = acct_user; | |
306 | ||
307 | if (info.acct_type) { | |
308 | //ldap/keystone for s3 users | |
309 | user_info.type = info.acct_type; | |
310 | } | |
311 | ||
312 | /* An upper layer may enforce creating new accounts within their own | |
313 | * tenants. */ | |
314 | if (new_acct_user.tenant.empty() && implicit_tenants) { | |
315 | new_acct_user.tenant = new_acct_user.id; | |
316 | } | |
317 | ||
318 | user_info.user_id = new_acct_user; | |
319 | user_info.display_name = info.acct_name; | |
320 | ||
321 | int ret = rgw_store_user_info(store, user_info, nullptr, nullptr, | |
322 | real_time(), true); | |
323 | if (ret < 0) { | |
324 | ldout(cct, 0) << "ERROR: failed to store new user info: user=" | |
325 | << user_info.user_id << " ret=" << ret << dendl; | |
326 | throw ret; | |
327 | } | |
328 | } | |
329 | ||
330 | /* TODO(rzarzynski): we need to handle display_name changes. */ | |
331 | void rgw::auth::RemoteApplier::load_acct_info(RGWUserInfo& user_info) const /* out */ | |
332 | { | |
333 | /* It's supposed that RGWRemoteAuthApplier tries to load account info | |
334 | * that belongs to the authenticated identity. Another policy may be | |
335 | * applied by using a RGWThirdPartyAccountAuthApplier decorator. */ | |
336 | const rgw_user& acct_user = info.acct_user; | |
337 | ||
338 | /* Normally, empty "tenant" field of acct_user means the authenticated | |
339 | * identity has the legacy, global tenant. However, due to inclusion | |
340 | * of multi-tenancy, we got some special compatibility kludge for remote | |
341 | * backends like Keystone. | |
342 | * If the global tenant is the requested one, we try the same tenant as | |
343 | * the user name first. If that RGWUserInfo exists, we use it. This way, | |
344 | * migrated OpenStack users can get their namespaced containers and nobody's | |
345 | * the wiser. | |
346 | * If that fails, we look up in the requested (possibly empty) tenant. | |
347 | * If that fails too, we create the account within the global or separated | |
348 | * namespace depending on rgw_keystone_implicit_tenants. */ | |
349 | if (acct_user.tenant.empty()) { | |
350 | const rgw_user tenanted_uid(acct_user.id, acct_user.id); | |
351 | ||
352 | if (rgw_get_user_info_by_uid(store, tenanted_uid, user_info) >= 0) { | |
353 | /* Succeeded. */ | |
354 | return; | |
355 | } | |
356 | } | |
357 | ||
358 | if (rgw_get_user_info_by_uid(store, acct_user, user_info) < 0) { | |
359 | ldout(cct, 0) << "NOTICE: couldn't map swift user " << acct_user << dendl; | |
360 | create_account(acct_user, user_info); | |
361 | } | |
362 | ||
363 | /* Succeeded if we are here (create_account() hasn't throwed). */ | |
364 | } | |
365 | ||
366 | ||
367 | /* rgw::auth::LocalApplier */ | |
368 | /* static declaration */ | |
369 | const std::string rgw::auth::LocalApplier::NO_SUBUSER; | |
370 | ||
371 | uint32_t rgw::auth::LocalApplier::get_perms_from_aclspec(const aclspec_t& aclspec) const | |
372 | { | |
373 | return rgw_perms_from_aclspec_default_strategy(user_info.user_id, aclspec); | |
374 | } | |
375 | ||
376 | bool rgw::auth::LocalApplier::is_admin_of(const rgw_user& uid) const | |
377 | { | |
378 | return user_info.admin || user_info.system; | |
379 | } | |
380 | ||
381 | bool rgw::auth::LocalApplier::is_owner_of(const rgw_user& uid) const | |
382 | { | |
383 | return uid == user_info.user_id; | |
384 | } | |
385 | ||
386 | void rgw::auth::LocalApplier::to_str(std::ostream& out) const | |
387 | { | |
388 | out << "rgw::auth::LocalApplier(acct_user=" << user_info.user_id | |
389 | << ", acct_name=" << user_info.display_name | |
390 | << ", subuser=" << subuser | |
391 | << ", perm_mask=" << get_perm_mask() | |
392 | << ", is_admin=" << static_cast<bool>(user_info.admin) << ")"; | |
393 | } | |
394 | ||
395 | uint32_t rgw::auth::LocalApplier::get_perm_mask(const std::string& subuser_name, | |
396 | const RGWUserInfo &uinfo) const | |
397 | { | |
398 | if (! subuser_name.empty() && subuser_name != NO_SUBUSER) { | |
399 | const auto iter = uinfo.subusers.find(subuser_name); | |
400 | ||
401 | if (iter != std::end(uinfo.subusers)) { | |
402 | return iter->second.perm_mask; | |
403 | } else { | |
404 | /* Subuser specified but not found. */ | |
405 | return RGW_PERM_NONE; | |
406 | } | |
407 | } else { | |
408 | /* Due to backward compatibility. */ | |
409 | return RGW_PERM_FULL_CONTROL; | |
410 | } | |
411 | } | |
412 | ||
413 | void rgw::auth::LocalApplier::load_acct_info(RGWUserInfo& user_info) const /* out */ | |
414 | { | |
415 | /* Load the account that belongs to the authenticated identity. An extra call | |
416 | * to RADOS may be safely skipped in this case. */ | |
417 | user_info = this->user_info; | |
418 | } | |
419 | ||
420 | ||
421 | rgw::auth::Engine::result_t | |
422 | rgw::auth::AnonymousEngine::authenticate(const req_state* const s) const | |
423 | { | |
424 | if (! is_applicable(s)) { | |
425 | return result_t::deny(); | |
426 | } else { | |
427 | RGWUserInfo user_info; | |
428 | rgw_get_anon_user(user_info); | |
429 | ||
430 | // FIXME: over 80 columns | |
431 | auto apl = apl_factory->create_apl_local(cct, s, user_info, | |
432 | rgw::auth::LocalApplier::NO_SUBUSER); | |
433 | return result_t::grant(std::move(apl)); | |
434 | } | |
435 | } |