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
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:
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 == '='));
}
.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;
* 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);
#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"
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 {
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 */
/* 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,
#ifndef RGW_RGW_CRYPT_SANITIZE_H_
#define RGW_RGW_CRYPT_SANITIZE_H_
+#include <boost/utility/string_view.hpp>
+
#include "rgw_common.h"
namespace rgw {
* 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) {}
};
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;
}
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;
}
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;
}
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>
}
}
-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);
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>
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>
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>
}
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>
}
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>
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
{
//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;
}
}*/
}
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);
}
#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"
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
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>
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>
}
std::tuple<access_key_id_t,
- signature_t,
+ client_signature_t,
string_to_sign_t,
signature_factory_t,
completer_factory_t>
}
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>;
}
std::tuple<access_key_id_t,
- signature_t,
+ client_signature_t,
string_to_sign_t,
signature_factory_t,
completer_factory_t>
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: