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 boost::optional
<rgw_user
> user
,
48 rgw::auth::Identity
* identity
)
50 uuid_d accessKey
, secretKey
;
51 char accessKeyId_str
[MAX_ACCESS_KEY_LEN
], secretAccessKey_str
[MAX_SECRET_KEY_LEN
];
54 gen_rand_alphanumeric_plain(cct
, accessKeyId_str
, sizeof(accessKeyId_str
));
55 accessKeyId
= accessKeyId_str
;
58 gen_rand_alphanumeric_upper(cct
, secretAccessKey_str
, sizeof(secretAccessKey_str
));
59 secretAccessKey
= secretAccessKey_str
;
62 real_clock::time_point t
= real_clock::now();
63 real_clock::time_point exp
= t
+ std::chrono::seconds(duration
);
64 expiration
= ceph::to_iso_8601(exp
);
66 //Session Token - Encrypt using AES
67 auto* cryptohandler
= cct
->get_crypto_handler(CEPH_CRYPTO_AES
);
68 if (! cryptohandler
) {
71 string secret_s
= cct
->_conf
->rgw_sts_key
;
72 buffer::ptr
secret(secret_s
.c_str(), secret_s
.length());
74 if (ret
= cryptohandler
->validate_secret(secret
); ret
< 0) {
75 ldout(cct
, 0) << "ERROR: Invalid secret key" << dendl
;
79 auto* keyhandler
= cryptohandler
->get_key_handler(secret
, error
);
84 //Storing policy and roleId as part of token, so that they can be extracted
85 // from the token itself for policy evaluation.
88 token
.access_key_id
= accessKeyId
;
89 token
.secret_access_key
= secretAccessKey
;
90 token
.expiration
= expiration
;
94 token
.policy
= *policy
;
99 token
.roleId
= *roleId
;
111 token
.acct_name
= identity
->get_acct_name();
112 token
.perm_mask
= identity
->get_perm_mask();
113 token
.is_admin
= identity
->is_admin_of(token
.user
);
114 token
.acct_type
= identity
->get_identity_type();
116 token
.acct_name
= {};
119 token
.acct_type
= TYPE_ROLE
;
122 buffer::list input
, enc_output
;
123 encode(token
, input
);
125 if (ret
= keyhandler
->encrypt(input
, enc_output
, &error
); ret
< 0) {
129 bufferlist encoded_op
;
130 enc_output
.encode_base64(encoded_op
);
131 encoded_op
.append('\0');
132 sessionToken
= encoded_op
.c_str();
137 void AssumedRoleUser::dump(Formatter
*f
) const
139 encode_json("Arn", arn
, f
);
140 encode_json("AssumeRoleId", assumeRoleId
, f
);
143 int AssumedRoleUser::generateAssumedRoleUser(CephContext
* cct
,
144 rgw::sal::RGWRadosStore
*store
,
145 const string
& roleId
,
146 const rgw::ARN
& roleArn
,
147 const string
& roleSessionName
)
149 string resource
= std::move(roleArn
.resource
);
150 boost::replace_first(resource
, "role", "assumed-role");
151 resource
.append("/");
152 resource
.append(roleSessionName
);
154 rgw::ARN
assumed_role_arn(rgw::Partition::aws
,
156 "", roleArn
.account
, resource
);
157 arn
= assumed_role_arn
.to_string();
159 //Assumeroleid = roleid:rolesessionname
160 assumeRoleId
= roleId
+ ":" + roleSessionName
;
165 AssumeRoleRequestBase::AssumeRoleRequestBase( const string
& duration
,
166 const string
& iamPolicy
,
167 const string
& roleArn
,
168 const string
& roleSessionName
)
169 : iamPolicy(iamPolicy
), roleArn(roleArn
), roleSessionName(roleSessionName
)
171 if (duration
.empty()) {
172 this->duration
= DEFAULT_DURATION_IN_SECS
;
174 this->duration
= strict_strtoll(duration
.c_str(), 10, &this->err_msg
);
178 int AssumeRoleRequestBase::validate_input() const
180 if (!err_msg
.empty()) {
184 if (duration
< MIN_DURATION_IN_SECS
||
185 duration
> MAX_DURATION_IN_SECS
) {
189 if (! iamPolicy
.empty() &&
190 (iamPolicy
.size() < MIN_POLICY_SIZE
|| iamPolicy
.size() > MAX_POLICY_SIZE
)) {
191 return -ERR_PACKED_POLICY_TOO_LARGE
;
194 if (! roleArn
.empty() &&
195 (roleArn
.size() < MIN_ROLE_ARN_SIZE
|| roleArn
.size() > MAX_ROLE_ARN_SIZE
)) {
199 if (! roleSessionName
.empty()) {
200 if (roleSessionName
.size() < MIN_ROLE_SESSION_SIZE
|| roleSessionName
.size() > MAX_ROLE_SESSION_SIZE
) {
204 std::regex
regex_roleSession("[A-Za-z0-9_=,.@-]+");
205 if (! std::regex_match(roleSessionName
, regex_roleSession
)) {
213 int AssumeRoleWithWebIdentityRequest::validate_input() const
215 if (! providerId
.empty()) {
216 if (providerId
.length() < MIN_PROVIDER_ID_LEN
||
217 providerId
.length() > MAX_PROVIDER_ID_LEN
) {
221 return AssumeRoleRequestBase::validate_input();
224 int AssumeRoleRequest::validate_input() const
226 if (! externalId
.empty()) {
227 if (externalId
.length() < MIN_EXTERNAL_ID_LEN
||
228 externalId
.length() > MAX_EXTERNAL_ID_LEN
) {
232 std::regex
regex_externalId("[A-Za-z0-9_=,.@:/-]+");
233 if (! std::regex_match(externalId
, regex_externalId
)) {
237 if (! serialNumber
.empty()){
238 if (serialNumber
.size() < MIN_SERIAL_NUMBER_SIZE
|| serialNumber
.size() > MAX_SERIAL_NUMBER_SIZE
) {
242 std::regex
regex_serialNumber("[A-Za-z0-9_=/:,.@-]+");
243 if (! std::regex_match(serialNumber
, regex_serialNumber
)) {
247 if (! tokenCode
.empty() && tokenCode
.size() == TOKEN_CODE_SIZE
) {
251 return AssumeRoleRequestBase::validate_input();
254 std::tuple
<int, RGWRole
> STSService::getRoleInfo(const string
& arn
)
256 if (auto r_arn
= rgw::ARN::parse(arn
); r_arn
) {
257 auto pos
= r_arn
->resource
.find_last_of('/');
258 string roleName
= r_arn
->resource
.substr(pos
+ 1);
259 RGWRole
role(cct
, store
->getRados()->pctl
, roleName
, r_arn
->account
);
260 if (int ret
= role
.get(); ret
< 0) {
261 if (ret
== -ENOENT
) {
262 ret
= -ERR_NO_ROLE_FOUND
;
264 return make_tuple(ret
, this->role
);
266 this->role
= std::move(role
);
267 return make_tuple(0, this->role
);
270 return make_tuple(-EINVAL
, this->role
);
274 int STSService::storeARN(string
& arn
)
278 if (ret
= rgw_get_user_info_by_uid(store
->ctl()->user
, user_id
, info
); ret
< 0) {
279 return -ERR_NO_SUCH_ENTITY
;
282 info
.assumed_role_arn
= arn
;
284 RGWObjVersionTracker objv_tracker
;
285 if (ret
= rgw_store_user_info(store
->ctl()->user
, info
, &info
, &objv_tracker
, real_time(),
287 return -ERR_INTERNAL_ERROR
;
292 AssumeRoleWithWebIdentityResponse
STSService::assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest
& req
)
294 AssumeRoleWithWebIdentityResponse response
;
295 response
.assumeRoleResp
.packedPolicySize
= 0;
297 if (req
.getProviderId().empty()) {
298 response
.providerId
= req
.getIss();
300 response
.aud
= req
.getAud();
301 response
.sub
= req
.getSub();
303 //Get the role info which is being assumed
304 boost::optional
<rgw::ARN
> r_arn
= rgw::ARN::parse(req
.getRoleARN());
305 if (r_arn
== boost::none
) {
306 response
.assumeRoleResp
.retCode
= -EINVAL
;
310 string roleId
= role
.get_id();
311 uint64_t roleMaxSessionDuration
= role
.get_max_session_duration();
312 req
.setMaxDuration(roleMaxSessionDuration
);
315 response
.assumeRoleResp
.retCode
= req
.validate_input();
316 if (response
.assumeRoleResp
.retCode
< 0) {
320 //Calculate PackedPolicySize
321 string policy
= req
.getPolicy();
322 response
.assumeRoleResp
.packedPolicySize
= (policy
.size() / req
.getMaxPolicySize()) * 100;
324 //Generate Assumed Role User
325 response
.assumeRoleResp
.retCode
= response
.assumeRoleResp
.user
.generateAssumedRoleUser(cct
,
329 req
.getRoleSessionName());
330 if (response
.assumeRoleResp
.retCode
< 0) {
334 //Generate Credentials
335 //Role and Policy provide the authorization info, user id and applier info are not needed
336 response
.assumeRoleResp
.retCode
= response
.assumeRoleResp
.creds
.generateCredentials(cct
, req
.getDuration(),
337 req
.getPolicy(), roleId
,
339 if (response
.assumeRoleResp
.retCode
< 0) {
343 response
.assumeRoleResp
.retCode
= 0;
347 AssumeRoleResponse
STSService::assumeRole(AssumeRoleRequest
& req
)
349 AssumeRoleResponse response
;
350 response
.packedPolicySize
= 0;
352 //Get the role info which is being assumed
353 boost::optional
<rgw::ARN
> r_arn
= rgw::ARN::parse(req
.getRoleARN());
354 if (r_arn
== boost::none
) {
355 response
.retCode
= -EINVAL
;
359 string roleId
= role
.get_id();
360 uint64_t roleMaxSessionDuration
= role
.get_max_session_duration();
361 req
.setMaxDuration(roleMaxSessionDuration
);
364 response
.retCode
= req
.validate_input();
365 if (response
.retCode
< 0) {
369 //Calculate PackedPolicySize
370 string policy
= req
.getPolicy();
371 response
.packedPolicySize
= (policy
.size() / req
.getMaxPolicySize()) * 100;
373 //Generate Assumed Role User
374 response
.retCode
= response
.user
.generateAssumedRoleUser(cct
, store
, roleId
, r_arn
.get(), req
.getRoleSessionName());
375 if (response
.retCode
< 0) {
379 //Generate Credentials
380 //Role and Policy provide the authorization info, user id and applier info are not needed
381 response
.retCode
= response
.creds
.generateCredentials(cct
, req
.getDuration(),
382 req
.getPolicy(), roleId
,
384 if (response
.retCode
< 0) {
388 //Save ARN with the user
389 string arn
= response
.user
.getARN();
390 response
.retCode
= storeARN(arn
);
391 if (response
.retCode
< 0) {
395 response
.retCode
= 0;
399 GetSessionTokenRequest::GetSessionTokenRequest(const string
& duration
, const string
& serialNumber
, const string
& tokenCode
)
401 if (duration
.empty()) {
402 this->duration
= DEFAULT_DURATION_IN_SECS
;
404 this->duration
= stoull(duration
);
406 this->serialNumber
= serialNumber
;
407 this->tokenCode
= tokenCode
;
410 GetSessionTokenResponse
STSService::getSessionToken(GetSessionTokenRequest
& req
)
415 //Generate Credentials
416 if (ret
= cred
.generateCredentials(cct
,
421 identity
); ret
< 0) {
422 return make_tuple(ret
, cred
);
425 return make_tuple(0, cred
);