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