Skip to content

Commit e2023d2

Browse files
rgw: tempurl signature fixes: support sha256 and sha512
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 <[email protected]>
1 parent 86c2c9a commit e2023d2

File tree

2 files changed

+196
-38
lines changed

2 files changed

+196
-38
lines changed

src/rgw/rgw_swift_auth.cc

Lines changed: 80 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void TempURLEngine::get_owner_info(const DoutPrefixProvider* dpp, const req_stat
8282
const string& bucket_name = s->init_state.url_bucket;
8383

8484
/* TempURL requires that bucket and object names are specified. */
85-
if (bucket_name.empty() || s->object->empty()) {
85+
if (bucket_name.empty() || rgw::sal::Object::empty(s->object)) {
8686
throw -EPERM;
8787
}
8888

@@ -190,90 +190,132 @@ std::string extract_swift_subuser(const std::string& swift_user_name)
190190
}
191191
}
192192

193-
class TempURLEngine::SignatureHelper
194-
{
195-
private:
196-
static constexpr uint32_t output_size =
197-
CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2 + 1;
198-
199-
unsigned char dest[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE]; // 20
200-
char dest_str[output_size];
201-
193+
template <class HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
194+
class TempURLSignatureT : public rgw::auth::swift::FormatSignature<HASHFLAVOR,SIGNATUREFLAVOR> {
195+
using UCHARPTR = const unsigned char*;
196+
using base_t = SignatureHelperT<HASHFLAVOR>;
197+
using format_signature_t = rgw::auth::swift::FormatSignature<HASHFLAVOR,SIGNATUREFLAVOR>;
202198
public:
203-
SignatureHelper() = default;
204-
205199
const char* calc(const std::string& key,
206200
const std::string_view& method,
207201
const std::string_view& path,
208202
const std::string& expires) {
203+
HASHFLAVOR hmac((UCHARPTR) key.data(), key.size());
209204

210-
using ceph::crypto::HMACSHA1;
211-
using UCHARPTR = const unsigned char*;
212-
213-
HMACSHA1 hmac((UCHARPTR) key.c_str(), key.size());
214205
hmac.Update((UCHARPTR) method.data(), method.size());
215206
hmac.Update((UCHARPTR) "\n", 1);
216207
hmac.Update((UCHARPTR) expires.c_str(), expires.size());
217208
hmac.Update((UCHARPTR) "\n", 1);
218209
hmac.Update((UCHARPTR) path.data(), path.size());
219-
hmac.Final(dest);
210+
hmac.Final(base_t::dest);
220211

221-
buf_to_hex((UCHARPTR) dest, sizeof(dest), dest_str);
222-
223-
return dest_str;
212+
return format_signature_t::result();
224213
}
225-
226-
bool is_equal_to(const std::string& rhs) const {
227-
/* never allow out-of-range exception */
228-
if (rhs.size() < (output_size - 1)) {
229-
return false;
214+
}; /* TempURLSignatureT */
215+
class TempURLEngine::SignatureHelper {
216+
public:
217+
SignatureHelper() {};
218+
virtual ~SignatureHelper() {};
219+
virtual const char* calc(const std::string& key,
220+
const std::string_view& method,
221+
const std::string_view& path,
222+
const std::string& expires) {
223+
return nullptr;
224+
}
225+
virtual bool is_equal_to(const std::string& rhs) {
226+
return false;
227+
};
228+
static std::unique_ptr<SignatureHelper> get_sig_helper(std::string_view x);
229+
};
230+
class TempURLSignature {
231+
friend TempURLEngine;
232+
using BadSignatureHelper = TempURLEngine::SignatureHelper;
233+
template<typename HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
234+
class SignatureHelper_x : public TempURLEngine::SignatureHelper
235+
{
236+
friend TempURLEngine;
237+
TempURLSignatureT<HASHFLAVOR,SIGNATUREFLAVOR> d;
238+
public:
239+
SignatureHelper_x() {};
240+
~SignatureHelper_x() { };
241+
virtual const char* calc(const std::string& key,
242+
const std::string_view& method,
243+
const std::string_view& path,
244+
const std::string& expires) {
245+
return d.calc(key,method,path,expires);
230246
}
231-
return rhs.compare(0 /* pos */, output_size, dest_str) == 0;
247+
virtual bool is_equal_to(const std::string& rhs) {
248+
return d.is_equal_to(rhs);
249+
};
250+
};
251+
};
252+
253+
std::unique_ptr<TempURLEngine::SignatureHelper> TempURLEngine::SignatureHelper::get_sig_helper(std::string_view x) {
254+
size_t pos = x.find(':');
255+
if (pos == x.npos || pos <= 0) {
256+
switch(x.length()) {
257+
case CEPH_CRYPTO_HMACSHA1_DIGESTSIZE*2:
258+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA1,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
259+
case CEPH_CRYPTO_HMACSHA256_DIGESTSIZE*2:
260+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA256,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
261+
case CEPH_CRYPTO_HMACSHA512_DIGESTSIZE*2:
262+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA512,rgw::auth::swift::SignatureFlavor::BARE_HEX>>();
263+
}
264+
return std::make_unique<TempURLSignature::BadSignatureHelper>();
232265
}
266+
std::string_view type { x.substr(0,pos) };
267+
if (type == "sha1") {
268+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA1,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
269+
} else if (type == "sha256") {
270+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA256,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
271+
} else if (type == "sha512") {
272+
return std::make_unique<TempURLSignature::SignatureHelper_x<ceph::crypto::HMACSHA512,rgw::auth::swift::SignatureFlavor::NAMED_BASE64>>();
273+
}
274+
return std::make_unique<TempURLSignature::BadSignatureHelper>();
275+
};
233276

234-
}; /* TempURLEngine::SignatureHelper */
235-
236-
class TempURLEngine::PrefixableSignatureHelper
237-
: private TempURLEngine::SignatureHelper {
238-
using base_t = SignatureHelper;
277+
class TempURLEngine::PrefixableSignatureHelper {
239278

240279
const std::string_view decoded_uri;
241280
const std::string_view object_name;
242281
std::string_view no_obj_uri;
243282

244283
const boost::optional<const std::string&> prefix;
284+
std::unique_ptr<SignatureHelper> base_sig_helper;
245285

246286
public:
247-
PrefixableSignatureHelper(const std::string& _decoded_uri,
287+
PrefixableSignatureHelper(const std::string_view sig,
288+
const std::string& _decoded_uri,
248289
const std::string& object_name,
249290
const boost::optional<const std::string&> prefix)
250291
: decoded_uri(_decoded_uri),
251292
object_name(object_name),
252-
prefix(prefix) {
293+
prefix(prefix),
294+
base_sig_helper(TempURLEngine::SignatureHelper::get_sig_helper(sig)) {
253295
/* Transform: v1/acct/cont/obj - > v1/acct/cont/
254296
*
255297
* NOTE(rzarzynski): we really want to substr() on std::string_view,
256298
* not std::string. Otherwise we would end with no_obj_uri referencing
257299
* a temporary. */
258300
no_obj_uri = \
259301
decoded_uri.substr(0, decoded_uri.length() - object_name.length());
260-
}
302+
};
261303

262304
const char* calc(const std::string& key,
263305
const std::string_view& method,
264306
const std::string_view& path,
265307
const std::string& expires) {
266308
if (!prefix) {
267-
return base_t::calc(key, method, path, expires);
309+
return base_sig_helper->calc(key, method, path, expires);
268310
} else {
269311
const auto prefixed_path = \
270312
string_cat_reserve("prefix:", no_obj_uri, *prefix);
271-
return base_t::calc(key, method, prefixed_path, expires);
313+
return base_sig_helper->calc(key, method, prefixed_path, expires);
272314
}
273315
}
274316

275317
bool is_equal_to(const std::string& rhs) const {
276-
bool is_auth_ok = base_t::is_equal_to(rhs);
318+
bool is_auth_ok = base_sig_helper->is_equal_to(rhs);
277319

278320
if (prefix && is_auth_ok) {
279321
const auto prefix_uri = string_cat_reserve(no_obj_uri, *prefix);
@@ -360,6 +402,7 @@ TempURLEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* cons
360402

361403
/* Need to try each combination of keys, allowed path and methods. */
362404
PrefixableSignatureHelper sig_helper {
405+
temp_url_sig,
363406
s->decoded_uri,
364407
s->object->get_name(),
365408
temp_url_prefix
@@ -772,4 +815,3 @@ RGWOp *RGWHandler_SWIFT_Auth::op_get()
772815
{
773816
return new RGW_SWIFT_Auth_Get;
774817
}
775-

src/rgw/rgw_swift_auth.h

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "rgw_auth_keystone.h"
1212
#include "rgw_auth_filters.h"
1313
#include "rgw_sal.h"
14+
#include "rgw_b64.h"
1415

1516
#define RGW_SWIFT_TOKEN_EXPIRATION (15 * 60)
1617

@@ -39,6 +40,7 @@ class TempURLApplier : public rgw::auth::LocalApplier {
3940

4041
/* TempURL: engine */
4142
class TempURLEngine : public rgw::auth::Engine {
43+
friend class TempURLSignature;
4244
using result_t = rgw::auth::Engine::result_t;
4345

4446
CephContext* const cct;
@@ -303,6 +305,120 @@ class DefaultStrategy : public rgw::auth::Strategy,
303305
}
304306
};
305307

308+
// shared logic for swift tempurl and formpost signatures
309+
template <class HASHFLAVOR>
310+
inline constexpr uint32_t signature_hash_size = -1;
311+
template <>
312+
inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA1> = CEPH_CRYPTO_HMACSHA1_DIGESTSIZE;
313+
template<>
314+
inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA256> = CEPH_CRYPTO_HMACSHA256_DIGESTSIZE;
315+
template<>
316+
inline constexpr uint32_t signature_hash_size<ceph::crypto::HMACSHA512> = CEPH_CRYPTO_HMACSHA512_DIGESTSIZE;
317+
318+
const char sha1_name[] = "sha1";
319+
const char sha256_name[] = "sha256";
320+
const char sha512_name[] = "sha512";
321+
322+
template <class HASHFLAVOR>
323+
const char * signature_hash_name;
324+
template<>
325+
inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA1> = sha1_name;;
326+
template<>
327+
inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA256> = sha256_name;
328+
template<>
329+
inline constexpr const char * signature_hash_name<ceph::crypto::HMACSHA512> = sha512_name;
330+
331+
template <class HASHFLAVOR>
332+
inline const uint32_t signature_hash_name_size = -1;
333+
template<>
334+
inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA1> = sizeof sha1_name;;
335+
template<>
336+
inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA256> = sizeof sha256_name;
337+
template<>
338+
inline constexpr uint32_t signature_hash_name_size<ceph::crypto::HMACSHA512> = sizeof sha512_name;
339+
340+
template <class HASHFLAVOR>
341+
class SignatureHelperT {
342+
protected:
343+
static constexpr uint32_t hash_size = signature_hash_size<HASHFLAVOR>;
344+
static constexpr uint32_t output_size = hash_size * 2 + 1;
345+
const char * signature_name = signature_hash_name<HASHFLAVOR>;
346+
uint32_t signature_name_size = signature_hash_name_size<HASHFLAVOR>;
347+
char dest_str[output_size];
348+
uint32_t dest_size = 0;
349+
unsigned char dest[hash_size];
350+
351+
public:
352+
~SignatureHelperT() { };
353+
354+
void Update(const unsigned char *input, size_t length);
355+
356+
const char* get_signature() const {
357+
return dest_str;
358+
}
359+
360+
bool is_equal_to(const std::string& rhs) const {
361+
/* never allow out-of-range exception */
362+
if (!dest_size || rhs.size() < dest_size) {
363+
return false;
364+
}
365+
return rhs.compare(0 /* pos */, dest_size + 1, dest_str) == 0;
366+
}
367+
};
368+
369+
enum class SignatureFlavor {
370+
BARE_HEX,
371+
NAMED_BASE64
372+
};
373+
374+
template <typename HASHFLAVOR, SignatureFlavor SIGNATUREFLAVOR>
375+
class FormatSignature {
376+
};
377+
378+
// hexadecimal
379+
template <typename HASHFLAVOR>
380+
class FormatSignature<HASHFLAVOR, SignatureFlavor::BARE_HEX> : public SignatureHelperT<HASHFLAVOR> {
381+
using UCHARPTR = const unsigned char*;
382+
using base_t = SignatureHelperT<HASHFLAVOR>;
383+
public:
384+
const char *result() {
385+
buf_to_hex((UCHARPTR) base_t::dest,
386+
signature_hash_size<HASHFLAVOR>,
387+
base_t::dest_str);
388+
base_t::dest_size = strlen(base_t::dest_str);
389+
return base_t::dest_str;
390+
};
391+
};
392+
393+
// prefix:base64
394+
template <typename HASHFLAVOR>
395+
class FormatSignature<HASHFLAVOR, SignatureFlavor::NAMED_BASE64> : public SignatureHelperT<HASHFLAVOR> {
396+
using UCHARPTR = const unsigned char*;
397+
using base_t = SignatureHelperT<HASHFLAVOR>;
398+
public:
399+
char * const result() {
400+
const char *prefix = base_t::signature_name;
401+
const int prefix_size = base_t::signature_name_size;
402+
std::string_view dest_view((char*)base_t::dest, sizeof base_t::dest);
403+
auto b { rgw::to_base64(dest_view) };
404+
for (auto &v: b ) { // translate to "url safe" (rfc 4648 section 5)
405+
switch(v) {
406+
case '+': v = '-'; break;
407+
case '/': v = '_'; break;
408+
}
409+
}
410+
base_t::dest_size = prefix_size + b.length();
411+
if (base_t::dest_size < base_t::output_size) {
412+
::memcpy(base_t::dest_str, prefix, prefix_size - 1);
413+
base_t::dest_str[prefix_size-1] = ':';
414+
::strcpy(base_t::dest_str + prefix_size, b.c_str());
415+
} else {
416+
base_t::dest_size = 0;
417+
}
418+
return base_t::dest_str;
419+
};
420+
};
421+
306422
} /* namespace swift */
307423
} /* namespace auth */
308424
} /* namespace rgw */

0 commit comments

Comments
 (0)