]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_auth_s3.h
Import ceph 15.2.8
[ceph.git] / ceph / src / rgw / rgw_auth_s3.h
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#ifndef CEPH_RGW_AUTH_S3_H
5#define CEPH_RGW_AUTH_S3_H
6
31f18b77
FG
7#include <array>
8#include <memory>
7c673cae
FG
9#include <string>
10#include <tuple>
11
31f18b77
FG
12#include <boost/algorithm/string.hpp>
13#include <boost/container/static_vector.hpp>
14#include <boost/utility/string_ref.hpp>
15#include <boost/utility/string_view.hpp>
16
17#include "common/sstring.hh"
7c673cae
FG
18#include "rgw_common.h"
19#include "rgw_rest_s3.h"
7c673cae
FG
20#include "rgw_auth.h"
21#include "rgw_auth_filters.h"
22#include "rgw_auth_keystone.h"
23
24
25namespace rgw {
26namespace auth {
27namespace s3 {
28
94b18763
FG
29static constexpr auto RGW_AUTH_GRACE = std::chrono::minutes{15};
30
31// returns true if the request time is within RGW_AUTH_GRACE of the current time
32bool is_time_skew_ok(time_t t);
33
11fdf7f2
TL
34class STSAuthStrategy : public rgw::auth::Strategy,
35 public rgw::auth::RemoteApplier::Factory,
36 public rgw::auth::LocalApplier::Factory,
37 public rgw::auth::RoleApplier::Factory {
38 typedef rgw::auth::IdentityApplier::aplptr_t aplptr_t;
9f95a23c
TL
39 RGWCtl* const ctl;
40 rgw::auth::ImplicitTenants& implicit_tenant_context;
11fdf7f2
TL
41
42 STSEngine sts_engine;
43
44 aplptr_t create_apl_remote(CephContext* const cct,
45 const req_state* const s,
46 rgw::auth::RemoteApplier::acl_strategy_t&& acl_alg,
47 const rgw::auth::RemoteApplier::AuthInfo &info
48 ) const override {
9f95a23c
TL
49 auto apl = rgw::auth::add_sysreq(cct, ctl, s,
50 rgw::auth::RemoteApplier(cct, ctl, std::move(acl_alg), info,
51 implicit_tenant_context,
52 rgw::auth::ImplicitTenants::IMPLICIT_TENANTS_S3));
11fdf7f2
TL
53 return aplptr_t(new decltype(apl)(std::move(apl)));
54 }
55
56 aplptr_t create_apl_local(CephContext* const cct,
57 const req_state* const s,
58 const RGWUserInfo& user_info,
59 const std::string& subuser,
60 const boost::optional<uint32_t>& perm_mask) const override {
9f95a23c 61 auto apl = rgw::auth::add_sysreq(cct, ctl, s,
11fdf7f2
TL
62 rgw::auth::LocalApplier(cct, user_info, subuser, perm_mask));
63 return aplptr_t(new decltype(apl)(std::move(apl)));
64 }
65
66 aplptr_t create_apl_role(CephContext* const cct,
67 const req_state* const s,
f91f0fd5 68 const rgw::auth::RoleApplier::Role& role,
11fdf7f2 69 const rgw_user& user_id,
f91f0fd5
TL
70 const std::string& token_policy,
71 const std::string& role_session_name) const override {
9f95a23c 72 auto apl = rgw::auth::add_sysreq(cct, ctl, s,
f91f0fd5 73 rgw::auth::RoleApplier(cct, role, user_id, token_policy, role_session_name));
11fdf7f2
TL
74 return aplptr_t(new decltype(apl)(std::move(apl)));
75 }
76
77public:
78 STSAuthStrategy(CephContext* const cct,
9f95a23c
TL
79 RGWCtl* const ctl,
80 rgw::auth::ImplicitTenants& implicit_tenant_context,
11fdf7f2 81 AWSEngine::VersionAbstractor* const ver_abstractor)
9f95a23c
TL
82 : ctl(ctl),
83 implicit_tenant_context(implicit_tenant_context),
84 sts_engine(cct, ctl, *ver_abstractor,
11fdf7f2
TL
85 static_cast<rgw::auth::LocalApplier::Factory*>(this),
86 static_cast<rgw::auth::RemoteApplier::Factory*>(this),
87 static_cast<rgw::auth::RoleApplier::Factory*>(this)) {
88 if (cct->_conf->rgw_s3_auth_use_sts) {
89 add_engine(Control::SUFFICIENT, sts_engine);
90 }
91 }
92
93 const char* get_name() const noexcept override {
94 return "rgw::auth::s3::STSAuthStrategy";
95 }
96};
97
7c673cae
FG
98class ExternalAuthStrategy : public rgw::auth::Strategy,
99 public rgw::auth::RemoteApplier::Factory {
100 typedef rgw::auth::IdentityApplier::aplptr_t aplptr_t;
9f95a23c
TL
101 RGWCtl* const ctl;
102 rgw::auth::ImplicitTenants& implicit_tenant_context;
7c673cae
FG
103
104 using keystone_config_t = rgw::keystone::CephCtxConfig;
105 using keystone_cache_t = rgw::keystone::TokenCache;
9f95a23c 106 using secret_cache_t = rgw::auth::keystone::SecretCache;
7c673cae
FG
107 using EC2Engine = rgw::auth::keystone::EC2Engine;
108
3efd9988 109 boost::optional <EC2Engine> keystone_engine;
7c673cae
FG
110 LDAPEngine ldap_engine;
111
112 aplptr_t create_apl_remote(CephContext* const cct,
113 const req_state* const s,
114 rgw::auth::RemoteApplier::acl_strategy_t&& acl_alg,
11fdf7f2 115 const rgw::auth::RemoteApplier::AuthInfo &info
7c673cae 116 ) const override {
9f95a23c
TL
117 auto apl = rgw::auth::add_sysreq(cct, ctl, s,
118 rgw::auth::RemoteApplier(cct, ctl, std::move(acl_alg), info,
119 implicit_tenant_context,
120 rgw::auth::ImplicitTenants::IMPLICIT_TENANTS_S3));
7c673cae
FG
121 /* TODO(rzarzynski): replace with static_ptr. */
122 return aplptr_t(new decltype(apl)(std::move(apl)));
123 }
124
125public:
126 ExternalAuthStrategy(CephContext* const cct,
9f95a23c
TL
127 RGWCtl* const ctl,
128 rgw::auth::ImplicitTenants& implicit_tenant_context,
31f18b77 129 AWSEngine::VersionAbstractor* const ver_abstractor)
9f95a23c
TL
130 : ctl(ctl),
131 implicit_tenant_context(implicit_tenant_context),
132 ldap_engine(cct, ctl, *ver_abstractor,
7c673cae
FG
133 static_cast<rgw::auth::RemoteApplier::Factory*>(this)) {
134
135 if (cct->_conf->rgw_s3_auth_use_keystone &&
136 ! cct->_conf->rgw_keystone_url.empty()) {
3efd9988
FG
137
138 keystone_engine.emplace(cct, ver_abstractor,
139 static_cast<rgw::auth::RemoteApplier::Factory*>(this),
140 keystone_config_t::get_instance(),
9f95a23c
TL
141 keystone_cache_t::get_instance<keystone_config_t>(),
142 secret_cache_t::get_instance());
3efd9988
FG
143 add_engine(Control::SUFFICIENT, *keystone_engine);
144
7c673cae
FG
145 }
146
a8e16298 147 if (ldap_engine.valid()) {
7c673cae
FG
148 add_engine(Control::SUFFICIENT, ldap_engine);
149 }
150 }
151
152 const char* get_name() const noexcept override {
153 return "rgw::auth::s3::AWSv2ExternalAuthStrategy";
154 }
155};
156
157
d2e6a577
FG
158template <class AbstractorT,
159 bool AllowAnonAccessT = false>
31f18b77
FG
160class AWSAuthStrategy : public rgw::auth::Strategy,
161 public rgw::auth::LocalApplier::Factory {
7c673cae
FG
162 typedef rgw::auth::IdentityApplier::aplptr_t aplptr_t;
163
31f18b77
FG
164 static_assert(std::is_base_of<rgw::auth::s3::AWSEngine::VersionAbstractor,
165 AbstractorT>::value,
166 "AbstractorT must be a subclass of rgw::auth::s3::VersionAbstractor");
7c673cae 167
9f95a23c 168 RGWCtl* const ctl;
31f18b77 169 AbstractorT ver_abstractor;
7c673cae 170
d2e6a577 171 S3AnonymousEngine anonymous_engine;
7c673cae 172 ExternalAuthStrategy external_engines;
11fdf7f2 173 STSAuthStrategy sts_engine;
31f18b77 174 LocalEngine local_engine;
7c673cae
FG
175
176 aplptr_t create_apl_local(CephContext* const cct,
177 const req_state* const s,
178 const RGWUserInfo& user_info,
11fdf7f2
TL
179 const std::string& subuser,
180 const boost::optional<uint32_t>& perm_mask) const override {
9f95a23c 181 auto apl = rgw::auth::add_sysreq(cct, ctl, s,
11fdf7f2 182 rgw::auth::LocalApplier(cct, user_info, subuser, perm_mask));
7c673cae
FG
183 /* TODO(rzarzynski): replace with static_ptr. */
184 return aplptr_t(new decltype(apl)(std::move(apl)));
185 }
186
187public:
1adf2230
AA
188 using engine_map_t = std::map <std::string, std::reference_wrapper<const Engine>>;
189 void add_engines(const std::vector <std::string>& auth_order,
190 engine_map_t eng_map)
191 {
192 auto ctrl_flag = Control::SUFFICIENT;
193 for (const auto &eng : auth_order) {
194 // fallback to the last engine, in case of multiple engines, since ctrl
195 // flag is sufficient for others, error from earlier engine is returned
196 if (&eng == &auth_order.back() && eng_map.size() > 1) {
197 ctrl_flag = Control::FALLBACK;
198 }
11fdf7f2
TL
199 if (const auto kv = eng_map.find(eng);
200 kv != eng_map.end()) {
1adf2230
AA
201 add_engine(ctrl_flag, kv->second);
202 }
203 }
204 }
205
11fdf7f2 206 auto parse_auth_order(CephContext* const cct)
1adf2230
AA
207 {
208 std::vector <std::string> result;
209
11fdf7f2
TL
210 const std::set <std::string_view> allowed_auth = { "sts", "external", "local" };
211 std::vector <std::string> default_order = { "sts", "external", "local" };
1adf2230
AA
212 // supplied strings may contain a space, so let's bypass that
213 boost::split(result, cct->_conf->rgw_s3_auth_order,
214 boost::is_any_of(", "), boost::token_compress_on);
215
216 if (std::any_of(result.begin(), result.end(),
11fdf7f2 217 [allowed_auth](std::string_view s)
1adf2230
AA
218 { return allowed_auth.find(s) == allowed_auth.end();})){
219 return default_order;
220 }
221 return result;
222 }
223
31f18b77 224 AWSAuthStrategy(CephContext* const cct,
9f95a23c
TL
225 rgw::auth::ImplicitTenants& implicit_tenant_context,
226 RGWCtl* const ctl)
227 : ctl(ctl),
31f18b77 228 ver_abstractor(cct),
d2e6a577
FG
229 anonymous_engine(cct,
230 static_cast<rgw::auth::LocalApplier::Factory*>(this)),
9f95a23c
TL
231 external_engines(cct, ctl, implicit_tenant_context, &ver_abstractor),
232 sts_engine(cct, ctl, implicit_tenant_context, &ver_abstractor),
233 local_engine(cct, ctl, ver_abstractor,
7c673cae 234 static_cast<rgw::auth::LocalApplier::Factory*>(this)) {
11fdf7f2 235 /* The anonymous auth. */
d2e6a577
FG
236 if (AllowAnonAccessT) {
237 add_engine(Control::SUFFICIENT, anonymous_engine);
238 }
7c673cae 239
1adf2230
AA
240 auto auth_order = parse_auth_order(cct);
241 engine_map_t engine_map;
11fdf7f2
TL
242
243 /* STS Auth*/
244 if (! sts_engine.is_empty()) {
245 engine_map.insert(std::make_pair("sts", std::cref(sts_engine)));
246 }
247
d2e6a577 248 /* The external auth. */
7c673cae 249 if (! external_engines.is_empty()) {
1adf2230 250 engine_map.insert(std::make_pair("external", std::cref(external_engines)));
7c673cae 251 }
d2e6a577 252 /* The local auth. */
7c673cae 253 if (cct->_conf->rgw_s3_auth_use_rados) {
1adf2230 254 engine_map.insert(std::make_pair("local", std::cref(local_engine)));
7c673cae 255 }
11fdf7f2 256
1adf2230 257 add_engines(auth_order, engine_map);
7c673cae
FG
258 }
259
260 const char* get_name() const noexcept override {
31f18b77 261 return "rgw::auth::s3::AWSAuthStrategy";
7c673cae
FG
262 }
263};
264
31f18b77
FG
265
266class AWSv4ComplMulti : public rgw::auth::Completer,
267 public rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>,
268 public std::enable_shared_from_this<AWSv4ComplMulti> {
269 using io_base_t = rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>;
11fdf7f2 270 using signing_key_t = sha256_digest_t;
31f18b77
FG
271
272 CephContext* const cct;
273
274 const boost::string_view date;
275 const boost::string_view credential_scope;
276 const signing_key_t signing_key;
277
278 class ChunkMeta {
279 size_t data_offset_in_stream = 0;
280 size_t data_length = 0;
281 std::string signature;
282
283 ChunkMeta(const size_t data_starts_in_stream,
284 const size_t data_length,
285 const boost::string_ref signature)
286 : data_offset_in_stream(data_starts_in_stream),
287 data_length(data_length),
288 signature(signature.to_string()) {
289 }
290
11fdf7f2 291 explicit ChunkMeta(const boost::string_view& signature)
31f18b77
FG
292 : signature(signature.to_string()) {
293 }
294
295 public:
296 static constexpr size_t SIG_SIZE = 64;
297
298 /* Let's suppose the data length fields can't exceed uint64_t. */
299 static constexpr size_t META_MAX_SIZE = \
300 sarrlen("\r\nffffffffffffffff;chunk-signature=") + SIG_SIZE + sarrlen("\r\n");
301
302 /* The metadata size of for the last, empty chunk. */
303 static constexpr size_t META_MIN_SIZE = \
304 sarrlen("0;chunk-signature=") + SIG_SIZE + sarrlen("\r\n");
305
306 /* Detect whether a given stream_pos fits in boundaries of a chunk. */
307 bool is_new_chunk_in_stream(size_t stream_pos) const;
308
309 /* Get the remaining data size. */
310 size_t get_data_size(size_t stream_pos) const;
311
312 const std::string& get_signature() const {
313 return signature;
314 }
315
316 /* Factory: create an object representing metadata of first, initial chunk
317 * in a stream. */
318 static ChunkMeta create_first(const boost::string_view& seed_signature) {
319 return ChunkMeta(seed_signature);
320 }
321
322 /* Factory: parse a block of META_MAX_SIZE bytes and creates an object
323 * representing non-first chunk in a stream. As the process is sequential
324 * and depends on the previous chunk, caller must pass it. */
325 static std::pair<ChunkMeta, size_t> create_next(CephContext* cct,
326 ChunkMeta&& prev,
327 const char* metabuf,
328 size_t metabuf_len);
329 } chunk_meta;
330
331 size_t stream_pos;
332 boost::container::static_vector<char, ChunkMeta::META_MAX_SIZE> parsing_buf;
333 ceph::crypto::SHA256* sha256_hash;
334 std::string prev_chunk_signature;
335
336 bool is_signature_mismatched();
337 std::string calc_chunk_signature(const std::string& payload_hash) const;
338
339public:
340 /* We need the constructor to be public because of the std::make_shared that
341 * is employed by the create() method. */
342 AWSv4ComplMulti(const req_state* const s,
343 boost::string_view date,
344 boost::string_view credential_scope,
345 boost::string_view seed_signature,
346 const signing_key_t& signing_key)
347 : io_base_t(nullptr),
348 cct(s->cct),
349 date(std::move(date)),
350 credential_scope(std::move(credential_scope)),
351 signing_key(signing_key),
352
353 /* The evolving state. */
354 chunk_meta(ChunkMeta::create_first(seed_signature)),
355 stream_pos(0),
356 sha256_hash(calc_hash_sha256_open_stream()),
357 prev_chunk_signature(std::move(seed_signature)) {
358 }
359
360 ~AWSv4ComplMulti() {
361 if (sha256_hash) {
362 calc_hash_sha256_close_stream(&sha256_hash);
363 }
364 }
365
366 /* rgw::io::DecoratedRestfulClient. */
367 size_t recv_body(char* buf, size_t max) override;
368
369 /* rgw::auth::Completer. */
11fdf7f2 370 void modify_request_state(const DoutPrefixProvider* dpp, req_state* s_rw) override;
31f18b77
FG
371 bool complete() override;
372
373 /* Factories. */
374 static cmplptr_t create(const req_state* s,
375 boost::string_view date,
376 boost::string_view credential_scope,
377 boost::string_view seed_signature,
378 const boost::optional<std::string>& secret_key);
379
380};
381
382class AWSv4ComplSingle : public rgw::auth::Completer,
383 public rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>,
384 public std::enable_shared_from_this<AWSv4ComplSingle> {
385 using io_base_t = rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>;
386
387 CephContext* const cct;
388 const char* const expected_request_payload_hash;
389 ceph::crypto::SHA256* sha256_hash = nullptr;
390
391public:
392 /* Defined in rgw_auth_s3.cc because of get_v4_exp_payload_hash(). We need
393 * the constructor to be public because of the std::make_shared employed by
394 * the create() method. */
11fdf7f2 395 explicit AWSv4ComplSingle(const req_state* const s);
31f18b77
FG
396
397 ~AWSv4ComplSingle() {
398 if (sha256_hash) {
399 calc_hash_sha256_close_stream(&sha256_hash);
400 }
401 }
402
403 /* rgw::io::DecoratedRestfulClient. */
404 size_t recv_body(char* buf, size_t max) override;
405
406 /* rgw::auth::Completer. */
11fdf7f2 407 void modify_request_state(const DoutPrefixProvider* dpp, req_state* s_rw) override;
31f18b77
FG
408 bool complete() override;
409
410 /* Factories. */
411 static cmplptr_t create(const req_state* s,
412 const boost::optional<std::string>&);
413
414};
415
7c673cae
FG
416} /* namespace s3 */
417} /* namespace auth */
418} /* namespace rgw */
419
420void rgw_create_s3_canonical_header(
421 const char *method,
422 const char *content_md5,
423 const char *content_type,
424 const char *date,
9f95a23c
TL
425 const meta_map_t& meta_map,
426 const meta_map_t& qs_map,
7c673cae
FG
427 const char *request_uri,
428 const std::map<std::string, std::string>& sub_resources,
429 std::string& dest_str);
430bool rgw_create_s3_canonical_header(const req_info& info,
431 utime_t *header_time, /* out */
432 std::string& dest, /* out */
433 bool qsr);
434static inline std::tuple<bool, std::string, utime_t>
435rgw_create_s3_canonical_header(const req_info& info, const bool qsr) {
436 std::string dest;
437 utime_t header_time;
438
439 const bool ok = rgw_create_s3_canonical_header(info, &header_time, dest, qsr);
440 return std::make_tuple(ok, dest, header_time);
441}
442
31f18b77
FG
443namespace rgw {
444namespace auth {
445namespace s3 {
446
447static constexpr char AWS4_HMAC_SHA256_STR[] = "AWS4-HMAC-SHA256";
224ce89b 448static constexpr char AWS4_HMAC_SHA256_PAYLOAD_STR[] = "AWS4-HMAC-SHA256-PAYLOAD";
31f18b77
FG
449
450static constexpr char AWS4_EMPTY_PAYLOAD_HASH[] = \
451 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
452
453static constexpr char AWS4_UNSIGNED_PAYLOAD_HASH[] = "UNSIGNED-PAYLOAD";
454
455static constexpr char AWS4_STREAMING_PAYLOAD_HASH[] = \
456 "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
457
a8e16298
TL
458int parse_v4_credentials(const req_info& info, /* in */
459 boost::string_view& access_key_id, /* out */
460 boost::string_view& credential_scope, /* out */
461 boost::string_view& signedheaders, /* out */
462 boost::string_view& signature, /* out */
463 boost::string_view& date, /* out */
11fdf7f2
TL
464 boost::string_view& session_token, /* out */
465 const bool using_qs); /* in */
466
467static inline bool char_needs_aws4_escaping(const char c, bool encode_slash)
468{
469 if ((c >= 'a' && c <= 'z') ||
470 (c >= 'A' && c <= 'Z') ||
471 (c >= '0' && c <= '9')) {
472 return false;
473 }
474
475 switch (c) {
476 case '-':
477 case '_':
478 case '.':
479 case '~':
480 return false;
481 }
482
483 if (c == '/' && !encode_slash)
484 return false;
485
486 return true;
487}
488
489static inline std::string aws4_uri_encode(const std::string& src, bool encode_slash)
490{
491 std::string result;
492
493 for (const std::string::value_type c : src) {
494 if (char_needs_aws4_escaping(c, encode_slash)) {
495 rgw_uri_escape_char(c, result);
496 } else {
497 result.push_back(c);
498 }
499 }
500
501 return result;
502}
503
504static inline std::string aws4_uri_recode(const boost::string_view& src, bool encode_slash)
505{
506 std::string decoded = url_decode(src);
507 return aws4_uri_encode(decoded, encode_slash);
508}
31f18b77
FG
509
510static inline std::string get_v4_canonical_uri(const req_info& info) {
511 /* The code should normalize according to RFC 3986 but S3 does NOT do path
512 * normalization that SigV4 typically does. This code follows the same
513 * approach that boto library. See auth.py:canonical_uri(...). */
514
11fdf7f2 515 std::string canonical_uri = aws4_uri_recode(info.request_uri_aws4, false);
31f18b77
FG
516
517 if (canonical_uri.empty()) {
518 canonical_uri = "/";
519 } else {
520 boost::replace_all(canonical_uri, "+", "%20");
521 }
522
523 return canonical_uri;
524}
525
11fdf7f2
TL
526static inline const string calc_v4_payload_hash(const string& payload)
527{
528 ceph::crypto::SHA256* sha256_hash = calc_hash_sha256_open_stream();
529 calc_hash_sha256_update_stream(sha256_hash, payload.c_str(), payload.length());
530 const auto payload_hash = calc_hash_sha256_close_stream(&sha256_hash);
531 return payload_hash;
532}
533
31f18b77
FG
534static inline const char* get_v4_exp_payload_hash(const req_info& info)
535{
11fdf7f2 536 /* In AWSv4 the hash of real, transferred payload IS NOT necessary to form
31f18b77
FG
537 * a Canonical Request, and thus verify a Signature. x-amz-content-sha256
538 * header lets get the information very early -- before seeing first byte
539 * of HTTP body. As a consequence, we can decouple Signature verification
540 * from payload's fingerprint check. */
541 const char *expected_request_payload_hash = \
542 info.env->get("HTTP_X_AMZ_CONTENT_SHA256");
543
544 if (!expected_request_payload_hash) {
545 /* An HTTP client MUST send x-amz-content-sha256. The single exception
546 * is the case of using the Query Parameters where "UNSIGNED-PAYLOAD"
547 * literals are used for crafting Canonical Request:
548 *
549 * You don't include a payload hash in the Canonical Request, because
550 * when you create a presigned URL, you don't know the payload content
551 * because the URL is used to upload an arbitrary payload. Instead, you
552 * use a constant string UNSIGNED-PAYLOAD. */
553 expected_request_payload_hash = AWS4_UNSIGNED_PAYLOAD_HASH;
554 }
555
556 return expected_request_payload_hash;
557}
558
559static inline bool is_v4_payload_unsigned(const char* const exp_payload_hash)
560{
561 return boost::equals(exp_payload_hash, AWS4_UNSIGNED_PAYLOAD_HASH);
562}
563
564static inline bool is_v4_payload_empty(const req_state* const s)
565{
566 /* from rfc2616 - 4.3 Message Body
567 *
568 * "The presence of a message-body in a request is signaled by the inclusion
569 * of a Content-Length or Transfer-Encoding header field in the request's
570 * message-headers." */
571 return s->content_length == 0 &&
572 s->info.env->get("HTTP_TRANSFER_ENCODING") == nullptr;
573}
574
575static inline bool is_v4_payload_streamed(const char* const exp_payload_hash)
576{
577 return boost::equals(exp_payload_hash, AWS4_STREAMING_PAYLOAD_HASH);
578}
579
580std::string get_v4_canonical_qs(const req_info& info, bool using_qs);
581
582boost::optional<std::string>
583get_v4_canonical_headers(const req_info& info,
584 const boost::string_view& signedheaders,
585 bool using_qs,
586 bool force_boto2_compat);
587
588extern sha256_digest_t
589get_v4_canon_req_hash(CephContext* cct,
590 const boost::string_view& http_verb,
591 const std::string& canonical_uri,
592 const std::string& canonical_qs,
593 const std::string& canonical_hdrs,
594 const boost::string_view& signed_hdrs,
595 const boost::string_view& request_payload_hash);
596
597AWSEngine::VersionAbstractor::string_to_sign_t
598get_v4_string_to_sign(CephContext* cct,
599 const boost::string_view& algorithm,
600 const boost::string_view& request_date,
601 const boost::string_view& credential_scope,
602 const sha256_digest_t& canonreq_hash);
603
604extern AWSEngine::VersionAbstractor::server_signature_t
605get_v4_signature(const boost::string_view& credential_scope,
606 CephContext* const cct,
607 const boost::string_view& secret_key,
608 const AWSEngine::VersionAbstractor::string_to_sign_t& string_to_sign);
609
610extern AWSEngine::VersionAbstractor::server_signature_t
611get_v2_signature(CephContext*,
612 const std::string& secret_key,
613 const AWSEngine::VersionAbstractor::string_to_sign_t& string_to_sign);
31f18b77
FG
614} /* namespace s3 */
615} /* namespace auth */
616} /* namespace rgw */
7c673cae
FG
617
618#endif