1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
4 #include <boost/algorithm/string/predicate.hpp>
5 #include <boost/format.hpp>
6 #include <boost/optional.hpp>
7 #include <boost/utility/in_place_factory.hpp>
8 #include <boost/tokenizer.hpp>
12 #include "common/Formatter.h"
13 #include "common/utf8.h"
14 #include "common/ceph_json.h"
18 #include "rgw_auth_registry.h"
19 #include "rgw_rest_sts.h"
21 #include "rgw_formats.h"
22 #include "rgw_client_io.h"
24 #include "rgw_request.h"
25 #include "rgw_process.h"
26 #include "rgw_iam_policy.h"
27 #include "rgw_iam_policy_keywords.h"
35 #include <boost/utility/string_ref.hpp>
37 #define dout_context g_ceph_context
38 #define dout_subsys ceph_subsys_rgw
40 namespace rgw::auth::sts
{
43 WebTokenEngine::is_applicable(const std::string
& token
) const noexcept
45 return ! token
.empty();
48 boost::optional
<WebTokenEngine::token_t
>
49 WebTokenEngine::get_from_idp(const DoutPrefixProvider
* dpp
, const std::string
& token
) const
51 //Access token conforming to OAuth2.0
52 if (! cct
->_conf
->rgw_sts_token_introspection_url
.empty()) {
53 bufferlist introspect_resp
;
54 RGWHTTPTransceiver
introspect_req(cct
, "POST", cct
->_conf
->rgw_sts_token_introspection_url
, &introspect_resp
);
56 introspect_req
.append_header("Content-Type", "application/x-www-form-urlencoded");
57 string base64_creds
= "Basic " + rgw::to_base64(cct
->_conf
->rgw_sts_client_id
+ ":" + cct
->_conf
->rgw_sts_client_secret
);
58 introspect_req
.append_header("Authorization", base64_creds
);
60 string post_data
= "token=" + token
;
61 introspect_req
.set_post_data(post_data
);
62 introspect_req
.set_send_length(post_data
.length());
64 int res
= introspect_req
.process(null_yield
);
66 ldpp_dout(dpp
, 10) << "HTTP request res: " << res
<< dendl
;
70 ldpp_dout(dpp
, 20) << "HTTP status: " << introspect_req
.get_http_status() << dendl
;
71 ldpp_dout(dpp
, 20) << "JSON Response is: " << introspect_resp
.c_str() << dendl
;
74 WebTokenEngine::token_t token
;
75 if (!parser
.parse(introspect_resp
.c_str(), introspect_resp
.length())) {
76 ldpp_dout(dpp
, 2) << "Malformed json" << dendl
;
80 JSONDecoder::decode_json("active", is_active
, &parser
);
82 ldpp_dout(dpp
, 0) << "Active state is false" << dendl
;
83 throw -ERR_INVALID_IDENTITY_TOKEN
;
85 JSONDecoder::decode_json("iss", token
.iss
, &parser
);
86 JSONDecoder::decode_json("aud", token
.aud
, &parser
);
87 JSONDecoder::decode_json("sub", token
.sub
, &parser
);
88 JSONDecoder::decode_json("user_name", token
.user_name
, &parser
);
95 WebTokenEngine::result_t
96 WebTokenEngine::authenticate( const DoutPrefixProvider
* dpp
,
97 const std::string
& token
,
98 const req_state
* const s
) const
100 boost::optional
<WebTokenEngine::token_t
> t
;
102 if (! is_applicable(token
)) {
103 return result_t::deny();
107 t
= get_from_idp(dpp
, token
);
109 return result_t::deny(-EACCES
);
113 auto apl
= apl_factory
->create_apl_web_identity(cct
, s
, *t
);
114 return result_t::grant(std::move(apl
));
116 return result_t::deny(-EACCES
);
119 } // namespace rgw::auth::s3
121 int RGWREST_STS::verify_permission()
123 STS::STSService
_sts(s
->cct
, store
, s
->user
->get_id(), s
->auth
.identity
.get());
124 sts
= std::move(_sts
);
126 string rArn
= s
->info
.args
.get("RoleArn");
127 const auto& [ret
, role
] = sts
.getRoleInfo(rArn
);
131 string policy
= role
.get_assume_role_policy();
132 buffer::list bl
= buffer::list::static_from_string(policy
);
135 //TODO - This step should be part of Role Creation
137 const rgw::IAM::Policy
p(s
->cct
, s
->user
->get_tenant(), bl
);
138 //Check if the input role arn is there as one of the Principals in the policy,
139 // If yes, then return 0, else -EPERM
140 auto p_res
= p
.eval_principal(s
->env
, *s
->auth
.identity
);
141 if (p_res
== rgw::IAM::Effect::Deny
) {
144 auto c_res
= p
.eval_conditions(s
->env
);
145 if (c_res
== rgw::IAM::Effect::Deny
) {
148 } catch (rgw::IAM::PolicyParseException
& e
) {
149 ldout(s
->cct
, 20) << "failed to parse policy: " << e
.what() << dendl
;
156 void RGWREST_STS::send_response()
159 set_req_state_err(s
, op_ret
);
165 int RGWSTSGetSessionToken::verify_permission()
167 rgw::Partition partition
= rgw::Partition::aws
;
168 rgw::Service service
= rgw::Service::s3
;
169 if (!verify_user_permission(this,
171 rgw::ARN(partition
, service
, "", s
->user
->get_tenant(), ""),
172 rgw::IAM::stsGetSessionToken
)) {
179 int RGWSTSGetSessionToken::get_params()
181 duration
= s
->info
.args
.get("DurationSeconds");
182 serialNumber
= s
->info
.args
.get("SerialNumber");
183 tokenCode
= s
->info
.args
.get("TokenCode");
185 if (! duration
.empty()) {
187 uint64_t duration_in_secs
= strict_strtoll(duration
.c_str(), 10, &err
);
192 if (duration_in_secs
< STS::GetSessionTokenRequest::getMinDuration() ||
193 duration_in_secs
> s
->cct
->_conf
->rgw_sts_max_session_duration
)
200 void RGWSTSGetSessionToken::execute()
202 if (op_ret
= get_params(); op_ret
< 0) {
206 STS::STSService
sts(s
->cct
, store
, s
->user
->get_id(), s
->auth
.identity
.get());
208 STS::GetSessionTokenRequest
req(duration
, serialNumber
, tokenCode
);
209 const auto& [ret
, creds
] = sts
.getSessionToken(req
);
210 op_ret
= std::move(ret
);
213 s
->formatter
->open_object_section("GetSessionTokenResponse");
214 s
->formatter
->open_object_section("GetSessionTokenResult");
215 s
->formatter
->open_object_section("Credentials");
216 creds
.dump(s
->formatter
);
217 s
->formatter
->close_section();
218 s
->formatter
->close_section();
219 s
->formatter
->close_section();
223 int RGWSTSAssumeRoleWithWebIdentity::get_params()
225 duration
= s
->info
.args
.get("DurationSeconds");
226 providerId
= s
->info
.args
.get("ProviderId");
227 policy
= s
->info
.args
.get("Policy");
228 roleArn
= s
->info
.args
.get("RoleArn");
229 roleSessionName
= s
->info
.args
.get("RoleSessionName");
230 iss
= s
->info
.args
.get("provider_id");
231 sub
= s
->info
.args
.get("sub");
232 aud
= s
->info
.args
.get("aud");
234 if (roleArn
.empty() || roleSessionName
.empty() || sub
.empty() || aud
.empty()) {
235 ldout(s
->cct
, 20) << "ERROR: one of role arn or role session name or token is empty" << dendl
;
239 if (! policy
.empty()) {
240 bufferlist bl
= bufferlist::static_from_string(policy
);
242 const rgw::IAM::Policy
p(s
->cct
, s
->user
->get_tenant(), bl
);
244 catch (rgw::IAM::PolicyParseException
& e
) {
245 ldout(s
->cct
, 20) << "failed to parse policy: " << e
.what() << "policy" << policy
<< dendl
;
246 return -ERR_MALFORMED_DOC
;
253 void RGWSTSAssumeRoleWithWebIdentity::execute()
255 if (op_ret
= get_params(); op_ret
< 0) {
259 STS::AssumeRoleWithWebIdentityRequest
req(duration
, providerId
, policy
, roleArn
,
260 roleSessionName
, iss
, sub
, aud
);
261 STS::AssumeRoleWithWebIdentityResponse response
= sts
.assumeRoleWithWebIdentity(req
);
262 op_ret
= std::move(response
.assumeRoleResp
.retCode
);
266 s
->formatter
->open_object_section("AssumeRoleWithWebIdentityResponse");
267 s
->formatter
->open_object_section("AssumeRoleWithWebIdentityResult");
268 encode_json("SubjectFromWebIdentityToken", response
.sub
, s
->formatter
);
269 encode_json("Audience", response
.aud
, s
->formatter
);
270 s
->formatter
->open_object_section("AssumedRoleUser");
271 response
.assumeRoleResp
.user
.dump(s
->formatter
);
272 s
->formatter
->close_section();
273 s
->formatter
->open_object_section("Credentials");
274 response
.assumeRoleResp
.creds
.dump(s
->formatter
);
275 s
->formatter
->close_section();
276 encode_json("Provider", response
.providerId
, s
->formatter
);
277 encode_json("PackedPolicySize", response
.assumeRoleResp
.packedPolicySize
, s
->formatter
);
278 s
->formatter
->close_section();
279 s
->formatter
->close_section();
283 int RGWSTSAssumeRole::get_params()
285 duration
= s
->info
.args
.get("DurationSeconds");
286 externalId
= s
->info
.args
.get("ExternalId");
287 policy
= s
->info
.args
.get("Policy");
288 roleArn
= s
->info
.args
.get("RoleArn");
289 roleSessionName
= s
->info
.args
.get("RoleSessionName");
290 serialNumber
= s
->info
.args
.get("SerialNumber");
291 tokenCode
= s
->info
.args
.get("TokenCode");
293 if (roleArn
.empty() || roleSessionName
.empty()) {
294 ldout(s
->cct
, 20) << "ERROR: one of role arn or role session name is empty" << dendl
;
298 if (! policy
.empty()) {
299 bufferlist bl
= bufferlist::static_from_string(policy
);
301 const rgw::IAM::Policy
p(s
->cct
, s
->user
->get_tenant(), bl
);
303 catch (rgw::IAM::PolicyParseException
& e
) {
304 ldout(s
->cct
, 20) << "failed to parse policy: " << e
.what() << "policy" << policy
<< dendl
;
305 return -ERR_MALFORMED_DOC
;
312 void RGWSTSAssumeRole::execute()
314 if (op_ret
= get_params(); op_ret
< 0) {
318 STS::AssumeRoleRequest
req(duration
, externalId
, policy
, roleArn
,
319 roleSessionName
, serialNumber
, tokenCode
);
320 STS::AssumeRoleResponse response
= sts
.assumeRole(req
);
321 op_ret
= std::move(response
.retCode
);
324 s
->formatter
->open_object_section("AssumeRoleResponse");
325 s
->formatter
->open_object_section("AssumeRoleResult");
326 s
->formatter
->open_object_section("Credentials");
327 response
.creds
.dump(s
->formatter
);
328 s
->formatter
->close_section();
329 s
->formatter
->open_object_section("AssumedRoleUser");
330 response
.user
.dump(s
->formatter
);
331 s
->formatter
->close_section();
332 encode_json("PackedPolicySize", response
.packedPolicySize
, s
->formatter
);
333 s
->formatter
->close_section();
334 s
->formatter
->close_section();
338 int RGW_Auth_STS::authorize(const DoutPrefixProvider
*dpp
,
339 rgw::sal::RGWRadosStore
*store
,
340 const rgw::auth::StrategyRegistry
& auth_registry
,
343 return rgw::auth::Strategy::apply(dpp
, auth_registry
.get_sts(), s
);
346 void RGWHandler_REST_STS::rgw_sts_parse_input()
348 if (post_body
.size() > 0) {
349 ldout(s
->cct
, 10) << "Content of POST: " << post_body
<< dendl
;
351 if (post_body
.find("Action") != string::npos
) {
352 boost::char_separator
<char> sep("&");
353 boost::tokenizer
<boost::char_separator
<char>> tokens(post_body
, sep
);
354 for (const auto& t
: tokens
) {
355 auto pos
= t
.find("=");
356 if (pos
!= string::npos
) {
357 s
->info
.args
.append(t
.substr(0,pos
),
358 url_decode(t
.substr(pos
+1, t
.size() -1)));
363 auto payload_hash
= rgw::auth::s3::calc_v4_payload_hash(post_body
);
364 s
->info
.args
.append("PayloadHash", payload_hash
);
367 RGWOp
*RGWHandler_REST_STS::op_post()
369 rgw_sts_parse_input();
371 if (s
->info
.args
.exists("Action")) {
372 string action
= s
->info
.args
.get("Action");
373 if (action
== "AssumeRole") {
374 return new RGWSTSAssumeRole
;
375 } else if (action
== "GetSessionToken") {
376 return new RGWSTSGetSessionToken
;
377 } else if (action
== "AssumeRoleWithWebIdentity") {
378 return new RGWSTSAssumeRoleWithWebIdentity
;
385 int RGWHandler_REST_STS::init(rgw::sal::RGWRadosStore
*store
,
387 rgw::io::BasicClient
*cio
)
391 if (int ret
= RGWHandler_REST_STS::init_from_header(s
, RGW_FORMAT_XML
, true); ret
< 0) {
392 ldout(s
->cct
, 10) << "init_from_header returned err=" << ret
<< dendl
;
396 return RGWHandler_REST::init(store
, s
, cio
);
399 int RGWHandler_REST_STS::authorize(const DoutPrefixProvider
* dpp
)
401 if (s
->info
.args
.exists("Action") && s
->info
.args
.get("Action") == "AssumeRoleWithWebIdentity") {
402 return RGW_Auth_STS::authorize(dpp
, store
, auth_registry
, s
);
404 return RGW_Auth_S3::authorize(dpp
, store
, auth_registry
, s
);
407 int RGWHandler_REST_STS::init_from_header(struct req_state
* s
,
408 int default_formatter
,
409 bool configurable_format
)
414 s
->prot_flags
= RGW_REST_STS
;
416 const char *p
, *req_name
;
417 if (req_name
= s
->relative_uri
.c_str(); *req_name
== '?') {
420 p
= s
->info
.request_params
.c_str();
424 s
->info
.args
.parse();
426 /* must be called after the args parsing */
427 if (int ret
= allocate_formatter(s
, default_formatter
, configurable_format
); ret
< 0)
430 if (*req_name
!= '/')
439 int pos
= req
.find('/');
441 first
= req
.substr(0, pos
);
450 RGWRESTMgr_STS::get_handler(struct req_state
* const s
,
451 const rgw::auth::StrategyRegistry
& auth_registry
,
452 const std::string
& frontend_prefix
)
454 return new RGWHandler_REST_STS(auth_registry
);