1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
20 #include "rgw_auth_s3.h"
22 #include "rgw_formats.h"
23 #include "rgw_client_io.h"
25 #include "rgw_request.h"
26 #include "rgw_process.h"
27 #include "rgw_iam_policy.h"
28 #include "rgw_iam_policy_keywords.h"
36 #include <boost/utility/string_ref.hpp>
38 #define dout_context g_ceph_context
39 #define dout_subsys ceph_subsys_rgw
46 WebTokenEngine::is_applicable(const std::string
& token
) const noexcept
48 return ! token
.empty();
51 boost::optional
<WebTokenEngine::token_t
>
52 WebTokenEngine::get_from_idp(const DoutPrefixProvider
* dpp
, const std::string
& token
) const
54 //Access token conforming to OAuth2.0
55 if (! cct
->_conf
->rgw_sts_token_introspection_url
.empty()) {
56 bufferlist introspect_resp
;
57 RGWHTTPTransceiver
introspect_req(cct
, "POST", cct
->_conf
->rgw_sts_token_introspection_url
, &introspect_resp
);
59 introspect_req
.append_header("Content-Type", "application/x-www-form-urlencoded");
60 string base64_creds
= "Basic " + rgw::to_base64(cct
->_conf
->rgw_sts_client_id
+ ":" + cct
->_conf
->rgw_sts_client_secret
);
61 introspect_req
.append_header("Authorization", base64_creds
);
63 string post_data
= "token=" + token
;
64 introspect_req
.set_post_data(post_data
);
65 introspect_req
.set_send_length(post_data
.length());
67 int res
= introspect_req
.process();
69 ldpp_dout(dpp
, 10) << "HTTP request res: " << res
<< dendl
;
73 ldpp_dout(dpp
, 20) << "HTTP status: " << introspect_req
.get_http_status() << dendl
;
74 ldpp_dout(dpp
, 20) << "JSON Response is: " << introspect_resp
.c_str() << dendl
;
77 WebTokenEngine::token_t token
;
78 if (!parser
.parse(introspect_resp
.c_str(), introspect_resp
.length())) {
79 ldpp_dout(dpp
, 2) << "Malformed json" << dendl
;
83 JSONDecoder::decode_json("active", is_active
, &parser
);
85 ldpp_dout(dpp
, 0) << "Active state is false" << dendl
;
86 throw -ERR_INVALID_IDENTITY_TOKEN
;
88 JSONDecoder::decode_json("iss", token
.iss
, &parser
);
89 JSONDecoder::decode_json("aud", token
.aud
, &parser
);
90 JSONDecoder::decode_json("sub", token
.sub
, &parser
);
91 JSONDecoder::decode_json("user_name", token
.user_name
, &parser
);
98 WebTokenEngine::result_t
99 WebTokenEngine::authenticate( const DoutPrefixProvider
* dpp
,
100 const std::string
& token
,
101 const req_state
* const s
) const
103 boost::optional
<WebTokenEngine::token_t
> t
;
105 if (! is_applicable(token
)) {
106 return result_t::deny();
110 t
= get_from_idp(dpp
, token
);
112 return result_t::deny(-EACCES
);
116 auto apl
= apl_factory
->create_apl_web_identity(cct
, s
, *t
);
117 return result_t::grant(std::move(apl
));
119 return result_t::deny(-EACCES
);
122 }; /* namespace sts */
123 }; /* namespace auth */
124 }; /* namespace rgw */
126 int RGWREST_STS::verify_permission()
128 STS::STSService
_sts(s
->cct
, store
, s
->user
->user_id
, s
->auth
.identity
.get());
129 sts
= std::move(_sts
);
131 string rArn
= s
->info
.args
.get("RoleArn");
132 const auto& [ret
, role
] = sts
.getRoleInfo(rArn
);
136 string policy
= role
.get_assume_role_policy();
137 buffer::list bl
= buffer::list::static_from_string(policy
);
140 //TODO - This step should be part of Role Creation
142 const rgw::IAM::Policy
p(s
->cct
, s
->user
->user_id
.tenant
, bl
);
143 //Check if the input role arn is there as one of the Principals in the policy,
144 // If yes, then return 0, else -EPERM
145 auto p_res
= p
.eval_principal(s
->env
, *s
->auth
.identity
);
146 if (p_res
== rgw::IAM::Effect::Deny
) {
149 auto c_res
= p
.eval_conditions(s
->env
);
150 if (c_res
== rgw::IAM::Effect::Deny
) {
153 } catch (rgw::IAM::PolicyParseException
& e
) {
154 ldout(s
->cct
, 20) << "failed to parse policy: " << e
.what() << dendl
;
161 void RGWREST_STS::send_response()
164 set_req_state_err(s
, op_ret
);
170 int RGWSTSGetSessionToken::verify_permission()
172 rgw::IAM::Partition partition
= rgw::IAM::Partition::aws
;
173 rgw::IAM::Service service
= rgw::IAM::Service::s3
;
174 if (!verify_user_permission(this,
176 rgw::IAM::ARN(partition
, service
, "", s
->user
->user_id
.tenant
, ""),
177 rgw::IAM::stsGetSessionToken
)) {
184 int RGWSTSGetSessionToken::get_params()
186 duration
= s
->info
.args
.get("DurationSeconds");
187 serialNumber
= s
->info
.args
.get("SerialNumber");
188 tokenCode
= s
->info
.args
.get("TokenCode");
190 if (! duration
.empty()) {
191 uint64_t duration_in_secs
= stoull(duration
);
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
->user_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
->user_id
.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
->user_id
.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
,
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 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
352 std::tie(ret
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
353 string post_body
= data
.to_str();
354 if (data
.length() > 0) {
355 ldout(s
->cct
, 10) << "Content of POST: " << post_body
<< dendl
;
357 if (post_body
.find("Action") != string::npos
) {
358 boost::char_separator
<char> sep("&");
359 boost::tokenizer
<boost::char_separator
<char>> tokens(post_body
, sep
);
360 for (const auto& t
: tokens
) {
361 auto pos
= t
.find("=");
362 if (pos
!= string::npos
) {
363 std::string key
= t
.substr(0, pos
);
364 std::string value
= t
.substr(pos
+ 1, t
.size() - 1);
365 if (key
== "RoleArn") {
366 value
= url_decode(value
);
368 ldout(s
->cct
, 10) << "Key: " << key
<< "Value: " << value
<< dendl
;
369 s
->info
.args
.append(key
, value
);
374 auto payload_hash
= rgw::auth::s3::calc_v4_payload_hash(post_body
);
375 s
->info
.args
.append("PayloadHash", payload_hash
);
378 RGWOp
*RGWHandler_REST_STS::op_post()
380 rgw_sts_parse_input();
382 if (s
->info
.args
.exists("Action")) {
383 string action
= s
->info
.args
.get("Action");
384 if (action
== "AssumeRole") {
385 return new RGWSTSAssumeRole
;
386 } else if (action
== "GetSessionToken") {
387 return new RGWSTSGetSessionToken
;
388 } else if (action
== "AssumeRoleWithWebIdentity") {
389 return new RGWSTSAssumeRoleWithWebIdentity
;
396 int RGWHandler_REST_STS::init(RGWRados
*store
,
398 rgw::io::BasicClient
*cio
)
402 if (int ret
= RGWHandler_REST_STS::init_from_header(s
, RGW_FORMAT_XML
, true); ret
< 0) {
403 ldout(s
->cct
, 10) << "init_from_header returned err=" << ret
<< dendl
;
407 return RGWHandler_REST::init(store
, s
, cio
);
410 int RGWHandler_REST_STS::authorize(const DoutPrefixProvider
* dpp
)
412 if (s
->info
.args
.exists("Action") && s
->info
.args
.get("Action") == "AssumeRoleWithWebIdentity") {
413 return RGW_Auth_STS::authorize(dpp
, store
, auth_registry
, s
);
415 return RGW_Auth_S3::authorize(dpp
, store
, auth_registry
, s
);
418 int RGWHandler_REST_STS::init_from_header(struct req_state
* s
,
419 int default_formatter
,
420 bool configurable_format
)
425 s
->prot_flags
|= RGW_REST_STS
;
427 const char *p
, *req_name
;
428 if (req_name
= s
->relative_uri
.c_str(); *req_name
== '?') {
431 p
= s
->info
.request_params
.c_str();
435 s
->info
.args
.parse();
437 /* must be called after the args parsing */
438 if (int ret
= allocate_formatter(s
, default_formatter
, configurable_format
); ret
< 0)
441 if (*req_name
!= '/')
450 int pos
= req
.find('/');
452 first
= req
.substr(0, pos
);
461 RGWRESTMgr_STS::get_handler(struct req_state
* const s
,
462 const rgw::auth::StrategyRegistry
& auth_registry
,
463 const std::string
& frontend_prefix
)
465 return new RGWHandler_REST_STS(auth_registry
);