1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
7 #include <boost/format.hpp>
8 #include <boost/algorithm/string/replace.hpp>
10 #include "common/errno.h"
11 #include "common/Formatter.h"
12 #include "common/ceph_json.h"
13 #include "common/ceph_time.h"
14 #include "rgw_rados.h"
15 #include "auth/Crypto.h"
16 #include "include/ceph_fs.h"
17 #include "common/iso_8601.h"
19 #include "include/types.h"
20 #include "rgw_string.h"
23 #include "rgw_common.h"
24 #include "rgw_tools.h"
27 #include "rgw_iam_policy.h"
31 #define dout_subsys ceph_subsys_rgw
35 void Credentials::dump(Formatter
*f
) const
37 encode_json("AccessKeyId", accessKeyId
, f
);
38 encode_json("Expiration", expiration
, f
);
39 encode_json("SecretAccessKey", secretAccessKey
, f
);
40 encode_json("SessionToken", sessionToken
, f
);
43 int Credentials::generateCredentials(CephContext
* cct
,
44 const uint64_t& duration
,
45 const boost::optional
<string
>& policy
,
46 const boost::optional
<string
>& roleId
,
47 const boost::optional
<string
>& role_session
,
48 const boost::optional
<std::vector
<string
>> token_claims
,
49 boost::optional
<rgw_user
> user
,
50 rgw::auth::Identity
* identity
)
52 uuid_d accessKey
, secretKey
;
53 char accessKeyId_str
[MAX_ACCESS_KEY_LEN
], secretAccessKey_str
[MAX_SECRET_KEY_LEN
];
56 gen_rand_alphanumeric_plain(cct
, accessKeyId_str
, sizeof(accessKeyId_str
));
57 accessKeyId
= accessKeyId_str
;
60 gen_rand_alphanumeric_upper(cct
, secretAccessKey_str
, sizeof(secretAccessKey_str
));
61 secretAccessKey
= secretAccessKey_str
;
64 real_clock::time_point t
= real_clock::now();
65 real_clock::time_point exp
= t
+ std::chrono::seconds(duration
);
66 expiration
= ceph::to_iso_8601(exp
);
68 //Session Token - Encrypt using AES
69 auto* cryptohandler
= cct
->get_crypto_handler(CEPH_CRYPTO_AES
);
70 if (! cryptohandler
) {
73 string secret_s
= cct
->_conf
->rgw_sts_key
;
74 buffer::ptr
secret(secret_s
.c_str(), secret_s
.length());
76 if (ret
= cryptohandler
->validate_secret(secret
); ret
< 0) {
77 ldout(cct
, 0) << "ERROR: Invalid secret key" << dendl
;
81 auto* keyhandler
= cryptohandler
->get_key_handler(secret
, error
);
86 //Storing policy and roleId as part of token, so that they can be extracted
87 // from the token itself for policy evaluation.
90 token
.access_key_id
= accessKeyId
;
91 token
.secret_access_key
= secretAccessKey
;
92 token
.expiration
= expiration
;
96 token
.policy
= *policy
;
101 token
.roleId
= *roleId
;
113 token
.token_claims
= std::move(*token_claims
);
117 token
.acct_name
= identity
->get_acct_name();
118 token
.perm_mask
= identity
->get_perm_mask();
119 token
.is_admin
= identity
->is_admin_of(token
.user
);
120 token
.acct_type
= identity
->get_identity_type();
122 token
.acct_name
= {};
125 token
.acct_type
= TYPE_ROLE
;
126 token
.role_session
= role_session
.get();
129 buffer::list input
, enc_output
;
130 encode(token
, input
);
132 if (ret
= keyhandler
->encrypt(input
, enc_output
, &error
); ret
< 0) {
136 bufferlist encoded_op
;
137 enc_output
.encode_base64(encoded_op
);
138 encoded_op
.append('\0');
139 sessionToken
= encoded_op
.c_str();
144 void AssumedRoleUser::dump(Formatter
*f
) const
146 encode_json("Arn", arn
, f
);
147 encode_json("AssumeRoleId", assumeRoleId
, f
);
150 int AssumedRoleUser::generateAssumedRoleUser(CephContext
* cct
,
151 rgw::sal::RGWRadosStore
*store
,
152 const string
& roleId
,
153 const rgw::ARN
& roleArn
,
154 const string
& roleSessionName
)
156 string resource
= std::move(roleArn
.resource
);
157 boost::replace_first(resource
, "role", "assumed-role");
158 resource
.append("/");
159 resource
.append(roleSessionName
);
161 rgw::ARN
assumed_role_arn(rgw::Partition::aws
,
163 "", roleArn
.account
, resource
);
164 arn
= assumed_role_arn
.to_string();
166 //Assumeroleid = roleid:rolesessionname
167 assumeRoleId
= roleId
+ ":" + roleSessionName
;
172 AssumeRoleRequestBase::AssumeRoleRequestBase( const string
& duration
,
173 const string
& iamPolicy
,
174 const string
& roleArn
,
175 const string
& roleSessionName
)
176 : iamPolicy(iamPolicy
), roleArn(roleArn
), roleSessionName(roleSessionName
)
178 if (duration
.empty()) {
179 this->duration
= DEFAULT_DURATION_IN_SECS
;
181 this->duration
= strict_strtoll(duration
.c_str(), 10, &this->err_msg
);
185 int AssumeRoleRequestBase::validate_input() const
187 if (!err_msg
.empty()) {
191 if (duration
< MIN_DURATION_IN_SECS
||
192 duration
> MAX_DURATION_IN_SECS
) {
196 if (! iamPolicy
.empty() &&
197 (iamPolicy
.size() < MIN_POLICY_SIZE
|| iamPolicy
.size() > MAX_POLICY_SIZE
)) {
198 return -ERR_PACKED_POLICY_TOO_LARGE
;
201 if (! roleArn
.empty() &&
202 (roleArn
.size() < MIN_ROLE_ARN_SIZE
|| roleArn
.size() > MAX_ROLE_ARN_SIZE
)) {
206 if (! roleSessionName
.empty()) {
207 if (roleSessionName
.size() < MIN_ROLE_SESSION_SIZE
|| roleSessionName
.size() > MAX_ROLE_SESSION_SIZE
) {
211 std::regex
regex_roleSession("[A-Za-z0-9_=,.@-]+");
212 if (! std::regex_match(roleSessionName
, regex_roleSession
)) {
220 int AssumeRoleWithWebIdentityRequest::validate_input() const
222 if (! providerId
.empty()) {
223 if (providerId
.length() < MIN_PROVIDER_ID_LEN
||
224 providerId
.length() > MAX_PROVIDER_ID_LEN
) {
228 return AssumeRoleRequestBase::validate_input();
231 int AssumeRoleRequest::validate_input() const
233 if (! externalId
.empty()) {
234 if (externalId
.length() < MIN_EXTERNAL_ID_LEN
||
235 externalId
.length() > MAX_EXTERNAL_ID_LEN
) {
239 std::regex
regex_externalId("[A-Za-z0-9_=,.@:/-]+");
240 if (! std::regex_match(externalId
, regex_externalId
)) {
244 if (! serialNumber
.empty()){
245 if (serialNumber
.size() < MIN_SERIAL_NUMBER_SIZE
|| serialNumber
.size() > MAX_SERIAL_NUMBER_SIZE
) {
249 std::regex
regex_serialNumber("[A-Za-z0-9_=/:,.@-]+");
250 if (! std::regex_match(serialNumber
, regex_serialNumber
)) {
254 if (! tokenCode
.empty() && tokenCode
.size() == TOKEN_CODE_SIZE
) {
258 return AssumeRoleRequestBase::validate_input();
261 std::tuple
<int, RGWRole
> STSService::getRoleInfo(const string
& arn
)
263 if (auto r_arn
= rgw::ARN::parse(arn
); r_arn
) {
264 auto pos
= r_arn
->resource
.find_last_of('/');
265 string roleName
= r_arn
->resource
.substr(pos
+ 1);
266 RGWRole
role(cct
, store
->getRados()->pctl
, roleName
, r_arn
->account
);
267 if (int ret
= role
.get(); ret
< 0) {
268 if (ret
== -ENOENT
) {
269 ret
= -ERR_NO_ROLE_FOUND
;
271 return make_tuple(ret
, this->role
);
273 this->role
= std::move(role
);
274 return make_tuple(0, this->role
);
277 return make_tuple(-EINVAL
, this->role
);
281 int STSService::storeARN(string
& arn
)
285 if (ret
= rgw_get_user_info_by_uid(store
->ctl()->user
, user_id
, info
); ret
< 0) {
286 return -ERR_NO_SUCH_ENTITY
;
289 info
.assumed_role_arn
= arn
;
291 RGWObjVersionTracker objv_tracker
;
292 if (ret
= rgw_store_user_info(store
->ctl()->user
, info
, &info
, &objv_tracker
, real_time(),
294 return -ERR_INTERNAL_ERROR
;
299 AssumeRoleWithWebIdentityResponse
STSService::assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest
& req
)
301 AssumeRoleWithWebIdentityResponse response
;
302 response
.assumeRoleResp
.packedPolicySize
= 0;
303 std::vector
<string
> token_claims
;
305 if (req
.getProviderId().empty()) {
306 response
.providerId
= req
.getIss();
308 response
.aud
= req
.getAud();
309 response
.sub
= req
.getSub();
311 token_claims
.emplace_back(string("iss") + ":" + req
.getIss());
312 token_claims
.emplace_back(string("aud") + ":" + req
.getAud());
313 token_claims
.emplace_back(string("sub") + ":" + req
.getSub());
315 //Get the role info which is being assumed
316 boost::optional
<rgw::ARN
> r_arn
= rgw::ARN::parse(req
.getRoleARN());
317 if (r_arn
== boost::none
) {
318 response
.assumeRoleResp
.retCode
= -EINVAL
;
322 string roleId
= role
.get_id();
323 uint64_t roleMaxSessionDuration
= role
.get_max_session_duration();
324 req
.setMaxDuration(roleMaxSessionDuration
);
327 response
.assumeRoleResp
.retCode
= req
.validate_input();
328 if (response
.assumeRoleResp
.retCode
< 0) {
332 //Calculate PackedPolicySize
333 string policy
= req
.getPolicy();
334 response
.assumeRoleResp
.packedPolicySize
= (policy
.size() / req
.getMaxPolicySize()) * 100;
336 //Generate Assumed Role User
337 response
.assumeRoleResp
.retCode
= response
.assumeRoleResp
.user
.generateAssumedRoleUser(cct
,
341 req
.getRoleSessionName());
342 if (response
.assumeRoleResp
.retCode
< 0) {
346 //Generate Credentials
347 //Role and Policy provide the authorization info, user id and applier info are not needed
348 response
.assumeRoleResp
.retCode
= response
.assumeRoleResp
.creds
.generateCredentials(cct
, req
.getDuration(),
349 req
.getPolicy(), roleId
,
350 req
.getRoleSessionName(),
353 if (response
.assumeRoleResp
.retCode
< 0) {
357 response
.assumeRoleResp
.retCode
= 0;
361 AssumeRoleResponse
STSService::assumeRole(AssumeRoleRequest
& req
)
363 AssumeRoleResponse response
;
364 response
.packedPolicySize
= 0;
366 //Get the role info which is being assumed
367 boost::optional
<rgw::ARN
> r_arn
= rgw::ARN::parse(req
.getRoleARN());
368 if (r_arn
== boost::none
) {
369 response
.retCode
= -EINVAL
;
373 string roleId
= role
.get_id();
374 uint64_t roleMaxSessionDuration
= role
.get_max_session_duration();
375 req
.setMaxDuration(roleMaxSessionDuration
);
378 response
.retCode
= req
.validate_input();
379 if (response
.retCode
< 0) {
383 //Calculate PackedPolicySize
384 string policy
= req
.getPolicy();
385 response
.packedPolicySize
= (policy
.size() / req
.getMaxPolicySize()) * 100;
387 //Generate Assumed Role User
388 response
.retCode
= response
.user
.generateAssumedRoleUser(cct
, store
, roleId
, r_arn
.get(), req
.getRoleSessionName());
389 if (response
.retCode
< 0) {
393 //Generate Credentials
394 //Role and Policy provide the authorization info, user id and applier info are not needed
395 response
.retCode
= response
.creds
.generateCredentials(cct
, req
.getDuration(),
396 req
.getPolicy(), roleId
,
397 req
.getRoleSessionName(),
400 if (response
.retCode
< 0) {
404 //Save ARN with the user
405 string arn
= response
.user
.getARN();
406 response
.retCode
= storeARN(arn
);
407 if (response
.retCode
< 0) {
411 response
.retCode
= 0;
415 GetSessionTokenRequest::GetSessionTokenRequest(const string
& duration
, const string
& serialNumber
, const string
& tokenCode
)
417 if (duration
.empty()) {
418 this->duration
= DEFAULT_DURATION_IN_SECS
;
420 this->duration
= stoull(duration
);
422 this->serialNumber
= serialNumber
;
423 this->tokenCode
= tokenCode
;
426 GetSessionTokenResponse
STSService::getSessionToken(GetSessionTokenRequest
& req
)
431 //Generate Credentials
432 if (ret
= cred
.generateCredentials(cct
,
439 identity
); ret
< 0) {
440 return make_tuple(ret
, cred
);
443 return make_tuple(0, cred
);