]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_sts.cc
de4e33fb4aa788956d5af909e97656a23b709b1a
[ceph.git] / ceph / src / rgw / rgw_sts.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 #include <errno.h>
5 #include <ctime>
6 #include <regex>
7 #include <boost/format.hpp>
8 #include <boost/algorithm/string/replace.hpp>
9
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"
18
19 #include "include/types.h"
20 #include "rgw_string.h"
21
22 #include "rgw_b64.h"
23 #include "rgw_common.h"
24 #include "rgw_tools.h"
25 #include "rgw_role.h"
26 #include "rgw_user.h"
27 #include "rgw_iam_policy.h"
28 #include "rgw_sts.h"
29 #include "rgw_sal.h"
30
31 #define dout_subsys ceph_subsys_rgw
32
33 namespace STS {
34
35 void Credentials::dump(Formatter *f) const
36 {
37 encode_json("AccessKeyId", accessKeyId , f);
38 encode_json("Expiration", expiration , f);
39 encode_json("SecretAccessKey", secretAccessKey , f);
40 encode_json("SessionToken", sessionToken , f);
41 }
42
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)
49 {
50 uuid_d accessKey, secretKey;
51 char accessKeyId_str[MAX_ACCESS_KEY_LEN], secretAccessKey_str[MAX_SECRET_KEY_LEN];
52
53 //AccessKeyId
54 gen_rand_alphanumeric_plain(cct, accessKeyId_str, sizeof(accessKeyId_str));
55 accessKeyId = accessKeyId_str;
56
57 //SecretAccessKey
58 gen_rand_alphanumeric_upper(cct, secretAccessKey_str, sizeof(secretAccessKey_str));
59 secretAccessKey = secretAccessKey_str;
60
61 //Expiration
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);
65
66 //Session Token - Encrypt using AES
67 auto* cryptohandler = cct->get_crypto_handler(CEPH_CRYPTO_AES);
68 if (! cryptohandler) {
69 return -EINVAL;
70 }
71 string secret_s = cct->_conf->rgw_sts_key;
72 buffer::ptr secret(secret_s.c_str(), secret_s.length());
73 int ret = 0;
74 if (ret = cryptohandler->validate_secret(secret); ret < 0) {
75 ldout(cct, 0) << "ERROR: Invalid secret key" << dendl;
76 return ret;
77 }
78 string error;
79 auto* keyhandler = cryptohandler->get_key_handler(secret, error);
80 if (! keyhandler) {
81 return -EINVAL;
82 }
83 error.clear();
84 //Storing policy and roleId as part of token, so that they can be extracted
85 // from the token itself for policy evaluation.
86 SessionToken token;
87 //authentication info
88 token.access_key_id = accessKeyId;
89 token.secret_access_key = secretAccessKey;
90 token.expiration = expiration;
91
92 //Authorization info
93 if (policy)
94 token.policy = *policy;
95 else
96 token.policy = {};
97
98 if (roleId)
99 token.roleId = *roleId;
100 else
101 token.roleId = {};
102
103 if (user)
104 token.user = *user;
105 else {
106 rgw_user u({}, {});
107 token.user = u;
108 }
109
110 if (identity) {
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();
115 } else {
116 token.acct_name = {};
117 token.perm_mask = 0;
118 token.is_admin = 0;
119 token.acct_type = TYPE_ROLE;
120 }
121
122 buffer::list input, enc_output;
123 encode(token, input);
124
125 if (ret = keyhandler->encrypt(input, enc_output, &error); ret < 0) {
126 return ret;
127 }
128
129 bufferlist encoded_op;
130 enc_output.encode_base64(encoded_op);
131 encoded_op.append('\0');
132 sessionToken = encoded_op.c_str();
133
134 return ret;
135 }
136
137 void AssumedRoleUser::dump(Formatter *f) const
138 {
139 encode_json("Arn", arn , f);
140 encode_json("AssumeRoleId", assumeRoleId , f);
141 }
142
143 int AssumedRoleUser::generateAssumedRoleUser(CephContext* cct,
144 rgw::sal::RGWRadosStore *store,
145 const string& roleId,
146 const rgw::ARN& roleArn,
147 const string& roleSessionName)
148 {
149 string resource = std::move(roleArn.resource);
150 boost::replace_first(resource, "role", "assumed-role");
151 resource.append("/");
152 resource.append(roleSessionName);
153
154 rgw::ARN assumed_role_arn(rgw::Partition::aws,
155 rgw::Service::sts,
156 "", roleArn.account, resource);
157 arn = assumed_role_arn.to_string();
158
159 //Assumeroleid = roleid:rolesessionname
160 assumeRoleId = roleId + ":" + roleSessionName;
161
162 return 0;
163 }
164
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)
170 {
171 if (duration.empty()) {
172 this->duration = DEFAULT_DURATION_IN_SECS;
173 } else {
174 this->duration = strict_strtoll(duration.c_str(), 10, &this->err_msg);
175 }
176 }
177
178 int AssumeRoleRequestBase::validate_input() const
179 {
180 if (!err_msg.empty()) {
181 return -EINVAL;
182 }
183
184 if (duration < MIN_DURATION_IN_SECS ||
185 duration > MAX_DURATION_IN_SECS) {
186 return -EINVAL;
187 }
188
189 if (! iamPolicy.empty() &&
190 (iamPolicy.size() < MIN_POLICY_SIZE || iamPolicy.size() > MAX_POLICY_SIZE)) {
191 return -ERR_PACKED_POLICY_TOO_LARGE;
192 }
193
194 if (! roleArn.empty() &&
195 (roleArn.size() < MIN_ROLE_ARN_SIZE || roleArn.size() > MAX_ROLE_ARN_SIZE)) {
196 return -EINVAL;
197 }
198
199 if (! roleSessionName.empty()) {
200 if (roleSessionName.size() < MIN_ROLE_SESSION_SIZE || roleSessionName.size() > MAX_ROLE_SESSION_SIZE) {
201 return -EINVAL;
202 }
203
204 std::regex regex_roleSession("[A-Za-z0-9_=,.@-]+");
205 if (! std::regex_match(roleSessionName, regex_roleSession)) {
206 return -EINVAL;
207 }
208 }
209
210 return 0;
211 }
212
213 int AssumeRoleWithWebIdentityRequest::validate_input() const
214 {
215 if (! providerId.empty()) {
216 if (providerId.length() < MIN_PROVIDER_ID_LEN ||
217 providerId.length() > MAX_PROVIDER_ID_LEN) {
218 return -EINVAL;
219 }
220 }
221 return AssumeRoleRequestBase::validate_input();
222 }
223
224 int AssumeRoleRequest::validate_input() const
225 {
226 if (! externalId.empty()) {
227 if (externalId.length() < MIN_EXTERNAL_ID_LEN ||
228 externalId.length() > MAX_EXTERNAL_ID_LEN) {
229 return -EINVAL;
230 }
231
232 std::regex regex_externalId("[A-Za-z0-9_=,.@:/-]+");
233 if (! std::regex_match(externalId, regex_externalId)) {
234 return -EINVAL;
235 }
236 }
237 if (! serialNumber.empty()){
238 if (serialNumber.size() < MIN_SERIAL_NUMBER_SIZE || serialNumber.size() > MAX_SERIAL_NUMBER_SIZE) {
239 return -EINVAL;
240 }
241
242 std::regex regex_serialNumber("[A-Za-z0-9_=/:,.@-]+");
243 if (! std::regex_match(serialNumber, regex_serialNumber)) {
244 return -EINVAL;
245 }
246 }
247 if (! tokenCode.empty() && tokenCode.size() == TOKEN_CODE_SIZE) {
248 return -EINVAL;
249 }
250
251 return AssumeRoleRequestBase::validate_input();
252 }
253
254 std::tuple<int, RGWRole> STSService::getRoleInfo(const string& arn)
255 {
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;
263 }
264 return make_tuple(ret, this->role);
265 } else {
266 this->role = std::move(role);
267 return make_tuple(0, this->role);
268 }
269 } else {
270 return make_tuple(-EINVAL, this->role);
271 }
272 }
273
274 int STSService::storeARN(string& arn)
275 {
276 int ret = 0;
277 RGWUserInfo info;
278 if (ret = rgw_get_user_info_by_uid(store->ctl()->user, user_id, info); ret < 0) {
279 return -ERR_NO_SUCH_ENTITY;
280 }
281
282 info.assumed_role_arn = arn;
283
284 RGWObjVersionTracker objv_tracker;
285 if (ret = rgw_store_user_info(store->ctl()->user, info, &info, &objv_tracker, real_time(),
286 false); ret < 0) {
287 return -ERR_INTERNAL_ERROR;
288 }
289 return ret;
290 }
291
292 AssumeRoleWithWebIdentityResponse STSService::assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest& req)
293 {
294 AssumeRoleWithWebIdentityResponse response;
295 response.assumeRoleResp.packedPolicySize = 0;
296
297 if (req.getProviderId().empty()) {
298 response.providerId = req.getIss();
299 }
300 response.aud = req.getAud();
301 response.sub = req.getSub();
302
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;
307 return response;
308 }
309
310 string roleId = role.get_id();
311 uint64_t roleMaxSessionDuration = role.get_max_session_duration();
312 req.setMaxDuration(roleMaxSessionDuration);
313
314 //Validate input
315 response.assumeRoleResp.retCode = req.validate_input();
316 if (response.assumeRoleResp.retCode < 0) {
317 return response;
318 }
319
320 //Calculate PackedPolicySize
321 string policy = req.getPolicy();
322 response.assumeRoleResp.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
323
324 //Generate Assumed Role User
325 response.assumeRoleResp.retCode = response.assumeRoleResp.user.generateAssumedRoleUser(cct,
326 store,
327 roleId,
328 r_arn.get(),
329 req.getRoleSessionName());
330 if (response.assumeRoleResp.retCode < 0) {
331 return response;
332 }
333
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,
338 user_id, nullptr);
339 if (response.assumeRoleResp.retCode < 0) {
340 return response;
341 }
342
343 response.assumeRoleResp.retCode = 0;
344 return response;
345 }
346
347 AssumeRoleResponse STSService::assumeRole(AssumeRoleRequest& req)
348 {
349 AssumeRoleResponse response;
350 response.packedPolicySize = 0;
351
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;
356 return response;
357 }
358
359 string roleId = role.get_id();
360 uint64_t roleMaxSessionDuration = role.get_max_session_duration();
361 req.setMaxDuration(roleMaxSessionDuration);
362
363 //Validate input
364 response.retCode = req.validate_input();
365 if (response.retCode < 0) {
366 return response;
367 }
368
369 //Calculate PackedPolicySize
370 string policy = req.getPolicy();
371 response.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
372
373 //Generate Assumed Role User
374 response.retCode = response.user.generateAssumedRoleUser(cct, store, roleId, r_arn.get(), req.getRoleSessionName());
375 if (response.retCode < 0) {
376 return response;
377 }
378
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,
383 user_id, nullptr);
384 if (response.retCode < 0) {
385 return response;
386 }
387
388 //Save ARN with the user
389 string arn = response.user.getARN();
390 response.retCode = storeARN(arn);
391 if (response.retCode < 0) {
392 return response;
393 }
394
395 response.retCode = 0;
396 return response;
397 }
398
399 GetSessionTokenRequest::GetSessionTokenRequest(const string& duration, const string& serialNumber, const string& tokenCode)
400 {
401 if (duration.empty()) {
402 this->duration = DEFAULT_DURATION_IN_SECS;
403 } else {
404 this->duration = stoull(duration);
405 }
406 this->serialNumber = serialNumber;
407 this->tokenCode = tokenCode;
408 }
409
410 GetSessionTokenResponse STSService::getSessionToken(GetSessionTokenRequest& req)
411 {
412 int ret;
413 Credentials cred;
414
415 //Generate Credentials
416 if (ret = cred.generateCredentials(cct,
417 req.getDuration(),
418 boost::none,
419 boost::none,
420 user_id,
421 identity); ret < 0) {
422 return make_tuple(ret, cred);
423 }
424
425 return make_tuple(0, cred);
426 }
427
428 }