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