]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_sts.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rgw / rgw_rest_sts.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3 #include <vector>
4 #include <string>
5 #include <array>
6 #include <string_view>
7 #include <sstream>
8 #include <memory>
9
10 #include <boost/algorithm/string/predicate.hpp>
11 #include <boost/format.hpp>
12 #include <boost/optional.hpp>
13 #include <boost/utility/in_place_factory.hpp>
14 #include <boost/tokenizer.hpp>
15
16
17
18 #include "ceph_ver.h"
19 #include "common/Formatter.h"
20 #include "common/utf8.h"
21 #include "common/ceph_json.h"
22
23 #include "rgw_rest.h"
24 #include "rgw_auth.h"
25 #include "rgw_auth_registry.h"
26 #include "jwt-cpp/jwt.h"
27 #include "rgw_rest_sts.h"
28
29 #include "rgw_formats.h"
30 #include "rgw_client_io.h"
31
32 #include "rgw_request.h"
33 #include "rgw_process.h"
34 #include "rgw_iam_policy.h"
35 #include "rgw_iam_policy_keywords.h"
36
37 #include "rgw_sts.h"
38 #include "rgw_rest_oidc_provider.h"
39
40
41 #define dout_context g_ceph_context
42 #define dout_subsys ceph_subsys_rgw
43
44 using namespace std;
45
46 namespace rgw::auth::sts {
47
48 bool
49 WebTokenEngine::is_applicable(const std::string& token) const noexcept
50 {
51 return ! token.empty();
52 }
53
54 std::string
55 WebTokenEngine::get_role_tenant(const string& role_arn) const
56 {
57 string tenant;
58 auto r_arn = rgw::ARN::parse(role_arn);
59 if (r_arn) {
60 tenant = r_arn->account;
61 }
62 return tenant;
63 }
64
65 std::string
66 WebTokenEngine::get_role_name(const string& role_arn) const
67 {
68 string role_name;
69 auto r_arn = rgw::ARN::parse(role_arn);
70 if (r_arn) {
71 role_name = r_arn->resource;
72 }
73 if (!role_name.empty()) {
74 auto pos = role_name.find_last_of('/');
75 if(pos != string::npos) {
76 role_name = role_name.substr(pos + 1);
77 }
78 }
79 return role_name;
80 }
81
82 std::unique_ptr<rgw::sal::RGWOIDCProvider>
83 WebTokenEngine::get_provider(const DoutPrefixProvider *dpp, const string& role_arn, const string& iss) const
84 {
85 string tenant = get_role_tenant(role_arn);
86
87 string idp_url = iss;
88 auto pos = idp_url.find("http://");
89 if (pos == std::string::npos) {
90 pos = idp_url.find("https://");
91 if (pos != std::string::npos) {
92 idp_url.erase(pos, 8);
93 } else {
94 pos = idp_url.find("www.");
95 if (pos != std::string::npos) {
96 idp_url.erase(pos, 4);
97 }
98 }
99 } else {
100 idp_url.erase(pos, 7);
101 }
102 auto provider_arn = rgw::ARN(idp_url, "oidc-provider", tenant);
103 string p_arn = provider_arn.to_string();
104 std::unique_ptr<rgw::sal::RGWOIDCProvider> provider = store->get_oidc_provider();
105 provider->set_arn(p_arn);
106 provider->set_tenant(tenant);
107 auto ret = provider->get(dpp);
108 if (ret < 0) {
109 return nullptr;
110 }
111 return provider;
112 }
113
114 bool
115 WebTokenEngine::is_client_id_valid(vector<string>& client_ids, const string& client_id) const
116 {
117 for (auto it : client_ids) {
118 if (it == client_id) {
119 return true;
120 }
121 }
122 return false;
123 }
124
125 bool
126 WebTokenEngine::is_cert_valid(const vector<string>& thumbprints, const string& cert) const
127 {
128 //calculate thumbprint of cert
129 std::unique_ptr<BIO, decltype(&BIO_free_all)> certbio(BIO_new_mem_buf(cert.data(), cert.size()), BIO_free_all);
130 std::unique_ptr<BIO, decltype(&BIO_free_all)> keybio(BIO_new(BIO_s_mem()), BIO_free_all);
131 string pw="";
132 std::unique_ptr<X509, decltype(&X509_free)> x_509cert(PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast<char*>(pw.c_str())), X509_free);
133 const EVP_MD* fprint_type = EVP_sha1();
134 unsigned int fprint_size;
135 unsigned char fprint[EVP_MAX_MD_SIZE];
136
137 if (!X509_digest(x_509cert.get(), fprint_type, fprint, &fprint_size)) {
138 return false;
139 }
140 stringstream ss;
141 for (unsigned int i = 0; i < fprint_size; i++) {
142 ss << std::setfill('0') << std::setw(2) << std::hex << (0xFF & (unsigned int)fprint[i]);
143 }
144 std::string digest = ss.str();
145
146 for (auto& it : thumbprints) {
147 if (boost::iequals(it,digest)) {
148 return true;
149 }
150 }
151 return false;
152 }
153
154 template <typename T>
155 void
156 WebTokenEngine::recurse_and_insert(const string& key, const jwt::claim& c, T& t) const
157 {
158 string s_val;
159 jwt::claim::type c_type = c.get_type();
160 switch(c_type) {
161 case jwt::claim::type::null:
162 break;
163 case jwt::claim::type::boolean:
164 case jwt::claim::type::number:
165 case jwt::claim::type::int64:
166 {
167 s_val = c.to_json().serialize();
168 t.emplace(std::make_pair(key, s_val));
169 break;
170 }
171 case jwt::claim::type::string:
172 {
173 s_val = c.to_json().to_str();
174 t.emplace(std::make_pair(key, s_val));
175 break;
176 }
177 case jwt::claim::type::array:
178 {
179 const picojson::array& arr = c.as_array();
180 for (auto& a : arr) {
181 recurse_and_insert(key, jwt::claim(a), t);
182 }
183 break;
184 }
185 case jwt::claim::type::object:
186 {
187 const picojson::object& obj = c.as_object();
188 for (auto& m : obj) {
189 recurse_and_insert(m.first, jwt::claim(m.second), t);
190 }
191 break;
192 }
193 }
194 return;
195 }
196
197 //Extract all token claims so that they can be later used in the Condition element of Role's trust policy
198 WebTokenEngine::token_t
199 WebTokenEngine::get_token_claims(const jwt::decoded_jwt& decoded) const
200 {
201 WebTokenEngine::token_t token;
202 const auto& claims = decoded.get_payload_claims();
203
204 for (auto& c : claims) {
205 if (c.first == string(princTagsNamespace)) {
206 continue;
207 }
208 recurse_and_insert(c.first, c.second, token);
209 }
210 return token;
211 }
212
213 //Offline validation of incoming Web Token which is a signed JWT (JSON Web Token)
214 std::tuple<boost::optional<WebTokenEngine::token_t>, boost::optional<WebTokenEngine::principal_tags_t>>
215 WebTokenEngine::get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s,
216 optional_yield y) const
217 {
218 WebTokenEngine::token_t t;
219 WebTokenEngine::principal_tags_t principal_tags;
220 try {
221 const auto& decoded = jwt::decode(token);
222
223 auto& payload = decoded.get_payload();
224 ldpp_dout(dpp, 20) << " payload = " << payload << dendl;
225
226 t = get_token_claims(decoded);
227
228 string iss;
229 if (decoded.has_issuer()) {
230 iss = decoded.get_issuer();
231 }
232
233 set<string> aud;
234 if (decoded.has_audience()) {
235 aud = decoded.get_audience();
236 }
237
238 string client_id;
239 if (decoded.has_payload_claim("client_id")) {
240 client_id = decoded.get_payload_claim("client_id").as_string();
241 }
242 if (client_id.empty() && decoded.has_payload_claim("clientId")) {
243 client_id = decoded.get_payload_claim("clientId").as_string();
244 }
245 string azp;
246 if (decoded.has_payload_claim("azp")) {
247 azp = decoded.get_payload_claim("azp").as_string();
248 }
249
250 string role_arn = s->info.args.get("RoleArn");
251 auto provider = get_provider(dpp, role_arn, iss);
252 if (! provider) {
253 ldpp_dout(dpp, 0) << "Couldn't get oidc provider info using input iss" << iss << dendl;
254 throw -EACCES;
255 }
256 if (decoded.has_payload_claim(string(princTagsNamespace))) {
257 auto& cl = decoded.get_payload_claim(string(princTagsNamespace));
258 if (cl.get_type() == jwt::claim::type::object || cl.get_type() == jwt::claim::type::array) {
259 recurse_and_insert("dummy", cl, principal_tags);
260 for (auto it : principal_tags) {
261 ldpp_dout(dpp, 5) << "Key: " << it.first << " Value: " << it.second << dendl;
262 }
263 } else {
264 ldpp_dout(dpp, 0) << "Malformed principal tags" << cl.as_string() << dendl;
265 throw -EINVAL;
266 }
267 }
268 vector<string> client_ids = provider->get_client_ids();
269 vector<string> thumbprints = provider->get_thumbprints();
270 if (! client_ids.empty()) {
271 bool found = false;
272 for (auto& it : aud) {
273 if (is_client_id_valid(client_ids, it)) {
274 found = true;
275 break;
276 }
277 }
278 if (! found && ! is_client_id_valid(client_ids, client_id) && ! is_client_id_valid(client_ids, azp)) {
279 ldpp_dout(dpp, 0) << "Client id in token doesn't match with that registered with oidc provider" << dendl;
280 throw -EACCES;
281 }
282 }
283 //Validate signature
284 if (decoded.has_algorithm()) {
285 auto& algorithm = decoded.get_algorithm();
286 try {
287 validate_signature(dpp, decoded, algorithm, iss, thumbprints, y);
288 } catch (...) {
289 throw -EACCES;
290 }
291 } else {
292 return {boost::none, boost::none};
293 }
294 } catch (int error) {
295 if (error == -EACCES) {
296 throw -EACCES;
297 }
298 ldpp_dout(dpp, 5) << "Invalid JWT token" << dendl;
299 return {boost::none, boost::none};
300 }
301 catch (...) {
302 ldpp_dout(dpp, 5) << "Invalid JWT token" << dendl;
303 return {boost::none, boost::none};
304 }
305 return {t, principal_tags};
306 }
307
308 std::string
309 WebTokenEngine::get_cert_url(const string& iss, const DoutPrefixProvider *dpp, optional_yield y) const
310 {
311 string cert_url;
312 string openidc_wellknown_url = iss + "/.well-known/openid-configuration";
313 bufferlist openidc_resp;
314 RGWHTTPTransceiver openidc_req(cct, "GET", openidc_wellknown_url, &openidc_resp);
315
316 //Headers
317 openidc_req.append_header("Content-Type", "application/x-www-form-urlencoded");
318
319 int res = openidc_req.process(y);
320 if (res < 0) {
321 ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
322 throw -EINVAL;
323 }
324
325 //Debug only
326 ldpp_dout(dpp, 20) << "HTTP status: " << openidc_req.get_http_status() << dendl;
327 ldpp_dout(dpp, 20) << "JSON Response is: " << openidc_resp.c_str() << dendl;
328
329 JSONParser parser;
330 if (parser.parse(openidc_resp.c_str(), openidc_resp.length())) {
331 JSONObj::data_val val;
332 if (parser.get_data("jwks_uri", &val)) {
333 cert_url = val.str.c_str();
334 ldpp_dout(dpp, 20) << "Cert URL is: " << cert_url.c_str() << dendl;
335 } else {
336 ldpp_dout(dpp, 0) << "Malformed json returned while fetching openidc url" << dendl;
337 }
338 }
339 return cert_url;
340 }
341
342 void
343 WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const string& algorithm, const string& iss, const vector<string>& thumbprints, optional_yield y) const
344 {
345 if (algorithm != "HS256" && algorithm != "HS384" && algorithm != "HS512") {
346 string cert_url = get_cert_url(iss, dpp, y);
347 if (cert_url.empty()) {
348 throw -EINVAL;
349 }
350
351 // Get certificate
352 bufferlist cert_resp;
353 RGWHTTPTransceiver cert_req(cct, "GET", cert_url, &cert_resp);
354 //Headers
355 cert_req.append_header("Content-Type", "application/x-www-form-urlencoded");
356
357 int res = cert_req.process(y);
358 if (res < 0) {
359 ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
360 throw -EINVAL;
361 }
362 //Debug only
363 ldpp_dout(dpp, 20) << "HTTP status: " << cert_req.get_http_status() << dendl;
364 ldpp_dout(dpp, 20) << "JSON Response is: " << cert_resp.c_str() << dendl;
365
366 JSONParser parser;
367 if (parser.parse(cert_resp.c_str(), cert_resp.length())) {
368 JSONObj::data_val val;
369 if (parser.get_data("keys", &val)) {
370 if (val.str[0] == '[') {
371 val.str.erase(0, 1);
372 }
373 if (val.str[val.str.size() - 1] == ']') {
374 val.str = val.str.erase(val.str.size() - 1, 1);
375 }
376 if (parser.parse(val.str.c_str(), val.str.size())) {
377 vector<string> x5c;
378 if (JSONDecoder::decode_json("x5c", x5c, &parser)) {
379 string cert;
380 bool found_valid_cert = false;
381 for (auto& it : x5c) {
382 cert = "-----BEGIN CERTIFICATE-----\n" + it + "\n-----END CERTIFICATE-----";
383 ldpp_dout(dpp, 20) << "Certificate is: " << cert.c_str() << dendl;
384 if (is_cert_valid(thumbprints, cert)) {
385 found_valid_cert = true;
386 break;
387 }
388 found_valid_cert = true;
389 }
390 if (! found_valid_cert) {
391 ldpp_dout(dpp, 0) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
392 throw -EINVAL;
393 }
394 try {
395 //verify method takes care of expired tokens also
396 if (algorithm == "RS256") {
397 auto verifier = jwt::verify()
398 .allow_algorithm(jwt::algorithm::rs256{cert});
399
400 verifier.verify(decoded);
401 } else if (algorithm == "RS384") {
402 auto verifier = jwt::verify()
403 .allow_algorithm(jwt::algorithm::rs384{cert});
404
405 verifier.verify(decoded);
406 } else if (algorithm == "RS512") {
407 auto verifier = jwt::verify()
408 .allow_algorithm(jwt::algorithm::rs512{cert});
409
410 verifier.verify(decoded);
411 } else if (algorithm == "ES256") {
412 auto verifier = jwt::verify()
413 .allow_algorithm(jwt::algorithm::es256{cert});
414
415 verifier.verify(decoded);
416 } else if (algorithm == "ES384") {
417 auto verifier = jwt::verify()
418 .allow_algorithm(jwt::algorithm::es384{cert});
419
420 verifier.verify(decoded);
421 } else if (algorithm == "ES512") {
422 auto verifier = jwt::verify()
423 .allow_algorithm(jwt::algorithm::es512{cert});
424
425 verifier.verify(decoded);
426 } else if (algorithm == "PS256") {
427 auto verifier = jwt::verify()
428 .allow_algorithm(jwt::algorithm::ps256{cert});
429
430 verifier.verify(decoded);
431 } else if (algorithm == "PS384") {
432 auto verifier = jwt::verify()
433 .allow_algorithm(jwt::algorithm::ps384{cert});
434
435 verifier.verify(decoded);
436 } else if (algorithm == "PS512") {
437 auto verifier = jwt::verify()
438 .allow_algorithm(jwt::algorithm::ps512{cert});
439
440 verifier.verify(decoded);
441 }
442 } catch (std::runtime_error& e) {
443 ldpp_dout(dpp, 0) << "Signature validation failed: " << e.what() << dendl;
444 throw;
445 }
446 catch (...) {
447 ldpp_dout(dpp, 0) << "Signature validation failed" << dendl;
448 throw;
449 }
450 } else {
451 ldpp_dout(dpp, 0) << "x5c not present" << dendl;
452 throw -EINVAL;
453 }
454 } else {
455 ldpp_dout(dpp, 0) << "Malformed JSON object for keys" << dendl;
456 throw -EINVAL;
457 }
458 } else {
459 ldpp_dout(dpp, 0) << "keys not present in JSON" << dendl;
460 throw -EINVAL;
461 } //if-else get-data
462 } else {
463 ldpp_dout(dpp, 0) << "Malformed json returned while fetching cert" << dendl;
464 throw -EINVAL;
465 } //if-else parser cert_resp
466 } else {
467 ldpp_dout(dpp, 0) << "JWT signed by HMAC algos are currently not supported" << dendl;
468 throw -EINVAL;
469 }
470 }
471
472 WebTokenEngine::result_t
473 WebTokenEngine::authenticate( const DoutPrefixProvider* dpp,
474 const std::string& token,
475 const req_state* const s,
476 optional_yield y) const
477 {
478 if (! is_applicable(token)) {
479 return result_t::deny();
480 }
481
482 try {
483 auto [t, princ_tags] = get_from_jwt(dpp, token, s, y);
484 if (t) {
485 string role_session = s->info.args.get("RoleSessionName");
486 if (role_session.empty()) {
487 ldout(s->cct, 0) << "Role Session Name is empty " << dendl;
488 return result_t::deny(-EACCES);
489 }
490 string role_arn = s->info.args.get("RoleArn");
491 string role_tenant = get_role_tenant(role_arn);
492 string role_name = get_role_name(role_arn);
493 std::unique_ptr<rgw::sal::RGWRole> role = store->get_role(role_name, role_tenant);
494 int ret = role->get(dpp, y);
495 if (ret < 0) {
496 ldpp_dout(dpp, 0) << "Role not found: name:" << role_name << " tenant: " << role_tenant << dendl;
497 return result_t::deny(-EACCES);
498 }
499 boost::optional<multimap<string,string>> role_tags = role->get_tags();
500 auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, role_tenant, *t, role_tags, princ_tags);
501 return result_t::grant(std::move(apl));
502 }
503 return result_t::deny(-EACCES);
504 }
505 catch (...) {
506 return result_t::deny(-EACCES);
507 }
508 }
509
510 } // namespace rgw::auth::sts
511
512 int RGWREST_STS::verify_permission(optional_yield y)
513 {
514 STS::STSService _sts(s->cct, store, s->user->get_id(), s->auth.identity.get());
515 sts = std::move(_sts);
516
517 string rArn = s->info.args.get("RoleArn");
518 const auto& [ret, role] = sts.getRoleInfo(s, rArn, y);
519 if (ret < 0) {
520 ldpp_dout(this, 0) << "failed to get role info using role arn: " << rArn << dendl;
521 return ret;
522 }
523 string policy = role->get_assume_role_policy();
524 buffer::list bl = buffer::list::static_from_string(policy);
525
526 //Parse the policy
527 //TODO - This step should be part of Role Creation
528 try {
529 const rgw::IAM::Policy p(s->cct, s->user->get_tenant(), bl);
530 if (!s->principal_tags.empty()) {
531 auto res = p.eval(s->env, *s->auth.identity, rgw::IAM::stsTagSession, boost::none);
532 if (res != rgw::IAM::Effect::Allow) {
533 ldout(s->cct, 0) << "evaluating policy for stsTagSession returned deny/pass" << dendl;
534 return -EPERM;
535 }
536 }
537 uint64_t op;
538 if (get_type() == RGW_STS_ASSUME_ROLE_WEB_IDENTITY) {
539 op = rgw::IAM::stsAssumeRoleWithWebIdentity;
540 } else {
541 op = rgw::IAM::stsAssumeRole;
542 }
543
544 auto res = p.eval(s->env, *s->auth.identity, op, boost::none);
545 if (res != rgw::IAM::Effect::Allow) {
546 ldout(s->cct, 0) << "evaluating policy for op: " << op << " returned deny/pass" << dendl;
547 return -EPERM;
548 }
549 } catch (rgw::IAM::PolicyParseException& e) {
550 ldpp_dout(this, 0) << "failed to parse policy: " << e.what() << dendl;
551 return -EPERM;
552 }
553
554 return 0;
555 }
556
557 void RGWREST_STS::send_response()
558 {
559 if (op_ret) {
560 set_req_state_err(s, op_ret);
561 }
562 dump_errno(s);
563 end_header(s);
564 }
565
566 int RGWSTSGetSessionToken::verify_permission(optional_yield y)
567 {
568 rgw::Partition partition = rgw::Partition::aws;
569 rgw::Service service = rgw::Service::s3;
570 if (!verify_user_permission(this,
571 s,
572 rgw::ARN(partition, service, "", s->user->get_tenant(), ""),
573 rgw::IAM::stsGetSessionToken)) {
574 ldpp_dout(this, 0) << "User does not have permssion to perform GetSessionToken" << dendl;
575 return -EACCES;
576 }
577
578 return 0;
579 }
580
581 int RGWSTSGetSessionToken::get_params()
582 {
583 duration = s->info.args.get("DurationSeconds");
584 serialNumber = s->info.args.get("SerialNumber");
585 tokenCode = s->info.args.get("TokenCode");
586
587 if (! duration.empty()) {
588 string err;
589 uint64_t duration_in_secs = strict_strtoll(duration.c_str(), 10, &err);
590 if (!err.empty()) {
591 ldpp_dout(this, 0) << "Invalid value of input duration: " << duration << dendl;
592 return -EINVAL;
593 }
594
595 if (duration_in_secs < STS::GetSessionTokenRequest::getMinDuration() ||
596 duration_in_secs > s->cct->_conf->rgw_sts_max_session_duration) {
597 ldpp_dout(this, 0) << "Invalid duration in secs: " << duration_in_secs << dendl;
598 return -EINVAL;
599 }
600 }
601
602 return 0;
603 }
604
605 void RGWSTSGetSessionToken::execute(optional_yield y)
606 {
607 if (op_ret = get_params(); op_ret < 0) {
608 return;
609 }
610
611 STS::STSService sts(s->cct, store, s->user->get_id(), s->auth.identity.get());
612
613 STS::GetSessionTokenRequest req(duration, serialNumber, tokenCode);
614 const auto& [ret, creds] = sts.getSessionToken(this, req);
615 op_ret = std::move(ret);
616 //Dump the output
617 if (op_ret == 0) {
618 s->formatter->open_object_section("GetSessionTokenResponse");
619 s->formatter->open_object_section("GetSessionTokenResult");
620 s->formatter->open_object_section("Credentials");
621 creds.dump(s->formatter);
622 s->formatter->close_section();
623 s->formatter->close_section();
624 s->formatter->close_section();
625 }
626 }
627
628 int RGWSTSAssumeRoleWithWebIdentity::get_params()
629 {
630 duration = s->info.args.get("DurationSeconds");
631 providerId = s->info.args.get("ProviderId");
632 policy = s->info.args.get("Policy");
633 roleArn = s->info.args.get("RoleArn");
634 roleSessionName = s->info.args.get("RoleSessionName");
635 iss = s->info.args.get("provider_id");
636 sub = s->info.args.get("sub");
637 aud = s->info.args.get("aud");
638
639 if (roleArn.empty() || roleSessionName.empty() || sub.empty() || aud.empty()) {
640 ldpp_dout(this, 0) << "ERROR: one of role arn or role session name or token is empty" << dendl;
641 return -EINVAL;
642 }
643
644 if (! policy.empty()) {
645 bufferlist bl = bufferlist::static_from_string(policy);
646 try {
647 const rgw::IAM::Policy p(s->cct, s->user->get_tenant(), bl);
648 }
649 catch (rgw::IAM::PolicyParseException& e) {
650 ldpp_dout(this, 20) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
651 return -ERR_MALFORMED_DOC;
652 }
653 }
654
655 return 0;
656 }
657
658 void RGWSTSAssumeRoleWithWebIdentity::execute(optional_yield y)
659 {
660 if (op_ret = get_params(); op_ret < 0) {
661 return;
662 }
663
664 STS::AssumeRoleWithWebIdentityRequest req(s->cct, duration, providerId, policy, roleArn,
665 roleSessionName, iss, sub, aud, s->principal_tags);
666 STS::AssumeRoleWithWebIdentityResponse response = sts.assumeRoleWithWebIdentity(this, req);
667 op_ret = std::move(response.assumeRoleResp.retCode);
668
669 //Dump the output
670 if (op_ret == 0) {
671 s->formatter->open_object_section("AssumeRoleWithWebIdentityResponse");
672 s->formatter->open_object_section("AssumeRoleWithWebIdentityResult");
673 encode_json("SubjectFromWebIdentityToken", response.sub , s->formatter);
674 encode_json("Audience", response.aud , s->formatter);
675 s->formatter->open_object_section("AssumedRoleUser");
676 response.assumeRoleResp.user.dump(s->formatter);
677 s->formatter->close_section();
678 s->formatter->open_object_section("Credentials");
679 response.assumeRoleResp.creds.dump(s->formatter);
680 s->formatter->close_section();
681 encode_json("Provider", response.providerId , s->formatter);
682 encode_json("PackedPolicySize", response.assumeRoleResp.packedPolicySize , s->formatter);
683 s->formatter->close_section();
684 s->formatter->close_section();
685 }
686 }
687
688 int RGWSTSAssumeRole::get_params()
689 {
690 duration = s->info.args.get("DurationSeconds");
691 externalId = s->info.args.get("ExternalId");
692 policy = s->info.args.get("Policy");
693 roleArn = s->info.args.get("RoleArn");
694 roleSessionName = s->info.args.get("RoleSessionName");
695 serialNumber = s->info.args.get("SerialNumber");
696 tokenCode = s->info.args.get("TokenCode");
697
698 if (roleArn.empty() || roleSessionName.empty()) {
699 ldpp_dout(this, 0) << "ERROR: one of role arn or role session name is empty" << dendl;
700 return -EINVAL;
701 }
702
703 if (! policy.empty()) {
704 bufferlist bl = bufferlist::static_from_string(policy);
705 try {
706 const rgw::IAM::Policy p(s->cct, s->user->get_tenant(), bl);
707 }
708 catch (rgw::IAM::PolicyParseException& e) {
709 ldpp_dout(this, 0) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
710 return -ERR_MALFORMED_DOC;
711 }
712 }
713
714 return 0;
715 }
716
717 void RGWSTSAssumeRole::execute(optional_yield y)
718 {
719 if (op_ret = get_params(); op_ret < 0) {
720 return;
721 }
722
723 STS::AssumeRoleRequest req(s->cct, duration, externalId, policy, roleArn,
724 roleSessionName, serialNumber, tokenCode);
725 STS::AssumeRoleResponse response = sts.assumeRole(s, req, y);
726 op_ret = std::move(response.retCode);
727 //Dump the output
728 if (op_ret == 0) {
729 s->formatter->open_object_section("AssumeRoleResponse");
730 s->formatter->open_object_section("AssumeRoleResult");
731 s->formatter->open_object_section("Credentials");
732 response.creds.dump(s->formatter);
733 s->formatter->close_section();
734 s->formatter->open_object_section("AssumedRoleUser");
735 response.user.dump(s->formatter);
736 s->formatter->close_section();
737 encode_json("PackedPolicySize", response.packedPolicySize , s->formatter);
738 s->formatter->close_section();
739 s->formatter->close_section();
740 }
741 }
742
743 int RGW_Auth_STS::authorize(const DoutPrefixProvider *dpp,
744 rgw::sal::Store* store,
745 const rgw::auth::StrategyRegistry& auth_registry,
746 struct req_state *s, optional_yield y)
747 {
748 return rgw::auth::Strategy::apply(dpp, auth_registry.get_sts(), s, y);
749 }
750
751 void RGWHandler_REST_STS::rgw_sts_parse_input()
752 {
753 if (post_body.size() > 0) {
754 ldpp_dout(s, 10) << "Content of POST: " << post_body << dendl;
755
756 if (post_body.find("Action") != string::npos) {
757 boost::char_separator<char> sep("&");
758 boost::tokenizer<boost::char_separator<char>> tokens(post_body, sep);
759 for (const auto& t : tokens) {
760 auto pos = t.find("=");
761 if (pos != string::npos) {
762 s->info.args.append(t.substr(0,pos),
763 url_decode(t.substr(pos+1, t.size() -1)));
764 }
765 }
766 }
767 }
768 auto payload_hash = rgw::auth::s3::calc_v4_payload_hash(post_body);
769 s->info.args.append("PayloadHash", payload_hash);
770 }
771
772 RGWOp *RGWHandler_REST_STS::op_post()
773 {
774 rgw_sts_parse_input();
775
776 if (s->info.args.exists("Action")) {
777 string action = s->info.args.get("Action");
778 if (action == "AssumeRole") {
779 return new RGWSTSAssumeRole;
780 } else if (action == "GetSessionToken") {
781 return new RGWSTSGetSessionToken;
782 } else if (action == "AssumeRoleWithWebIdentity") {
783 return new RGWSTSAssumeRoleWithWebIdentity;
784 }
785 }
786
787 return nullptr;
788 }
789
790 int RGWHandler_REST_STS::init(rgw::sal::Store* store,
791 struct req_state *s,
792 rgw::io::BasicClient *cio)
793 {
794 s->dialect = "sts";
795
796 if (int ret = RGWHandler_REST_STS::init_from_header(s, RGW_FORMAT_XML, true); ret < 0) {
797 ldpp_dout(s, 10) << "init_from_header returned err=" << ret << dendl;
798 return ret;
799 }
800
801 return RGWHandler_REST::init(store, s, cio);
802 }
803
804 int RGWHandler_REST_STS::authorize(const DoutPrefixProvider* dpp, optional_yield y)
805 {
806 if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
807 return RGW_Auth_STS::authorize(dpp, store, auth_registry, s, y);
808 }
809 return RGW_Auth_S3::authorize(dpp, store, auth_registry, s, y);
810 }
811
812 int RGWHandler_REST_STS::init_from_header(struct req_state* s,
813 int default_formatter,
814 bool configurable_format)
815 {
816 string req;
817 string first;
818
819 s->prot_flags = RGW_REST_STS;
820
821 const char *p, *req_name;
822 if (req_name = s->relative_uri.c_str(); *req_name == '?') {
823 p = req_name;
824 } else {
825 p = s->info.request_params.c_str();
826 }
827
828 s->info.args.set(p);
829 s->info.args.parse(s);
830
831 /* must be called after the args parsing */
832 if (int ret = allocate_formatter(s, default_formatter, configurable_format); ret < 0)
833 return ret;
834
835 if (*req_name != '/')
836 return 0;
837
838 req_name++;
839
840 if (!*req_name)
841 return 0;
842
843 req = req_name;
844 int pos = req.find('/');
845 if (pos >= 0) {
846 first = req.substr(0, pos);
847 } else {
848 first = req;
849 }
850
851 return 0;
852 }
853
854 RGWHandler_REST*
855 RGWRESTMgr_STS::get_handler(rgw::sal::Store* store,
856 struct req_state* const s,
857 const rgw::auth::StrategyRegistry& auth_registry,
858 const std::string& frontend_prefix)
859 {
860 return new RGWHandler_REST_STS(auth_registry);
861 }