]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: tempurl signature fixes: support sha256 and sha512
authorMarcus Watts <mwatts@redhat.com>
Mon, 22 Aug 2022 07:38:18 +0000 (03:38 -0400)
committerKonstantin Shalygin <k0ste@k0ste.ru>
Tue, 20 Aug 2024 12:55:17 +0000 (19:55 +0700)
The latest openstack swift logic supports sha256 and sha512 checksums
for tempurl and formpost, which can be either hexadecimal or
hashname:base64.  This adds logic to inspect the incoming signature
for type and to internally construct the same hash to compare.

Also:: fixes an incidental crash if a malformed swift path does not
contain an object name.

Fixes: https://tracker.ceph.com/issues/56564
Signed-off-by: Marcus Watts <mwatts@redhat.com>
(cherry picked from commit e2023d28dc6e6e835303716e7235df720d33a01c)

src/rgw/rgw_swift_auth.cc
src/rgw/rgw_swift_auth.h

index 05d4b28c124ee28e18bd95c2032a2cb12622b4a9..24bbaff6dbac443593003f4fb0145f5b443ae8e4 100644 (file)
@@ -82,7 +82,7 @@ void TempURLEngine::get_owner_info(const DoutPrefixProvider* dpp, const req_stat
   const string& bucket_name = s->init_state.url_bucket;
 
   /* TempURL requires that bucket and object names are specified. */
-  if (bucket_name.empty() || s->object->empty()) {
+  if (bucket_name.empty() || rgw::sal::Object::empty(s->object)) {
     throw -EPERM;
   }
 
@@ -190,66 +190,108 @@ std::string extract_swift_subuser(const std::string& swift_user_name)
   }
 }
 
