Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / rgw / rgw_auth_s3.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #ifndef CEPH_RGW_AUTH_S3_H
5 #define CEPH_RGW_AUTH_S3_H
6
7 #include <array>
8 #include <memory>
9 #include <string>
10 #include <tuple>
11
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"
18 #include "rgw_common.h"
19 #include "rgw_rest_s3.h"
20
21 #include "rgw_auth.h"
22 #include "rgw_auth_filters.h"
23 #include "rgw_auth_keystone.h"
24
25
26 namespace rgw {
27 namespace auth {
28 namespace s3 {
29
30 class ExternalAuthStrategy : public rgw::auth::Strategy,
31                              public rgw::auth::RemoteApplier::Factory {
32   typedef rgw::auth::IdentityApplier::aplptr_t aplptr_t;
33   RGWRados* const store;
34
35   using keystone_config_t = rgw::keystone::CephCtxConfig;
36   using keystone_cache_t = rgw::keystone::TokenCache;
37   using EC2Engine = rgw::auth::keystone::EC2Engine;
38
39   boost::optional <EC2Engine> keystone_engine;
40   LDAPEngine ldap_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,
45                              const rgw::auth::RemoteApplier::AuthInfo info
46                             ) const override {
47     auto apl = rgw::auth::add_sysreq(cct, store, s,
48       rgw::auth::RemoteApplier(cct, store, std::move(acl_alg), info,
49                                cct->_conf->rgw_keystone_implicit_tenants));
50     /* TODO(rzarzynski): replace with static_ptr. */
51     return aplptr_t(new decltype(apl)(std::move(apl)));
52   }
53
54 public:
55   ExternalAuthStrategy(CephContext* const cct,
56                        RGWRados* const store,
57                        AWSEngine::VersionAbstractor* const ver_abstractor)
58     : store(store),
59       ldap_engine(cct, store, *ver_abstractor,
60                   static_cast<rgw::auth::RemoteApplier::Factory*>(this)) {
61
62     if (cct->_conf->rgw_s3_auth_use_keystone &&
63         ! cct->_conf->rgw_keystone_url.empty()) {
64
65       keystone_engine.emplace(cct, ver_abstractor,
66                               static_cast<rgw::auth::RemoteApplier::Factory*>(this),
67                               keystone_config_t::get_instance(),
68                               keystone_cache_t::get_instance<keystone_config_t>());
69       add_engine(Control::SUFFICIENT, *keystone_engine);
70
71     }
72
73     if (cct->_conf->rgw_s3_auth_use_ldap &&
74         ! cct->_conf->rgw_ldap_uri.empty()) {
75       add_engine(Control::SUFFICIENT, ldap_engine);
76     }
77   }
78
79   const char* get_name() const noexcept override {
80     return "rgw::auth::s3::AWSv2ExternalAuthStrategy";
81   }
82 };
83
84
85 template <class AbstractorT,
86           bool AllowAnonAccessT = false>
87 class AWSAuthStrategy : public rgw::auth::Strategy,
88                         public rgw::auth::LocalApplier::Factory {
89   typedef rgw::auth::IdentityApplier::aplptr_t aplptr_t;
90
91   static_assert(std::is_base_of<rgw::auth::s3::AWSEngine::VersionAbstractor,
92                                 AbstractorT>::value,
93                 "AbstractorT must be a subclass of rgw::auth::s3::VersionAbstractor");
94
95   RGWRados* const store;
96   AbstractorT ver_abstractor;
97
98   S3AnonymousEngine anonymous_engine;
99   ExternalAuthStrategy external_engines;
100   LocalEngine local_engine;
101
102   aplptr_t create_apl_local(CephContext* const cct,
103                             const req_state* const s,
104                             const RGWUserInfo& user_info,
105                             const std::string& subuser) const override {
106     auto apl = rgw::auth::add_sysreq(cct, store, s,
107       rgw::auth::LocalApplier(cct, user_info, subuser));
108     /* TODO(rzarzynski): replace with static_ptr. */
109     return aplptr_t(new decltype(apl)(std::move(apl)));
110   }
111
112 public:
113   AWSAuthStrategy(CephContext* const cct,
114                   RGWRados* const store)
115     : store(store),
116       ver_abstractor(cct),
117       anonymous_engine(cct,
118                        static_cast<rgw::auth::LocalApplier::Factory*>(this)),
119       external_engines(cct, store, &ver_abstractor),
120       local_engine(cct, store, ver_abstractor,
121                    static_cast<rgw::auth::LocalApplier::Factory*>(this)) {
122     /* The anynoymous auth. */
123     if (AllowAnonAccessT) {
124       add_engine(Control::SUFFICIENT, anonymous_engine);
125     }
126
127     /* The external auth. */
128     Control local_engine_mode;
129     if (! external_engines.is_empty()) {
130       add_engine(Control::SUFFICIENT, external_engines);
131
132       local_engine_mode = Control::FALLBACK;
133     } else {
134       local_engine_mode = Control::SUFFICIENT;
135     }
136
137     /* The local auth. */
138     if (cct->_conf->rgw_s3_auth_use_rados) {
139       add_engine(local_engine_mode, local_engine);
140     }
141   }
142
143   const char* get_name() const noexcept override {
144     return "rgw::auth::s3::AWSAuthStrategy";
145   }
146 };
147
148
149 class AWSv4ComplMulti : public rgw::auth::Completer,
150                         public rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>,
151                         public std::enable_shared_from_this<AWSv4ComplMulti> {
152   using io_base_t = rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>;
153   using signing_key_t = std::array<unsigned char,
154                                    CEPH_CRYPTO_HMACSHA256_DIGESTSIZE>;
155
156   CephContext* const cct;
157
158   const boost::string_view date;
159   const boost::string_view credential_scope;
160   const signing_key_t signing_key;
161
162   class ChunkMeta {
163     size_t data_offset_in_stream = 0;
164     size_t data_length = 0;
165     std::string signature;
166
167     ChunkMeta(const size_t data_starts_in_stream,
168               const size_t data_length,
169               const boost::string_ref signature)
170       : data_offset_in_stream(data_starts_in_stream),
171         data_length(data_length),
172         signature(signature.to_string()) {
173     }
174
175     ChunkMeta(const boost::string_view& signature)
176       : signature(signature.to_string()) {
177     }
178
179   public:
180     static constexpr size_t SIG_SIZE = 64;
181
182     /* Let's suppose the data length fields can't exceed uint64_t. */
183     static constexpr size_t META_MAX_SIZE = \
184       sarrlen("\r\nffffffffffffffff;chunk-signature=") + SIG_SIZE + sarrlen("\r\n");
185
186     /* The metadata size of for the last, empty chunk. */
187     static constexpr size_t META_MIN_SIZE = \
188       sarrlen("0;chunk-signature=") + SIG_SIZE + sarrlen("\r\n");
189
190     /* Detect whether a given stream_pos fits in boundaries of a chunk. */
191     bool is_new_chunk_in_stream(size_t stream_pos) const;
192
193     /* Get the remaining data size. */
194     size_t get_data_size(size_t stream_pos) const;
195
196     const std::string& get_signature() const {
197       return signature;
198     }
199
200     /* Factory: create an object representing metadata of first, initial chunk
201      * in a stream. */
202     static ChunkMeta create_first(const boost::string_view& seed_signature) {
203       return ChunkMeta(seed_signature);
204     }
205
206     /* Factory: parse a block of META_MAX_SIZE bytes and creates an object
207      * representing non-first chunk in a stream. As the process is sequential
208      * and depends on the previous chunk, caller must pass it. */
209     static std::pair<ChunkMeta, size_t> create_next(CephContext* cct,
210                                                     ChunkMeta&& prev,
211                                                     const char* metabuf,
212                                                     size_t metabuf_len);
213   } chunk_meta;
214
215   size_t stream_pos;
216   boost::container::static_vector<char, ChunkMeta::META_MAX_SIZE> parsing_buf;
217   ceph::crypto::SHA256* sha256_hash;
218   std::string prev_chunk_signature;
219
220   bool is_signature_mismatched();
221   std::string calc_chunk_signature(const std::string& payload_hash) const;
222
223 public:
224   /* We need the constructor to be public because of the std::make_shared that
225    * is employed by the create() method. */
226   AWSv4ComplMulti(const req_state* const s,
227                   boost::string_view date,
228                   boost::string_view credential_scope,
229                   boost::string_view seed_signature,
230                   const signing_key_t& signing_key)
231     : io_base_t(nullptr),
232       cct(s->cct),
233       date(std::move(date)),
234       credential_scope(std::move(credential_scope)),
235       signing_key(signing_key),
236
237       /* The evolving state. */
238       chunk_meta(ChunkMeta::create_first(seed_signature)),
239       stream_pos(0),
240       sha256_hash(calc_hash_sha256_open_stream()),
241       prev_chunk_signature(std::move(seed_signature)) {
242   }
243
244   ~AWSv4ComplMulti() {
245     if (sha256_hash) {
246       calc_hash_sha256_close_stream(&sha256_hash);
247     }
248   }
249
250   /* rgw::io::DecoratedRestfulClient. */
251   size_t recv_body(char* buf, size_t max) override;
252
253   /* rgw::auth::Completer. */
254   void modify_request_state(req_state* s_rw) override;
255   bool complete() override;
256
257   /* Factories. */
258   static cmplptr_t create(const req_state* s,
259                           boost::string_view date,
260                           boost::string_view credential_scope,
261                           boost::string_view seed_signature,
262                           const boost::optional<std::string>& secret_key);
263
264 };
265
266 class AWSv4ComplSingle : public rgw::auth::Completer,
267                          public rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>,
268                          public std::enable_shared_from_this<AWSv4ComplSingle> {
269   using io_base_t = rgw::io::DecoratedRestfulClient<rgw::io::RestfulClient*>;
270
271   CephContext* const cct;
272   const char* const expected_request_payload_hash;
273   ceph::crypto::SHA256* sha256_hash = nullptr;
274
275 public:
276   /* Defined in rgw_auth_s3.cc because of get_v4_exp_payload_hash(). We need
277    * the constructor to be public because of the std::make_shared employed by
278    * the create() method. */
279   AWSv4ComplSingle(const req_state* const s);
280
281   ~AWSv4ComplSingle() {
282     if (sha256_hash) {
283       calc_hash_sha256_close_stream(&sha256_hash);
284     }
285   }
286
287   /* rgw::io::DecoratedRestfulClient. */
288   size_t recv_body(char* buf, size_t max) override;
289
290   /* rgw::auth::Completer. */
291   void modify_request_state(req_state* s_rw) override;
292   bool complete() override;
293
294   /* Factories. */
295   static cmplptr_t create(const req_state* s,
296                           const boost::optional<std::string>&);
297
298 };
299
300 } /* namespace s3 */
301 } /* namespace auth */
302 } /* namespace rgw */
303
304 void rgw_create_s3_canonical_header(
305   const char *method,
306   const char *content_md5,
307   const char *content_type,
308   const char *date,
309   const std::map<std::string, std::string>& meta_map,
310   const char *request_uri,
311   const std::map<std::string, std::string>& sub_resources,
312   std::string& dest_str);
313 bool rgw_create_s3_canonical_header(const req_info& info,
314                                     utime_t *header_time,       /* out */
315                                     std::string& dest,          /* out */
316                                     bool qsr);
317 static inline std::tuple<bool, std::string, utime_t>
318 rgw_create_s3_canonical_header(const req_info& info, const bool qsr) {
319   std::string dest;
320   utime_t header_time;
321
322   const bool ok = rgw_create_s3_canonical_header(info, &header_time, dest, qsr);
323   return std::make_tuple(ok, dest, header_time);
324 }
325
326 namespace rgw {
327 namespace auth {
328 namespace s3 {
329
330 static constexpr char AWS4_HMAC_SHA256_STR[] = "AWS4-HMAC-SHA256";
331 static constexpr char AWS4_HMAC_SHA256_PAYLOAD_STR[] = "AWS4-HMAC-SHA256-PAYLOAD";
332
333 static constexpr char AWS4_EMPTY_PAYLOAD_HASH[] = \
334   "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
335
336 static constexpr char AWS4_UNSIGNED_PAYLOAD_HASH[] = "UNSIGNED-PAYLOAD";
337
338 static constexpr char AWS4_STREAMING_PAYLOAD_HASH[] = \
339   "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
340
341 int parse_credentials(const req_info& info,                     /* in */
342                       boost::string_view& access_key_id,        /* out */
343                       boost::string_view& credential_scope,     /* out */
344                       boost::string_view& signedheaders,        /* out */
345                       boost::string_view& signature,            /* out */
346                       boost::string_view& date,                 /* out */
347                       bool& using_qs);                          /* out */
348
349 static inline std::string get_v4_canonical_uri(const req_info& info) {
350   /* The code should normalize according to RFC 3986 but S3 does NOT do path
351    * normalization that SigV4 typically does. This code follows the same
352    * approach that boto library. See auth.py:canonical_uri(...). */
353
354   std::string canonical_uri = info.request_uri_aws4;
355
356   if (canonical_uri.empty()) {
357     canonical_uri = "/";
358   } else {
359     boost::replace_all(canonical_uri, "+", "%20");
360   }
361
362   return canonical_uri;
363 }
364
365 static inline const char* get_v4_exp_payload_hash(const req_info& info)
366 {
367   /* In AWSv4 the hash of real, transfered payload IS NOT necessary to form
368    * a Canonical Request, and thus verify a Signature. x-amz-content-sha256
369    * header lets get the information very early -- before seeing first byte
370    * of HTTP body. As a consequence, we can decouple Signature verification
371    * from payload's fingerprint check. */
372   const char *expected_request_payload_hash = \
373     info.env->get("HTTP_X_AMZ_CONTENT_SHA256");
374
375   if (!expected_request_payload_hash) {
376     /* An HTTP client MUST send x-amz-content-sha256. The single exception
377      * is the case of using the Query Parameters where "UNSIGNED-PAYLOAD"
378      * literals are used for crafting Canonical Request:
379      *
380      *  You don't include a payload hash in the Canonical Request, because
381      *  when you create a presigned URL, you don't know the payload content
382      *  because the URL is used to upload an arbitrary payload. Instead, you
383      *  use a constant string UNSIGNED-PAYLOAD. */
384     expected_request_payload_hash = AWS4_UNSIGNED_PAYLOAD_HASH;
385   }
386
387   return expected_request_payload_hash;
388 }
389
390 static inline bool is_v4_payload_unsigned(const char* const exp_payload_hash)
391 {
392   return boost::equals(exp_payload_hash, AWS4_UNSIGNED_PAYLOAD_HASH);
393 }
394
395 static inline bool is_v4_payload_empty(const req_state* const s)
396 {
397   /* from rfc2616 - 4.3 Message Body
398    *
399    * "The presence of a message-body in a request is signaled by the inclusion
400    * of a Content-Length or Transfer-Encoding header field in the request's
401    * message-headers." */
402   return s->content_length == 0 &&
403          s->info.env->get("HTTP_TRANSFER_ENCODING") == nullptr;
404 }
405
406 static inline bool is_v4_payload_streamed(const char* const exp_payload_hash)
407 {
408   return boost::equals(exp_payload_hash, AWS4_STREAMING_PAYLOAD_HASH);
409 }
410
411 std::string get_v4_canonical_qs(const req_info& info, bool using_qs);
412
413 boost::optional<std::string>
414 get_v4_canonical_headers(const req_info& info,
415                          const boost::string_view& signedheaders,
416                          bool using_qs,
417                          bool force_boto2_compat);
418
419 extern sha256_digest_t
420 get_v4_canon_req_hash(CephContext* cct,
421                       const boost::string_view& http_verb,
422                       const std::string& canonical_uri,
423                       const std::string& canonical_qs,
424                       const std::string& canonical_hdrs,
425                       const boost::string_view& signed_hdrs,
426                       const boost::string_view& request_payload_hash);
427
428 AWSEngine::VersionAbstractor::string_to_sign_t
429 get_v4_string_to_sign(CephContext* cct,
430                       const boost::string_view& algorithm,
431                       const boost::string_view& request_date,
432                       const boost::string_view& credential_scope,
433                       const sha256_digest_t& canonreq_hash);
434
435 extern AWSEngine::VersionAbstractor::server_signature_t
436 get_v4_signature(const boost::string_view& credential_scope,
437                  CephContext* const cct,
438                  const boost::string_view& secret_key,
439                  const AWSEngine::VersionAbstractor::string_to_sign_t& string_to_sign);
440
441 extern AWSEngine::VersionAbstractor::server_signature_t
442 get_v2_signature(CephContext*,
443                  const std::string& secret_key,
444                  const AWSEngine::VersionAbstractor::string_to_sign_t& string_to_sign);
445
446 } /* namespace s3 */
447 } /* namespace auth */
448 } /* namespace rgw */
449
450 #endif