]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: switch to Ceph's sstring in AWS signature generation process.
authorRadoslaw Zarzynski <rzarzynski@mirantis.com>
Tue, 23 May 2017 15:32:42 +0000 (17:32 +0200)
committerRadoslaw Zarzynski <rzarzynski@mirantis.com>
Wed, 7 Jun 2017 10:46:21 +0000 (12:46 +0200)
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
src/rgw/rgw_auth_keystone.cc
src/rgw/rgw_auth_keystone.h
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_auth_s3.h
src/rgw/rgw_common.h
src/rgw/rgw_crypt_sanitize.h
src/rgw/rgw_loadgen.cc
src/rgw/rgw_rest_client.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index 5ab70e945977cbb79ddd52599589a5b1b745fd8b..4a9685e92fab728eec429edf308d89caf1754a73 100644 (file)
@@ -417,7 +417,7 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(
   const boost::string_view& access_key_id,
   const boost::string_view& signature,
   const std::string& string_to_sign,
-  const signature_factory_t& signature_factory,
+  const signature_factory_t&,
   const completer_factory_t& completer_factory,
   /* Passthorugh only! */
   const req_state* s) const
index 53884ebe37f915311000b3a5ccb054ca841580aa..18905ba64c04701b3054e79c2f68b45faa1425b5 100644 (file)
@@ -94,7 +94,7 @@ class EC2Engine : public rgw::auth::s3::AWSEngine {
   result_t authenticate(const boost::string_view& access_key_id,
                         const boost::string_view& signature,
                         const std::string& string_to_sign,
-                        const signature_factory_t& signature_factory,
+                        const signature_factory_t&,
                         const completer_factory_t& completer_factory,
                         const req_state* s) const override;
 public:
index 88b1c2bd00d22ba42af71bcdb2ea36c9c604be77..0bd7f9a33acda0c8cceb1a58468e1300d7c0d3e8 100644 (file)
@@ -147,30 +147,6 @@ void rgw_create_s3_canonical_header(
   dest_str = dest;
 }
 
-int rgw_get_s3_header_digest(const std::string& auth_hdr,
-                             const std::string& key,
-                             std::string& dest)
-{
-  if (key.empty())
-    return -EINVAL;
-
-  char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
-  calc_hmac_sha1(key.c_str(), key.size(), auth_hdr.c_str(), auth_hdr.size(), hmac_sha1);
-
-  char b64[64]; /* 64 is really enough */
-  int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
-                      hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
-  if (ret < 0) {
-    dout(10) << "ceph_armor failed" << dendl;
-    return ret;
-  }
-  b64[ret] = '\0';
-
-  dest = b64;
-
-  return 0;
-}
-
 static inline bool is_base64_for_content_md5(unsigned char c) {
   return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '='));
 }
