]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: AWS4 auth support for positive content-length
authorJavier M. Mellid <jmunhoz@igalia.com>
Mon, 21 Sep 2015 17:09:22 +0000 (19:09 +0200)
committerJavier M. Mellid <jmunhoz@igalia.com>
Sat, 13 Feb 2016 12:31:07 +0000 (12:31 +0000)
Handle AWS4 auth on requests with positive content-length. It splits the
auth process along several steps to process the body content on the fly
instead of reading the whole body in memory. After that, it completes
the delayed AWS4 auth properly.

Requests with content-length <= 0 are validated as usual. They don't
require any kind of completion.

Requests with content-length > 0 use a streaming approach together with
a completion step.

Fixes: #10333
Signed-off-by: Javier M. Mellid <jmunhoz@igalia.com>
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_auth_s3.h
src/rgw/rgw_client_io.cc
src/rgw/rgw_client_io.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_op.cc
src/rgw/rgw_rest.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index cd3adc41683efc3f9a77ecdb9d55d5951a8bc0ef..e370eee2b09cf311eb6a0ce567d901c4b70a32cf 100644 (file)
@@ -4,6 +4,7 @@
 #include "common/armor.h"
 #include "common/utf8.h"
 #include "rgw_common.h"
+#include "rgw_client_io.h"
 
 #define dout_subsys ceph_subsys_rgw
 
@@ -148,9 +149,9 @@ int rgw_get_s3_header_digest(const string& auth_hdr, const string& key, string&
   return 0;
 }
 
