return -EPERM;
}
+int RGW_Auth_S3::authorize_aws4_auth_complete(RGWRados *store, struct req_state *s)
+{
+ return authorize_v4_complete(store, s, "", false);
+}
+
+int RGW_Auth_S3::authorize_v4_complete(RGWRados *store, struct req_state *s, string request_payload, bool unsigned_payload)
+{
+ size_t pos;
+
+ /* craft canonical request */
+
+ string canonical_req;
+ string canonical_req_hash;
+
+ rgw_create_s3_v4_canonical_request(s, s->aws4_auth_canonical_uri, s->aws4_auth_canonical_qs,
+ s->aws4_auth_canonical_hdrs, s->aws4_auth_signed_hdrs, request_payload, unsigned_payload,
+ canonical_req, canonical_req_hash);
+
+ /*
+ * create a string to sign
+ *
+ * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
+ */
+
+ string string_to_sign;
+
+ rgw_create_s3_v4_string_to_sign("AWS4-HMAC-SHA256", s->aws4_auth_date, s->aws4_auth_credential_scope,
+ canonical_req_hash, string_to_sign);
+
+ /*
+ * calculate the AWS signature
+ *
+ * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
+ */
+
+ string cs_aux = s->aws4_auth_credential_scope;
+
+ string date_cs = cs_aux;
+ pos = date_cs.find("/");
+ date_cs = date_cs.substr(0, pos);
+ cs_aux = cs_aux.substr(pos + 1, cs_aux.length());
+
+ string region_cs = cs_aux;
+ pos = region_cs.find("/");
+ region_cs = region_cs.substr(0, pos);
+ cs_aux = cs_aux.substr(pos + 1, cs_aux.length());
+
+ string service_cs = cs_aux;
+ pos = service_cs.find("/");
+ service_cs = service_cs.substr(0, pos);
+
+ int err = rgw_calculate_s3_v4_aws_signature(s, s->aws4_auth_access_key_id, date_cs,
+ region_cs, service_cs, string_to_sign, s->aws4_auth_new_signature);
+
+ dout(10) << "----------------------------- Verifying signatures" << dendl;
+ dout(10) << "Signature = " << s->aws4_auth_signature << dendl;
+ dout(10) << "New Signature = " << s->aws4_auth_new_signature << dendl;
+ dout(10) << "-----------------------------" << dendl;
+
+ if (err) {
+ return err;
+ }
+
+ return 0;
+
+}
+
/*
* handle v4 signatures (rados auth only)
*/
int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
{
string::size_type pos;
- string date;
- string credential;
- string signedheaders;
- string signature;
- string access_key_id;
- string credential_scope;
bool using_qs;
/* v4 requires rados auth */
/* look for required params */
using_qs = true;
- credential = s->info.args.get("X-Amz-Credential");
- if (credential.size() == 0) {
+ s->aws4_auth_credential = s->info.args.get("X-Amz-Credential");
+ if (s->aws4_auth_credential.size() == 0) {
return -EPERM;
}
- date = s->info.args.get("X-Amz-Date");
- if (date.size() == 0) {
+ s->aws4_auth_date = s->info.args.get("X-Amz-Date");
+ if (s->aws4_auth_date.size() == 0) {
return -EPERM;
}
- signedheaders = s->info.args.get("X-Amz-SignedHeaders");
- if (signedheaders.size() == 0) {
+ s->aws4_auth_signedheaders = s->info.args.get("X-Amz-SignedHeaders");
+ if (s->aws4_auth_signedheaders.size() == 0) {
return -EPERM;
}
- signature = s->info.args.get("X-Amz-Signature");
- if (signature.size() == 0) {
+ s->aws4_auth_signature = s->info.args.get("X-Amz-Signature");
+ if (s->aws4_auth_signature.size() == 0) {
return -EPERM;
}
/* auth ships in headers ... */
/* ------------------------- handle Credential header */
+
using_qs = false;
- credential = s->http_auth;
+ s->aws4_auth_credential = s->http_auth;
- credential = credential.substr(17, credential.length());
+ s->aws4_auth_credential = s->aws4_auth_credential.substr(17, s->aws4_auth_credential.length());
- pos = credential.find("Credential");
+ pos = s->aws4_auth_credential.find("Credential");
if (pos == std::string::npos) {
return -EINVAL;
}
- credential = credential.substr(pos, credential.find(","));
+ s->aws4_auth_credential = s->aws4_auth_credential.substr(pos, s->aws4_auth_credential.find(","));
- credential = credential.substr(pos + 1, credential.length());
+ s->aws4_auth_credential = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length());
- pos = credential.find("=");
+ pos = s->aws4_auth_credential.find("=");
- credential = credential.substr(pos + 1, credential.length());
+ s->aws4_auth_credential = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length());
/* ------------------------- handle SignedHeaders header */
- signedheaders = s->http_auth;
+ s->aws4_auth_signedheaders = s->http_auth;
- signedheaders = signedheaders.substr(17, signedheaders.length());
+ s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(17, s->aws4_auth_signedheaders.length());
- pos = signedheaders.find("SignedHeaders");
+ pos = s->aws4_auth_signedheaders.find("SignedHeaders");
if (pos == std::string::npos) {
return -EINVAL;
}
- signedheaders = signedheaders.substr(pos, signedheaders.length());
+ s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(pos, s->aws4_auth_signedheaders.length());
- pos = signedheaders.find(",");
+ pos = s->aws4_auth_signedheaders.find(",");
if (pos == std::string::npos) {
return -EINVAL;
}
- signedheaders = signedheaders.substr(0, pos);
+ s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(0, pos);
- pos = signedheaders.find("=");
+ pos = s->aws4_auth_signedheaders.find("=");
if (pos == std::string::npos) {
return -EINVAL;
}
- signedheaders = signedheaders.substr(pos + 1, signedheaders.length());
+ s->aws4_auth_signedheaders = s->aws4_auth_signedheaders.substr(pos + 1, s->aws4_auth_signedheaders.length());
/* host;user-agent;x-amz-content-sha256;x-amz-date */
- dout(10) << "v4 signedheaders format = " << signedheaders << dendl;
+ dout(10) << "v4 signedheaders format = " << s->aws4_auth_signedheaders << dendl;
/* ------------------------- handle Signature header */
- signature = s->http_auth;
+ s->aws4_auth_signature = s->http_auth;
- signature = signature.substr(17, signature.length());
+ s->aws4_auth_signature = s->aws4_auth_signature.substr(17, s->aws4_auth_signature.length());
- pos = signature.find("Signature");
+ pos = s->aws4_auth_signature.find("Signature");
if (pos == std::string::npos) {
return -EINVAL;
}
- signature = signature.substr(pos, signature.length());
+ s->aws4_auth_signature = s->aws4_auth_signature.substr(pos, s->aws4_auth_signature.length());
- pos = signature.find("=");
+ pos = s->aws4_auth_signature.find("=");
if (pos == std::string::npos) {
return -EINVAL;
}
- signature = signature.substr(pos + 1, signature.length());
+ s->aws4_auth_signature = s->aws4_auth_signature.substr(pos + 1, s->aws4_auth_signature.length());
/* sig hex str */
- dout(10) << "v4 signature format = " << signature << dendl;
+ dout(10) << "v4 signature format = " << s->aws4_auth_signature << dendl;
/* ------------------------- handle x-amz-date header */
/* grab date */
- date = s->info.env->get("HTTP_X_AMZ_DATE");
- if (date.empty()) {
+ s->aws4_auth_date = s->info.env->get("HTTP_X_AMZ_DATE");
+ if (s->aws4_auth_date.empty()) {
+ dout(10) << "error reading date via http_x_amz_date" << dendl;
return -EINVAL;
}
}
/* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */
- dout(10) << "v4 credential format = " << credential << dendl;
+ dout(10) << "v4 credential format = " << s->aws4_auth_credential << dendl;
- if (std::count(credential.begin(), credential.end(), '/') != 4) {
+ if (std::count(s->aws4_auth_credential.begin(), s->aws4_auth_credential.end(), '/') != 4) {
return -EINVAL;
}
/* credential must end with 'aws4_request' */
- if (credential.find("aws4_request") == std::string::npos) {
+ if (s->aws4_auth_credential.find("aws4_request") == std::string::npos) {
return -EINVAL;
}
/* grab access key id */
- pos = credential.find("/");
- access_key_id = credential.substr(0, pos);
+ pos = s->aws4_auth_credential.find("/");
+ s->aws4_auth_access_key_id = s->aws4_auth_credential.substr(0, pos);
- dout(10) << "access key id = " << access_key_id << dendl;
+ dout(10) << "access key id = " << s->aws4_auth_access_key_id << dendl;
/* grab credential scope */
- credential_scope = credential.substr(pos + 1, credential.length());
+ s->aws4_auth_credential_scope = s->aws4_auth_credential.substr(pos + 1, s->aws4_auth_credential.length());
- dout(10) << "credential scope = " << credential_scope << dendl;
+ dout(10) << "credential scope = " << s->aws4_auth_credential_scope << dendl;
/* grab user information */
- if (rgw_get_user_info_by_access_key(store, access_key_id, s->user) < 0) {
- dout(10) << "error reading user info, uid=" << access_key_id
+ if (rgw_get_user_info_by_access_key(store, s->aws4_auth_access_key_id, s->user) < 0) {
+ dout(10) << "error reading user info, uid=" << s->aws4_auth_access_key_id
<< " can't authenticate" << dendl;
return -ERR_INVALID_ACCESS_KEY;
}
* that SigV4 typically does. this code follows the same approach that boto library
* see auth.py:canonical_uri(...) */
- string canonical_uri = s->info.request_uri;
+ s->aws4_auth_canonical_uri = s->info.request_uri;
- if (canonical_uri.empty()) {
- canonical_uri = "/";
+ if (s->aws4_auth_canonical_uri.empty()) {
+ s->aws4_auth_canonical_uri = "/";
}
/* craft canonical query string */
- string canonical_qs = s->info.request_params;
+ s->aws4_auth_canonical_qs = s->info.request_params;
- if (!canonical_qs.empty()) {
+ if (!s->aws4_auth_canonical_qs.empty()) {
/* handle case when query string exists. Step 3 in
* http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html */
map<string, string> canonical_qs_map;
- istringstream cqs(canonical_qs);
+ istringstream cqs(s->aws4_auth_canonical_qs);
string keyval;
while (getline(cqs, keyval, '&')) {
}
}
- canonical_qs = "";
+ s->aws4_auth_canonical_qs = "";
map<string, string>::iterator last = canonical_qs_map.end();
--last;
for (map<string, string>::iterator it = canonical_qs_map.begin();
it != canonical_qs_map.end(); ++it) {
- canonical_qs.append(it->first + "=" + it->second);
+ s->aws4_auth_canonical_qs.append(it->first + "=" + it->second);
if (it != last) {
- canonical_qs.append("&");
+ s->aws4_auth_canonical_qs.append("&");
}
}
/* craft canonical headers */
map<string, string> canonical_hdrs_map;
- istringstream sh(signedheaders);
+ istringstream sh(s->aws4_auth_signedheaders);
string token;
while (getline(sh, token, ';')) {
string token_env = "HTTP_" + token;
transform(token_env.begin(), token_env.end(), token_env.begin(), ::toupper);
replace(token_env.begin(), token_env.end(), '-', '_');
+ if (token_env == "HTTP_CONTENT_LENGTH") {
+ token_env = "CONTENT_LENGTH";
+ }
+ if (token_env == "HTTP_CONTENT_TYPE") {
+ token_env = "CONTENT_TYPE";
+ }
const char *t = s->info.env->get(token_env.c_str());
- if (!t)
+ if (!t) {
+ dout(10) << "error getting env var" << dendl;
return -EINVAL;
+ }
string token_value = string(t);
canonical_hdrs_map[token] = rgw_trim_whitespace(token_value);
}
- string canonical_hdrs;
-
for (map<string, string>::iterator it = canonical_hdrs_map.begin();
it != canonical_hdrs_map.end(); ++it) {
- canonical_hdrs.append(it->first + ":" + it->second + "\n");
+ s->aws4_auth_canonical_hdrs.append(it->first + ":" + it->second + "\n");
}
- dout(10) << "canonical headers format = " << canonical_hdrs << dendl;
+ dout(10) << "canonical headers format = " << s->aws4_auth_canonical_hdrs << dendl;
/* craft signed headers */
- string signed_hdrs = signedheaders;
+ s->aws4_auth_signed_hdrs = s->aws4_auth_signedheaders;
- /* TODO: craft request payload */
+ /* handle request payload */
/* from rfc2616 - 4.3 Message Body
*
* Content-Length or Transfer-Encoding header field in the request's message-headers."
*/
+ s->aws4_auth_payload_hash = "";
+
string request_payload;
bool unsigned_payload = false;
/* requests lacking of body are authenticated now */
- /* craft canonical request */
-
- string canonical_req;
- string canonical_req_hash;
-
- rgw_create_s3_v4_canonical_request(s, canonical_uri, canonical_qs,
- canonical_hdrs, signed_hdrs, request_payload, unsigned_payload,
- canonical_req, canonical_req_hash);
-
- /* TODO: read body in request_payload */
-
- }
-
- /* craft canonical request */
+ /* complete aws4 auth */
- string canonical_req;
- string canonical_req_hash;
-
- rgw_create_s3_v4_canonical_request(s, canonical_uri, canonical_qs,
- canonical_hdrs, signed_hdrs, request_payload, canonical_req,
- canonical_req_hash);
-
- /*
- * create a string to sign
- *
- * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
- */
-
- string string_to_sign;
+ int err = authorize_v4_complete(store, s, request_payload, unsigned_payload);
+ if (err) {
+ return err;
+ }
- rgw_create_s3_v4_string_to_sign("AWS4-HMAC-SHA256", date, credential_scope,
- canonical_req_hash, string_to_sign);
+ /* verify signature */
- /*
- * calculate the AWS signature
- *
- * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
- */
+ if (s->aws4_auth_signature != s->aws4_auth_new_signature) {
+ return -ERR_SIGNATURE_NO_MATCH;
+ }
- string cs_aux = credential_scope;
+ /* authorization ok */
- string date_cs = cs_aux;
- pos = date_cs.find("/");
- date_cs = date_cs.substr(0, pos);
- cs_aux = cs_aux.substr(pos + 1, cs_aux.length());
+ dout(10) << "v4 auth ok" << dendl;
- string region_cs = cs_aux;
- pos = region_cs.find("/");
- region_cs = region_cs.substr(0, pos);
- cs_aux = cs_aux.substr(pos + 1, cs_aux.length());
+ /* aws4 auth completed */
- string service_cs = cs_aux;
- pos = service_cs.find("/");
- service_cs = service_cs.substr(0, pos);
+ s->aws4_auth_complete = false;
- string new_signature;
+ } else {
- int err = rgw_calculate_s3_v4_aws_signature(s, access_key_id, date_cs,
- region_cs, service_cs, string_to_sign, new_signature);
- if (err) {
- return err;
- }
+ /* aws4 auth not completed... delay aws4 auth */
- /* verify signature */
+ s->aws4_auth_complete = true;
- dout(10) << "----------------------------- Verifying signatures" << dendl;
- dout(10) << "Signature = " << signature << dendl;
- dout(10) << "New Signature = " << new_signature << dendl;
- dout(10) << "-----------------------------" << dendl;
+ dout(10) << "body content detected... delaying v4 auth" << dendl;
- if (signature != new_signature) {
- return -ERR_SIGNATURE_NO_MATCH;
}
- /* authorization ok */
-
- dout(10) << "v4 auth ok" << dendl;
-
- map<string, RGWAccessKey>::iterator iter = s->user.access_keys.find(access_key_id);
+ map<string, RGWAccessKey>::iterator iter = s->user.access_keys.find(s->aws4_auth_access_key_id);
if (iter == s->user.access_keys.end()) {
dout(0) << "ERROR: access key not encoded in user info" << dendl;
return -EPERM;