]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: AWS4 authentication minimal support
authorJavier M. Mellid <jmunhoz@igalia.com>
Fri, 12 Jun 2015 13:04:23 +0000 (15:04 +0200)
committerJavier M. Mellid <jmunhoz@igalia.com>
Sat, 13 Feb 2016 12:22:36 +0000 (12:22 +0000)
Amazon S3 supports Signature Version 4. This patch contains the minimal
implementation supporting AWS4 in RGW. It implements AWS4 authentication
on http methods without body content and vars not shipping in the
request query string.

Fixes: #10333
Signed-off-by: Javier M. Mellid <jmunhoz@igalia.com>
src/common/ceph_crypto.cc
src/common/ceph_crypto.h
src/common/utf8.c
src/common/utf8.h
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_auth_s3.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index de5a03b790abb0204daa7c649a022069ac821dff..6da3232b1dc421e8e0e5f6cbaefc4fb1693c0787 100644 (file)
@@ -36,6 +36,10 @@ ceph::crypto::HMACSHA1::~HMACSHA1()
 {
 }
 
+ceph::crypto::HMACSHA256::~HMACSHA256()
+{
+}
+
 #elif defined(USE_NSS)
 
 // for SECMOD_RestartModules()
@@ -85,7 +89,7 @@ void ceph::crypto::shutdown()
   pthread_mutex_unlock(&crypto_init_mutex);
 }
 
-ceph::crypto::HMACSHA1::~HMACSHA1()
+ceph::crypto::HMAC::~HMAC()
 {
   PK11_DestroyContext(ctx, PR_TRUE);
   PK11_FreeSymKey(symkey);
index 4ef8821f836bce9a082a89ed1c3f608b25d244bf..689128fd1c22e96b7a8797178996cf5061b97e1f 100644 (file)
@@ -6,6 +6,7 @@
 #define CEPH_CRYPTO_MD5_DIGESTSIZE 16
 #define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20
 #define CEPH_CRYPTO_SHA1_DIGESTSIZE 20
+#define CEPH_CRYPTO_HMACSHA256_DIGESTSIZE 32
 #define CEPH_CRYPTO_SHA256_DIGESTSIZE 32
 
 #ifdef USE_CRYPTOPP
@@ -36,6 +37,15 @@ namespace ceph {
        }
       ~HMACSHA1();
     };
+
+    class HMACSHA256: public CryptoPP::HMAC<CryptoPP::SHA256> {
+    public:
+      HMACSHA256 (const byte *key, size_t length)
+        : CryptoPP::HMAC<CryptoPP::SHA256>(key, length)
+        {
+        }
+      ~HMACSHA256();
+    };
   }
 }
 #elif defined(USE_NSS)
@@ -107,31 +117,33 @@ namespace ceph {
       SHA256 () : Digest(SEC_OID_SHA256, CEPH_CRYPTO_SHA256_DIGESTSIZE) { }
     };
 