@@ -716,7 +692,7 @@ std::string get_v4_string_to_sign(CephContext* const cct,
     .append(hexed_cr_hash.data(), hexed_cr_hash.size() - 1);
 
   ldout(cct, 10) << "string to sign = "
-                 << rgw::crypt_sanitize::log_content{string_to_sign.c_str()}
+                 << rgw::crypt_sanitize::log_content{string_to_sign}
                  << dendl;
 
   return string_to_sign;
@@ -799,20 +775,53 @@ sha256_digest_t get_v4_signing_key(CephContext* const cct,
 
  * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
  */
-std::array<char, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1>
+AWSEngine::VersionAbstractor::server_signature_t
 get_v4_signature(CephContext* const cct,
                  const sha256_digest_t& signing_key,
                  const boost::string_view& string_to_sign)
 {
-  /* The server-side generated signature for comparison. */
-  const auto signature = \
-    buf_to_hex(calc_hmac_sha256(signing_key, string_to_sign));
+  /* The server-side generated digest for comparison. */
+  const auto digest = calc_hmac_sha256(signing_key, string_to_sign);
+
+  /* TODO(rzarzynski): I would love to see our sstring having reserve() and
+   * the non-const data() variant like C++17's std::string. */
+  using srv_signature_t = AWSEngine::VersionAbstractor::server_signature_t;
+  srv_signature_t signature(srv_signature_t::initialized_later(),
+                            digest.size() * 2);
+  buf_to_hex(digest.data(), digest.size(), signature.begin());
 
-  ldout(cct, 10) << "generated signature = " << signature.data() << dendl;
+  ldout(cct, 10) << "generated signature = " << signature << dendl;
 
   return signature;
 }
 
+AWSEngine::VersionAbstractor::server_signature_t
+get_v2_signature(CephContext* const cct,
+                 const std::string& secret_key,
+                 const std::string& string_to_sign)
+{
+  if (secret_key.empty()) {
+    throw -EINVAL;
+  }
+
+  const auto digest = calc_hmac_sha1(secret_key, string_to_sign);
+
+  /* 64 is really enough */;
+  char buf[64];
+  const int ret = ceph_armor(std::begin(buf),
+                             std::begin(buf) + 64,
+                             std::begin(digest),
+                             std::begin(digest) + digest.size());
+  if (ret < 0) {
+    ldout(cct, 10) << "ceph_armor failed" << dendl;
+    throw ret;
+  } else {
+    buf[ret] = '\0';
+    using srv_signature_t = AWSEngine::VersionAbstractor::server_signature_t;
+    return srv_signature_t(buf, ret);
+  }
+}
+
 bool AWSv4ComplMulti::ChunkMeta::is_new_chunk_in_stream(size_t stream_pos) const
 {
   return stream_pos >= (data_offset_in_stream + data_length);
index 2cb8c0da6a00ee1319c71221adc1c3135341ac3a..9e59d7f6afc7a389661b377e5e88a0e1798ed1d1 100644 (file)
@@ -14,6 +14,7 @@
 #include <boost/utility/string_ref.hpp>
 #include <boost/utility/string_view.hpp>
 
+#include "common/sstring.hh"
 #include "rgw_common.h"
 #include "rgw_rest_s3.h"
 
@@ -310,10 +311,6 @@ rgw_create_s3_canonical_header(const req_info& info, const bool qsr) {
   return std::make_tuple(ok, dest, header_time);
 }
 
-int rgw_get_s3_header_digest(const std::string& auth_hdr,
-                             const std::string& key,
-                             std::string& dest);
-
 namespace rgw {
 namespace auth {
 namespace s3 {
@@ -426,24 +423,15 @@ get_v4_signing_key(CephContext* const cct,
                    const boost::string_view& credential_scope,
                    const boost::string_view& access_key_secret);
 
-extern std::array<char, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE * 2 + 1>
+extern AWSEngine::VersionAbstractor::server_signature_t
 get_v4_signature(CephContext* cct,
                  const sha256_digest_t& signing_key,
                  const boost::string_view& string_to_sign);
 
-static inline
-std::string get_v2_signature(CephContext*,
-                             const std::string& secret_key,
-                             const std::string& string_to_sign) {
-  std::string signature_dest;
-  const int ret = rgw_get_s3_header_digest(string_to_sign, secret_key,
-                                           signature_dest);
-  if (ret < 0) {
-    throw ret;
-  } else {
-    return signature_dest;
-  }
-}
+extern AWSEngine::VersionAbstractor::server_signature_t
+get_v2_signature(CephContext*,
+                 const std::string& secret_key,
+                 const std::string& string_to_sign);
 
 } /* namespace s3 */
 } /* namespace auth */
index 88d49765dc3c30d2f0b15a60da027bd3f1206ae1..d041cac85ed0ff23cef44f7d0ccc02459039bbc0 100644 (file)
@@ -2223,6 +2223,17 @@ extern std::string url_encode(const std::string& src);
 /* 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);
+
+using sha1_digest_t = \
+  std::array<char, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE>;
+
+static inline sha1_digest_t
+calc_hmac_sha1(const boost::string_view& key, const boost::string_view& msg) {
+  sha1_digest_t dest;
+  calc_hmac_sha1(key.data(), key.size(), msg.data(), msg.size(), dest.data());
+  return 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,
index 644e12669baf960245002cb0c2263cdf54e1700f..628577f90c74856e97f5d87a043f646c692cfe2c 100644 (file)
@@ -4,6 +4,8 @@
 #ifndef RGW_RGW_CRYPT_SANITIZE_H_
 #define RGW_RGW_CRYPT_SANITIZE_H_
 
+#include <boost/utility/string_view.hpp>
+
 #include "rgw_common.h"
 
 namespace rgw {
@@ -54,8 +56,8 @@ struct auth {
  * Temporary container for suppressing printing if log made from civetweb may contain secret key.
  */
 struct log_content {
-  const char* buf;
-  log_content(const char* buf)
+  const boost::string_view buf;
+  log_content(const boost::string_view buf)
   : buf(buf) {}
 };
 
index 3c61cf21d79efd63efea2a0647263c60876f774b..edf95cc2ca9c1f5fbf5934995404b7f272ac1210 100644 (file)
@@ -35,14 +35,18 @@ int RGWLoadGenRequestEnv::sign(RGWAccessKey& access_key)
                                  sub_resources,
                                  canonical_header);
 
-  int ret = rgw_get_s3_header_digest(canonical_header, access_key.key, digest);
-  if (ret < 0) {
+  headers["HTTP_DATE"] = date_str;
+  try {
+    /* FIXME(rzarzynski): kill the dependency on g_ceph_context. */
+    const auto signature = static_cast<std::string>(
+      rgw::auth::s3::get_v2_signature(g_ceph_context, canonical_header,
+                                      access_key.key));
+    headers["HTTP_AUTHORIZATION"] = \
+      std::string("AWS ") + access_key.id + ":" + signature;
+  } catch (int ret) {
     return ret;
   }
 
-  headers["HTTP_DATE"] = date_str;
-  headers["HTTP_AUTHORIZATION"] = string("AWS ") + access_key.id + ":" + digest;
-
   return 0;
 }
 
index 1cccbab7d02b52ac15e597a7450700a877be2b0a..aa3de5fac8378fee5843cbd4acd67beafe53e5cc 100644 (file)
@@ -130,8 +130,9 @@ int RGWRESTSimpleRequest::execute(RGWAccessKey& key, const char *method, const c
                             canonical_header);
 
   string digest;
-  int ret = rgw_get_s3_header_digest(canonical_header, key.key, digest);
-  if (ret < 0) {
+  try {
+    digest = rgw::auth::s3::get_v2_signature(cct, canonical_header, key.key);
+  } catch (int ret) {
     return ret;
   }
 
@@ -233,8 +234,9 @@ int RGWRESTSimpleRequest::sign_request(RGWAccessKey& key, RGWEnv& env, req_info&
   ldout(cct, 10) << "generated canonical header: " << canonical_header << dendl;
 
   string digest;
-  int ret = rgw_get_s3_header_digest(canonical_header, key.key, digest);
-  if (ret < 0) {
+  try {
+    digest = rgw::auth::s3::get_v2_signature(cct, canonical_header, key.key);
+  } catch (int ret) {
     return ret;
   }
 
index 49895bee710aeecb6085f687e45096eb43da5c56..1a1dc433795922790da26e930309684d7f4879d2 100644 (file)
@@ -3545,7 +3545,7 @@ null_completer_factory(const boost::optional<std::string>& secret_key)
 using AWSVerAbstractor = AWSEngine::VersionAbstractor;
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3565,12 +3565,15 @@ AWSGeneralAbstractor::get_auth_data(const req_state* const s) const
   }
 }
 
-static inline
-std::string v4_signature(const boost::string_view& credential_scope,
+/* srv_signature_t is an alias over Ceph's basic_sstring. We're using
+ * it to keep everything within the stack boundaries instead of doing
+ * dynamic allocations. */
+static inline AWSVerAbstractor::server_signature_t
+v4_signature(const boost::string_view& credential_scope,
 
-                         CephContext* const cct,
-                         const boost::string_view& secret_key,
-                         const boost::string_view& string_to_sign)
+             CephContext* const cct,
+             const boost::string_view& secret_key,
+             const boost::string_view& string_to_sign)
 {
   auto signing_key = \
     rgw::auth::s3::get_v4_signing_key(cct, credential_scope, secret_key);
@@ -3578,12 +3581,11 @@ std::string v4_signature(const boost::string_view& credential_scope,
   auto server_signature = \
     rgw::auth::s3::get_v4_signature(cct, std::move(signing_key),
                                     string_to_sign);
-
-  return std::string(server_signature.data(), server_signature.size() - 1);
+  return server_signature;
 }
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3753,7 +3755,7 @@ AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
 
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3819,7 +3821,7 @@ AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const
 
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3833,7 +3835,7 @@ AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
 }
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3864,7 +3866,7 @@ AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
 }
 
 std::tuple<AWSVerAbstractor::access_key_id_t,
-           AWSVerAbstractor::signature_t,
+           AWSVerAbstractor::client_signature_t,
            AWSVerAbstractor::string_to_sign_t,
            AWSVerAbstractor::signature_factory_t,
            AWSVerAbstractor::completer_factory_t>
@@ -3938,7 +3940,7 @@ rgw::auth::s3::LDAPEngine::authenticate(
   const boost::string_view& access_key_id,
   const boost::string_view& signature,
   const std::string& string_to_sign,
-  const signature_factory_t& signature_factory,
+  const signature_factory_t&,
   const completer_factory_t& completer_factory,
   const req_state* const s) const
 {
@@ -3998,7 +4000,8 @@ rgw::auth::s3::LocalEngine::authenticate(
   //TODO: Uncomment, when we have a migration plan in place.
   /*else {
     if (s->user->type != TYPE_RGW) {
-      ldout(cct, 10) << "ERROR: User id of type: " << s->user->type << " is present" << dendl;
+      ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
+                     << " is present" << dendl;
       throw -EPERM;
     }
   }*/
@@ -4010,14 +4013,17 @@ rgw::auth::s3::LocalEngine::authenticate(
   }
   const RGWAccessKey& k = iter->second;
 
-  std::string digest = signature_factory(cct, k.key, string_to_sign);
+  const VersionAbstractor::server_signature_t server_signature = \
+    signature_factory(cct, k.key, string_to_sign);
 
-  ldout(cct, 15) << "string_to_sign=" << rgw::crypt_sanitize::log_content{string_to_sign.c_str()} << dendl;
-  ldout(cct, 15) << "calculated digest=" << digest << dendl;
-  ldout(cct, 15) << "auth signature=" << signature << dendl;
-  ldout(cct, 15) << "compare=" << signature.compare(digest) << dendl;
+  ldout(cct, 15) << "string_to_sign="
+                 << rgw::crypt_sanitize::log_content{string_to_sign}
+                 << dendl;
+  ldout(cct, 15) << "server signature=" << server_signature << dendl;
+  ldout(cct, 15) << "client signature=" << signature << dendl;
+  ldout(cct, 15) << "compare=" << signature.compare(server_signature) << dendl;
 
-  if (signature != digest) {
+  if (static_cast<boost::string_view>(server_signature) != signature) {
     return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
   }
 
index 0c34d5b66a193cc3a0f5df77ba667c7f5963abdf..f6fdcd7b0f78394133ddb893359931644979c9e3 100644 (file)
@@ -10,6 +10,8 @@
 
 #include <boost/utility/string_view.hpp>
 
+#include "common/backport14.h"
+#include "common/sstring.hh"
 #include "rgw_op.h"
 #include "rgw_rest.h"
 #include "rgw_http_errors.h"
@@ -659,24 +661,33 @@ namespace rgw {
 namespace auth {
 namespace s3 {
 
-
 class AWSEngine : public rgw::auth::Engine {
 public:
   class VersionAbstractor {
+    static constexpr size_t DIGEST_SIZE_V2 = CEPH_CRYPTO_HMACSHA1_DIGESTSIZE;
+    static constexpr size_t DIGEST_SIZE_V4 = CEPH_CRYPTO_HMACSHA256_DIGESTSIZE;
+
+    /* Knowing the signature max size allows us to employ the sstring, and thus
+     * avoid dynamic allocations. The multiplier comes from representing digest
+     * in the base64-encoded form. */
+    static constexpr size_t SIGNATURE_MAX_SIZE = \
+      ceph::max(DIGEST_SIZE_V2, DIGEST_SIZE_V4) * 2 + sizeof('\0');
+
   public:
     virtual ~VersionAbstractor() {};
 
     using access_key_id_t = boost::string_view;
-    using signature_t = boost::string_view;
+    using client_signature_t = boost::string_view;
+    using server_signature_t = basic_sstring<char, uint16_t, SIGNATURE_MAX_SIZE>;
     using string_to_sign_t = std::string;
 
     /* Transformation for crafting the AWS signature at server side which is
      * used later to compare with the user-provided one. The methodology for
      * doing that depends on AWS auth version. */
     using signature_factory_t = \
-      std::function<std::string(CephContext* cct,
-                                const std::string& secret_key,
-                                const std::string& string_to_sign)>;
+      std::function<server_signature_t(CephContext* cct,
+                                       const std::string& secret_key,
+                                       const std::string& string_to_sign)>;
 
     /* Return an instance of Completer for verifying the payload's fingerprint
      * if necessary. Otherwise caller gets nullptr. Caller may provide secret
@@ -686,7 +697,7 @@ public:
         const boost::optional<std::string>& secret_key)>;
 
     virtual std::tuple<access_key_id_t,
-                       signature_t,
+                       client_signature_t,
                        string_to_sign_t,
                        signature_factory_t,
                        completer_factory_t>
@@ -748,14 +759,14 @@ class AWSGeneralAbstractor : public AWSEngine::VersionAbstractor {
                        const bool qsr) const;
 
   std::tuple<access_key_id_t,
-             signature_t,
+             client_signature_t,
              string_to_sign_t,
              signature_factory_t,
              completer_factory_t>
   get_auth_data_v2(const req_state* s) const;
 
   std::tuple<access_key_id_t,
-             signature_t,
+             client_signature_t,
              string_to_sign_t,
              signature_factory_t,
              completer_factory_t>
@@ -767,7 +778,7 @@ public:
   }
 
   std::tuple<access_key_id_t,
-             signature_t,
+             client_signature_t,
              string_to_sign_t,
              signature_factory_t,
              completer_factory_t>
@@ -782,7 +793,7 @@ class AWSBrowserUploadAbstractor : public AWSEngine::VersionAbstractor {
   }
 
   using auth_data_t = std::tuple<access_key_id_t,
-                                 signature_t,
+                                 client_signature_t,
                                  string_to_sign_t,
                                  signature_factory_t,
                                  completer_factory_t>;
@@ -795,7 +806,7 @@ public:
   }
 
   std::tuple<access_key_id_t,
-             signature_t,
+             client_signature_t,
              string_to_sign_t,
              signature_factory_t,
              completer_factory_t>
@@ -823,7 +834,7 @@ protected:
   result_t authenticate(const boost::string_view& access_key_id,
                         const boost::string_view& signature,
                         const std::string& string_to_sign,
-                        const signature_factory_t& signature_factory,
+                        const signature_factory_t&,
                         const completer_factory_t& completer_factory,
                         const req_state* s) const override;
 public: