#include "common/utf8.h"
#include "common/ceph_json.h"
#include "common/safe_io.h"
-#include "common/backport14.h"
+#include "auth/Crypto.h"
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/utility/string_view.hpp>
+#include <boost/tokenizer.hpp>
+
+#include <liboath/oath.h>
#include "rgw_rest.h"
#include "rgw_rest_s3.h"
#include "rgw_rest_role.h"
#include "rgw_crypt.h"
#include "rgw_crypt_sanitize.h"
+#include "rgw_rest_user_policy.h"
+#include "rgw_zone.h"
+
+#include "services/svc_zone.h"
-#include "include/assert.h"
+#include "include/ceph_assert.h"
+#include "rgw_role.h"
+#include "rgw_rest_sts.h"
+#include "rgw_sts.h"
#define dout_context g_ceph_context
#define dout_subsys ceph_subsys_rgw
iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
if (iter != attrs.end()) {
bufferlist &bl = iter->second;
- s->redirect = string(bl.c_str(), bl.length());
+ s->redirect = bl.c_str();
s->err.http_ret = 301;
ldout(s->cct, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
op_ret = -ERR_WEBSITE_REDIRECT;
*result = def_val;
return 0;
}
- bufferlist::iterator bliter = bl.begin();
+ auto bliter = bl.cbegin();
try {
- ::decode(*result, bliter);
+ decode(*result, bliter);
} catch (buffer::error& err) {
return -EIO;
}
dump_content_length(s, total_len);
dump_last_modified(s, lastmod);
- if (!version_id.empty()) {
- dump_header(s, "x-amz-version-id", version_id);
+ dump_header_if_nonempty(s, "x-amz-version-id", version_id);
+ if (attrs.find(RGW_ATTR_APPEND_PART_NUM) != attrs.end()) {
+ dump_header(s, "x-rgw-object-type", "Appendable");
+ dump_header(s, "x-rgw-next-append-position", s->obj_size);
+ } else {
+ dump_header(s, "x-rgw-object-type", "Normal");
}
-
-
if (! op_ret) {
if (! lo_etag.empty()) {
/* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
} else {
auto iter = attrs.find(RGW_ATTR_ETAG);
if (iter != attrs.end()) {
- dump_etag(s, iter->second);
+ dump_etag(s, iter->second.to_str());
}
}
if (aiter != rgw_to_http_attrs.end()) {
if (response_attrs.count(aiter->second) == 0) {
/* Was not already overridden by a response param. */
- response_attrs[aiter->second] = iter->second.c_str();
+
+ size_t len = iter->second.length();
+ string s(iter->second.c_str(), len);
+ while (len && !s[len - 1]) {
+ --len;
+ s.resize(len);
+ }
+ response_attrs[aiter->second] = s;
}
} else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
/* Special handling for content_type. */
if (!content_type) {
- content_type = iter->second.c_str();
+ content_type_str = rgw_bl_str(iter->second);
+ content_type = content_type_str.c_str();
}
} else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
- // this attr has an extra length prefix from ::encode() in prior versions
+ // this attr has an extra length prefix from encode() in prior versions
dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
} else if (strncmp(name, RGW_ATTR_META_PREFIX,
sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
} else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
RGWObjTags obj_tags;
try{
- bufferlist::iterator it = iter->second.begin();
+ auto it = iter->second.cbegin();
obj_tags.decode(it);
} catch (buffer::error &err) {
ldout(s->cct,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
return 0;
}
-int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetDataCB> *filter, RGWGetDataCB* cb, bufferlist* manifest_bl)
+int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetObj_Filter> *filter, RGWGetObj_Filter* cb, bufferlist* manifest_bl)
{
if (skip_decrypt) { // bypass decryption for multisite sync requests
return 0;
res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
if (res == 0) {
if (block_crypt != nullptr) {
- auto f = ceph::make_unique<RGWGetObj_BlockDecrypt>(s->cct, cb, std::move(block_crypt));
+ auto f = std::make_unique<RGWGetObj_BlockDecrypt>(s->cct, cb, std::move(block_crypt));
if (manifest_bl != nullptr) {
res = f->read_manifest(*manifest_bl);
if (res == 0) {
s->formatter->open_object_section("TagSet");
if (has_tags){
RGWObjTagSet_S3 tagset;
- bufferlist::iterator iter = bl.begin();
+ auto iter = bl.cbegin();
try {
tagset.decode(iter);
} catch (buffer::error& err) {
int RGWPutObjTags_ObjStore_S3::get_params()
{
- RGWObjTagsXMLParser parser;
+ RGWXMLParser parser;
if (!parser.init()){
return -EINVAL;
}
- char *data=nullptr;
- int len=0;
-
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
- int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ int r = 0;
+ bufferlist data;
+ std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
if (r < 0)
return r;
- auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
-
- if (!parser.parse(data, len, 1)) {
+ if (!parser.parse(data.c_str(), data.length(), 1)) {
return -ERR_MALFORMED_XML;
}
- RGWObjTagSet_S3 *obj_tags_s3;
- RGWObjTagging_S3 *tagging;
+ RGWObjTagging_S3 tagging;
- tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
- obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
- if(!obj_tags_s3){
+ try {
+ RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
+ } catch (RGWXMLDecoder::err& err) {
+ ldout(s->cct, 5) << "Malformed tagging request: " << err << dendl;
return -ERR_MALFORMED_XML;
}
RGWObjTags obj_tags;
- r = obj_tags_s3->rebuild(obj_tags);
+ r = tagging.rebuild(obj_tags);
if (r < 0)
return r;
set_req_state_err(s, op_ret);
dump_errno(s);
dump_start(s);
- end_header(s, NULL, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, NULL, "application/xml", CHUNKED_TRANSFER_ENCODING);
if (! op_ret) {
list_all_buckets_start(s);
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
dump_start(s);
if (op_ret < 0)
return;
if (!iter->is_delete_marker()) {
s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
s->formatter->dump_int("Size", iter->meta.accounted_size);
- s->formatter->dump_string("StorageClass", "STANDARD");
+ auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
+ s->formatter->dump_string("StorageClass", storage_class.c_str());
}
dump_owner(s, iter->meta.owner, iter->meta.owner_display_name);
+ if (iter->meta.appendable) {
+ s->formatter->dump_string("Type", "Appendable");
+ } else {
+ s->formatter->dump_string("Type", "Normal");
+ }
s->formatter->close_section();
}
if (objs_container) {
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
dump_start(s);
if (op_ret < 0)
return;
dump_time(s, "LastModified", &iter->meta.mtime);
s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
s->formatter->dump_int("Size", iter->meta.accounted_size);
- s->formatter->dump_string("StorageClass", "STANDARD");
+ auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
+ s->formatter->dump_string("StorageClass", storage_class.c_str());
dump_owner(s, iter->meta.owner, iter->meta.owner_display_name);
if (s->system_request) {
s->formatter->dump_string("RgwxTag", iter->tag);
}
+ if (iter->meta.appendable) {
+ s->formatter->dump_string("Type", "Appendable");
+ } else {
+ s->formatter->dump_string("Type", "Normal");
+ }
s->formatter->close_section();
}
if (!common_prefixes.empty()) {
RGWZoneGroup zonegroup;
string api_name;
- int ret = store->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
+ int ret = store->svc.zone->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
if (ret >= 0) {
api_name = zonegroup.api_name;
} else {
if (versioned) {
const char *status = (versioning_enabled ? "Enabled" : "Suspended");
s->formatter->dump_string("Status", status);
+ const char *mfa_status = (mfa_enabled ? "Enabled" : "Disabled");
+ s->formatter->dump_string("MfaDelete", mfa_status);
}
s->formatter->close_section();
rgw_flush_formatter_and_reset(s, s->formatter);
}
-class RGWSetBucketVersioningParser : public RGWXMLParser
-{
- XMLObj *alloc_obj(const char *el) override {
- return new XMLObj;
- }
-
-public:
- RGWSetBucketVersioningParser() {}
- ~RGWSetBucketVersioningParser() override {}
-
- int get_versioning_status(bool *status) {
- XMLObj *config = find_first("VersioningConfiguration");
- if (!config)
- return -EINVAL;
-
- *status = false;
-
- XMLObj *field = config->find_first("Status");
- if (!field)
- return 0;
+struct ver_config_status {
+ int status{VersioningSuspended};
+
+ enum MFAStatus {
+ MFA_UNKNOWN,
+ MFA_DISABLED,
+ MFA_ENABLED,
+ } mfa_status{MFA_UNKNOWN};
+ int retcode{0};
+
+ void decode_xml(XMLObj *obj) {
+ string status_str;
+ string mfa_str;
+ RGWXMLDecoder::decode_xml("Status", status_str, obj);
+ if (status_str == "Enabled") {
+ status = VersioningEnabled;
+ } else if (status_str != "Suspended") {
+ status = VersioningStatusInvalid;
+ }
- string& s = field->get_data();
- if (stringcasecmp(s, "Enabled") == 0) {
- *status = true;
- } else if (stringcasecmp(s, "Suspended") != 0) {
- return -EINVAL;
+ if (RGWXMLDecoder::decode_xml("MfaDelete", mfa_str, obj)) {
+ if (mfa_str == "Enabled") {
+ mfa_status = MFA_ENABLED;
+ } else if (mfa_str == "Disabled") {
+ mfa_status = MFA_DISABLED;
+ } else {
+ retcode = -EINVAL;
+ }
}
-
- return 0;
}
};
int RGWSetBucketVersioning_ObjStore_S3::get_params()
{
- char *data = nullptr;
- int len = 0;
- int r =
- rgw_rest_read_all_input(s, &data, &len, s->cct->_conf->rgw_max_put_param_size, false);
+ int r = 0;
+ bufferlist data;
+ std::tie(r, data) =
+ rgw_rest_read_all_input(s, s->cct->_conf->rgw_max_put_param_size, false);
if (r < 0) {
return r;
}
-
- auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
r = do_aws4_auth_completion();
if (r < 0) {
return r;
}
- RGWSetBucketVersioningParser parser;
-
+ RGWXMLDecoder::XMLParser parser;
if (!parser.init()) {
ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
- r = -EIO;
- return r;
+ return -EIO;
}
- if (!parser.parse(data, len, 1)) {
- ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
+ char* buf = data.c_str();
+ if (!parser.parse(buf, data.length(), 1)) {
+ ldout(s->cct, 10) << "NOTICE: failed to parse data: " << buf << dendl;
r = -EINVAL;
return r;
}
- if (!store->is_meta_master()) {
+ ver_config_status status_conf;
+
+ if (!RGWXMLDecoder::decode_xml("VersioningConfiguration", status_conf, &parser)) {
+ ldout(s->cct, 10) << "NOTICE: bad versioning config input" << dendl;
+ return -EINVAL;
+ }
+
+ if (!store->svc.zone->is_meta_master()) {
/* only need to keep this data around if we're not meta master */
- in_data.append(data, len);
+ in_data.append(data);
}
- r = parser.get_versioning_status(&enable_versioning);
-
+ versioning_status = status_conf.status;
+ if (versioning_status == VersioningStatusInvalid) {
+ r = -EINVAL;
+ }
+
+ if (status_conf.mfa_status != ver_config_status::MFA_UNKNOWN) {
+ mfa_set_status = true;
+ switch (status_conf.mfa_status) {
+ case ver_config_status::MFA_DISABLED:
+ mfa_status = false;
+ break;
+ case ver_config_status::MFA_ENABLED:
+ mfa_status = true;
+ break;
+ default:
+ ldout(s->cct, 0) << "ERROR: RGWSetBucketVersioning_ObjStore_S3::get_params(): unexpected switch case mfa_status=" << status_conf.mfa_status << dendl;
+ r = -EIO;
+ }
+ } else if (status_conf.retcode < 0) {
+ r = status_conf.retcode;
+ }
return r;
}
if (op_ret)
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s);
+ end_header(s, this, "application/xml");
}
int RGWSetBucketWebsite_ObjStore_S3::get_params()
{
- char *data = nullptr;
- int len = 0;
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
- int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ int r = 0;
+ bufferlist data;
+ std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
if (r < 0) {
return r;
}
- auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
-
r = do_aws4_auth_completion();
if (r < 0) {
return r;
}
- bufferptr in_ptr(data, len);
- in_data.append(in_ptr);
+ in_data.append(data);
RGWXMLDecoder::XMLParser parser;
if (!parser.init()) {
return -EIO;
}
- if (!parser.parse(data, len, 1)) {
- string str(data, len);
- ldout(s->cct, 5) << "failed to parse xml: " << str << dendl;
+ char* buf = data.c_str();
+ if (!parser.parse(buf, data.length(), 1)) {
+ ldout(s->cct, 5) << "failed to parse xml: " << buf << dendl;
return -EINVAL;
}
try {
RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf, &parser, true);
} catch (RGWXMLDecoder::err& err) {
- string str(data, len);
- ldout(s->cct, 5) << "unexpected xml: " << str << dendl;
+ ldout(s->cct, 5) << "unexpected xml: " << buf << dendl;
return -EINVAL;
}
+#define WEBSITE_ROUTING_RULES_MAX_NUM 50
+ int max_num = s->cct->_conf->rgw_website_routing_rules_max_num;
+ if (max_num < 0) {
+ max_num = WEBSITE_ROUTING_RULES_MAX_NUM;
+ }
+ int routing_rules_num = website_conf.routing_rules.rules.size();
+ if (routing_rules_num > max_num) {
+ ldout(s->cct, 4) << "An website routing config can have up to "
+ << max_num
+ << " rules, request website routing rules num: "
+ << routing_rules_num << dendl;
+ op_ret = -ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR;
+ s->err.message = std::to_string(routing_rules_num) +" routing rules provided, the number of routing rules in a website configuration is limited to "
+ + std::to_string(max_num)
+ + ".";
+ return -ERR_INVALID_REQUEST;
+ }
+
return 0;
}
if (op_ret < 0)
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s);
+ end_header(s, this, "application/xml");
}
void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
}
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s);
+ end_header(s, this, "application/xml");
}
void RGWGetBucketWebsite_ObjStore_S3::send_response()
policy = s3policy;
- int len = 0;
- char *data = nullptr;
-
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
- op_ret = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ int op_ret = 0;
+ bufferlist data;
+ std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
if ((op_ret < 0) && (op_ret != -ERR_LENGTH_REQUIRED))
return op_ret;
- auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
-
const int auth_ret = do_aws4_auth_completion();
if (auth_ret < 0) {
return auth_ret;
}
- bufferptr in_ptr(data, len);
- in_data.append(in_ptr);
+ in_data.append(data);
- if (len) {
+ if (data.length()) {
RGWCreateBucketParser parser;
if (!parser.init()) {
return -EIO;
}
- bool success = parser.parse(data, len, 1);
- ldout(s->cct, 20) << "create bucket input data=" << data << dendl;
+ char* buf = data.c_str();
+ bool success = parser.parse(buf, data.length(), 1);
+ ldout(s->cct, 20) << "create bucket input data=" << buf << dendl;
if (!success) {
- ldout(s->cct, 0) << "failed to parse input: " << data << dendl;
+ ldout(s->cct, 0) << "failed to parse input: " << buf << dendl;
return -EINVAL;
}
size_t pos = location_constraint.find(':');
if (pos != string::npos) {
- placement_rule = location_constraint.substr(pos + 1);
+ placement_rule.init(location_constraint.substr(pos + 1), s->info.storage_class);
location_constraint = location_constraint.substr(0, pos);
+ } else {
+ placement_rule.storage_class = s->info.storage_class;
}
return 0;
if (!s->length)
return -ERR_LENGTH_REQUIRED;
- RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
map<string, bufferlist> src_attrs;
size_t pos;
int ret;
return ret;
}
}
- ret = store->get_bucket_info(obj_ctx,
+ ret = store->get_bucket_info(*s->sysobj_ctx,
copy_source_tenant_name,
copy_source_bucket_name,
copy_source_bucket_info,
/* handle object tagging */
auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
if (tag_str){
- obj_tags = ceph::make_unique<RGWObjTags>();
+ obj_tags = std::make_unique<RGWObjTags>();
ret = obj_tags->set_from_string(tag_str);
if (ret < 0){
ldout(s->cct,0) << "setting obj tags failed with " << ret << dendl;
}
}
+ multipart_upload_id = s->info.args.get("uploadId");
+ multipart_part_str = s->info.args.get("partNumber");
+ if (!multipart_part_str.empty()) {
+ string err;
+ multipart_part_num = strict_strtol(multipart_part_str.c_str(), 10, &err);
+ if (!err.empty()) {
+ ldpp_dout(s, 10) << "bad part number: " << multipart_part_str << ": " << err << dendl;
+ return -EINVAL;
+ }
+ } else if (!multipart_upload_id.empty()) {
+ ldpp_dout(s, 10) << "part number with no multipart upload id" << dendl;
+ return -EINVAL;
+ }
+
+ append = s->info.args.exists("append");
+ if (append) {
+ string pos_str = s->info.args.get("position");
+ if (pos_str.empty()) {
+ return -EINVAL;
+ } else {
+ position = strtoull(pos_str.c_str(), NULL, 10);
+ }
+ }
+
return RGWPutObj_ObjStore::get_params();
}
dump_errno(s);
dump_etag(s, etag);
dump_content_length(s, 0);
+ dump_header_if_nonempty(s, "x-amz-version-id", version_id);
for (auto &it : crypt_http_responses)
dump_header(s, it.first, it.second);
} else {
dump_errno(s);
+ dump_header_if_nonempty(s, "x-amz-version-id", version_id);
end_header(s, this, "application/xml");
dump_start(s);
struct tm tmp;
return;
}
}
+ if (append) {
+ if (op_ret == 0 || op_ret == -ERR_POSITION_NOT_EQUAL_TO_LENGTH) {
+ dump_header(s, "x-rgw-next-append-position", cur_accounted_size);
+ }
+ }
if (s->system_request && !real_clock::is_zero(mtime)) {
dump_epoch_header(s, "Rgwx-Mtime", mtime);
}
static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
{
bufferlist bl;
- ::encode(value,bl);
+ encode(value,bl);
attrs.emplace(key, std::move(bl));
}
static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
{
bufferlist bl;
- ::encode(value,bl);
+ encode(value,bl);
attrs.emplace(key, std::move(bl));
}
int RGWPutObj_ObjStore_S3::get_decrypt_filter(
- std::unique_ptr<RGWGetDataCB>* filter,
- RGWGetDataCB* cb,
+ std::unique_ptr<RGWGetObj_Filter>* filter,
+ RGWGetObj_Filter* cb,
map<string, bufferlist>& attrs,
bufferlist* manifest_bl)
{
}
int RGWPutObj_ObjStore_S3::get_encrypt_filter(
- std::unique_ptr<RGWPutObjDataProcessor>* filter,
- RGWPutObjDataProcessor* cb)
+ std::unique_ptr<rgw::putobj::DataProcessor> *filter,
+ rgw::putobj::DataProcessor *cb)
{
int res = 0;
- RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast<RGWPutObjProcessor_Multipart*>(cb);
- if (multi_processor != nullptr) {
- RGWMPObj* mp = nullptr;
- multi_processor->get_mp(&mp);
- if (mp != nullptr) {
- map<string, bufferlist> xattrs;
- string meta_oid;
- meta_oid = mp->get_meta();
-
- rgw_obj obj;
- obj.init_ns(s->bucket, meta_oid, RGW_OBJ_NS_MULTIPART);
- obj.set_in_extra_data(true);
- res = get_obj_attrs(store, s, obj, xattrs);
- if (res == 0) {
- std::unique_ptr<BlockCrypt> block_crypt;
- /* We are adding to existing object.
- * We use crypto mode that configured as if we were decrypting. */
- res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
- if (res == 0 && block_crypt != nullptr)
- *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
- new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
- }
+ if (!multipart_upload_id.empty()) {
+ RGWMPObj mp(s->object.name, multipart_upload_id);
+ rgw_obj obj;
+ obj.init_ns(s->bucket, mp.get_meta(), RGW_OBJ_NS_MULTIPART);
+ obj.set_in_extra_data(true);
+ map<string, bufferlist> xattrs;
+ res = get_obj_attrs(store, s, obj, xattrs);
+ if (res == 0) {
+ std::unique_ptr<BlockCrypt> block_crypt;
+ /* We are adding to existing object.
+ * We use crypto mode that configured as if we were decrypting. */
+ res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
+ if (res == 0 && block_crypt != nullptr)
+ filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
/* it is ok, to not have encryption at all */
}
std::unique_ptr<BlockCrypt> block_crypt;
res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
if (res == 0 && block_crypt != nullptr) {
- *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
- new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
+ filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
}
return res;
if (r < 0)
return r;
- if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 20)) {
+ if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
ldout(s->cct, 20) << "read part header -- part.name="
<< part.name << dendl;
{
string tags_str;
if (part_str(parts, "tagging", &tags_str)) {
- RGWObjTagsXMLParser parser;
+ RGWXMLParser parser;
if (!parser.init()){
ldout(s->cct, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
err_msg = "Server couldn't process the request";
return -EINVAL;
}
- RGWObjTagSet_S3 *obj_tags_s3;
- RGWObjTagging_S3 *tagging;
+ RGWObjTagging_S3 tagging;
- tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
- obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
- if(!obj_tags_s3){
- return -ERR_MALFORMED_XML;
+ try {
+ RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
+ } catch (RGWXMLDecoder::err& err) {
+ ldout(s->cct, 5) << "Malformed tagging request: " << err << dendl;
+ return -EINVAL;
}
RGWObjTags obj_tags;
- int r = obj_tags_s3->rebuild(obj_tags);
+ int r = tagging.rebuild(obj_tags);
if (r < 0)
return r;
}
}
+ part_str(parts, "x-amz-security-token", &s->auth.s3_postobj_creds.x_amz_security_token);
+
/* FIXME: this is a makeshift solution. The browser upload authentication will be
* handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
* method. */
- const int ret = rgw::auth::Strategy::apply(auth_registry_ptr->get_s3_post(), s);
+ const int ret = rgw::auth::Strategy::apply(this, auth_registry_ptr->get_s3_post(), s);
if (ret != 0) {
return -EACCES;
} else {
for (auto &it : crypt_http_responses)
dump_header(s, it.first, it.second);
s->formatter->open_object_section("PostResponse");
- if (g_conf->rgw_dns_name.length())
- s->formatter->dump_format("Location", "%s/%s",
- s->info.script_uri.c_str(),
- s->object.name.c_str());
- if (!s->bucket_tenant.empty())
+ std::string base_uri = compute_domain_uri(s);
+ if (!s->bucket_tenant.empty()){
+ s->formatter->dump_format("Location", "%s/%s:%s/%s",
+ base_uri.c_str(),
+ url_encode(s->bucket_tenant).c_str(),
+ url_encode(s->bucket_name).c_str(),
+ url_encode(s->object.name).c_str());
s->formatter->dump_string("Tenant", s->bucket_tenant);
+ } else {
+ s->formatter->dump_format("Location", "%s/%s/%s",
+ base_uri.c_str(),
+ url_encode(s->bucket_name).c_str(),
+ url_encode(s->object.name).c_str());
+ }
s->formatter->dump_string("Bucket", s->bucket_name);
s->formatter->dump_string("Key", s->object.name);
+ s->formatter->dump_string("ETag", etag);
s->formatter->close_section();
}
s->err.message = err_msg;
}
int RGWPostObj_ObjStore_S3::get_encrypt_filter(
- std::unique_ptr<RGWPutObjDataProcessor>* filter, RGWPutObjDataProcessor* cb)
+ std::unique_ptr<rgw::putobj::DataProcessor> *filter,
+ rgw::putobj::DataProcessor *cb)
{
- int res = 0;
std::unique_ptr<BlockCrypt> block_crypt;
- res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt, crypt_http_responses);
+ int res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt,
+ crypt_http_responses);
if (res == 0 && block_crypt != nullptr) {
- *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
- new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
+ filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
- else
- *filter = nullptr;
return res;
}
set_req_state_err(s, r);
dump_errno(s);
- if (!version_id.empty()) {
- dump_header(s, "x-amz-version-id", version_id);
- }
+ dump_header_if_nonempty(s, "x-amz-version-id", version_id);
if (delete_marker) {
dump_header(s, "x-amz-delete-marker", "true");
}
if (s->system_request) {
source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", ©_if_newer, false);
- if (!source_zone.empty()) {
- client_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "client-id");
- op_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "op-id");
-
- if (client_id.empty() || op_id.empty()) {
- ldout(s->cct, 0) <<
- RGW_SYS_PARAM_PREFIX "client-id or "
- RGW_SYS_PARAM_PREFIX "op-id were not provided, "
- "required for intra-region copy"
- << dendl;
- return -EINVAL;
- }
- }
}
- const char *md_directive = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
+ copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
+ md_directive = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
if (md_directive) {
if (strcasecmp(md_directive, "COPY") == 0) {
attrs_mod = RGWRados::ATTRSMOD_NONE;
} else if (!source_zone.empty()) {
attrs_mod = RGWRados::ATTRSMOD_NONE; // default for intra-zone_group copy
} else {
- ldout(s->cct, 0) << "invalid metadata directive" << dendl;
+ s->err.message = "Unknown metadata directive.";
+ ldout(s->cct, 0) << s->err.message << dendl;
return -EINVAL;
}
}
(dest_object.compare(src_object.name) == 0) &&
src_object.instance.empty() &&
(attrs_mod != RGWRados::ATTRSMOD_REPLACE)) {
+ need_to_check_storage_class = true;
+ }
+
+ return 0;
+}
+
+int RGWCopyObj_ObjStore_S3::check_storage_class(const rgw_placement_rule& src_placement)
+{
+ if (src_placement == s->dest_placement) {
/* can only copy object into itself if replacing attrs */
- ldout(s->cct, 0) << "can't copy object into itself if not replacing attrs"
- << dendl;
+ s->err.message = "This copy request is illegal because it is trying to copy "
+ "an object to itself without changing the object's metadata, "
+ "storage class, website redirect location or encryption attributes.";
+ ldout(s->cct, 0) << s->err.message << dendl;
return -ERR_INVALID_REQUEST;
}
return 0;
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
dump_start(s);
if (op_ret == 0) {
s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
if (op_ret == 0) {
dump_time(s, "LastModified", &mtime);
- std::string etag_str = etag.to_str();
- if (! etag_str.empty()) {
- s->formatter->dump_string("ETag", std::move(etag_str));
+ if (!etag.empty()) {
+ s->formatter->dump_string("ETag", std::move(etag));
}
s->formatter->close_section();
rgw_flush_formatter_and_reset(s, s->formatter);
return;
}
- bufferlist::iterator iter(&aiter->second);
+ bufferlist::const_iterator iter{&aiter->second};
try {
config.decode(iter);
} catch (const buffer::error& e) {
if (op_ret < 0)
return;
- config.dump_xml(s->formatter);
+ encode_xml("LifecycleConfiguration", XMLNS_AWS_S3, config, s->formatter);
rgw_flush_formatter_and_reset(s, s->formatter);
}
int RGWPutCORS_ObjStore_S3::get_params()
{
- int r;
- char *data = nullptr;
- int len = 0;
RGWCORSXMLParser_S3 parser(s->cct);
RGWCORSConfiguration_S3 *cors_config;
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
- r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ int r = 0;
+ bufferlist data;
+ std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
if (r < 0) {
return r;
}
- auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
-
r = do_aws4_auth_completion();
if (r < 0) {
return r;
return -EINVAL;
}
- if (!data || !parser.parse(data, len, 1)) {
- return -EINVAL;
+ char* buf = data.c_str();
+ if (!buf || !parser.parse(buf, data.length(), 1)) {
+ return -ERR_MALFORMED_XML;
}
cors_config =
static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
"CORSConfiguration"));
if (!cors_config) {
- return -EINVAL;
+ return -ERR_MALFORMED_XML;
+ }
+
+#define CORS_RULES_MAX_NUM 100
+ int max_num = s->cct->_conf->rgw_cors_rules_max_num;
+ if (max_num < 0) {
+ max_num = CORS_RULES_MAX_NUM;
+ }
+ int cors_rules_num = cors_config->get_rules().size();
+ if (cors_rules_num > max_num) {
+ ldout(s->cct, 4) << "An cors config can have up to "
+ << max_num
+ << " rules, request cors rules num: "
+ << cors_rules_num << dendl;
+ op_ret = -ERR_INVALID_CORS_RULES_ERROR;
+ s->err.message = "The number of CORS rules should not exceed allowed limit of "
+ + std::to_string(max_num) + " rules.";
+ return -ERR_INVALID_REQUEST;
}
// forward bucket cors requests to meta master zone
- if (!store->is_meta_master()) {
+ if (!store->svc.zone->is_meta_master()) {
/* only need to keep this data around if we're not meta master */
- in_data.append(data, len);
+ in_data.append(data);
}
- if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) {
+ if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
ldout(s->cct, 15) << "CORSConfiguration";
cors_config->to_xml(*_dout);
*_dout << dendl;
if (!field)
return 0;
- string& s = field->get_data();
+ auto& s = field->get_data();
if (stringcasecmp(s, "Requester") == 0) {
*requester_pays = true;
int RGWSetRequestPayment_ObjStore_S3::get_params()
{
- char *data;
- int len = 0;
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
- int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
+
+ int r = 0;
+ bufferlist data;
+ std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
if (r < 0) {
return r;
if (!parser.init()) {
ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
- r = -EIO;
- goto done;
+ return -EIO;
}
- if (!parser.parse(data, len, 1)) {
- ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
- r = -EINVAL;
- goto done;
+ char* buf = data.c_str();
+ if (!parser.parse(buf, data.length(), 1)) {
+ ldout(s->cct, 10) << "failed to parse data: " << buf << dendl;
+ return -EINVAL;
}
- r = parser.get_request_payment_payer(&requester_pays);
-
-done:
- free(data);
-
- return r;
+ return parser.get_request_payment_payer(&requester_pays);
}
void RGWSetRequestPayment_ObjStore_S3::send_response()
if (op_ret)
set_req_state_err(s, op_ret);
dump_errno(s);
+ dump_header_if_nonempty(s, "x-amz-version-id", version_id);
end_header(s, this, "application/xml");
if (op_ret == 0) {
dump_start(s);
if (op_ret)
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
if (op_ret == 0) {
dump_start(s);
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
dump_start(s);
if (op_ret < 0)
return;
s->formatter->dump_string("Bucket", s->bucket_name);
if (!prefix.empty())
s->formatter->dump_string("ListMultipartUploadsResult.Prefix", prefix);
- string& key_marker = marker.get_key();
+ const string& key_marker = marker.get_key();
if (!key_marker.empty())
s->formatter->dump_string("KeyMarker", key_marker);
- string& upload_id_marker = marker.get_upload_id();
+ const string& upload_id_marker = marker.get_upload_id();
if (!upload_id_marker.empty())
s->formatter->dump_string("UploadIdMarker", upload_id_marker);
string next_key = next_marker.mp.get_key();
}
dump_start(s);
- end_header(s, this, "application/xml");
+ // Explicitly use chunked transfer encoding so that we can stream the result
+ // to the user without having to wait for the full length of it.
+ end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
rgw_flush_formatter(s, s->formatter);
for (auto miter = manifest->obj_begin(); miter != manifest->obj_end(); ++miter) {
f.open_object_section("obj");
rgw_raw_obj raw_loc = miter.get_location().get_raw_obj(store);
+ uint64_t ofs = miter.get_ofs();
+ uint64_t left = manifest->get_obj_size() - ofs;
::encode_json("ofs", miter.get_ofs(), &f);
::encode_json("loc", raw_loc, &f);
::encode_json("loc_ofs", miter.location_ofs(), &f);
- ::encode_json("loc_size", miter.get_stripe_size(), &f);
+ uint64_t loc_size = miter.get_stripe_size();
+ if (loc_size > left) {
+ loc_size = left;
+ }
+ ::encode_json("loc_size", loc_size, &f);
f.close_section();
rgw_flush_formatter(s, &f);
}
return new RGWListRolePolicies;
if (action.compare("DeleteRolePolicy") == 0)
return new RGWDeleteRolePolicy;
+ if (action.compare("PutUserPolicy") == 0)
+ return new RGWPutUserPolicy;
+ if (action.compare("GetUserPolicy") == 0)
+ return new RGWGetUserPolicy;
+ if (action.compare("ListUserPolicies") == 0)
+ return new RGWListUserPolicies;
+ if (action.compare("DeleteUserPolicy") == 0)
+ return new RGWDeleteUserPolicy;
+ }
+ if (this->isSTSenabled) {
+ RGWHandler_REST_STS sts_handler(auth_registry);
+ sts_handler.init(store, s, s->cio);
+ return sts_handler.get_op(store);
}
return NULL;
}
RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
{
- if (is_acl_op()) {
- return new RGWGetACLs_ObjStore_S3;
- }
RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
get_obj_op->set_get_data(get_data);
return get_obj_op;
return 0;
}
+static int verify_mfa(RGWRados *store, RGWUserInfo *user, const string& mfa_str, bool *verified)
+{
+ vector<string> params;
+ get_str_vec(mfa_str, " ", params);
+
+ if (params.size() != 2) {
+ ldout(store->ctx(), 5) << "NOTICE: invalid mfa string provided: " << mfa_str << dendl;
+ return -EINVAL;
+ }
+
+ string& serial = params[0];
+ string& pin = params[1];
+
+ auto i = user->mfa_ids.find(serial);
+ if (i == user->mfa_ids.end()) {
+ ldout(store->ctx(), 5) << "NOTICE: user does not have mfa device with serial=" << serial << dendl;
+ return -EACCES;
+ }
+
+ int ret = store->check_mfa(user->user_id, serial, pin);
+ if (ret < 0) {
+ ldout(store->ctx(), 20) << "NOTICE: failed to check MFA, serial=" << serial << dendl;
+ return -EACCES;
+ }
+
+ *verified = true;
+
+ return 0;
+}
+
int RGWHandler_REST_S3::postauth_init()
{
struct req_init_state *t = &s->init_state;
if (ret)
return ret;
}
+
+ const char *mfa = s->info.env->get("HTTP_X_AMZ_MFA");
+ if (mfa) {
+ ret = verify_mfa(store, s->user, string(mfa), &s->mfa_verified);
+ }
+
return 0;
}
}
}
+ const char *sc = s->info.env->get("HTTP_X_AMZ_STORAGE_CLASS");
+ if (sc) {
+ s->info.storage_class = sc;
+ }
+
return RGWHandler_REST::init(store, s, cio);
}
+int RGWHandler_REST_S3::authorize(const DoutPrefixProvider *dpp)
+{
+ if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+ return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
+ }
+ return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
+}
+
enum class AwsVersion {
- UNKOWN,
+ UNKNOWN,
V2,
V4
};
enum class AwsRoute {
- UNKOWN,
+ UNKNOWN,
QUERY_STRING,
HEADERS
};
{
using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
- AwsVersion version = AwsVersion::UNKOWN;
- AwsRoute route = AwsRoute::UNKOWN;
+ AwsVersion version = AwsVersion::UNKNOWN;
+ AwsRoute route = AwsRoute::UNKNOWN;
const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
if (http_auth && http_auth[0]) {
return std::make_pair(version, route);
}
-static void init_anon_user(struct req_state *s)
-{
- rgw_get_anon_user(*(s->user));
- s->perm_mask = RGW_PERM_FULL_CONTROL;
-}
-
/*
* verify that a signed request comes from the keyholder
* by checking the signature against our locally-computed version
*
* it tries AWS v4 before AWS v2
*/
-int RGW_Auth_S3::authorize(RGWRados* const store,
+int RGW_Auth_S3::authorize(const DoutPrefixProvider *dpp,
+ RGWRados* const store,
const rgw::auth::StrategyRegistry& auth_registry,
struct req_state* const s)
{
if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
!store->ctx()->_conf->rgw_s3_auth_use_keystone &&
!store->ctx()->_conf->rgw_s3_auth_use_ldap) {
- dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
+ ldpp_dout(dpp, 0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
return -EPERM;
}
- const auto ret = rgw::auth::Strategy::apply(auth_registry.get_s3_main(), s);
+ const auto ret = rgw::auth::Strategy::apply(dpp, auth_registry.get_s3_main(), s);
if (ret == 0) {
/* Populate the owner info. */
s->owner.set_id(s->user->user_id);
}
} else {
if (s->init_state.url_bucket.empty()) {
- handler = new RGWHandler_REST_Service_S3(auth_registry);
+ handler = new RGWHandler_REST_Service_S3(auth_registry, enable_sts);
} else if (s->object.empty()) {
handler = new RGWHandler_REST_Bucket_S3(auth_registry);
} else {
rgw_obj obj(s->bucket, subdir_name);
RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
- obj_ctx.obj.set_atomic(obj);
- obj_ctx.obj.set_prefetch_data(obj);
+ obj_ctx.set_atomic(obj);
+ obj_ctx.set_prefetch_data(obj);
RGWObjState* state = nullptr;
if (store->get_obj_state(&obj_ctx, s->bucket_info, obj, &state, false) < 0) {
int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op) {
*new_op = op;
- ldout(s->cct, 10) << __func__ << "Starting retarget" << dendl;
+ ldout(s->cct, 10) << __func__ << " Starting retarget" << dendl;
if (!(s->prot_flags & RGW_REST_WEBSITE))
return 0;
- RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
- int ret = store->get_bucket_info(obj_ctx, s->bucket_tenant,
+ int ret = store->get_bucket_info(*s->sysobj_ctx, s->bucket_tenant,
s->bucket_name, s->bucket_info, NULL,
&s->bucket_attrs);
if (ret < 0) {
AWSEngine::VersionAbstractor::auth_data_t
AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
- /* FIXME: const. */
- bool using_qs) const
+ const bool using_qs) const
{
boost::string_view access_key_id;
boost::string_view signed_hdrs;
boost::string_view date;
boost::string_view credential_scope;
boost::string_view client_signature;
+ boost::string_view session_token;
int ret = rgw::auth::s3::parse_v4_credentials(s->info,
access_key_id,
signed_hdrs,
client_signature,
date,
+ session_token,
using_qs);
if (ret < 0) {
throw ret;
throw -EPERM;
}
- /* Get the expected hash. */
- auto exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
+ bool is_non_s3_op = false;
+ if (s->op_type == RGW_STS_GET_SESSION_TOKEN ||
+ s->op_type == RGW_STS_ASSUME_ROLE ||
+ s->op_type == RGW_STS_ASSUME_ROLE_WEB_IDENTITY) {
+ is_non_s3_op = true;
+ }
+
+ const char* exp_payload_hash = nullptr;
+ string payload_hash;
+ if (is_non_s3_op) {
+ //For non s3 ops, we need to calculate the payload hash
+ payload_hash = s->info.args.get("PayloadHash");
+ exp_payload_hash = payload_hash.c_str();
+ } else {
+ /* Get the expected hash. */
+ exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
+ }
/* Craft canonical URI. Using std::move later so let it be non-const. */
auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);
* This means we have absolutely no business in spawning completer. Both
* aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
* by default. We don't need to change that. */
- if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s)) {
+ if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s) || is_non_s3_op) {
return {
access_key_id,
client_signature,
+ session_token,
std::move(string_to_sign),
sig_factory,
null_completer_factory
return {
access_key_id,
client_signature,
+ session_token,
std::move(string_to_sign),
sig_factory,
cmpl_factory
return {
access_key_id,
client_signature,
+ session_token,
std::move(string_to_sign),
sig_factory,
cmpl_factory
{
boost::string_view access_key_id;
boost::string_view signature;
+ boost::string_view session_token;
bool qsr = false;
const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
if (now >= exp) {
throw -EPERM;
}
+ if (s->info.args.exists("X-Amz-Security-Token")) {
+ session_token = s->info.args.get("X-Amz-Security-Token");
+ if (session_token.size() == 0) {
+ throw -EPERM;
+ }
+ }
+
} else {
/* The "Authorization" HTTP header is being used. */
const boost::string_view auth_str(http_auth + strlen("AWS "));
access_key_id = auth_str.substr(0, pos);
signature = auth_str.substr(pos + 1);
}
+
+ if (s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
+ session_token = s->info.env->get("HTTP_X_AMZ_SECURITY_TOKEN");
+ if (session_token.size() == 0) {
+ throw -EPERM;
+ }
+ }
}
/* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
return {
std::move(access_key_id),
std::move(signature),
+ std::move(session_token),
std::move(string_to_sign),
rgw::auth::s3::get_v2_signature,
null_completer_factory
return {
s->auth.s3_postobj_creds.access_key,
s->auth.s3_postobj_creds.signature,
+ s->auth.s3_postobj_creds.x_amz_security_token,
s->auth.s3_postobj_creds.encoded_policy.to_str(),
rgw::auth::s3::get_v2_signature,
null_completer_factory
return {
access_key_id,
s->auth.s3_postobj_creds.signature,
+ s->auth.s3_postobj_creds.x_amz_security_token,
s->auth.s3_postobj_creds.encoded_policy.to_str(),
sig_factory,
null_completer_factory
}
}
-
AWSEngine::result_t
-AWSEngine::authenticate(const req_state* const s) const
+AWSEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* const s) const
{
/* Small reminder: an ver_abstractor is allowed to throw! */
const auto auth_data = ver_abstractor.get_auth_data(s);
if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
return result_t::deny(-EINVAL);
} else {
- return authenticate(auth_data.access_key_id,
+ return authenticate(dpp,
+ auth_data.access_key_id,
auth_data.client_signature,
+ auth_data.session_token,
auth_data.string_to_sign,
auth_data.signature_factory,
auth_data.completer_factory,
rgw::auth::Engine::result_t
rgw::auth::s3::LDAPEngine::authenticate(
+ const DoutPrefixProvider* dpp,
const boost::string_view& access_key_id,
const boost::string_view& signature,
+ const boost::string_view& session_token,
const string_to_sign_t& string_to_sign,
const signature_factory_t&,
const completer_factory_t& completer_factory,
user_info.user_id = base64_token.id;
if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
if (user_info.type != TYPE_LDAP) {
- ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
+ ldpp_dout(dpp, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
return nullptr;
}
}*/
auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
get_creds_info(base64_token));
return result_t::grant(std::move(apl), completer_factory(boost::none));
-}
+} /* rgw::auth::s3::LDAPEngine::authenticate */
+void rgw::auth::s3::LDAPEngine::shutdown() {
+ if (ldh) {
+ delete ldh;
+ ldh = nullptr;
+ }
+}
-/* LocalEndgine */
+/* LocalEngine */
rgw::auth::Engine::result_t
rgw::auth::s3::LocalEngine::authenticate(
+ const DoutPrefixProvider* dpp,
const boost::string_view& _access_key_id,
const boost::string_view& signature,
+ const boost::string_view& session_token,
const string_to_sign_t& string_to_sign,
const signature_factory_t& signature_factory,
const completer_factory_t& completer_factory,
/* TODO(rzarzynski): we need to have string-view taking variant. */
const std::string access_key_id = _access_key_id.to_string();
if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) {
- ldout(cct, 5) << "error reading user info, uid=" << access_key_id
+ ldpp_dout(dpp, 5) << "error reading user info, uid=" << access_key_id
<< " can't authenticate" << dendl;
return result_t::deny(-ERR_INVALID_ACCESS_KEY);
}
//TODO: Uncomment, when we have a migration plan in place.
/*else {
if (s->user->type != TYPE_RGW) {
- ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
+ ldpp_dout(dpp, 10) << "ERROR: User id of type: " << s->user->type
<< " is present" << dendl;
throw -EPERM;
}
const auto iter = user_info.access_keys.find(access_key_id);
if (iter == std::end(user_info.access_keys)) {
- ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl;
+ ldpp_dout(dpp, 0) << "ERROR: access key not encoded in user info" << dendl;
return result_t::deny(-EPERM);
}
const RGWAccessKey& k = iter->second;
const VersionAbstractor::server_signature_t server_signature = \
signature_factory(cct, k.key, string_to_sign);
+ auto compare = signature.compare(server_signature);
- ldout(cct, 15) << "string_to_sign="
+ ldpp_dout(dpp, 15) << "string_to_sign="
<< rgw::crypt_sanitize::log_content{string_to_sign}
<< dendl;
- ldout(cct, 15) << "server signature=" << server_signature << dendl;
- ldout(cct, 15) << "client signature=" << signature << dendl;
- ldout(cct, 15) << "compare=" << signature.compare(server_signature) << dendl;
+ ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
+ ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
+ ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
- if (static_cast<boost::string_view>(server_signature) != signature) {
+ if (compare != 0) {
return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
}
- auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser);
+ auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser, boost::none);
return result_t::grant(std::move(apl), completer_factory(k.key));
}
+rgw::auth::RemoteApplier::AuthInfo
+rgw::auth::s3::STSEngine::get_creds_info(const STS::SessionToken& token) const noexcept
+{
+ using acct_privilege_t = \
+ rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
+
+ return rgw::auth::RemoteApplier::AuthInfo {
+ token.user,
+ token.acct_name,
+ token.perm_mask,
+ (token.is_admin) ? acct_privilege_t::IS_ADMIN_ACCT: acct_privilege_t::IS_PLAIN_ACCT,
+ token.acct_type
+ };
+}
+
+int
+rgw::auth::s3::STSEngine::get_session_token(const boost::string_view& session_token,
+ STS::SessionToken& token) const
+{
+ string decodedSessionToken = rgw::from_base64(session_token);
+
+ auto* cryptohandler = cct->get_crypto_handler(CEPH_CRYPTO_AES);
+ if (! cryptohandler) {
+ return -EINVAL;
+ }
+ string secret_s = cct->_conf->rgw_sts_key;
+ buffer::ptr secret(secret_s.c_str(), secret_s.length());
+ int ret = 0;
+ if (ret = cryptohandler->validate_secret(secret); ret < 0) {
+ ldout(cct, 0) << "ERROR: Invalid secret key" << dendl;
+ return -EINVAL;
+ }
+ string error;
+ auto* keyhandler = cryptohandler->get_key_handler(secret, error);
+ if (! keyhandler) {
+ return -EINVAL;
+ }
+ error.clear();
+
+ string decrypted_str;
+ buffer::list en_input, dec_output;
+ en_input = buffer::list::static_from_string(decodedSessionToken);
+
+ ret = keyhandler->decrypt(en_input, dec_output, &error);
+ if (ret < 0) {
+ ldout(cct, 0) << "ERROR: Decryption failed: " << error << dendl;
+ return -EPERM;
+ } else {
+ dec_output.append('\0');
+ auto iter = dec_output.cbegin();
+ decode(token, iter);
+ }
+ return 0;
+}
+
+rgw::auth::Engine::result_t
+rgw::auth::s3::STSEngine::authenticate(
+ const DoutPrefixProvider* dpp,
+ const boost::string_view& _access_key_id,
+ const boost::string_view& signature,
+ const boost::string_view& session_token,
+ const string_to_sign_t& string_to_sign,
+ const signature_factory_t& signature_factory,
+ const completer_factory_t& completer_factory,
+ const req_state* const s) const
+{
+ if (! s->info.args.exists("X-Amz-Security-Token") &&
+ ! s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
+ return result_t::deny();
+ }
+
+ STS::SessionToken token;
+ if (int ret = get_session_token(session_token, token); ret < 0) {
+ return result_t::reject(ret);
+ }
+ //Authentication
+ //Check if access key is not the same passed in by client
+ if (token.access_key_id != _access_key_id) {
+ ldpp_dout(dpp, 0) << "Invalid access key" << dendl;
+ return result_t::reject(-EPERM);
+ }
+ //Check if the token has expired
+ if (! token.expiration.empty()) {
+ std::string expiration = token.expiration;
+ if (! expiration.empty()) {
+ boost::optional<real_clock::time_point> exp = ceph::from_iso_8601(expiration, false);
+ if (exp) {
+ real_clock::time_point now = real_clock::now();
+ if (now >= *exp) {
+ ldpp_dout(dpp, 0) << "ERROR: Token expired" << dendl;
+ return result_t::reject(-EPERM);
+ }
+ } else {
+ ldpp_dout(dpp, 0) << "ERROR: Invalid expiration: " << expiration << dendl;
+ return result_t::reject(-EPERM);
+ }
+ }
+ }
+ //Check for signature mismatch
+ const VersionAbstractor::server_signature_t server_signature = \
+ signature_factory(cct, token.secret_access_key, string_to_sign);
+ auto compare = signature.compare(server_signature);
+
+ ldpp_dout(dpp, 15) << "string_to_sign="
+ << rgw::crypt_sanitize::log_content{string_to_sign}
+ << dendl;
+ ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
+ ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
+ ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
+
+ if (compare != 0) {
+ return result_t::reject(-ERR_SIGNATURE_NO_MATCH);
+ }
+
+ // Get all the authorization info
+ RGWUserInfo user_info;
+ rgw_user user_id;
+ vector<string> role_policies;
+ string role_name;
+ if (! token.roleId.empty()) {
+ RGWRole role(s->cct, store, token.roleId);
+ if (role.get_by_id() < 0) {
+ return result_t::deny(-EPERM);
+ }
+ vector<string> role_policy_names = role.get_role_policy_names();
+ for (auto& policy_name : role_policy_names) {
+ string perm_policy;
+ if (int ret = role.get_role_policy(policy_name, perm_policy); ret == 0) {
+ role_policies.push_back(std::move(perm_policy));
+ }
+ }
+ if (! token.policy.empty()) {
+ role_policies.push_back(std::move(token.policy));
+ }
+ // This is mostly needed to assign the owner of a bucket during its creation
+ user_id = token.user;
+ role_name = role.get_name();
+ }
+
+ if (! token.user.empty() && token.acct_type != TYPE_ROLE) {
+ // get user info
+ int ret = rgw_get_user_info_by_uid(store, token.user, user_info, NULL);
+ if (ret < 0) {
+ ldpp_dout(dpp, 5) << "ERROR: failed reading user info: uid=" << token.user << dendl;
+ return result_t::reject(-EPERM);
+ }
+ }
+
+ if (token.acct_type == TYPE_KEYSTONE || token.acct_type == TYPE_LDAP) {
+ auto apl = remote_apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
+ get_creds_info(token));
+ return result_t::grant(std::move(apl), completer_factory(boost::none));
+ } else if (token.acct_type == TYPE_ROLE) {
+ auto apl = role_apl_factory->create_apl_role(cct, s, role_name, user_id, role_policies);
+ return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
+ } else { // This is for all local users of type TYPE_RGW or TYPE_NONE
+ string subuser;
+ auto apl = local_apl_factory->create_apl_local(cct, s, user_info, subuser, token.perm_mask);
+ return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
+ }
+}
+
bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
const req_state* s
) const noexcept {
AwsRoute route;
std::tie(version, route) = discover_aws_flavour(s->info);
- return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKOWN;
+ return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKNOWN;
}