-    class HMACSHA1 {
+    class HMAC {
     private:
       PK11SlotInfo *slot;
       PK11SymKey *symkey;
       PK11Context *ctx;
+      unsigned int digest_size;
     public:
-      HMACSHA1 (const byte *key, size_t length) {
-       slot = PK11_GetBestSlot(CKM_SHA_1_HMAC, NULL);
+      HMAC (CK_MECHANISM_TYPE cktype, unsigned int digestsize, const byte *key, size_t length) {
+        digest_size = digestsize;
+       slot = PK11_GetBestSlot(cktype, NULL);
        assert(slot);
        SECItem keyItem;
        keyItem.type = siBuffer;
        keyItem.data = (unsigned char*)key;
        keyItem.len = length;
-       symkey = PK11_ImportSymKey(slot, CKM_SHA_1_HMAC, PK11_OriginUnwrap,
+       symkey = PK11_ImportSymKey(slot, cktype, PK11_OriginUnwrap,
                                   CKA_SIGN,  &keyItem, NULL);
        assert(symkey);
        SECItem param;
        param.type = siBuffer;
        param.data = NULL;
        param.len = 0;
-       ctx = PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, CKA_SIGN, symkey, &param);
+       ctx = PK11_CreateContextBySymKey(cktype, CKA_SIGN, symkey, &param);
        assert(ctx);
        Restart();
       }
-      ~HMACSHA1 ();
+      ~HMAC ();
       void Restart() {
        SECStatus s;
        s = PK11_DigestBegin(ctx);
@@ -145,12 +157,22 @@ namespace ceph {
       void Final (byte *digest) {
        SECStatus s;
        unsigned int dummy;
-       s = PK11_DigestFinal(ctx, digest, &dummy, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+       s = PK11_DigestFinal(ctx, digest, &dummy, digest_size);
        assert(s == SECSuccess);
-       assert(dummy == CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
+       assert(dummy == digest_size);
        Restart();
       }
     };
+
+    class HMACSHA1 : public HMAC {
+    public:
+      HMACSHA1 (const byte *key, size_t length) : HMAC(CKM_SHA_1_HMAC, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, key, length) { }
+    };
+
+    class HMACSHA256 : public HMAC {
+    public:
+      HMACSHA256 (const byte *key, size_t length) : HMAC(CKM_SHA256_HMAC, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, key, length) { }
+    };
   }
 }
 
index 5bf0a5be8508574d6876d0d28e17ca8e88a72257..3bc77c32cded5578e33528fedd0f48f6b1b45870 100644 (file)
@@ -16,9 +16,6 @@
 #include <stdio.h>
 #include <string.h>
 
-#define MAX_UTF8_SZ 6
-#define INVALID_UTF8_CHAR 0xfffffffful
-
 static int high_bits_set(int c)
 {
        int ret = 0;
index 28649effaec436c18e1ea27d42b7cea67bb32221..83efe6fd6dad43c81e417208102c39f15e75dba6 100644 (file)
@@ -15,6 +15,9 @@
 #ifndef CEPH_COMMON_UTF8_H
 #define CEPH_COMMON_UTF8_H
 
+#define MAX_UTF8_SZ 6
+#define INVALID_UTF8_CHAR 0xfffffffful
+
 #ifdef __cplusplus
 extern "C" {
 #endif
index b372630cb3e265b03e6bcf9225ac142f3625127b..c132a35482c06d4ad36e4a13b9ef23fe8ff2109b 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "common/armor.h"
+#include "common/utf8.h"
 #include "rgw_common.h"
 
 #define dout_subsys ceph_subsys_rgw
@@ -147,6 +148,11 @@ 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)
+{
+  calc_hash_sha256(str, dest);
+}
+
 static inline bool is_base64_for_content_md5(unsigned char c) {
   return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '='));
 }
@@ -214,3 +220,182 @@ bool rgw_create_s3_canonical_header(req_info& info, utime_t *header_time, string
 
   return true;
 }
+
+/*
+ * assemble canonical request for signature version 4
+ */
+void rgw_assemble_s3_v4_canonical_request(const char *method, const char *canonical_uri, const char *canonical_qs,
+                                          const char *canonical_hdrs, const char *signed_hdrs, const char *request_payload_hash,
+                                          string& dest_str)
+{
+  string dest;
+
+  if (method)
+    dest = method;
+  dest.append("\n");
+
+  if (canonical_uri) {
+    dest.append(canonical_uri);
+  }
+  dest.append("\n");
+
+  if (canonical_qs)
+    dest.append(canonical_qs);
+  dest.append("\n");
+
+  if (canonical_hdrs)
+    dest.append(canonical_hdrs);
+  dest.append("\n");
+
+  if (signed_hdrs)
+    dest.append(signed_hdrs);
+  dest.append("\n");
+
+  if (request_payload_hash)
+    dest.append(request_payload_hash);
+
+  dest_str = dest;
+}
+
+/*
+ * create canonical request for signature version 4
+ */
+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,
+                                        string& canonical_req, string& canonical_req_hash)
+{
+  string request_payload_hash;
+
+  rgw_hash_s3_string_sha256(request_payload, 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);
+
+  dout(10) << "canonical request = " << canonical_req << dendl;
+  dout(10) << "canonical request hash = " << canonical_req_hash << dendl;
+}
+
+/*
+ * assemble string to sign for signature version 4
+ */
+void rgw_assemble_s3_v4_string_to_sign(const char *algorithm, const char *request_date,
+                                       const char *credential_scope, const char *hashed_qr, string& dest_str)
+{
+  string dest;
+
+  if (algorithm)
+    dest = algorithm;
+  dest.append("\n");
+
+  if (request_date)
+    dest.append(request_date);
+  dest.append("\n");
+
+  if (credential_scope)
+    dest.append(credential_scope);
+  dest.append("\n");
+
+  if (hashed_qr)
+    dest.append(hashed_qr);
+
+  dest_str = dest;
+}
+
+/*
+ * create string to sign for signature version 4
+ */
+void rgw_create_s3_v4_string_to_sign(const string& algorithm, const string& request_date,
+                                     const string& credential_scope, const string& hashed_qr,
+                                     string& string_to_sign) {
+
+  rgw_assemble_s3_v4_string_to_sign(algorithm.c_str(), request_date.c_str(),
+      credential_scope.c_str(), hashed_qr.c_str(), string_to_sign);
+
+  dout(10) << "string to sign = " << string_to_sign << dendl;
+}
+
+/*
+ * calculate the AWS signature version 4
+ */
+int rgw_calculate_s3_v4_aws_signature(struct req_state *s,
+    const string& access_key_id, const string &date, const string& region,
+    const string& service, const string& string_to_sign, string& signature) {
+
+  map<string, RGWAccessKey>::iterator iter = s->user.access_keys.find(access_key_id);
+  if (iter == s->user.access_keys.end()) {
+    dout(10) << "ERROR: access key not encoded in user info" << dendl;
+    return -EPERM;
+  }
+
+  RGWAccessKey& k = iter->second;
+
+  string secret_key = "AWS4" + k.key;
+
+  char secret_k[secret_key.size() * MAX_UTF8_SZ];
+
+  size_t n = 0;
+
+  for (size_t i = 0; i < secret_key.size(); i++) {
+    n += encode_utf8(secret_key[i], (unsigned char *) (secret_k + n));
+  }
+
+  string secret_key_utf8_k(secret_k, n);
+
+  /* date */
+
+  char date_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+  calc_hmac_sha256(secret_key_utf8_k.c_str(), secret_key_utf8_k.size(),
+      date.c_str(), date.size(), date_k);
+
+  char aux[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1];
+  buf_to_hex((unsigned char *) date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+  dout(10) << "date_k        = " << string(aux) << dendl;
+
+  /* region */
+
+  char region_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+  calc_hmac_sha256(date_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, region.c_str(), region.size(), region_k);
+
+  buf_to_hex((unsigned char *) region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+  dout(10) << "region_k      = " << string(aux) << dendl;
+
+  /* service */
+
+  char service_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+  calc_hmac_sha256(region_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, service.c_str(), service.size(), service_k);
+
+  buf_to_hex((unsigned char *) service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+  dout(10) << "service_k     = " << string(aux) << dendl;
+
+  /* aws4_request */
+
+  char signing_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+  calc_hmac_sha256(service_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, "aws4_request", 12, signing_k);
+
+  buf_to_hex((unsigned char *) signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+  dout(10) << "signing_k     = " << string(aux) << dendl;
+
+  /* new signature */
+
+  char signature_k[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+  calc_hmac_sha256(signing_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, string_to_sign.c_str(), string_to_sign.size(), signature_k);
+
+  buf_to_hex((unsigned char *) signature_k, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE, aux);
+
+  dout(10) << "signature_k   = " << string(aux) << dendl;
+
+  signature = string(aux);
+
+  dout(10) << "new signature = " << signature << dendl;
+
+  return 0;
+}
index 3a7da098c6a64d733fc5559c2c39fed5342f6292..b08fb560a1a51d627eb768b3e9c2f6d493e75a08 100644 (file)
@@ -12,7 +12,11 @@ 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_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,
+                                        string& canonical_req, string& canonical_req_hash);
+void rgw_create_s3_v4_string_to_sign(const string& algorithm, const string& request_date, const string& credential_scope, const string& hashed_qr, string& string_to_sign);
+int rgw_calculate_s3_v4_aws_signature(struct req_state *s, const string& access_key_id, const string &date, const string& region, const string& service, const string& string_to_sign, string& signature);
 
 #endif
index 80b6c1bd4ace6bbefb51f3434a870ed24fee360b..2de86c54117f052fd8d05f8f99b1226e4b73b4f2 100644 (file)
@@ -419,6 +419,38 @@ void calc_hmac_sha1(const char *key, int key_len,
   hmac.Final((unsigned char *)dest);
 }
 
+/*
+ * 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)
+{
+  char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+
+  HMACSHA256 hmac((const unsigned char *)key, key_len);
+  hmac.Update((const unsigned char *)msg, msg_len);
+  hmac.Final((unsigned char *)hash_sha256);
+
+  memcpy(dest, hash_sha256, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE);
+}
+
+/*
+ * calculate the sha256 hash value of a given msg
+ */
+void calc_hash_sha256(const string& msg, string& dest)
+{
+  char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
+
+  SHA256 hash;
+  hash.Update((const unsigned char *)msg.c_str(), msg.size());
+  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);
+
+  dest = 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 e444f735c4425f156f49c27248df631f1551aad0..948c2797af19492629447287dc83f38479b6a0b8 100644 (file)
@@ -1712,9 +1712,12 @@ extern bool verify_object_permission(struct req_state *s, int perm);
 extern bool url_decode(const string& src_str, string& dest_str, bool in_query = false);
 extern void url_encode(const string& src, string& dst);
 
+/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
 extern void calc_hmac_sha1(const char *key, int key_len,
                           const char *msg, int msg_len, char *dest);
-/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
+/* 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 string& msg, string& dest);
 
 extern int rgw_parse_op_type_list(const string& str, uint32_t *perm);
 
index dd22378415af19b8704ec2d9d4b8646d96b63f4f..41b7e7cfd67eb79e70ca66f131008c8b18de020f 100644 (file)
@@ -2634,6 +2634,365 @@ static void init_anon_user(struct req_state *s)
   s->perm_mask = RGW_PERM_FULL_CONTROL;
 }
 
+/*
+ * handle v4 signatures (rados auth only)
+ */
+int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s)
+{
+  string::size_type pos;
+  string algorithm;
+  string date;
+  string credential;
+  string signedheaders;
+  string signature;
+  string access_key_id;
+  string credential_scope;
+
+  /* v4 requires rados auth */
+  if (!store->ctx()->_conf->rgw_s3_auth_use_rados) {
+    return -EPERM;
+  }
+
+  if ((!s->http_auth) || !(*s->http_auth)) {
+
+    /* auth ships with req params ... */
+
+    /* look for required params */
+
+    algorithm = s->info.args.get("X-Amz-Algorithm");
+    if (algorithm.size() == 0) {
+      return -EPERM;
+    }
+    credential = s->info.args.get("X-Amz-Credential");
+    if (credential.size() == 0) {
+      return -EPERM;
+    }
+    date = s->info.args.get("X-Amz-Date");
+    if (date.size() == 0) {
+      return -EPERM;
+    }
+    signedheaders = s->info.args.get("X-Amz-SignedHeaders");
+    if (signedheaders.size() == 0) {
+      return -EPERM;
+    }
+
+    signature = s->info.args.get("X-Amz-Signature");
+    if (signature.size() == 0) {
+      return -EPERM;
+    }
+
+  } else {
+
+    /* auth ships in headers ... */
+
+    /* valid algorithm? */
+
+    if (strncmp(s->http_auth, "AWS4-HMAC-SHA256", 16)) {
+      return -EPERM;
+    }
+
+    algorithm = "AWS4-HMAC-SHA256";
+
+    /* ------------------------- handle Credential header */
+
+    credential = s->http_auth;
+
+    credential = credential.substr(17, credential.length());
+
+    pos = credential.find("Credential");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    credential = credential.substr(pos, credential.find(","));
+
+    credential = credential.substr(pos + 1, credential.length());
+
+    pos = credential.find("=");
+
+    credential = credential.substr(pos + 1, credential.length());
+
+    /* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */
+    dout(10) << "v4 credential format = " << credential << dendl;
+
+    if (std::count(credential.begin(), credential.end(), '/') != 4) {
+      return -EINVAL;
+    }
+
+    /* credential must end with 'aws4_request' */
+    if (credential.find("aws4_request") == std::string::npos) {
+      return -EINVAL;
+    }
+
+    /* grab access key id */
+
+    pos = credential.find("/");
+    access_key_id = credential.substr(0, pos);
+
+    dout(10) << "access key id = " << access_key_id << dendl;
+
+    /* grab credential scope */
+
+    credential_scope = credential.substr(pos + 1, credential.length());
+
+    dout(10) << "credential scope = " << credential_scope << dendl;
+
+    /* ------------------------- handle SignedHeaders header */
+
+    signedheaders = s->http_auth;
+
+    signedheaders = signedheaders.substr(17, signedheaders.length());
+
+    pos = signedheaders.find("SignedHeaders");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    signedheaders = signedheaders.substr(pos, signedheaders.length());
+
+    pos = signedheaders.find(",");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    signedheaders = signedheaders.substr(0, pos);
+
+    pos = signedheaders.find("=");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    signedheaders = signedheaders.substr(pos + 1, signedheaders.length());
+
+    /* host;user-agent;x-amz-content-sha256;x-amz-date */
+    dout(10) << "v4 signedheaders format = " << signedheaders << dendl;
+
+    /* ------------------------- handle Signature header */
+
+    signature = s->http_auth;
+
+    signature = signature.substr(17, signature.length());
+
+    pos = signature.find("Signature");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    signature = signature.substr(pos, signature.length());
+
+    pos = signature.find("=");
+    if (pos == std::string::npos) {
+      return -EINVAL;
+    }
+
+    signature = signature.substr(pos + 1, signature.length());
+
+    /* sig hex str */
+    dout(10) << "v4 signature format = " << signature << dendl;
+
+    /* ------------------------- handle x-amz-date header */
+
+    /* grab date */
+
+    date = s->info.env->get("HTTP_X_AMZ_DATE");
+    if (date.empty()) {
+      return -EINVAL;
+    }
+
+  }
+
+  /* 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
+              << " can't authenticate" << dendl;
+    return -ERR_INVALID_ACCESS_KEY;
+  }
+
+  /*
+   * create a canonical request
+   *
+   * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+   */
+
+  /* craft canonical uri */
+
+  /* here code should normalize via rfc3986 but S3 does **NOT** do path normalization
+   * 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;
+
+  if (canonical_uri.empty()) {
+    canonical_uri = "/";
+  }
+
+  /* craft canonical query string */
+
+  string canonical_qs = s->info.request_params;
+
+  if (!canonical_qs.empty()) {
+
+    /* TODO: implement step 3 in
+     * http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html */
+
+    /* handle case when query string exists */
+  }
+
+  /* craft canonical headers */
+
+  map<string, string> canonical_hdrs_map;
+  istringstream sh(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(), '-', '_');
+    const char *t = s->info.env->get(token_env.c_str());
+    if (!t)
+      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");
+  }
+
+  dout(10) << "canonical headers format = " << canonical_hdrs << dendl;
+
+  /* craft signed headers */
+
+  string signed_hdrs = signedheaders;
+
+  /* TODO: craft request payload */
+
+  /* from rfc2616 - 4.3 Message Body
+   *
+   * "The presence of a message-body in a request is signaled by the inclusion of a
+   *  Content-Length or Transfer-Encoding header field in the request's message-headers."
+   */
+
+  string request_payload;
+
+  if ((s->content_length > 0) || s->info.env->get("HTTP_TRANSFER_ENCODING")) {
+
+    /* TODO: read body in request_payload */
+
+  }
+
+  /* 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, 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", date, 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 = 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);
+
+  string new_signature;
+
+  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;
+  }
+
+  /* verify signature */
+
+  dout(10) << "----------------------------- Verifying signatures" << dendl;
+  dout(10) << "Signature     = " << signature << dendl;
+  dout(10) << "New Signature = " << new_signature << dendl;
+  dout(10) << "-----------------------------" << 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);
+  if (iter == s->user.access_keys.end()) {
+    dout(0) << "ERROR: access key not encoded in user info" << dendl;
+    return -EPERM;
+  }
+
+  RGWAccessKey& k = iter->second;
+
+  if (!k.subuser.empty()) {
+    map<string, RGWSubUser>::iterator uiter = s->user.subusers.find(k.subuser);
+    if (uiter == s->user.subusers.end()) {
+      dout(0) << "NOTICE: could not find subuser: " << k.subuser << dendl;
+      return -EPERM;
+    }
+    RGWSubUser& subuser = uiter->second;
+    s->perm_mask = subuser.perm_mask;
+  } else {
+    s->perm_mask = RGW_PERM_FULL_CONTROL;
+  }
+
+  if (s->user.system) {
+    s->system_request = true;
+    dout(20) << "system request" << dendl;
+    s->info.args.set_system();
+    string effective_uid = s->info.args.get(RGW_SYS_PARAM_PREFIX "uid");
+    RGWUserInfo effective_user;
+    if (!effective_uid.empty()) {
+      int ret = rgw_get_user_info_by_uid(store, effective_uid, effective_user);
+      if (ret < 0) {
+        ldout(s->cct, 0) << "User lookup failed!" << dendl;
+        return -ENOENT;
+      }
+      s->user = effective_user;
+    }
+  }
+
+  // populate the owner info
+  s->owner.set_id(s->user.user_id);
+  s->owner.set_name(s->user.display_name);
+
+  return 0;
+}
+
 /*
  * verify that a signed request comes from the keyholder
  * by checking the signature against our locally-computed version
@@ -2644,6 +3003,9 @@ int RGW_Auth_S3::authorize(RGWRados *store, struct req_state *s)
   string auth_id;
   string auth_sign;
 
+  if (!authorize_v4(store, s))
+         return 0;
+
   time_t now;
   time(&now);
 
index ada556290f771d411282b64af5ee57ef43e432e2..c264f34b09b1d6fa7928daf42b397a5425945ed7 100644 (file)
@@ -386,6 +386,8 @@ public:
 class RGW_Auth_S3 {
 public:
   static int authorize(RGWRados *store, struct req_state *s);
+private:
+  static int authorize_v4(RGWRados *store, struct req_state *s);
 };
 
 class RGWHandler_Auth_S3 : public RGWHandler_ObjStore {