-class TempURLEngine::SignatureHelper
-{
-private:
-  static constexpr uint32_t output_size =
-    CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2 + 1;
-
-  unsigned char dest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE]; // 20
-  char dest_str[output_size];
-
+template <class HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
+class TempURLSignatureT : public rgw::auth::swift::FormatSignature<HASHFLAVOR,SIGNATUREFLAVOR> {
+  using UCHARPTR = const unsigned char*;
+  using base_t = SignatureHelperT<HASHFLAVOR>;
+  using format_signature_t = rgw::auth::swift::FormatSignature<HASHFLAVOR,SIGNATUREFLAVOR>;
 public:
-  SignatureHelper() = default;
-
   const char* calc(const std::string& key,
                    const std::string_view& method,
                    const std::string_view& path,
                    const std::string& expires) {
+    HASHFLAVOR hmac((UCHARPTR) key.data(), key.size());
 
-    using ceph::crypto::HMACSHA1;
-    using UCHARPTR = const unsigned char*;
-
-    HMACSHA1 hmac((UCHARPTR) key.c_str(), key.size());
     hmac.Update((UCHARPTR) method.data(), method.size());
     hmac.Update((UCHARPTR) "\n", 1);
     hmac.Update((UCHARPTR) expires.c_str(), expires.size());
     hmac.Update((UCHARPTR) "\n", 1);
     hmac.Update((UCHARPTR) path.data(), path.size());
-    hmac.Final(dest);
+    hmac.Final(base_t::dest);
 
-    buf_to_hex((UCHARPTR) dest, sizeof(dest), dest_str);
-
-    return dest_str;
+    return  format_signature_t::result();
   }
-
-  bool is_equal_to(const std::string& rhs) const {
-    /* never allow out-of-range exception */
-    if (rhs.size() < (output_size - 1)) {
-      return false;
+}; /* TempURLSignatureT */
+class TempURLEngine::SignatureHelper {
+public:
+  SignatureHelper() {};
+  virtual ~SignatureHelper() {};
+  virtual const char* calc(const std::string& key,
+    const std::string_view& method,
+    const std::string_view& path,
+    const std::string& expires) {
+    return nullptr;
+  }
+  virtual bool is_equal_to(const std::string& rhs) {
+    return false;
+  };
+  static std::unique_ptr<SignatureHelper> get_sig_helper(std::string_view x);
+};
+class TempURLSignature {
+  friend TempURLEngine;
+  using BadSignatureHelper = TempURLEngine::SignatureHelper;
+  template<typename HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
+  class SignatureHelper_x : public TempURLEngine::SignatureHelper
+  {
+    friend TempURLEngine;
+    TempURLSignatureT<HASHFLAVOR,SIGNATUREFLAVOR> d;
+  public:
+    SignatureHelper_x() {};
+    ~SignatureHelper_x() { };
+    virtual const char* calc(const std::string& key,
+      const std::string_view& method,
+      const std::string_view& path,
+      const std::string& expires) {
+      return d.calc(key,method,path,expires);
     }
-    return rhs.compare(0 /* pos */,  output_size, dest_str) == 0;
+    virtual bool is_equal_to(const std::string& rhs) {
+      return d.is_equal_to(rhs);
+    };
+  };
+};
+
+std::unique_ptr<TempURLEngine::SignatureHelper> TempURLEngine::SignatureHelper::get_sig_helper(std::string_view x) {
+  size_t pos = x.find(':');
+  if (pos == x.npos || pos <= 0) {
+    switch(x.length()) {
+    case CEPH_CRYPTO_HMACSHA1_DIGESTSIZE*2:
+      return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA1,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
+    case CEPH_CRYPTO_HMACSHA256_DIGESTSIZE*2:
+      return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA256,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
+    case CEPH_CRYPTO_HMACSHA512_DIGESTSIZE*2:
+      return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA512,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
+    }
+    return std::make_unique<TempURLSignature::BadSignatureHelper>();
   }
+  std::string_view type { x.substr(0,pos) };
+  if (type == "sha1") {
+    return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA1,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
+  } else if (type == "sha256") {
+    return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA256,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
+  } else if (type == "sha512") {
+    return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA512,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
+  }
+  return std::make_unique<TempURLSignature::BadSignatureHelper>();
+};
 
-}; /* TempURLEngine::SignatureHelper */
-
-class TempURLEngine::PrefixableSignatureHelper
-    : private TempURLEngine::SignatureHelper {
-  using base_t = SignatureHelper;
+class TempURLEngine::PrefixableSignatureHelper {
 
   const std::string_view decoded_uri;
   const std::string_view object_name;
   std::string_view no_obj_uri;
 
   const boost::optional<const std::string&> prefix;
+  std::unique_ptr<SignatureHelper> base_sig_helper;
 
 public:
-  PrefixableSignatureHelper(const std::string& _decoded_uri,
+  PrefixableSignatureHelper(const std::string_view sig,
+                           const std::string& _decoded_uri,
                            const std::string& object_name,
                             const boost::optional<const std::string&> prefix)
     : decoded_uri(_decoded_uri),
       object_name(object_name),
-      prefix(prefix) {
+      prefix(prefix),
+      base_sig_helper(TempURLEngine::SignatureHelper::get_sig_helper(sig)) {
     /* Transform: v1/acct/cont/obj - > v1/acct/cont/
      *
      * NOTE(rzarzynski): we really want to substr() on std::string_view,
@@ -257,23 +299,23 @@ public:
      * a temporary. */
     no_obj_uri = \
       decoded_uri.substr(0, decoded_uri.length() - object_name.length());
-  }
+  };
 
   const char* calc(const std::string& key,
                    const std::string_view& method,
                    const std::string_view& path,
                    const std::string& expires) {
     if (!prefix) {
-      return base_t::calc(key, method, path, expires);
+      return base_sig_helper->calc(key, method, path, expires);
     } else {
       const auto prefixed_path = \
         string_cat_reserve("prefix:", no_obj_uri, *prefix);
-      return base_t::calc(key, method, prefixed_path, expires);
+      return base_sig_helper->calc(key, method, prefixed_path, expires);
     }
   }
 
   bool is_equal_to(const std::string& rhs) const {
-    bool is_auth_ok = base_t::is_equal_to(rhs);
+    bool is_auth_ok = base_sig_helper->is_equal_to(rhs);
 
     if (prefix && is_auth_ok) {
       const auto prefix_uri = string_cat_reserve(no_obj_uri, *prefix);
@@ -360,6 +402,7 @@ TempURLEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* cons
 
   /* Need to try each combination of keys, allowed path and methods. */
   PrefixableSignatureHelper sig_helper {
+    temp_url_sig,
     s->decoded_uri,
     s->object->get_name(),
     temp_url_prefix
@@ -772,4 +815,3 @@ RGWOp *RGWHandler_SWIFT_Auth::op_get()
 {
   return new RGW_SWIFT_Auth_Get;
 }
-
index 85a103dbfd5aa4e6db7410c0bd4b5e4f01280d31..3564a6b39b5c6d209cc7cb2ce2c67527c293d557 100644 (file)
@@ -11,6 +11,7 @@
 #include "rgw_auth_keystone.h"
 #include "rgw_auth_filters.h"
 #include "rgw_sal.h"
+#include "rgw_b64.h"
 
 #define RGW_SWIFT_TOKEN_EXPIRATION (15 * 60)
 
@@ -39,6 +40,7 @@ public:
 
 /* TempURL: engine */
 class TempURLEngine : public rgw::auth::Engine {
+  friend class TempURLSignature;
   using result_t = rgw::auth::Engine::result_t;
 
   CephContext* const cct;
@@ -303,6 +305,120 @@ public:
   }
 };
 
+// shared logic for swift tempurl and formpost signatures
+template <class HASHFLAVOR>
+inline constexpr uint32_t signature_hash_size = -1;
+template <>
+inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA1> = CEPH_CRYPTO_HMACSHA1_DIGESTSIZE;
+template<>
+inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA256> = CEPH_CRYPTO_HMACSHA256_DIGESTSIZE;
+template<>
+inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA512> = CEPH_CRYPTO_HMACSHA512_DIGESTSIZE;
+
+const char sha1_name[] = "sha1";
+const char sha256_name[] = "sha256";
+const char sha512_name[] = "sha512";
+
+template <class HASHFLAVOR>
+const char * signature_hash_name;
+template<>
+inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA1> = sha1_name;;
+template<>
+inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA256> = sha256_name;
+template<>
+inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA512> = sha512_name;
+
+template <class HASHFLAVOR>
+inline const uint32_t signature_hash_name_size = -1;
+template<>
+inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA1> = sizeof sha1_name;;
+template<>
+inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA256> = sizeof sha256_name;
+template<>
+inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA512> = sizeof sha512_name;
+
+template <class HASHFLAVOR>
+class SignatureHelperT {
+protected:
+  static constexpr uint32_t hash_size = signature_hash_size<HASHFLAVOR>;
+  static constexpr uint32_t output_size = hash_size * 2 + 1;
+  const char * signature_name = signature_hash_name<HASHFLAVOR>;
+  uint32_t signature_name_size = signature_hash_name_size<HASHFLAVOR>;
+  char dest_str[output_size];
+  uint32_t dest_size = 0;
+  unsigned char dest[hash_size];
+
+public:
+  ~SignatureHelperT() { };
+
+  void Update(const unsigned char *input, size_t length);
+
+  const char* get_signature() const {
+    return dest_str;
+  }
+
+  bool is_equal_to(const std::string& rhs) const {
+    /* never allow out-of-range exception */
+    if (!dest_size || rhs.size() < dest_size) {
+      return false;
+    }
+    return rhs.compare(0 /* pos */,  dest_size + 1, dest_str) == 0;
+  }
+};
+
+enum class SignatureFlavor {
+  BARE_HEX,
+  NAMED_BASE64
+};
+
+template <typename HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
+class FormatSignature {
+};
+
+// hexadecimal
+template <typename HASHFLAVOR>
+class FormatSignature<HASHFLAVOR, SignatureFlavor::BARE_HEX> : public SignatureHelperT<HASHFLAVOR> {
+  using UCHARPTR = const unsigned char*;
+  using base_t = SignatureHelperT<HASHFLAVOR>;
+public:
+  const char *result() {
+    buf_to_hex((UCHARPTR) base_t::dest,
+      signature_hash_size<HASHFLAVOR>,
+      base_t::dest_str);
+    base_t::dest_size = strlen(base_t::dest_str);
+    return base_t::dest_str;
+  };
+};
+
+// prefix:base64
+template <typename HASHFLAVOR>
+class FormatSignature<HASHFLAVOR, SignatureFlavor::NAMED_BASE64> : public SignatureHelperT<HASHFLAVOR> {
+  using UCHARPTR = const unsigned char*;
+  using base_t = SignatureHelperT<HASHFLAVOR>;
+public:
+  char * const result() {
+    const char *prefix = base_t::signature_name;
+    const int prefix_size = base_t::signature_name_size;
+    std::string_view dest_view((char*)base_t::dest, sizeof base_t::dest);
+    auto b { rgw::to_base64(dest_view) };
+    for (auto &v: b ) {        // translate to "url safe" (rfc 4648 section 5)
+      switch(v) {
+      case '+': v = '-'; break;
+      case '/': v = '_'; break;
+      }
+    }
+    base_t::dest_size = prefix_size + b.length();
+    if (base_t::dest_size < base_t::output_size) {
+      ::memcpy(base_t::dest_str, prefix, prefix_size - 1);
+      base_t::dest_str[prefix_size-1] = ':';
+      ::strcpy(base_t::dest_str + prefix_size, b.c_str());
+    } else {
+      base_t::dest_size = 0;
+    }
+    return base_t::dest_str;
+  };
+};
+
 } /* namespace swift */
 } /* namespace auth */
 } /* namespace rgw */