return 0;
}
+int RGWPutObj_ObjStore::get_padding_last_aws4_chunk_encoded(bufferlist &bl, uint64_t chunk_size) {
+
+ const int chunk_str_min_len = 1 + 17 + 64 + 2; /* len('0') = 1 */
+
+ char *chunk_str = bl.c_str();
+ int budget = bl.length();
+
+ unsigned int chunk_data_size;
+ unsigned int chunk_offset = 0;
+
+ while (1) {
+
+ /* check available metadata */
+ if (budget < chunk_str_min_len) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ chunk_offset = 0;
+
+ /* grab chunk size */
+ while ((*(chunk_str+chunk_offset) != ';') && (chunk_offset < chunk_str_min_len))
+ chunk_offset++;
+ string str = string(chunk_str, chunk_offset);
+ stringstream ss;
+ ss << std::hex << str;
+ ss >> chunk_data_size;
+
+ /* next chunk */
+ chunk_offset += 17 + 64 + 2 + chunk_data_size;
+
+ /* last chunk? */
+ budget -= chunk_offset;
+ if (budget < 0) {
+ budget *= -1;
+ break;
+ }
+
+ chunk_str += chunk_offset;
+ }
+
+ return budget;
+}
+
int RGWPutObj_ObjStore::get_data(bufferlist& bl)
{
size_t cl;
len = read_len;
bl.append(bp, 0, len);
+
+ /* read last aws4 chunk padding */
+ if (s->aws4_auth_streaming_mode && len == (int)chunk_size) {
+ int ret_auth = get_padding_last_aws4_chunk_encoded(bl, chunk_size);
+ if (ret_auth < 0) {
+ return ret_auth;
+ }
+ int len_padding = ret_auth;
+ if (len_padding) {
+ int read_len;
+ bufferptr bp_extra(len_padding);
+ int r = STREAM_IO(s)->read(bp_extra.c_str(), len_padding, &read_len,
+ s->aws4_auth_needs_complete);
+ if (r < 0) {
+ return r;
+ }
+ if (read_len != len_padding) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+ bl.append(bp_extra.c_str(), len_padding);
+ bl.rebuild();
+ }
+ }
+
}
if ((uint64_t)ofs + len > s->cct->_conf->rgw_max_put_size) {
return RGWPutObj_ObjStore::get_params();
}
+int RGWPutObj_ObjStore_S3::validate_aws4_single_chunk(char *chunk_str,
+ char *chunk_data_str,
+ unsigned int chunk_data_size,
+ string chunk_signature)
+{
+
+ /* string to sign */
+
+ string hash_empty_str;
+ rgw_hash_s3_string_sha256("", 0, hash_empty_str);
+
+ string hash_chunk_data;
+ rgw_hash_s3_string_sha256(chunk_data_str, chunk_data_size, hash_chunk_data);
+
+ string string_to_sign = "AWS4-HMAC-SHA256-PAYLOAD\n";
+ string_to_sign.append(s->aws4_auth->date + "\n");
+ string_to_sign.append(s->aws4_auth->credential_scope + "\n");
+ string_to_sign.append(s->aws4_auth->seed_signature + "\n");
+ string_to_sign.append(hash_empty_str + "\n");
+ string_to_sign.append(hash_chunk_data);
+
+ /* new chunk signature */
+
+ char signature_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+ calc_hmac_sha256(s->aws4_auth->signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE,
+ string_to_sign.c_str(), string_to_sign.size(), signature_k);
+
+ char aux[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1];
+ buf_to_hex((unsigned char *) signature_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+ string new_chunk_signature = string(aux);
+
+ ldout(s->cct, 20) << "--------------- aws4 chunk validation" << dendl;
+ ldout(s->cct, 20) << "chunk_signature = " << chunk_signature << dendl;
+ ldout(s->cct, 20) << "new_chunk_signature = " << new_chunk_signature << dendl;
+ ldout(s->cct, 20) << "aws4 chunk signing_key = " << s->aws4_auth->signing_key << dendl;
+ ldout(s->cct, 20) << "aws4 chunk string_to_sign = " << string_to_sign << dendl;
+
+ /* chunk auth ok? */
+
+ if (new_chunk_signature != chunk_signature) {
+ ldout(s->cct, 20) << "ERROR: AWS4 chunk signature does NOT match (new_chunk_signature != chunk_signature)" << dendl;
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ /* update seed signature */
+
+ s->aws4_auth->seed_signature = new_chunk_signature;
+
+ return 0;
+}
+
+int RGWPutObj_ObjStore_S3::validate_and_unwrap_available_aws4_chunked_data(bufferlist& bl_in,
+ bufferlist& bl_out)
+{
+
+ /* string(IntHexBase(chunk-size)) + ";chunk-signature=" + signature + \r\n + chunk-data + \r\n */
+
+ const unsigned int chunk_str_min_len = 1 + 17 + 64 + 2; /* len('0') = 1 */
+
+ char *chunk_str = bl_in.c_str();
+ unsigned int budget = bl_in.length();
+
+ bl_out.clear();
+
+ while (true) {
+
+ /* check available metadata */
+
+ if (budget < chunk_str_min_len) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ unsigned int chunk_offset = 0;
+
+ /* grab chunk size */
+
+ while ((*(chunk_str+chunk_offset) != ';') && (chunk_offset < chunk_str_min_len))
+ chunk_offset++;
+ string str = string(chunk_str, chunk_offset);
+ unsigned int chunk_data_size;
+ stringstream ss;
+ ss << std::hex << str;
+ ss >> chunk_data_size;
+ if (ss.fail()) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ /* grab chunk signature */
+
+ chunk_offset += 17;
+ string chunk_signature = string(chunk_str, chunk_offset, 64);
+
+ /* get chunk data */
+
+ chunk_offset += 64 + 2;
+ char *chunk_data_str = chunk_str + chunk_offset;
+
+ /* handle budget */
+
+ budget -= chunk_offset;
+ if (budget < chunk_data_size) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ } else {
+ budget -= chunk_data_size;
+ }
+
+ /* auth single chunk */
+
+ if (validate_aws4_single_chunk(chunk_str, chunk_data_str, chunk_data_size, chunk_signature) < 0) {
+ ldout(s->cct, 20) << "ERROR AWS4 single chunk validation" << dendl;
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ /* aggregate single chunk */
+
+ bl_out.append(chunk_data_str, chunk_data_size);
+
+ /* last chunk or no more budget? */
+
+ if ((chunk_data_size == 0) || (budget == 0))
+ break;
+
+ /* next chunk */
+
+ chunk_offset += chunk_data_size;
+ chunk_str += chunk_offset;
+ }
+
+ /* authorization ok */
+
+ return 0;
+
+}
+
int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
{
int ret = RGWPutObj_ObjStore::get_data(bl);
if (ret < 0)
s->aws4_auth_needs_complete = false;
+
+ int ret_auth;
+
+ if (s->aws4_auth_streaming_mode && ret > 0) {
+ ret_auth = validate_and_unwrap_available_aws4_chunked_data(bl, s->aws4_auth->bl);
+ if (ret_auth < 0) {
+ return ret_auth;
+ }
+ }
+
if ((ret == 0) && s->aws4_auth_needs_complete) {
- int ret_auth = do_aws4_auth_completion();
+ ret_auth = do_aws4_auth_completion();
if (ret_auth < 0) {
return ret_auth;
}
}
+
return ret;
}
if (s->aws4_auth_needs_complete) {
const char *expected_request_payload_hash = s->info.env->get("HTTP_X_AMZ_CONTENT_SHA256");
if (expected_request_payload_hash &&
- s->aws4_auth->payload_hash.compare(expected_request_payload_hash) != 0) {
+ s->aws4_auth->payload_hash.compare(expected_request_payload_hash) != 0) {
ldout(s->cct, 10) << "ERROR: x-amz-content-sha256 does not match" << dendl;
return -ERR_AMZ_CONTENT_SHA256_MISMATCH;
}
return err;
}
+ s->aws4_auth->seed_signature = s->aws4_auth->new_signature;
+
return 0;
}
string request_payload;
bool unsigned_payload = false;
+ s->aws4_auth_streaming_mode = false;
if (using_qs) {
/* query parameters auth */
} else {
/* header auth */
const char *request_payload_hash = s->info.env->get("HTTP_X_AMZ_CONTENT_SHA256");
- if (request_payload_hash && string("UNSIGNED-PAYLOAD").compare(request_payload_hash) == 0) {
- unsigned_payload = true;
+ if (request_payload_hash) {
+ unsigned_payload = string("UNSIGNED-PAYLOAD").compare(request_payload_hash) == 0;
+ if (!unsigned_payload) {
+ s->aws4_auth_streaming_mode = string("STREAMING-AWS4-HMAC-SHA256-PAYLOAD").compare(request_payload_hash) == 0;
+ }
}
}
/* aws4 auth not completed... delay aws4 auth */
- dout(10) << "body content detected... delaying v4 auth" << dendl;
-
- switch (s->op_type)
- {
- case RGW_OP_CREATE_BUCKET:
- case RGW_OP_PUT_OBJ:
- case RGW_OP_PUT_ACLS:
- case RGW_OP_PUT_CORS:
- case RGW_OP_COMPLETE_MULTIPART:
- case RGW_OP_SET_BUCKET_VERSIONING:
- case RGW_OP_DELETE_MULTI_OBJ:
- case RGW_OP_ADMIN_SET_METADATA:
- case RGW_OP_SET_BUCKET_WEBSITE:
- break;
- default:
- dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
- return -ERR_NOT_IMPLEMENTED;
- }
+ if (!s->aws4_auth_streaming_mode) {
+
+ dout(10) << "delaying v4 auth" << dendl;
+
+ /* payload in a single chunk */
+
+ switch (s->op_type)
+ {
+ case RGW_OP_CREATE_BUCKET:
+ case RGW_OP_PUT_OBJ:
+ case RGW_OP_PUT_ACLS:
+ case RGW_OP_PUT_CORS:
+ case RGW_OP_COMPLETE_MULTIPART:
+ case RGW_OP_SET_BUCKET_VERSIONING:
+ case RGW_OP_DELETE_MULTI_OBJ:
+ case RGW_OP_ADMIN_SET_METADATA:
+ case RGW_OP_SET_BUCKET_WEBSITE:
+ break;
+ default:
+ dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
+ return -ERR_NOT_IMPLEMENTED;
+ }
+
+ s->aws4_auth_needs_complete = true;
+
+ } else {
+
+ dout(10) << "body content detected in multiple chunks" << dendl;
+
+ /* payload in multiple chunks */
- s->aws4_auth_needs_complete = true;
+ switch(s->op_type)
+ {
+ case RGW_OP_PUT_OBJ:
+ break;
+ default:
+ dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
+ return -ERR_NOT_IMPLEMENTED;
+ }
+
+ /* calculate seed */
+
+ int err = authorize_v4_complete(store, s, "", unsigned_payload);
+ if (err) {
+ return err;
+ }
+
+ /* verify seed signature */
+
+ if (s->aws4_auth->signature != s->aws4_auth->new_signature) {
+ dout(10) << "ERROR: AWS4 seed signature does NOT match!" << dendl;
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
+
+ dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
+
+ s->aws4_auth_needs_complete = false;
+
+ }
}