-void rgw_hash_s3_string_sha256(const string& str, string& dest)
+void rgw_hash_s3_string_sha256(const char *data, int len, string& dest)
 {
-  calc_hash_sha256(str, dest);
+  calc_hash_sha256(data, len, dest);
 }
 
 static inline bool is_base64_for_content_md5(unsigned char c) {
@@ -269,16 +270,22 @@ void rgw_create_s3_v4_canonical_request(struct req_state *s, const string& canon
   if (unsigned_payload) {
     request_payload_hash = "UNSIGNED-PAYLOAD";
   } else {
-    rgw_hash_s3_string_sha256(request_payload.c_str(), request_payload.size(), request_payload_hash);
+    if (s->aws4_auth_complete) {
+      request_payload_hash = s->cio->grab_aws4_sha256_hash();
+    } else {
+      rgw_hash_s3_string_sha256(request_payload.c_str(), request_payload.size(), request_payload_hash);
+    }
   }
 
+  s->aws4_auth_payload_hash = request_payload_hash;
+
   dout(10) << "payload request hash = " << request_payload_hash << dendl;
 
   rgw_assemble_s3_v4_canonical_request(s->info.method, canonical_uri.c_str(),
       canonical_qs.c_str(), canonical_hdrs.c_str(), signed_hdrs.c_str(),
       request_payload_hash.c_str(), canonical_req);
 
-  rgw_hash_s3_string_sha256(canonical_req, canonical_req_hash);
+  rgw_hash_s3_string_sha256(canonical_req.c_str(), canonical_req.size(), canonical_req_hash);
 
   dout(10) << "canonical request = " << canonical_req << dendl;
   dout(10) << "canonical request hash = " << canonical_req_hash << dendl;
index cb56a181d2039679a8a4b66f874afbb23ad934a5..ed4aa010f6db2bfab8a07915d0139c4f0ffd184d 100644 (file)
@@ -12,7 +12,7 @@ void rgw_create_s3_canonical_header(const char *method, const char *content_md5,
                             string& dest_str);
 bool rgw_create_s3_canonical_header(req_info& info, utime_t *header_time, string& dest, bool qsr);
 int rgw_get_s3_header_digest(const string& auth_hdr, const string& key, string& dest);
-void rgw_hash_s3_string_sha256(const string& str, string& dest);
+void rgw_hash_s3_string_sha256(const char *data, int len, string& dest);
 void rgw_create_s3_v4_canonical_request(struct req_state *s, const string& canonical_uri, const string& canonical_qs,
                                         const string& canonical_hdrs, const string& signed_hdrs, const string& request_payload,
                                         bool unsigned_payload, string& canonical_req, string& canonical_req_hash);
index b6ef745c4f275dec652b83841090f6368b26d115..c280b4209e7a30de8ad936fcb98f2881c8357a82 100644 (file)
@@ -71,7 +71,7 @@ int RGWClientIO::write(const char *buf, int len)
 }
 
 
-int RGWClientIO::read(char *buf, int max, int *actual)
+int RGWClientIO::read(char *buf, int max, int *actual, bool hash /* = false */)
 {
   int ret = read_data(buf, max);
   if (ret < 0)
@@ -81,6 +81,15 @@ int RGWClientIO::read(char *buf, int max, int *actual)
 
   bytes_received += *actual;
 
+  if (hash) {
+    calc_hash_sha256_update_stream(sha256_hash, buf, *actual);
+  }
+
   return 0;
 }
 
+
+string RGWClientIO::grab_aws4_sha256_hash()
+{
+  return calc_hash_sha256_close_stream(sha256_hash);
+}
index ac610c6215f3d3bb805d7dec46a516ebf1bc467e..07c11c777f485ca7971440ee98cdc9e31def5224 100644 (file)
@@ -20,6 +20,7 @@ class RGWClientIO {
 
 protected:
   RGWEnv env;
+  SHA256 *sha256_hash;
 
   virtual void init_env(CephContext *cct) = 0;
 
@@ -28,13 +29,17 @@ protected:
 
 public:
   virtual ~RGWClientIO() {}
-  RGWClientIO() : account(false), bytes_sent(0), bytes_received(0) {}
+  RGWClientIO() : account(false), bytes_sent(0), bytes_received(0) {
+    sha256_hash = calc_hash_sha256_open_stream();
+  }
 
   void init(CephContext *cct);
   int print(const char *format, ...);
   int write(const char *buf, int len);
   virtual void flush() = 0;
-  int read(char *buf, int max, int *actual);
+  int read(char *buf, int max, int *actual, bool hash = false);
+
+  string grab_aws4_sha256_hash();
 
   virtual int send_status(int status, const char *status_name) = 0;
   virtual int send_100_continue() = 0;
index d30fd38ed5a2c30f3d29aea1203382b1aa8fba01..af28173c8a03e026a4bf59f77f5d926d8f8e1678 100644 (file)
@@ -423,7 +423,7 @@ void calc_hmac_sha1(const char *key, int key_len,
  * calculate the sha256 value of a given msg and key
  */
 void calc_hmac_sha256(const char *key, int key_len,
-                                         const char *msg, int msg_len, char *dest)
+                      const char *msg, int msg_len, char *dest)
 {
   char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
 
@@ -437,12 +437,12 @@ void calc_hmac_sha256(const char *key, int key_len,
 /*
  * calculate the sha256 hash value of a given msg
  */
-void calc_hash_sha256(const string& msg, string& dest)
+void calc_hash_sha256(const char *msg, int len, string& dest)
 {
   char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
 
   SHA256 hash;
-  hash.Update((const unsigned char *)msg.c_str(), msg.size());
+  hash.Update((const unsigned char *)msg, len);
   hash.Final((unsigned char *)hash_sha256);
 
   char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1];
@@ -451,6 +451,32 @@ void calc_hash_sha256(const string& msg, string& dest)
   dest = std::string(hex_str);
 }
 
+using ceph::crypto::SHA256;
+
+SHA256* calc_hash_sha256_open_stream()
+{
+  return new SHA256;
+}
+
+void calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len)
+{
+  hash->Update((const unsigned char *)msg, len);
+}
+
+string calc_hash_sha256_close_stream(SHA256* hash)
+{
+  char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+
+  hash->Final((unsigned char *)hash_sha256);
+
+  char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1];
+  buf_to_hex((unsigned char *)hash_sha256, CEPH_CRYPTO_SHA256_DIGESTSIZE, hex_str);
+
+  delete hash;
+
+  return std::string(hex_str);
+}
+
 int gen_rand_base64(CephContext *cct, char *dest, int size) /* size should be the required string size + 1 */
 {
   char buf[size];
index ed2e619a871806ffe980a7ec513a5519f0b0fd74..48c4e292a9e0dcbb196a2e4c62ae09a9cdf0aeac 100644 (file)
@@ -1131,6 +1131,21 @@ struct req_state {
    string swift_user;
    string swift_groups;
 
+   /* aws4 auth support */
+   bool   aws4_auth_complete;
+   string aws4_auth_date;
+   string aws4_auth_credential;
+   string aws4_auth_signedheaders;
+   string aws4_auth_signed_hdrs;
+   string aws4_auth_access_key_id;
+   string aws4_auth_credential_scope;
+   string aws4_auth_canonical_uri;
+   string aws4_auth_canonical_qs;
+   string aws4_auth_canonical_hdrs;
+   string aws4_auth_signature;
+   string aws4_auth_new_signature;
+   string aws4_auth_payload_hash;
+
    utime_t time;
 
    void *obj_ctx;
@@ -1717,8 +1732,14 @@ extern void calc_hmac_sha1(const char *key, int key_len,
                           const char *msg, int msg_len, char *dest);
 /* destination should be CEPH_CRYPTO_HMACSHA256_DIGESTSIZE bytes long */
 extern void calc_hmac_sha256(const char *key, int key_len, const char *msg, int msg_len, char *dest);
+extern void calc_hash_sha256(const char *msg, int len, string& dest);
 extern void calc_hash_sha256(const string& msg, string& dest);
 
+using ceph::crypto::SHA256;
+extern SHA256* calc_hash_sha256_open_stream();
+extern void    calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len);
+extern string  calc_hash_sha256_close_stream(SHA256* hash);
+
 extern int rgw_parse_op_type_list(const string& str, uint32_t *perm);
 
 #endif
index e5a6af131be7735e8b56a985a5be42c6bb9e8c50..b5983b257075de822244cf83f7796ce36c18b3ce 100644 (file)
@@ -25,6 +25,7 @@
 #include "rgw_multi_del.h"
 #include "rgw_cors.h"
 #include "rgw_cors_s3.h"
+#include "rgw_rest_s3.h"
 
 #include "rgw_client_io.h"
 
@@ -2323,11 +2324,35 @@ void RGWPutObj::execute()
 
   perfcounter->inc(l_rgw_put_b, s->obj_size);
 
+  if (s->aws4_auth_complete) {
+
+    /* complete aws4 auth */
+
+    op_ret = RGW_Auth_S3::authorize_aws4_auth_complete(store, s);
+    if (op_ret) {
+      goto done;
+    }
+
+    s->aws4_auth_complete = false;
+
+    /* verify signature */
+
+    if (s->aws4_auth_signature != s->aws4_auth_new_signature) {
+      op_ret = -ERR_SIGNATURE_NO_MATCH;
+      ldout(s->cct, 20) << "delayed aws4 auth failed" << dendl;
+      goto done;
+    }
+
+    /* authorization ok */
+
+    dout(10) << "v4 auth ok" << dendl;
+
+  }
+
   op_ret = store->check_quota(s->bucket_owner.get_id(), s->bucket,
-                             user_quota, bucket_quota, s->obj_size);
+                              user_quota, bucket_quota, s->obj_size);
   if (op_ret < 0) {
-    ldout(s->cct, 20) << "second check_quota() returned ret=" << op_ret
-                     << dendl;
+    ldout(s->cct, 20) << "second check_quota() returned op_ret=" << op_ret << dendl;
     goto done;
   }
 
index ba7d429d75e6c97b8c4dbc8f4e5c30a34f199aae..dc8f65007a5c538cc5c0721db293ed220b1eeb68 100644 (file)
@@ -970,7 +970,7 @@ int RGWPutObj_ObjStore::get_data(bufferlist& bl)
     bufferptr bp(cl);
 
     int read_len; /* cio->read() expects int * */
-    int r = s->cio->read(bp.c_str(), cl, &read_len);
+    int r = s->cio->read(bp.c_str(), cl, &read_len, true);
     len = read_len;
     if (r < 0)
       return r;
index 5cdd2df490f44e35543dc70bc14fae782d5b26fb..1f195b5925f062cdbf1edcb9cd5ff1b1154def82 100644 (file)
@@ -2698,18 +2698,79 @@ int RGW_Auth_S3::authorize(RGWRados *store, struct req_state *s)
   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 */
@@ -2726,21 +2787,21 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
     /* 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;
     }
 
@@ -2749,117 +2810,119 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
     /* 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;
   }
@@ -2876,23 +2939,23 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
    * 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, '&')) {
@@ -2908,16 +2971,16 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
       }
     }
 
-    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("&");
       }
     }
 
@@ -2926,34 +2989,40 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
   /* 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
    *
@@ -2961,6 +3030,8 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
    *  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;
@@ -2972,85 +3043,38 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
 
     /* 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;
index a0fd7f0db6c71148bb76d586812feeff7b5a27fb..24b75a0eb37ff74bb1d9b1f1b1036aa02d63cbd1 100644 (file)
@@ -386,9 +386,13 @@ public:
 class RGW_Auth_S3 {
 public:
   static int authorize(RGWRados *store, struct req_state *s);
+  static int authorize_aws4_auth_complete(RGWRados *store, struct req_state *s);
 private:
   static int authorize_v2(RGWRados *store, struct req_state *s);
   static int authorize_v4(RGWRados *store, struct req_state *s);
+  static int authorize_v4_complete(RGWRados *store, struct req_state *s,
+                                   string request_payload, bool unsigned_payload);
+
 };
 
 class RGWHandler_Auth_S3 : public RGWHandler_ObjStore {