From: Adam Kupczyk Date: Fri, 10 Jun 2016 14:05:00 +0000 (+0200) Subject: Added aws:kms mode to get/put/post operations. X-Git-Tag: v12.0.2~34^2~30 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=3895773e2f869b0bb7a8a025f5424a7d7864667f;p=ceph.git Added aws:kms mode to get/put/post operations. Refactored to export selection of encryption to rgw/rgw_crypt.h/cc Added quick exit to from_base64 when string_ref is empty. Fixed POST logic, so aws encryption can also be applied to objects created from web forms Extended KeystoneService with barbican user configuration. Keystone provides token, Barbican provides secret. Added option rgw_barbican_url. Signed-off-by: Adam Kupczyk Signed-off-by: Radoslaw Zarzynski --- diff --git a/src/common/config_opts.h b/src/common/config_opts.h index 230ec6c8f7b6..687474e803f7 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -1460,6 +1460,11 @@ OPTION(rgw_keystone_admin_password, OPT_STR, "") // keystone admin user passwor OPTION(rgw_keystone_admin_tenant, OPT_STR, "") // keystone admin user tenant (for keystone v2.0) OPTION(rgw_keystone_admin_project, OPT_STR, "") // keystone admin user project (for keystone v3) OPTION(rgw_keystone_admin_domain, OPT_STR, "") // keystone admin user domain +OPTION(rgw_keystone_barbican_user, OPT_STR, "") // keystone user to access barbican secrets +OPTION(rgw_keystone_barbican_password, OPT_STR, "") // keystone password for barbican user +OPTION(rgw_keystone_barbican_tenant, OPT_STR, "") // keystone barbican user tenant (for keystone v2.0) +OPTION(rgw_keystone_barbican_project, OPT_STR, "") // keystone barbican user project (for keystone v3) +OPTION(rgw_keystone_barbican_domain, OPT_STR, "") // keystone barbican user domain OPTION(rgw_keystone_api_version, OPT_INT, 2) // Version of Keystone API to use (2 or 3) OPTION(rgw_keystone_accepted_roles, OPT_STR, "Member, admin") // roles required to serve requests OPTION(rgw_keystone_accepted_admin_roles, OPT_STR, "") // list of roles allowing an user to gain admin privileges @@ -1472,6 +1477,7 @@ OPTION(rgw_healthcheck_disabling_path, OPT_STR, "") // path that existence cause OPTION(rgw_s3_auth_use_rados, OPT_BOOL, true) // should we try to use the internal credentials for s3? OPTION(rgw_s3_auth_use_keystone, OPT_BOOL, false) // should we try to use keystone for s3? OPTION(rgw_s3_auth_aws4_force_boto2_compat, OPT_BOOL, true) // force aws4 auth boto2 compatibility +OPTION(rgw_barbican_url, OPT_STR, "") // url for barbican server /* OpenLDAP-style LDAP parameter strings */ /* rgw_ldap_uri space-separated list of LDAP servers in URI format */ diff --git a/src/rgw/rgw_b64.h b/src/rgw/rgw_b64.h index 52374f493635..3f79eccd0ee2 100644 --- a/src/rgw/rgw_b64.h +++ b/src/rgw/rgw_b64.h @@ -61,7 +61,8 @@ namespace rgw { inline std::string from_base64(boost::string_ref sref) { using namespace boost::archive::iterators; - + if (sref.empty()) + return std::string(); /* MIME-compliant input will have line-breaks, so we have to * filter WS */ typedef diff --git a/src/rgw/rgw_crypt.cc b/src/rgw/rgw_crypt.cc index c7a7af9d6777..2136e854f548 100644 --- a/src/rgw/rgw_crypt.cc +++ b/src/rgw/rgw_crypt.cc @@ -8,10 +8,17 @@ #include #include #include +#include +#include +#include +#include "include/assert.h" +#include +#include #define dout_subsys ceph_subsys_rgw using namespace CryptoPP; +using namespace rgw; class AES_256_CTR_impl { static const size_t AES_256_KEYSIZE = 256 / 8; @@ -393,11 +400,450 @@ int RGWPutObj_BlockEncrypt::throttle_data(void *handle, const rgw_obj& obj, } std::string create_random_key_selector() { - return "0123456789012345"; + char random[AES_256_KEYSIZE]; + if (get_random_bytes(&random[0], sizeof(random)) != 0) { + dout(0) << "ERROR: cannot get_random_bytes. " << dendl; + for (char& v:random) v=rand(); + } + return std::string(random, sizeof(random)); } -int get_actual_key_from_kms(CephContext *cct, const char* key_id, const std::string& key_selector, std::string& actual_key) { - actual_key = "abcdefghijabcdef"; + +//-H "Accept: application/octet-stream" -H "X-Auth-Token: fa7067ae04e942fb879eaf37f46411a5" +//curl -v -H "Accept: application/octet-stream" -H "X-Auth-Token: fa7067ae04e942fb879eaf37f46411a5" http://localhost:9311/v1/secrets/5206dbad-7970-4a7a-82de-bd7df9a016db + +int get_barbican_url(CephContext * const cct, + std::string& url) +{ + url = cct->_conf->rgw_barbican_url; + if (url.empty()) { + ldout(cct, 0) << "ERROR: conf rgw_barbican_url is not set" << dendl; + return -EINVAL; + } + + if (url[url.size() - 1] != '/') { + url.append("/"); + } + return 0; } +int request_key_from_barbican(CephContext *cct, + boost::string_ref key_id, + boost::string_ref key_selector, + const std::string& barbican_token, + std::string& actual_key) { + std::string secret_url; + if (get_barbican_url(cct, secret_url) < 0) { + return -EINVAL; + } + secret_url += "v1/secrets/" + std::string(key_id); + + int res; + bufferlist secret_bl; + RGWHTTPTransceiver secret_req(cct, &secret_bl); + secret_req.append_header("Accept", "application/octet-stream"); + secret_req.append_header("X-Auth-Token", barbican_token); + + res = secret_req.process("GET", secret_url.c_str()); + if (res < 0) { + return res; + } + if (secret_req.get_http_status() == + RGWHTTPTransceiver::HTTP_STATUS_UNAUTHORIZED) { + return -EACCES; + } + + if (secret_req.get_http_status() >=200 && + secret_req.get_http_status() < 300 && + secret_bl.length() == AES_256_KEYSIZE) { + actual_key = std::string(secret_bl.c_str(), secret_bl.length()); + } else { + res = -EACCES; + } + return res; +} + + +int get_actual_key_from_kms(CephContext *cct, boost::string_ref key_id, boost::string_ref key_selector, std::string& actual_key) +{ + int res = 0; + ldout(cct, 20) << "Getting KMS encryption key for key=" << key_id << dendl; + if (key_id.starts_with("testkey-")) { + /* test keys for testing purposes */ + boost::string_ref key = key_id.substr(sizeof("testkey-")-1); + std::string master_key; + if (key == "1") master_key = "012345678901234567890123456789012345"; + else if (key == "2") master_key = "abcdefghijklmnopqrstuvwxyzabcdefghij"; + else { + res = -EIO; + return res; + } + uint8_t _actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt((uint8_t*)master_key.c_str(), AES_256_KEYSIZE, + (uint8_t*)key_selector.data(), + _actual_key, AES_256_KEYSIZE)) { + actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE); + } else { + res = -EIO; + } + } + else { + std::string token; + if (rgw::keystone::Service::get_keystone_barbican_token(cct, token) < 0) { + ldout(cct, 20) << "Failed to retrieve token for barbican" << dendl; + res = -EINVAL; + return res; + } + + res = request_key_from_barbican(cct, key_id, key_selector, token, actual_key); + if (res != 0) { + ldout(cct, 0) << "Failed to retrieve secret from barbican:" << key_id << dendl; + } + } + return res; +} + +static inline void set_attr(map& attrs, const char* key, const std::string& value) +{ + bufferlist bl; + ::encode(value,bl); + attrs.emplace(key, std::move(bl)); +} + +static inline void set_attr(map& attrs, const char* key, const char* value) +{ + bufferlist bl; + ::encode(value,bl); + attrs.emplace(key, std::move(bl)); +} + +static inline void set_attr(map& attrs, const char* key, boost::string_ref value) +{ + bufferlist bl; + __u32 len = value.length(); + encode(len, bl); + if (len) + bl.append(value.data(), len); + attrs.emplace(key, std::move(bl)); +} + +static inline std::string get_str_attribute(map& attrs, const std::string& name, const std::string& default_value="") { + std::string value; + auto iter = attrs.find(name); + if (iter == attrs.end() ) { + value = default_value; + } else { + try { + ::decode(value, iter->second); + } catch (buffer::error& err) { + value = default_value; + dout(0) << "ERROR: failed to decode attr:" << name << dendl; + } + } + return value; +} + +typedef enum { + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM=0, + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, + X_AMZ_SERVER_SIDE_ENCRYPTION, + X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID, + X_AMZ_SERVER_SIDE_ENCRYPTION_LAST +} crypt_option_e; + +typedef struct { + const char* http_header_name; + const std::string post_part_name; +} crypt_option_names; + +static const crypt_option_names crypt_options[] = { + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", "x-amz-server-side-encryption-customer-algorithm"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "x-amz-server-side-encryption-customer-key"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "x-amz-server-side-encryption-customer-key-md5"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", "x-amz-server-side-encryption"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID", "x-amz-server-side-encryption-aws-kms-key-id"}, +}; + +boost::string_ref rgw_trim_whitespace(const boost::string_ref& src) +{ + if (src.empty()) { + return boost::string_ref(); + } + + int start = 0; + for (; start != (int)src.size(); start++) { + if (!isspace(src[start])) + break; + } + + int end = src.size() - 1; + if (end < start) { + return boost::string_ref(); + } + + for (; end > start; end--) { + if (!isspace(src[end])) + break; + } + + return src.substr(start, end - start + 1); +} + +static boost::string_ref get_crypt_attribute(RGWEnv* env, + map* parts, + crypt_option_e option) +{ + static_assert(X_AMZ_SERVER_SIDE_ENCRYPTION_LAST == sizeof(crypt_options)/sizeof(*crypt_options), "Missing items in crypt_options"); + if (parts != nullptr) { + map::iterator iter + = parts->find(crypt_options[option].post_part_name); + if (iter == parts->end()) + return boost::string_ref(); + bufferlist& data = iter->second.data; + boost::string_ref str = boost::string_ref(data.c_str(), data.length()); + return rgw_trim_whitespace(str); + } else { + const char* hdr = env->get(crypt_options[option].http_header_name, nullptr); + if (hdr != nullptr) { + return boost::string_ref(hdr); + } else { + return boost::string_ref(); + } + } +} + +int s3_prepare_encrypt(struct req_state* s, + map& attrs, + map* parts, + BlockCrypt** block_crypt, + std::string& crypt_http_responses) +{ + int res = 0; + crypt_http_responses = ""; + if (block_crypt) *block_crypt = nullptr; + { + boost::string_ref req_sse_ca = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM); + if (! req_sse_ca.empty()) { + if (req_sse_ca != "AES256") { + res = -ERR_INVALID_REQUEST; + goto done; + } + std::string key_bin = from_base64( + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY) ); + if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { + res = -ERR_INVALID_REQUEST; + goto done; + } + boost::string_ref keymd5 = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5); + std::string keymd5_bin = from_base64(keymd5); + if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { + res = -ERR_INVALID_DIGEST; + goto done; + } + MD5 key_hash; + uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; + key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); + key_hash.Final(key_hash_res); + + if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { + res = -ERR_INVALID_DIGEST; + goto done; + } + + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); + set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); + + if (block_crypt) { + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key(reinterpret_cast(key_bin.c_str()), AES_256_KEYSIZE); + *block_crypt = aes; + } + + crypt_http_responses = + "x-amz-server-side-encryption-customer-algorithm: AES256\r\n" + "x-amz-server-side-encryption-customer-key-MD5: " + std::string(keymd5) + "\r\n"; + goto done; + } + /* AMAZON server side encryption with KMS (key management service) */ + boost::string_ref req_sse = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION); + if (! req_sse.empty()) { + if (req_sse != "aws:kms") { + res = -ERR_INVALID_REQUEST; + goto done; + } + boost::string_ref key_id = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID); + if (key_id.empty()) { + /* TODO!!! retrieve key_id from bucket */ + res = -ERR_INVALID_ACCESS_KEY; + goto done; + } + /* try to retrieve actual key */ + std::string key_selector = create_random_key_selector(); + std::string actual_key; + res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); + if (res != 0) + goto done; + if (actual_key.size() != AES_256_KEYSIZE) { + ldout(s->cct, 0) << "ERROR: key obtained from key_id:" << + key_id << " is not 256 bit size" << dendl; + res = -ERR_INVALID_ACCESS_KEY; + goto done; + } + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS"); + set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id); + set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); + + if (block_crypt) { + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key(reinterpret_cast(actual_key.c_str()), AES_256_KEYSIZE); + *block_crypt = aes; + } + goto done; + } + + /* no other encryption mode, check if default encryption is selected */ + if (s->cct->_conf->rgw_crypt_default_encryption_key != "") { + std::string master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); + if (master_encryption_key.size() != 256 / 8) { + ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; + /* not an error to return; missing encryption does not inhibit processing */ + goto done; + } + + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); + std::string key_selector = create_random_key_selector(); + set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); + + uint8_t actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, + (uint8_t*)key_selector.c_str(), + actual_key, AES_256_KEYSIZE) != true) { + res = -EIO; + goto done; + } + if (block_crypt) { + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key(actual_key, AES_256_KEYSIZE); + *block_crypt = aes; + } + + goto done; + } + } + done: + return res; +} + +int s3_prepare_decrypt( + struct req_state* s, + map& attrs, + BlockCrypt** block_crypt, + std::map& crypt_http_responses) +{ + int res = 0; + std::string stored_mode = get_str_attribute(attrs, RGW_ATTR_CRYPT_MODE); + ldout(s->cct, 15) << "Encryption mode: " << stored_mode << dendl; + + if (stored_mode == "SSE-C-AES256") { + const char *req_cust_alg = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); + if ((nullptr == req_cust_alg) || (strcmp(req_cust_alg, "AES256") != 0)) { + res = -ERR_INVALID_REQUEST; + goto done; + } + + std::string key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); + if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { + res = -ERR_INVALID_REQUEST; + goto done; + } + + std::string keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); + std::string keymd5_bin = from_base64(keymd5); + if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { + res = -ERR_INVALID_DIGEST; + goto done; + } + MD5 key_hash; + uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; + key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); + key_hash.Final(key_hash_res); + + if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) || + (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) { + res = -ERR_INVALID_DIGEST; + goto done; + } + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CTR::AES_256_KEYSIZE); + if (block_crypt) *block_crypt = aes; + + crypt_http_responses = + "x-amz-server-side-encryption-customer-algorithm: AES256\r\n" + "x-amz-server-side-encryption-customer-key-MD5: " + keymd5 + "\r\n"; + goto done; + } + + if (stored_mode == "SSE-KMS") { + /* try to retrieve actual key */ + std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID); + std::string key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); + std::string actual_key; + res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); + if (res != 0) { + ldout(s->cct, 10) << "No encryption key for key-id=" << key_id << dendl; + goto done; + } + if (actual_key.size() != AES_256_KEYSIZE) { + ldout(s->cct, 0) << "ERROR: key obtained from key_id:" << + key_id << " is not 256 bit size" << dendl; + res = -ERR_INVALID_ACCESS_KEY; + goto done; + } + + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key(reinterpret_cast(actual_key.c_str()), AES_256_KEYSIZE); + + if (block_crypt) *block_crypt = aes; + + crypt_http_responses = + "x-amz-server-side-encryption: aws:kms\r\n" + "x-amz-server-side-encryption-aws-kms-key-id: " + key_id + "\r\n"; + goto done; + } + + if (stored_mode == "RGW-AUTO") { + std::string master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); + if (master_encryption_key.size() != 256 / 8) { + ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; + res = -EIO; + goto done; + } + std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); + if (attr_key_selector.size() != AES_256_CTR::AES_256_KEYSIZE) { + ldout(s->cct, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl; + res = -EIO; + goto done; + } + uint8_t actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, + (uint8_t*)attr_key_selector.c_str(), + actual_key, AES_256_KEYSIZE) != true) { + res = -EIO; + goto done; + } + AES_256_CTR* aes = new AES_256_CTR(s->cct); + aes->set_key(actual_key, AES_256_KEYSIZE); + + if (block_crypt) *block_crypt = aes; + goto done; + } + + done: + return res; +} diff --git a/src/rgw/rgw_crypt.h b/src/rgw/rgw_crypt.h index 07e99e0eabeb..8607e73fdb01 100644 --- a/src/rgw/rgw_crypt.h +++ b/src/rgw/rgw_crypt.h @@ -7,6 +7,8 @@ #define CEPH_RGW_CRYPT_H #include +#include +#include class BlockCrypt { public: @@ -80,7 +82,7 @@ class RGWGetObj_BlockDecrypt : public RGWGetObj_Filter { size_t block_size; std::vector parts_len; public: - RGWGetObj_BlockDecrypt(CephContext* cct, RGWGetDataCB& next, BlockCrypt* crypt); + RGWGetObj_BlockDecrypt(CephContext* cct, RGWGetDataCB* next, BlockCrypt* crypt); virtual ~RGWGetObj_BlockDecrypt(); virtual int fixup_range(off_t& bl_ofs, off_t& bl_end) override; @@ -98,14 +100,25 @@ class RGWPutObj_BlockEncrypt : public RGWPutObj_Filter bufferlist cache; size_t block_size; public: - RGWPutObj_BlockEncrypt(CephContext* cct, RGWPutObjDataProcessor& next, BlockCrypt* crypt); + RGWPutObj_BlockEncrypt(CephContext* cct, RGWPutObjDataProcessor* next, BlockCrypt* crypt); virtual ~RGWPutObj_BlockEncrypt(); virtual int handle_data(bufferlist& bl, off_t ofs, void **phandle, rgw_obj *pobj, bool *again) override; virtual int throttle_data(void *handle, const rgw_obj& obj, uint64_t size, bool need_to_wait) override; }; /* RGWPutObj_BlockEncrypt */ std::string create_random_key_selector(); -int get_actual_key_from_kms(CephContext *cct, const char* key_id, const std::string& key_selector, std::string& actual_key); +//int get_actual_key_from_kms(CephContext *cct, const std::string& key_id, const std::string& key_selector, std::string& actual_key); +int get_actual_key_from_kms(CephContext *cct, boost::string_ref key_id, boost::string_ref key_selector, std::string& actual_key); + +int s3_prepare_encrypt(struct req_state* s, + map& attrs, + map* parts, + BlockCrypt** block_crypt, + std::map& crypt_http_responses); +int s3_prepare_decrypt(struct req_state* s, + map& attrs, + BlockCrypt** block_crypt, + std::map& crypt_http_responses); #endif diff --git a/src/rgw/rgw_json_enc.cc b/src/rgw/rgw_json_enc.cc index e3f39231b165..067ad6fadbbb 100644 --- a/src/rgw/rgw_json_enc.cc +++ b/src/rgw/rgw_json_enc.cc @@ -1376,6 +1376,53 @@ void rgw::keystone::AdminTokenRequestVer3::dump(Formatter* const f) const } +void rgw::keystone::BarbicanTokenRequestVer2::dump(Formatter* const f) const +{ + f->open_object_section("token_request"); + f->open_object_section("auth"); + f->open_object_section("passwordCredentials"); + encode_json("username", cct->_conf->rgw_keystone_barbican_user, f); + encode_json("password", cct->_conf->rgw_keystone_barbican_password, f); + f->close_section(); + encode_json("tenantName", cct->_conf->rgw_keystone_barbican_tenant, f); + f->close_section(); + f->close_section(); +} + +void rgw::keystone::BarbicanTokenRequestVer3::dump(Formatter* const f) const +{ + f->open_object_section("token_request"); + f->open_object_section("auth"); + f->open_object_section("identity"); + f->open_array_section("methods"); + f->dump_string("", "password"); + f->close_section(); + f->open_object_section("password"); + f->open_object_section("user"); + f->open_object_section("domain"); + encode_json("name", cct->_conf->rgw_keystone_barbican_domain, f); + f->close_section(); + encode_json("name", cct->_conf->rgw_keystone_barbican_user, f); + encode_json("password", cct->_conf->rgw_keystone_barbican_password, f); + f->close_section(); + f->close_section(); + f->close_section(); + f->open_object_section("scope"); + f->open_object_section("project"); + if (!cct->_conf->rgw_keystone_barbican_project.empty()) { + encode_json("name", cct->_conf->rgw_keystone_barbican_project, f); + } else { + encode_json("name", cct->_conf->rgw_keystone_barbican_tenant, f); + } + f->open_object_section("domain"); + encode_json("name", cct->_conf->rgw_keystone_barbican_domain, f); + f->close_section(); + f->close_section(); + f->close_section(); + f->close_section(); + f->close_section(); +} + void RGWOrphanSearchStage::dump(Formatter *f) const { f->open_object_section("orphan_search_stage"); diff --git a/src/rgw/rgw_keystone.cc b/src/rgw/rgw_keystone.cc index 11583572d229..9ea60c7afd4e 100644 --- a/src/rgw/rgw_keystone.cc +++ b/src/rgw/rgw_keystone.cc @@ -259,6 +259,81 @@ int Service::issue_admin_token_request(CephContext* const cct, return 0; } +int Service::get_keystone_barbican_token(CephContext * const cct, + std::string& token) +{ + using keystone_config_t = rgw::keystone::CephCtxConfig; + using keystone_cache_t = rgw::keystone::TokenCache; + + auto& config = keystone_config_t::get_instance(); + auto& token_cache = keystone_cache_t::get_instance(); + + std::string token_url = config.get_endpoint_url(); + if (token_url.empty()) { + return -EINVAL; + } + + rgw::keystone::TokenEnvelope t; + + /* Try cache first. */ + if (token_cache.find_barbican(t)) { + ldout(cct, 20) << "found cached barbican token" << dendl; + token = t.token.id; + return 0; + } + + bufferlist token_bl; + RGWKeystoneHTTPTransceiver token_req(cct, &token_bl); + token_req.append_header("Content-Type", "application/json"); + JSONFormatter jf; + + const auto keystone_version = config.get_api_version(); + if (keystone_version == ApiVersion::VER_2) { + rgw::keystone::BarbicanTokenRequestVer2 req_serializer(cct); + req_serializer.dump(&jf); + + std::stringstream ss; + jf.flush(ss); + token_req.set_post_data(ss.str()); + token_req.set_send_length(ss.str().length()); + token_url.append("v2.0/tokens"); + + } else if (keystone_version == ApiVersion::VER_3) { + BarbicanTokenRequestVer3 req_serializer(cct); + req_serializer.dump(&jf); + + std::stringstream ss; + jf.flush(ss); + token_req.set_post_data(ss.str()); + token_req.set_send_length(ss.str().length()); + token_url.append("v3/auth/tokens"); + } else { + return -ENOTSUP; + } + + ldout(cct, 20) << "Requesting secret from barbican url=" << token_url << dendl; + const int ret = token_req.process("POST", token_url.c_str()); + if (ret < 0) { + ldout(cct, 20) << "Barbican process error:" << token_bl.c_str() << dendl; + return ret; + } + + /* Detect rejection earlier than during the token parsing step. */ + if (token_req.get_http_status() == + RGWKeystoneHTTPTransceiver::HTTP_STATUS_UNAUTHORIZED) { + return -EACCES; + } + + if (t.parse(cct, token_req.get_subject_token(), token_bl, + keystone_version) != 0) { + return -EINVAL; + } + + token_cache.add_barbican(t); + token = t.token.id; + return 0; +} + bool TokenEnvelope::has_role(const std::string& r) const { @@ -368,6 +443,13 @@ bool TokenCache::find_admin(rgw::keystone::TokenEnvelope& token) return find_locked(admin_token_id, token); } +bool TokenCache::find_barbican(rgw::keystone::TokenEnvelope& token) +{ + Mutex::Locker l(lock); + + return find(barbican_token_id, token); +} + void TokenCache::add(const std::string& token_id, const rgw::keystone::TokenEnvelope& token) { @@ -407,6 +489,14 @@ void TokenCache::add_admin(const rgw::keystone::TokenEnvelope& token) add_locked(admin_token_id, token); } +void TokenCache::add_barbican(const rgw::keystone::TokenEnvelope& token) +{ + Mutex::Locker l(lock); + + rgw_get_token_id(token.token.id, barbican_token_id); + add(barbican_token_id, token); +} + void TokenCache::invalidate(const std::string& token_id) { Mutex::Locker l(lock); diff --git a/src/rgw/rgw_keystone.h b/src/rgw/rgw_keystone.h index 91cf9b13c448..be354d054632 100644 --- a/src/rgw/rgw_keystone.h +++ b/src/rgw/rgw_keystone.h @@ -133,6 +133,8 @@ public: static int issue_admin_token_request(CephContext* const cct, const Config& config, TokenEnvelope& token); + static int get_keystone_barbican_token(CephContext * const cct, + std::string& token); }; @@ -243,6 +245,7 @@ class TokenCache { CephContext * const cct; std::string admin_token_id; + std::string barbican_token_id; std::map tokens; std::list tokens_lru; @@ -289,8 +292,10 @@ public: return boost::none; } bool find_admin(TokenEnvelope& token); + bool find_barbican(TokenEnvelope& token); void add(const std::string& token_id, const TokenEnvelope& token); void add_admin(const TokenEnvelope& token); + void add_barbican(const TokenEnvelope& token); void invalidate(const std::string& token_id); bool going_down() const; private: @@ -326,6 +331,27 @@ public: void dump(Formatter *f) const override; }; +class BarbicanTokenRequestVer2 : public AdminTokenRequest { + CephContext *cct; + +public: + BarbicanTokenRequestVer2(CephContext * const _cct) + : cct(_cct) { + } + void dump(Formatter *f) const; +}; + +class BarbicanTokenRequestVer3 : public AdminTokenRequest { + CephContext *cct; + +public: + BarbicanTokenRequestVer3(CephContext * const _cct) + : cct(_cct) { + } + void dump(Formatter *f) const; +}; + + }; /* namespace keystone */ }; /* namespace rgw */ diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 4b93da5a5045..ec147c303370 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -229,6 +229,9 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, } } + for (auto &it : crypt_http_responses) + dump_header(s, it.first, it.second); + dump_content_length(s, total_len); dump_last_modified(s, lastmod); @@ -314,143 +317,22 @@ send_data: int RGWGetObj_ObjStore_S3::get_decrypt_filter(RGWGetDataCB** filter, RGWGetDataCB* cb, bufferlist* manifest_bl) { int res = 0; - *filter = nullptr; - map::iterator attr_iter; - std::string attr_mode; - std::string attr_md5; - attr_iter = attrs.find(RGW_ATTR_CRYPT_MODE); - if (attr_iter == attrs.end() ) { - /* no encryption - no problem with that */ - goto done; - } - try { - ::decode(attr_mode, attr_iter->second); - } catch (buffer::error& err) { - ldout(s->cct, 0) << "ERROR: failed to decode " RGW_ATTR_CRYPT_MODE << dendl; - res = -EIO; - goto done; - } - ldout(s->cct, 20) << "Encryption mode=" << attr_mode << dendl; - if (attr_mode == "SSE-C-AES256") { - const char *enc_customer = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", ""); - if (strcmp(enc_customer, "AES256") != 0) { - ldout(s->cct, 10) << "ERROR. x-amz-server-side-encryption-customer-algorithm must be 'AES256'" << dendl; - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* key = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""); - std::string key_bin = from_base64(key); - if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { - ldout(s->cct, 10) << "ERROR. x-amz-server-side-encryption-customer-key must decode to 256 bit key" << dendl; - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); - std::string keymd5_bin = from_base64(keymd5); - if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { - ldout(s->cct, 10) << "ERROR. x-amz-server-side-encryption-customer-key-md5 mismatches key" << dendl; - res = -ERR_INVALID_DIGEST; - goto done; - } - - MD5 key_hash; - uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; - key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); - key_hash.Final(key_hash_res); - - if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { - ldout(s->cct, 20) << "MD5 failed" << dendl; - res = -ERR_INVALID_DIGEST; - goto done; - } - attr_iter = attrs.find(RGW_ATTR_CRYPT_KEYMD5); - if (attr_iter == attrs.end()) { - res = - ERR_INVALID_DIGEST; - goto done; - } - try { - ::decode(attr_md5, attr_iter->second); - } - catch (buffer::error& err) { - ldout(s->cct, 0) << "ERROR: failed to decode" RGW_ATTR_CRYPT_KEYMD5 << dendl; - res = -EIO; - goto done; - } - if (attr_md5 != keymd5_bin) { - ldout(s->cct, 20) << "MD5 from " RGW_ATTR_CRYPT_KEYMD5 " mismatches x-amz-server-side-encryption-customer-key-md5" << dendl; - res = - ERR_INVALID_DIGEST; - goto done; - } - - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CTR::AES_256_KEYSIZE); - - RGWGetObj_BlockDecrypt* f =new RGWGetObj_BlockDecrypt(s->cct, /*manifest_bl,*/ *cb, aes); - if (f != nullptr) { - res = f->read_manifest(*manifest_bl); - if (res == 0) { - *filter = f; - } else { - delete f; - } - } - ldout(s->cct, 20) << "Created decryptor key." << dendl; - goto done; - } - - if (attr_mode == "RGW-AUTO") { - std::string master_encryption_key = from_base64(/*std::string*/(s->cct->_conf->rgw_crypt_default_encryption_key)); - if (master_encryption_key.size() != 256 / 8) { - ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; - /* not an error to return; missing encryption does not inhibit processing */ - res = -EIO; - goto done; - } - std::string attr_key_selector; - attr_iter = attrs.find(RGW_ATTR_CRYPT_KEYSEL); - if (attr_iter == attrs.end()) { - res = - EIO; - goto done; - } - try { - ::decode(attr_key_selector, attr_iter->second); - //attr_key_selector = "abcdefghijabcdefghijabcdefghijab"; - } - catch (buffer::error& err) { - ldout(s->cct, 0) << "ERROR: failed to decode" RGW_ATTR_CRYPT_KEYMD5 << dendl; - res = -EIO; - goto done; - } - if (attr_key_selector.size() != AES_256_CTR::AES_256_KEYSIZE) { - res = -EIO; - goto done; - } - - uint8_t actual_key[AES_256_KEYSIZE]; - if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, - (uint8_t*)attr_key_selector.c_str(), - actual_key, AES_256_KEYSIZE) != true) { - res = -EIO; - goto done; - } - - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key(actual_key, AES_256_KEYSIZE); - - RGWGetObj_BlockDecrypt* f =new RGWGetObj_BlockDecrypt(s->cct, /*manifest_bl,*/ *cb, aes); - if (f != nullptr) { - if (manifest_bl != nullptr) - res = f->read_manifest(*manifest_bl); - if (res == 0) { - *filter = f; - } else { - delete f; + BlockCrypt* block_crypt = nullptr; + res = s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses); + if (res == 0) { + if (block_crypt != nullptr) { + RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, block_crypt); + if (f != nullptr) { + if (manifest_bl != nullptr) + res = f->read_manifest(*manifest_bl); + if (res == 0) { + *filter = f; + } else { + delete f; + } } } - ldout(s->cct, 20) << "Created decryptor key." << dendl; - goto done; } - done: return res; } @@ -1474,22 +1356,6 @@ static int get_success_retcode(int code) return 0; } -static std::string get_str_attribute(map& attrs, const std::string& name, const std::string& default_value="") { - std::string value; - auto iter = attrs.find(name); - if (iter == attrs.end() ) { - value = default_value; - } else { - try { - ::decode(value, iter->second); - } catch (buffer::error& err) { - value = default_value; - dout(0) << "ERROR: failed to decode attr:" << name << dendl; - } - } - return value; -} - void RGWPutObj_ObjStore_S3::send_response() { if (op_ret) { @@ -1562,8 +1428,6 @@ static inline void set_attr(map& attrs, const char* key, con int RGWPutObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) { int res = 0; - *filter = nullptr; - RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast(cb); if (multi_processor != nullptr) { RGWMPObj* mp = nullptr; @@ -1577,151 +1441,23 @@ int RGWPutObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, R 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) { - goto done; - } - std::string stored_mode = get_str_attribute(xattrs, RGW_ATTR_CRYPT_MODE); - ldout(s->cct, 15) << "Multipart encryption mode: " << stored_mode << dendl; - - const char *req_cust_alg = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); - if (stored_mode == "SSE-C-AES256") { - if ((nullptr == req_cust_alg) || (strcmp(req_cust_alg, "AES256") != 0)) { - res = -ERR_INVALID_REQUEST; - goto done; - } - - std::string key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); - if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { - res = -ERR_INVALID_REQUEST; - goto done; - } - - std::string keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); - std::string keymd5_bin = from_base64(keymd5); - if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { - res = -ERR_INVALID_DIGEST; - goto done; - } - MD5 key_hash; - uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; - key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); - key_hash.Final(key_hash_res); - - if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) || - (get_str_attribute(xattrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) { - res = -ERR_INVALID_DIGEST; - goto done; - } - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CTR::AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - - crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = - "AES256"; - crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = - keymd5; - goto done; - } - - if (stored_mode == "RGW-AUTO") { - std::string master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); - if (master_encryption_key.size() != 256 / 8) { - ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; - res = -EIO; - goto done; - } - std::string attr_key_selector = get_str_attribute(xattrs, RGW_ATTR_CRYPT_KEYSEL); - if (attr_key_selector.size() != AES_256_CTR::AES_256_KEYSIZE) { - ldout(s->cct, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl; - res = -EIO; - goto done; - } - uint8_t actual_key[AES_256_KEYSIZE]; - if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, - (uint8_t*)attr_key_selector.c_str(), - actual_key, AES_256_KEYSIZE) != true) { - res = -EIO; - goto done; - } - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key(actual_key, AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - goto done; + if (res == 0) { + BlockCrypt* block_crypt = nullptr; + res = s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses); + if (res == 0 && block_crypt != nullptr) + *filter=new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt); } } - /* no encryption at all for multipart*/ - goto done; + /* it is ok, to not have encryption at all */ } else { - const char* req_sse = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); - if (req_sse != NULL) { - if (strcmp(req_sse, "AES256") != 0) { - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* key = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", NULL); - std::string key_bin = (key ? from_base64(key) : ""); - if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { - res = -ERR_INVALID_REQUEST; - goto done; - } - std::string keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); - std::string keymd5_bin = from_base64(keymd5); - if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { - res = -ERR_INVALID_DIGEST; - goto done; - } - MD5 key_hash; - uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; - key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); - key_hash.Final(key_hash_res); - - if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { - res = -ERR_INVALID_DIGEST; - goto done; - } - - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); - set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); - - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CTR::AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = - "AES256"; - crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = - keymd5; - goto done; - } - - /* no other encryption mode, check if default encryption is selected */ - if (s->cct->_conf->rgw_crypt_default_encryption_key != "") { - std::string master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); - if (master_encryption_key.size() != 256 / 8) { - ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; - /* not an error to return; missing encryption does not inhibit processing */ - goto done; - } - - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); - std::string key_selector = create_random_key_selector(); - set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); - - uint8_t actual_key[AES_256_KEYSIZE]; - if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, - (uint8_t*)key_selector.c_str(), - actual_key, AES_256_KEYSIZE) != true) { - res = -EIO; - goto done; - } - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key(actual_key, AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - goto done; + BlockCrypt* block_crypt = nullptr; + res = s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses); + if (res == 0 && block_crypt!=nullptr) { + *filter = new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt); } } - done: return res; } /* @@ -2389,6 +2125,8 @@ void RGWPostObj_ObjStore_S3::send_response() done: if (op_ret == STATUS_CREATED) { + 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", @@ -2416,72 +2154,11 @@ done: int RGWPostObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) { int res = 0; - *filter = nullptr; - const char *enc_customer = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); - if (enc_customer != NULL) { - if (strcmp(enc_customer,"AES256") != 0) { - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* key = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", NULL); - std::string key_bin = from_base64(key); - if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", NULL); - std::string keymd5_bin = from_base64(keymd5); - if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { - res = -ERR_INVALID_DIGEST; - goto done; - } - - MD5 key_hash; - uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; - key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); - key_hash.Final(key_hash_res); - - if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { - res = -ERR_INVALID_DIGEST; - goto done; - } - - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); - set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); - - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CTR::AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - goto done; + BlockCrypt* block_crypt = nullptr; + res = s3_prepare_encrypt(s, attrs, &parts, &block_crypt, crypt_http_responses); + if (res == 0 && block_crypt != nullptr) { + *filter = new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt); } - /* no other encryption mode, check if default encryption is selected */ - if (s->cct->_conf->rgw_crypt_default_encryption_key != "") { - std::string master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); - if (master_encryption_key.size() != 256 / 8) { - ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; - /* not an error to return; missing encryption does not inhibit processing */ - goto done; - } - - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); - std::string key_selector = create_random_key_selector(); - set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); - - uint8_t actual_key[AES_256_KEYSIZE]; - if (AES_256_ECB_encrypt((uint8_t*)master_encryption_key.c_str(), AES_256_KEYSIZE, - (uint8_t*)key_selector.c_str(), - actual_key, AES_256_KEYSIZE) != true) { - res = -EIO; - goto done; - } - - AES_256_CTR* aes=new AES_256_CTR(s->cct); - aes->set_key(actual_key, AES_256_KEYSIZE); - *filter=new RGWPutObj_BlockEncrypt(s->cct, *cb, aes); - goto done; - } - - done: return res; } @@ -2991,6 +2668,8 @@ void RGWInitMultipart_ObjStore_S3::send_response() if (op_ret) set_req_state_err(s, op_ret); dump_errno(s); + for (auto &it : crypt_http_responses) + dump_header(s, it.first, it.second); end_header(s, this, "application/xml"); if (op_ret == 0) { dump_start(s); @@ -3008,41 +2687,7 @@ void RGWInitMultipart_ObjStore_S3::send_response() int RGWInitMultipart_ObjStore_S3::prepare_encryption(map& attrs) { int res = 0; - if (strcmp(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", ""), "AES256") == 0) - { - std::string key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); - if (key_bin.size() != AES_256_CTR::AES_256_KEYSIZE) { - res = -ERR_INVALID_REQUEST; - goto done; - } - const char* keymd5 = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); - std::string keymd5_bin = from_base64(keymd5); - if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { - res = -ERR_INVALID_DIGEST; - goto done; - } - MD5 key_hash; - uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; - key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size()); - key_hash.Final(key_hash_res); - - if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { - res = -ERR_INVALID_DIGEST; - goto done; - } - - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); - set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); - - goto done; - } - if (s->cct->_conf->rgw_crypt_default_encryption_key != "") - { - set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); - std::string key_selector = create_random_key_selector(); - set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); - } - done: + res = s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses); return res; } diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index 716f811f9519..8ac0a1d2bb41 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -17,6 +17,7 @@ #include "rgw_keystone.h" #include "rgw_rest_conn.h" #include "rgw_ldap.h" +#include "rgw_rest.h" #include "rgw_token.h" #include "include/assert.h" @@ -34,6 +35,7 @@ protected: // Serving a custom error page from an object is really a 200 response with // just the status line altered. int custom_http_ret = 0; + std::map crypt_http_responses; public: RGWGetObj_ObjStore_S3() {} ~RGWGetObj_ObjStore_S3() override {} @@ -165,6 +167,9 @@ public: }; class RGWPutObj_ObjStore_S3 : public RGWPutObj_ObjStore { +private: + std::map crypt_http_responses; + public: RGWPutObj_ObjStore_S3() {} ~RGWPutObj_ObjStore_S3() override {} @@ -203,6 +208,7 @@ class RGWPostObj_ObjStore_S3 : public RGWPostObj_ObjStore { RGWPolicyEnv env; RGWPolicy post_policy; string err_msg; + map crypt_http_responses; const rgw::auth::StrategyRegistry* auth_registry_ptr = nullptr; @@ -356,6 +362,8 @@ public: }; class RGWInitMultipart_ObjStore_S3 : public RGWInitMultipart_ObjStore { +private: + std::map crypt_http_responses; public: RGWInitMultipart_ObjStore_S3() {} ~RGWInitMultipart_ObjStore_S3() override {}