summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_rest_sts.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/rgw/rgw_rest_sts.cc
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/rgw/rgw_rest_sts.cc')
-rw-r--r--src/rgw/rgw_rest_sts.cc819
1 files changed, 819 insertions, 0 deletions
diff --git a/src/rgw/rgw_rest_sts.cc b/src/rgw/rgw_rest_sts.cc
new file mode 100644
index 000000000..09f77f61d
--- /dev/null
+++ b/src/rgw/rgw_rest_sts.cc
@@ -0,0 +1,819 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+#include <vector>
+#include <string>
+#include <array>
+#include <string_view>
+#include <sstream>
+#include <memory>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/format.hpp>
+#include <boost/optional.hpp>
+#include <boost/utility/in_place_factory.hpp>
+#include <boost/tokenizer.hpp>
+
+
+
+#include "ceph_ver.h"
+#include "common/Formatter.h"
+#include "common/utf8.h"
+#include "common/ceph_json.h"
+
+#include "rgw_rest.h"
+#include "rgw_auth.h"
+#include "rgw_auth_registry.h"
+#include "jwt-cpp/jwt.h"
+#include "rgw_rest_sts.h"
+
+#include "rgw_formats.h"
+#include "rgw_client_io.h"
+
+#include "rgw_request.h"
+#include "rgw_process.h"
+#include "rgw_iam_policy.h"
+#include "rgw_iam_policy_keywords.h"
+
+#include "rgw_sts.h"
+#include "rgw_rest_oidc_provider.h"
+
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+namespace rgw::auth::sts {
+
+bool
+WebTokenEngine::is_applicable(const std::string& token) const noexcept
+{
+ return ! token.empty();
+}
+
+std::string
+WebTokenEngine::get_role_tenant(const string& role_arn) const
+{
+ string tenant;
+ auto r_arn = rgw::ARN::parse(role_arn);
+ if (r_arn) {
+ tenant = r_arn->account;
+ }
+ return tenant;
+}
+
+std::string
+WebTokenEngine::get_role_name(const string& role_arn) const
+{
+ string role_name;
+ auto r_arn = rgw::ARN::parse(role_arn);
+ if (r_arn) {
+ role_name = r_arn->resource;
+ }
+ if (!role_name.empty()) {
+ auto pos = role_name.find_last_of('/');
+ if(pos != string::npos) {
+ role_name = role_name.substr(pos + 1);
+ }
+ }
+ return role_name;
+}
+
+std::unique_ptr<rgw::sal::RGWOIDCProvider>
+WebTokenEngine::get_provider(const DoutPrefixProvider *dpp, const string& role_arn, const string& iss) const
+{
+ string tenant = get_role_tenant(role_arn);
+
+ string idp_url = iss;
+ auto pos = idp_url.find("http://");
+ if (pos == std::string::npos) {
+ pos = idp_url.find("https://");
+ if (pos != std::string::npos) {
+ idp_url.erase(pos, 8);
+ } else {
+ pos = idp_url.find("www.");
+ if (pos != std::string::npos) {
+ idp_url.erase(pos, 4);
+ }
+ }
+ } else {
+ idp_url.erase(pos, 7);
+ }
+ auto provider_arn = rgw::ARN(idp_url, "oidc-provider", tenant);
+ string p_arn = provider_arn.to_string();
+ std::unique_ptr<rgw::sal::RGWOIDCProvider> provider = driver->get_oidc_provider();
+ provider->set_arn(p_arn);
+ provider->set_tenant(tenant);
+ auto ret = provider->get(dpp);
+ if (ret < 0) {
+ return nullptr;
+ }
+ return provider;
+}
+
+bool
+WebTokenEngine::is_client_id_valid(vector<string>& client_ids, const string& client_id) const
+{
+ for (auto it : client_ids) {
+ if (it == client_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+WebTokenEngine::is_cert_valid(const vector<string>& thumbprints, const string& cert) const
+{
+ //calculate thumbprint of cert
+ std::unique_ptr<BIO, decltype(&BIO_free_all)> certbio(BIO_new_mem_buf(cert.data(), cert.size()), BIO_free_all);
+ std::unique_ptr<BIO, decltype(&BIO_free_all)> keybio(BIO_new(BIO_s_mem()), BIO_free_all);
+ string pw="";
+ 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);
+ const EVP_MD* fprint_type = EVP_sha1();
+ unsigned int fprint_size;
+ unsigned char fprint[EVP_MAX_MD_SIZE];
+
+ if (!X509_digest(x_509cert.get(), fprint_type, fprint, &fprint_size)) {
+ return false;
+ }
+ stringstream ss;
+ for (unsigned int i = 0; i < fprint_size; i++) {
+ ss << std::setfill('0') << std::setw(2) << std::hex << (0xFF & (unsigned int)fprint[i]);
+ }
+ std::string digest = ss.str();
+
+ for (auto& it : thumbprints) {
+ if (boost::iequals(it,digest)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+void
+WebTokenEngine::recurse_and_insert(const string& key, const jwt::claim& c, T& t) const
+{
+ string s_val;
+ jwt::claim::type c_type = c.get_type();
+ switch(c_type) {
+ case jwt::claim::type::null:
+ break;
+ case jwt::claim::type::boolean:
+ case jwt::claim::type::number:
+ case jwt::claim::type::int64:
+ {
+ s_val = c.to_json().serialize();
+ t.emplace(std::make_pair(key, s_val));
+ break;
+ }
+ case jwt::claim::type::string:
+ {
+ s_val = c.to_json().to_str();
+ t.emplace(std::make_pair(key, s_val));
+ break;
+ }
+ case jwt::claim::type::array:
+ {
+ const picojson::array& arr = c.as_array();
+ for (auto& a : arr) {
+ recurse_and_insert(key, jwt::claim(a), t);
+ }
+ break;
+ }
+ case jwt::claim::type::object:
+ {
+ const picojson::object& obj = c.as_object();
+ for (auto& m : obj) {
+ recurse_and_insert(m.first, jwt::claim(m.second), t);
+ }
+ break;
+ }
+ }
+ return;
+}
+
+//Extract all token claims so that they can be later used in the Condition element of Role's trust policy
+WebTokenEngine::token_t
+WebTokenEngine::get_token_claims(const jwt::decoded_jwt& decoded) const
+{
+ WebTokenEngine::token_t token;
+ const auto& claims = decoded.get_payload_claims();
+
+ for (auto& c : claims) {
+ if (c.first == string(princTagsNamespace)) {
+ continue;
+ }
+ recurse_and_insert(c.first, c.second, token);
+ }
+ return token;
+}
+
+//Offline validation of incoming Web Token which is a signed JWT (JSON Web Token)
+std::tuple<boost::optional<WebTokenEngine::token_t>, boost::optional<WebTokenEngine::principal_tags_t>>
+WebTokenEngine::get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s,
+ optional_yield y) const
+{
+ WebTokenEngine::token_t t;
+ WebTokenEngine::principal_tags_t principal_tags;
+ try {
+ const auto& decoded = jwt::decode(token);
+
+ auto& payload = decoded.get_payload();
+ ldpp_dout(dpp, 20) << " payload = " << payload << dendl;
+
+ t = get_token_claims(decoded);
+
+ string iss;
+ if (decoded.has_issuer()) {
+ iss = decoded.get_issuer();
+ }
+
+ set<string> aud;
+ if (decoded.has_audience()) {
+ aud = decoded.get_audience();
+ }
+
+ string client_id;
+ if (decoded.has_payload_claim("client_id")) {
+ client_id = decoded.get_payload_claim("client_id").as_string();
+ }
+ if (client_id.empty() && decoded.has_payload_claim("clientId")) {
+ client_id = decoded.get_payload_claim("clientId").as_string();
+ }
+ string azp;
+ if (decoded.has_payload_claim("azp")) {
+ azp = decoded.get_payload_claim("azp").as_string();
+ }
+
+ string role_arn = s->info.args.get("RoleArn");
+ auto provider = get_provider(dpp, role_arn, iss);
+ if (! provider) {
+ ldpp_dout(dpp, 0) << "Couldn't get oidc provider info using input iss" << iss << dendl;
+ throw -EACCES;
+ }
+ if (decoded.has_payload_claim(string(princTagsNamespace))) {
+ auto& cl = decoded.get_payload_claim(string(princTagsNamespace));
+ if (cl.get_type() == jwt::claim::type::object || cl.get_type() == jwt::claim::type::array) {
+ recurse_and_insert("dummy", cl, principal_tags);
+ for (auto it : principal_tags) {
+ ldpp_dout(dpp, 5) << "Key: " << it.first << " Value: " << it.second << dendl;
+ }
+ } else {
+ ldpp_dout(dpp, 0) << "Malformed principal tags" << cl.as_string() << dendl;
+ throw -EINVAL;
+ }
+ }
+ vector<string> client_ids = provider->get_client_ids();
+ vector<string> thumbprints = provider->get_thumbprints();
+ if (! client_ids.empty()) {
+ bool found = false;
+ for (auto& it : aud) {
+ if (is_client_id_valid(client_ids, it)) {
+ found = true;
+ break;
+ }
+ }
+ if (! found && ! is_client_id_valid(client_ids, client_id) && ! is_client_id_valid(client_ids, azp)) {
+ ldpp_dout(dpp, 0) << "Client id in token doesn't match with that registered with oidc provider" << dendl;
+ throw -EACCES;
+ }
+ }
+ //Validate signature
+ if (decoded.has_algorithm()) {
+ auto& algorithm = decoded.get_algorithm();
+ try {
+ validate_signature(dpp, decoded, algorithm, iss, thumbprints, y);
+ } catch (...) {
+ throw -EACCES;
+ }
+ } else {
+ return {boost::none, boost::none};
+ }
+ } catch (int error) {
+ if (error == -EACCES) {
+ throw -EACCES;
+ }
+ ldpp_dout(dpp, 5) << "Invalid JWT token" << dendl;
+ return {boost::none, boost::none};
+ }
+ catch (...) {
+ ldpp_dout(dpp, 5) << "Invalid JWT token" << dendl;
+ return {boost::none, boost::none};
+ }
+ return {t, principal_tags};
+}
+
+std::string
+WebTokenEngine::get_cert_url(const string& iss, const DoutPrefixProvider *dpp, optional_yield y) const
+{
+ string cert_url;
+ string openidc_wellknown_url = iss;
+ bufferlist openidc_resp;
+
+ if (openidc_wellknown_url.back() == '/') {
+ openidc_wellknown_url.pop_back();
+ }
+ openidc_wellknown_url.append("/.well-known/openid-configuration");
+
+ RGWHTTPTransceiver openidc_req(cct, "GET", openidc_wellknown_url, &openidc_resp);
+
+ //Headers
+ openidc_req.append_header("Content-Type", "application/x-www-form-urlencoded");
+
+ int res = openidc_req.process(y);
+ if (res < 0) {
+ ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
+ throw -EINVAL;
+ }
+
+ //Debug only
+ ldpp_dout(dpp, 20) << "HTTP status: " << openidc_req.get_http_status() << dendl;
+ ldpp_dout(dpp, 20) << "JSON Response is: " << openidc_resp.c_str() << dendl;
+
+ JSONParser parser;
+ if (parser.parse(openidc_resp.c_str(), openidc_resp.length())) {
+ JSONObj::data_val val;
+ if (parser.get_data("jwks_uri", &val)) {
+ cert_url = val.str.c_str();
+ ldpp_dout(dpp, 20) << "Cert URL is: " << cert_url.c_str() << dendl;
+ } else {
+ ldpp_dout(dpp, 0) << "Malformed json returned while fetching openidc url" << dendl;
+ }
+ }
+ return cert_url;
+}
+
+void
+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
+{
+ if (algorithm != "HS256" && algorithm != "HS384" && algorithm != "HS512") {
+ string cert_url = get_cert_url(iss, dpp, y);
+ if (cert_url.empty()) {
+ throw -EINVAL;
+ }
+
+ // Get certificate
+ bufferlist cert_resp;
+ RGWHTTPTransceiver cert_req(cct, "GET", cert_url, &cert_resp);
+ //Headers
+ cert_req.append_header("Content-Type", "application/x-www-form-urlencoded");
+
+ int res = cert_req.process(y);
+ if (res < 0) {
+ ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
+ throw -EINVAL;
+ }
+ //Debug only
+ ldpp_dout(dpp, 20) << "HTTP status: " << cert_req.get_http_status() << dendl;
+ ldpp_dout(dpp, 20) << "JSON Response is: " << cert_resp.c_str() << dendl;
+
+ JSONParser parser;
+ if (parser.parse(cert_resp.c_str(), cert_resp.length())) {
+ JSONObj::data_val val;
+ if (parser.get_data("keys", &val)) {
+ if (val.str[0] == '[') {
+ val.str.erase(0, 1);
+ }
+ if (val.str[val.str.size() - 1] == ']') {
+ val.str = val.str.erase(val.str.size() - 1, 1);
+ }
+ if (parser.parse(val.str.c_str(), val.str.size())) {
+ vector<string> x5c;
+ if (JSONDecoder::decode_json("x5c", x5c, &parser)) {
+ string cert;
+ bool found_valid_cert = false;
+ for (auto& it : x5c) {
+ cert = "-----BEGIN CERTIFICATE-----\n" + it + "\n-----END CERTIFICATE-----";
+ ldpp_dout(dpp, 20) << "Certificate is: " << cert.c_str() << dendl;
+ if (is_cert_valid(thumbprints, cert)) {
+ found_valid_cert = true;
+ break;
+ }
+ found_valid_cert = true;
+ }
+ if (! found_valid_cert) {
+ ldpp_dout(dpp, 0) << "Cert doesn't match that with the thumbprints registered with oidc provider: " << cert.c_str() << dendl;
+ throw -EINVAL;
+ }
+ try {
+ //verify method takes care of expired tokens also
+ if (algorithm == "RS256") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::rs256{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "RS384") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::rs384{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "RS512") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::rs512{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "ES256") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::es256{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "ES384") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::es384{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "ES512") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::es512{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "PS256") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::ps256{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "PS384") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::ps384{cert});
+
+ verifier.verify(decoded);
+ } else if (algorithm == "PS512") {
+ auto verifier = jwt::verify()
+ .allow_algorithm(jwt::algorithm::ps512{cert});
+
+ verifier.verify(decoded);
+ }
+ } catch (std::runtime_error& e) {
+ ldpp_dout(dpp, 0) << "Signature validation failed: " << e.what() << dendl;
+ throw;
+ }
+ catch (...) {
+ ldpp_dout(dpp, 0) << "Signature validation failed" << dendl;
+ throw;
+ }
+ } else {
+ ldpp_dout(dpp, 0) << "x5c not present" << dendl;
+ throw -EINVAL;
+ }
+ } else {
+ ldpp_dout(dpp, 0) << "Malformed JSON object for keys" << dendl;
+ throw -EINVAL;
+ }
+ } else {
+ ldpp_dout(dpp, 0) << "keys not present in JSON" << dendl;
+ throw -EINVAL;
+ } //if-else get-data
+ } else {
+ ldpp_dout(dpp, 0) << "Malformed json returned while fetching cert" << dendl;
+ throw -EINVAL;
+ } //if-else parser cert_resp
+ } else {
+ ldpp_dout(dpp, 0) << "JWT signed by HMAC algos are currently not supported" << dendl;
+ throw -EINVAL;
+ }
+}
+
+WebTokenEngine::result_t
+WebTokenEngine::authenticate( const DoutPrefixProvider* dpp,
+ const std::string& token,
+ const req_state* const s,
+ optional_yield y) const
+{
+ if (! is_applicable(token)) {
+ return result_t::deny();
+ }
+
+ try {
+ auto [t, princ_tags] = get_from_jwt(dpp, token, s, y);
+ if (t) {
+ string role_session = s->info.args.get("RoleSessionName");
+ if (role_session.empty()) {
+ ldout(s->cct, 0) << "Role Session Name is empty " << dendl;
+ return result_t::deny(-EACCES);
+ }
+ string role_arn = s->info.args.get("RoleArn");
+ string role_tenant = get_role_tenant(role_arn);
+ string role_name = get_role_name(role_arn);
+ std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, role_tenant);
+ int ret = role->get(dpp, y);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "Role not found: name:" << role_name << " tenant: " << role_tenant << dendl;
+ return result_t::deny(-EACCES);
+ }
+ boost::optional<multimap<string,string>> role_tags = role->get_tags();
+ auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, role_tenant, *t, role_tags, princ_tags);
+ return result_t::grant(std::move(apl));
+ }
+ return result_t::deny(-EACCES);
+ }
+ catch (...) {
+ return result_t::deny(-EACCES);
+ }
+}
+
+} // namespace rgw::auth::sts
+
+int RGWREST_STS::verify_permission(optional_yield y)
+{
+ STS::STSService _sts(s->cct, driver, s->user->get_id(), s->auth.identity.get());
+ sts = std::move(_sts);
+
+ string rArn = s->info.args.get("RoleArn");
+ const auto& [ret, role] = sts.getRoleInfo(s, rArn, y);
+ if (ret < 0) {
+ ldpp_dout(this, 0) << "failed to get role info using role arn: " << rArn << dendl;
+ return ret;
+ }
+ string policy = role->get_assume_role_policy();
+ buffer::list bl = buffer::list::static_from_string(policy);
+
+ //Parse the policy
+ //TODO - This step should be part of Role Creation
+ try {
+ const rgw::IAM::Policy p(s->cct, s->user->get_tenant(), bl, false);
+ if (!s->principal_tags.empty()) {
+ auto res = p.eval(s->env, *s->auth.identity, rgw::IAM::stsTagSession, boost::none);
+ if (res != rgw::IAM::Effect::Allow) {
+ ldout(s->cct, 0) << "evaluating policy for stsTagSession returned deny/pass" << dendl;
+ return -EPERM;
+ }
+ }
+ uint64_t op;
+ if (get_type() == RGW_STS_ASSUME_ROLE_WEB_IDENTITY) {
+ op = rgw::IAM::stsAssumeRoleWithWebIdentity;
+ } else {
+ op = rgw::IAM::stsAssumeRole;
+ }
+
+ auto res = p.eval(s->env, *s->auth.identity, op, boost::none);
+ if (res != rgw::IAM::Effect::Allow) {
+ ldout(s->cct, 0) << "evaluating policy for op: " << op << " returned deny/pass" << dendl;
+ return -EPERM;
+ }
+ } catch (rgw::IAM::PolicyParseException& e) {
+ ldpp_dout(this, 0) << "failed to parse policy: " << e.what() << dendl;
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+void RGWREST_STS::send_response()
+{
+ if (op_ret) {
+ set_req_state_err(s, op_ret);
+ }
+ dump_errno(s);
+ end_header(s);
+}
+
+int RGWSTSGetSessionToken::verify_permission(optional_yield y)
+{
+ rgw::Partition partition = rgw::Partition::aws;
+ rgw::Service service = rgw::Service::s3;
+ if (!verify_user_permission(this,
+ s,
+ rgw::ARN(partition, service, "", s->user->get_tenant(), ""),
+ rgw::IAM::stsGetSessionToken)) {
+ ldpp_dout(this, 0) << "User does not have permssion to perform GetSessionToken" << dendl;
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+int RGWSTSGetSessionToken::get_params()
+{
+ duration = s->info.args.get("DurationSeconds");
+ serialNumber = s->info.args.get("SerialNumber");
+ tokenCode = s->info.args.get("TokenCode");
+
+ if (! duration.empty()) {
+ string err;
+ uint64_t duration_in_secs = strict_strtoll(duration.c_str(), 10, &err);
+ if (!err.empty()) {
+ ldpp_dout(this, 0) << "Invalid value of input duration: " << duration << dendl;
+ return -EINVAL;
+ }
+
+ if (duration_in_secs < STS::GetSessionTokenRequest::getMinDuration() ||
+ duration_in_secs > s->cct->_conf->rgw_sts_max_session_duration) {
+ ldpp_dout(this, 0) << "Invalid duration in secs: " << duration_in_secs << dendl;
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+void RGWSTSGetSessionToken::execute(optional_yield y)
+{
+ if (op_ret = get_params(); op_ret < 0) {
+ return;
+ }
+
+ STS::STSService sts(s->cct, driver, s->user->get_id(), s->auth.identity.get());
+
+ STS::GetSessionTokenRequest req(duration, serialNumber, tokenCode);
+ const auto& [ret, creds] = sts.getSessionToken(this, req);
+ op_ret = std::move(ret);
+ //Dump the output
+ if (op_ret == 0) {
+ s->formatter->open_object_section("GetSessionTokenResponse");
+ s->formatter->open_object_section("GetSessionTokenResult");
+ s->formatter->open_object_section("Credentials");
+ creds.dump(s->formatter);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
+
+int RGWSTSAssumeRoleWithWebIdentity::get_params()
+{
+ duration = s->info.args.get("DurationSeconds");
+ providerId = s->info.args.get("ProviderId");
+ policy = s->info.args.get("Policy");
+ roleArn = s->info.args.get("RoleArn");
+ roleSessionName = s->info.args.get("RoleSessionName");
+ iss = s->info.args.get("provider_id");
+ sub = s->info.args.get("sub");
+ aud = s->info.args.get("aud");
+
+ if (roleArn.empty() || roleSessionName.empty() || sub.empty() || aud.empty()) {
+ ldpp_dout(this, 0) << "ERROR: one of role arn or role session name or token is empty" << dendl;
+ return -EINVAL;
+ }
+
+ if (! policy.empty()) {
+ bufferlist bl = bufferlist::static_from_string(policy);
+ try {
+ const rgw::IAM::Policy p(
+ s->cct, s->user->get_tenant(), bl,
+ s->cct->_conf.get_val<bool>("rgw_policy_reject_invalid_principals"));
+ }
+ catch (rgw::IAM::PolicyParseException& e) {
+ ldpp_dout(this, 5) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
+ s->err.message = e.what();
+ return -ERR_MALFORMED_DOC;
+ }
+ }
+
+ return 0;
+}
+
+void RGWSTSAssumeRoleWithWebIdentity::execute(optional_yield y)
+{
+ if (op_ret = get_params(); op_ret < 0) {
+ return;
+ }
+
+ STS::AssumeRoleWithWebIdentityRequest req(s->cct, duration, providerId, policy, roleArn,
+ roleSessionName, iss, sub, aud, s->principal_tags);
+ STS::AssumeRoleWithWebIdentityResponse response = sts.assumeRoleWithWebIdentity(this, req);
+ op_ret = std::move(response.assumeRoleResp.retCode);
+
+ //Dump the output
+ if (op_ret == 0) {
+ s->formatter->open_object_section("AssumeRoleWithWebIdentityResponse");
+ s->formatter->open_object_section("AssumeRoleWithWebIdentityResult");
+ encode_json("SubjectFromWebIdentityToken", response.sub , s->formatter);
+ encode_json("Audience", response.aud , s->formatter);
+ s->formatter->open_object_section("AssumedRoleUser");
+ response.assumeRoleResp.user.dump(s->formatter);
+ s->formatter->close_section();
+ s->formatter->open_object_section("Credentials");
+ response.assumeRoleResp.creds.dump(s->formatter);
+ s->formatter->close_section();
+ encode_json("Provider", response.providerId , s->formatter);
+ encode_json("PackedPolicySize", response.assumeRoleResp.packedPolicySize , s->formatter);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
+
+int RGWSTSAssumeRole::get_params()
+{
+ duration = s->info.args.get("DurationSeconds");
+ externalId = s->info.args.get("ExternalId");
+ policy = s->info.args.get("Policy");
+ roleArn = s->info.args.get("RoleArn");
+ roleSessionName = s->info.args.get("RoleSessionName");
+ serialNumber = s->info.args.get("SerialNumber");
+ tokenCode = s->info.args.get("TokenCode");
+
+ if (roleArn.empty() || roleSessionName.empty()) {
+ ldpp_dout(this, 0) << "ERROR: one of role arn or role session name is empty" << dendl;
+ return -EINVAL;
+ }
+
+ if (! policy.empty()) {
+ bufferlist bl = bufferlist::static_from_string(policy);
+ try {
+ const rgw::IAM::Policy p(
+ s->cct, s->user->get_tenant(), bl,
+ s->cct->_conf.get_val<bool>("rgw_policy_reject_invalid_principals"));
+ }
+ catch (rgw::IAM::PolicyParseException& e) {
+ ldpp_dout(this, 0) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
+ s->err.message = e.what();
+ return -ERR_MALFORMED_DOC;
+ }
+ }
+
+ return 0;
+}
+
+void RGWSTSAssumeRole::execute(optional_yield y)
+{
+ if (op_ret = get_params(); op_ret < 0) {
+ return;
+ }
+
+ STS::AssumeRoleRequest req(s->cct, duration, externalId, policy, roleArn,
+ roleSessionName, serialNumber, tokenCode);
+ STS::AssumeRoleResponse response = sts.assumeRole(s, req, y);
+ op_ret = std::move(response.retCode);
+ //Dump the output
+ if (op_ret == 0) {
+ s->formatter->open_object_section("AssumeRoleResponse");
+ s->formatter->open_object_section("AssumeRoleResult");
+ s->formatter->open_object_section("Credentials");
+ response.creds.dump(s->formatter);
+ s->formatter->close_section();
+ s->formatter->open_object_section("AssumedRoleUser");
+ response.user.dump(s->formatter);
+ s->formatter->close_section();
+ encode_json("PackedPolicySize", response.packedPolicySize , s->formatter);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
+
+int RGW_Auth_STS::authorize(const DoutPrefixProvider *dpp,
+ rgw::sal::Driver* driver,
+ const rgw::auth::StrategyRegistry& auth_registry,
+ req_state *s, optional_yield y)
+{
+ return rgw::auth::Strategy::apply(dpp, auth_registry.get_sts(), s, y);
+}
+
+using op_generator = RGWOp*(*)();
+static const std::unordered_map<std::string_view, op_generator> op_generators = {
+ {"AssumeRole", []() -> RGWOp* {return new RGWSTSAssumeRole;}},
+ {"GetSessionToken", []() -> RGWOp* {return new RGWSTSGetSessionToken;}},
+ {"AssumeRoleWithWebIdentity", []() -> RGWOp* {return new RGWSTSAssumeRoleWithWebIdentity;}}
+};
+
+bool RGWHandler_REST_STS::action_exists(const req_state* s)
+{
+ if (s->info.args.exists("Action")) {
+ const std::string action_name = s->info.args.get("Action");
+ return op_generators.contains(action_name);
+ }
+ return false;
+}
+
+RGWOp *RGWHandler_REST_STS::op_post()
+{
+ if (s->info.args.exists("Action")) {
+ const std::string action_name = s->info.args.get("Action");
+ const auto action_it = op_generators.find(action_name);
+ if (action_it != op_generators.end()) {
+ return action_it->second();
+ }
+ ldpp_dout(s, 10) << "unknown action '" << action_name << "' for STS handler" << dendl;
+ } else {
+ ldpp_dout(s, 10) << "missing action argument in STS handler" << dendl;
+ }
+ return nullptr;
+}
+
+int RGWHandler_REST_STS::init(rgw::sal::Driver* driver,
+ req_state *s,
+ rgw::io::BasicClient *cio)
+{
+ s->dialect = "sts";
+ s->prot_flags = RGW_REST_STS;
+
+ return RGWHandler_REST::init(driver, s, cio);
+}
+
+int RGWHandler_REST_STS::authorize(const DoutPrefixProvider* dpp, optional_yield y)
+{
+ if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+ return RGW_Auth_STS::authorize(dpp, driver, auth_registry, s, y);
+ }
+ return RGW_Auth_S3::authorize(dpp, driver, auth_registry, s, y);
+}
+
+RGWHandler_REST*
+RGWRESTMgr_STS::get_handler(rgw::sal::Driver* driver,
+ req_state* const s,
+ const rgw::auth::StrategyRegistry& auth_registry,
+ const std::string& frontend_prefix)
+{
+ return new RGWHandler_REST_STS(auth_registry);
+}