]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: unify handling S3's ::authorize_v2 and ::get_policy methods.
authorRadoslaw Zarzynski <rzarzynski@mirantis.com>
Thu, 12 Jan 2017 00:25:15 +0000 (01:25 +0100)
committerRadoslaw Zarzynski <rzarzynski@mirantis.com>
Fri, 24 Mar 2017 15:55:42 +0000 (16:55 +0100)
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
src/rgw/rgw_auth_keystone.cc
src/rgw/rgw_auth_keystone.h
src/rgw/rgw_auth_s3.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_user.cc
src/rgw/rgw_user.h

index a7d3048d6ec9ff01777cbf29564b0b0c448757d1..b333be9f7e2cdf1dc1ee77a9201a60bf8a5091fd 100644 (file)
@@ -363,34 +363,6 @@ EC2Engine::get_from_keystone(const std::string& access_key_id,
   return std::move(token_envelope);
 }
 
-
-bool EC2Engine::is_time_skew_ok(const utime_t& header_time,
-                                const bool qsr) const
-{
-  /* Check for time skew first. */
-  const time_t req_sec = header_time.sec();
-  time_t now;
-  time(&now);
-
-  if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 ||
-       req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
-    ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now
-                   << "; now - RGW_AUTH_GRACE_MINS="
-                   << now - RGW_AUTH_GRACE_MINS * 60
-                   << "; now + RGW_AUTH_GRACE_MINS="
-                   << now + RGW_AUTH_GRACE_MINS * 60
-                   << dendl;
-
-    ldout(cct, 0)  << "NOTICE: request time skew too big now="
-                   << utime_t(now, 0)
-                   << " req_time=" << header_time
-                   << dendl;
-    return false;
-  } else {
-    return true;
-  }
-}
-
 EC2Engine::acl_strategy_t
 EC2Engine::get_acl_strategy(const EC2Engine::token_envelope_t&) const
 {
@@ -429,11 +401,9 @@ EC2Engine::get_creds_info(const EC2Engine::token_envelope_t& token,
   };
 }
 
-rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id,
-                                                    std::string signature,
-                                                    std::string expires,
-                                                    bool qsr,
-                                                    const req_info& info,
+rgw::auth::Engine::result_t EC2Engine::authenticate(const std::string& access_key_id,
+                                                    const std::string& signature,
+                                                    const std::string& string_to_sign,
                                                     /* Passthorugh only! */
                                                     const req_state* s) const
 {
@@ -452,16 +422,6 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id,
     std::vector<std::string> admin;
   } accepted_roles(cct);
 
-  bool is_ok;
-  std::string string_to_sign;
-  utime_t header_time;
-  std::tie(is_ok, string_to_sign, header_time) = \
-    rgw_create_s3_canonical_header(info, qsr);
-  if (! is_ok) {
-    dout(10) << "failed to create auth header\n" << string_to_sign << dendl;
-    throw -EPERM;
-  }
-
   const auto t = get_from_keystone(access_key_id, string_to_sign,
                                    signature);
   /* Verify expiration. */
@@ -472,10 +432,6 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(std::string access_key_id,
     return std::make_pair(nullptr, nullptr);
   }
 
-  if (! is_time_skew_ok(header_time, qsr)) {
-    throw -ERR_REQUEST_TIME_SKEWED;
-  }
-
   /* check if we have a valid role */
   bool found = false;
   for (const auto& role : accepted_roles.plain) {
index 3307b4865d8808c66d4f7a72e72515d040bb83e2..c3b00e47f238304991734f51cd7691abd91cadb0 100644 (file)
@@ -80,16 +80,12 @@ class EC2Engine : public rgw::auth::s3::Version2ndEngine {
   auth_info_t get_creds_info(const token_envelope_t& token,
                              const std::vector<std::string>& admin_roles
                             ) const noexcept;
-  bool is_time_skew_ok(const utime_t& header_time,
-                       const bool qsr) const;
   token_envelope_t get_from_keystone(const std::string& access_key_id,
                                      const std::string& string_to_sign,
                                      const std::string& signature) const;
-  result_t authenticate(std::string access_key_id,
-                        std::string signature,
-                        std::string expires,
-                        bool qsr,
-                        const req_info& info,
+  result_t authenticate(const std::string& access_key_id,
+                        const std::string& signature,
+                        const std::string& string_to_sign,
                         const req_state* s) const override;
 public:
   EC2Engine(CephContext* const cct,
index 99f5d015b5f515e0a49ba58d3d5851b78977df97..a50229cb3670b709f0af960678615a986b8d5816 100644 (file)
@@ -94,6 +94,7 @@ public:
   AWSv2AuthStrategy(CephContext* const cct,
                     RGWRados* const store)
     : store(store),
+      extractor(cct),
       external_engines(cct, store, &extractor),
       local_engine(cct, store, extractor,
                    static_cast<rgw::auth::LocalApplier::Factory*>(this)) {
@@ -103,6 +104,21 @@ public:
     }
   }
 
+  /* FIXME(rzarzynski): hack for S3's browsers upload. */
+  AWSv2AuthStrategy(CephContext* const cct,
+                    RGWRados* const store,
+                    Version2ndEngine::Extractor* const external_extractor)
+    : store(store),
+      extractor(cct),
+      external_engines(cct, store, external_extractor),
+      local_engine(cct, store, *external_extractor,
+                   static_cast<rgw::auth::LocalApplier::Factory*>(this)) {
+    add_engine(Control::SUFFICIENT, external_engines);
+    if (cct->_conf->rgw_s3_auth_use_rados) {
+      add_engine(Control::SUFFICIENT, local_engine);
+    }
+  }
+
   const char* get_name() const noexcept override {
     return "rgw::auth::s3::AWSv2AuthStrategy";
   }
index b86f36a0b49a67c17ab28cbe61f67bbe642c2ff6..ea4d1f34b0c2df0fb7eb57cee2623a787108bc3b 100644 (file)
@@ -1798,6 +1798,12 @@ int RGWPostObj_ObjStore_S3::get_params()
   return 0;
 }
 
+static std::string to_string(ceph::bufferlist& bl)
+{
+  return std::string(bl.c_str(),
+                     static_cast<std::string::size_type>(bl.length()));
+}
+
 int RGWPostObj_ObjStore_S3::get_policy()
 {
   bufferlist encoded_policy;
@@ -1818,11 +1824,13 @@ int RGWPostObj_ObjStore_S3::get_policy()
       return -EINVAL;
     }
 
-    rgw::auth::s3::RGWGetPolicyV2Extractor extr(s3_access_key, received_signature_str);
+    rgw::auth::s3::RGWGetPolicyV2Extractor extr(s3_access_key,
+                                                received_signature_str,
+                                                to_string(encoded_policy));
     /* FIXME: this is a makeshift solution. The browser upload authenication will be
      * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
      * method. Thus creating a strategy per request won't be necessary. */
-    const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store);
+    const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store, &extr);
     try {
       auto result = strategy.authenticate(s);
       auto& applier = result.first;
@@ -1847,7 +1855,8 @@ int RGWPostObj_ObjStore_S3::get_policy()
     }
 
     ldout(s->cct, 0) << "Successful Signature Verification!" << dendl;
-    bufferlist decoded_policy;
+
+    ceph::bufferlist decoded_policy;
     try {
       decoded_policy.decode_base64(encoded_policy);
     } catch (buffer::error& err) {
@@ -1857,9 +1866,9 @@ int RGWPostObj_ObjStore_S3::get_policy()
     }
 
     decoded_policy.append('\0'); // NULL terminate
-
     ldout(s->cct, 0) << "POST policy: " << decoded_policy.c_str() << dendl;
 
+
     int r = post_policy.from_json(decoded_policy, err_msg);
     if (r < 0) {
       if (err_msg.empty()) {
@@ -3884,30 +3893,6 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s, bool force_b
  */
 int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s)
 {
-  bool qsr = false;
-  string auth_id;
-  string auth_sign;
-
-  time_t now;
-  time(&now);
-
-  if (!s->http_auth || !(*s->http_auth)) {
-    auth_id = s->info.args.get("AWSAccessKeyId");
-    auth_sign = s->info.args.get("Signature");
-    string date = s->info.args.get("Expires");
-    time_t exp = atoll(date.c_str());
-    if (now >= exp)
-      return -EPERM;
-    qsr = true;
-  } else {
-    string auth_str(s->http_auth + 4);
-    int pos = auth_str.rfind(':');
-    if (pos < 0)
-      return -EINVAL;
-    auth_id = auth_str.substr(0, pos);
-    auth_sign = auth_str.substr(pos + 1);
-  }
-
   /* TODO(rzarzynski): this will be moved to the S3 handlers -- in exactly
    * way like we currently have in the case of Swift API. */
   static const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store);
@@ -4211,28 +4196,88 @@ namespace rgw {
 namespace auth {
 namespace s3 {
 
+bool rgw::auth::s3::RGWS3V2Extractor::is_time_skew_ok(const utime_t& header_time,
+                                                      const bool qsr) const
+{
+  /* Check for time skew first. */
+  const time_t req_sec = header_time.sec();
+  time_t now;
+  time(&now);
+
+  if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 ||
+       req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
+    ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now
+                   << "; now - RGW_AUTH_GRACE_MINS="
+                   << now - RGW_AUTH_GRACE_MINS * 60
+                   << "; now + RGW_AUTH_GRACE_MINS="
+                   << now + RGW_AUTH_GRACE_MINS * 60
+                   << dendl;
+
+    ldout(cct, 0)  << "NOTICE: request time skew too big now="
+                   << utime_t(now, 0)
+                   << " req_time=" << header_time
+                   << dendl;
+    return false;
+  } else {
+    return true;
+  }
+}
+
 std::tuple<Version2ndEngine::Extractor::access_key_id_t,
            Version2ndEngine::Extractor::signature_t,
-           Version2ndEngine::Extractor::expires_t,
-           Version2ndEngine::Extractor::qsr_t>
+           Version2ndEngine::Extractor::string_to_sign_t>
 rgw::auth::s3::RGWS3V2Extractor::get_auth_data(const req_state* const s) const
 {
+  std::string access_key_id;
+  std::string signature;
+  bool qsr = false;
+
   if (! s->http_auth || s->http_auth[0] == '\0') {
-    return std::make_tuple(s->info.args.get("AWSAccessKeyId"),
-                           s->info.args.get("Signature"),
-                           s->info.args.get("Expires"),
-                           true);
+    /* Credentials are provided in query string. We also need to verify
+     * the "Expires" parameter now. */
+    access_key_id = s->info.args.get("AWSAccessKeyId");
+    signature = s->info.args.get("Signature");
+    qsr = true;
+
+    std::string expires = s->info.args.get("Expires");
+    if (! expires.empty()) {
+      const time_t exp = atoll(expires.c_str());
+      time_t now;
+      time(&now);
+
+      if (now >= exp) {
+        throw -EPERM;
+      }
+    }
   } else {
+    /* The "Authorization" HTTP header is being used. */
     const std::string auth_str(s->http_auth + strlen("AWS "));
     const size_t pos = auth_str.rfind(':');
     if (pos != std::string::npos) {
-      return std::make_tuple(auth_str.substr(0, pos),
-                             auth_str.substr(pos + 1),
-                             std::string(), false);
+      access_key_id = auth_str.substr(0, pos);
+      signature = auth_str.substr(pos + 1);
     }
   }
 
-  return std::make_tuple("", "", "", false);
+  /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
+  std::string string_to_sign;
+  utime_t header_time;
+  if (! rgw_create_s3_canonical_header(s->info, &header_time, string_to_sign,
+        qsr)) {
+    ldout(cct, 10) << "failed to create the canonized auth header\n"
+                   << string_to_sign << dendl;
+    throw -EPERM;
+  }
+
+  ldout(cct, 10) << "string_to_sign:\n" << string_to_sign << dendl;
+
+  if (! is_time_skew_ok(header_time, qsr)) {
+    throw -ERR_REQUEST_TIME_SKEWED;
+  }
+
+  return std::make_tuple(std::move(access_key_id),
+                         std::move(signature),
+                         std::move(string_to_sign));
 }
 
 } /* namespace s3 */
@@ -4289,11 +4334,9 @@ rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noex
 }
 
 rgw::auth::Engine::result_t
-rgw::auth::s3::LDAPEngine::authenticate(const std::string access_key_id,
-                                        const std::string signature,
-                                        const std::string expires,
-                                        const bool qsr,
-                                        const req_info& /* unused */,
+rgw::auth::s3::LDAPEngine::authenticate(const std::string& access_key_id,
+                                        const std::string& signature,
+                                        const std::string& string_to_sign,
                                         const req_state* const s) const
 {
   /* boost filters and/or string_ref may throw on invalid input */
@@ -4332,11 +4375,9 @@ rgw::auth::s3::LDAPEngine::authenticate(const std::string access_key_id,
 
 /* LocalVersion2ndEngine */
 rgw::auth::Engine::result_t
-rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id,
-                                                   std::string signature,
-                                                   std::string expires,
-                                                   bool qsr,
-                                                   const req_info& info,
+rgw::auth::s3::LocalVersion2ndEngine::authenticate(const std::string& access_key_id,
+                                                   const std::string& signature,
+                                                   const std::string& string_to_sign,
                                                    const req_state* const s) const
 {
   if (access_key_id.empty() || signature.empty()) {
@@ -4344,14 +4385,6 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id,
     throw -EINVAL;
   }
 
-  time_t now;
-  time(&now);
-  if (! expires.empty()) {
-    time_t exp = atoll(expires.c_str());
-    if (now >= exp) {
-      throw -EPERM;
-    }
-  }
   /* get the user info */
   RGWUserInfo user_info;
   if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) {
@@ -4367,44 +4400,23 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(std::string access_key_id,
     }
   }*/
 
-  /* now verify  signature */
-  string auth_hdr;
-  utime_t header_time;
-  if (! rgw_create_s3_canonical_header(info, &header_time, auth_hdr,
-        qsr)) {
-    ldout(cct, 10) << "failed to create auth header\n" << auth_hdr << dendl;
-    throw -EPERM;
-  }
-  ldout(cct, 10) << "auth_hdr:\n" << auth_hdr << dendl;
 
-  time_t req_sec = header_time.sec();
-  if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 ||
-      req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
-    ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now
-             << "; now - RGW_AUTH_GRACE_MINS=" << now - RGW_AUTH_GRACE_MINS * 60
-             << "; now + RGW_AUTH_GRACE_MINS=" << now + RGW_AUTH_GRACE_MINS * 60
-             << dendl;
-    ldout(cct, 0)  << "NOTICE: request time skew too big now=" << utime_t(now, 0)
-             << " req_time=" << header_time
-             << dendl;
-    throw -ERR_REQUEST_TIME_SKEWED;
-  }
-
-  map<string, RGWAccessKey>::iterator iter = user_info.access_keys.find(access_key_id);
-  if (iter == user_info.access_keys.end()) {
+  const auto iter = user_info.access_keys.find(access_key_id);
+  if (iter == std::end(user_info.access_keys)) {
     ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl;
     throw -EPERM;
   }
-  RGWAccessKey& k = iter->second;
+  const RGWAccessKey& k = iter->second;
 
-  string digest;
-  int ret = rgw_get_s3_header_digest(auth_hdr, k.key, digest);
+  std::string digest;
+  int ret = rgw_get_s3_header_digest(string_to_sign, k.key, digest);
   if (ret < 0) {
     throw -EPERM;
   }
 
+  ldout(cct, 15) << "string_to_sign=" << string_to_sign << dendl;
   ldout(cct, 15) << "calculated digest=" << digest << dendl;
-  ldout(cct, 15) << "auth_sign=" << signature << dendl;
+  ldout(cct, 15) << "auth signature=" << signature << dendl;
   ldout(cct, 15) << "compare=" << signature.compare(digest) << dendl;
 
   if (signature != digest) {
index edc93966d39919b7c7d44a39bd18c64ad92a8bb5..7450dfe3d34ea870cae1c371179443912558caa3 100644 (file)
@@ -624,13 +624,12 @@ public:
 
     using access_key_id_t = std::string;
     using signature_t = std::string;
-    using expires_t = std::string;
-    using qsr_t = bool;
+    using string_to_sign_t = std::string;
 
     virtual std::tuple<access_key_id_t,
                        signature_t,
-                       expires_t,
-                       qsr_t> get_auth_data(const req_state* s) const = 0;
+                       string_to_sign_t>
+    get_auth_data(const req_state* s) const = 0;
   };
 
 protected:
@@ -644,38 +643,44 @@ protected:
 
   using result_t = rgw::auth::Engine::result_t;
 
-  virtual result_t authenticate(std::string access_key_id,
-                                std::string signature,
-                                std::string expires,
-                                bool qsr,
-                                const req_info& info,
+  virtual result_t authenticate(const std::string& access_key_id,
+                                const std::string& signature,
+                                const std::string& string_to_sign,
                                 const req_state* s) const = 0;
 
 public:
   result_t authenticate(const req_state* const s) const final {
     std::string access_key_id;
     std::string signature;
-    std::string expires;
-    bool qsr;
+    std::string string_to_sign;
 
-    std::tie(access_key_id, signature, expires, qsr) = \
+    /* Small reminder: an extractor is allowed to throw! */
+    std::tie(access_key_id, signature, string_to_sign) = \
       extractor.get_auth_data(s);
 
     if (access_key_id.empty() || signature.empty()) {
       return std::make_pair(nullptr, nullptr);
     } else {
-      return authenticate(std::move(access_key_id), std::move(signature),
-                          std::move(expires), qsr, s->info, s);
+      return authenticate(access_key_id, signature, string_to_sign, s);
     }
   }
 };
 
 class RGWS3V2Extractor : public Version2ndEngine::Extractor {
+  CephContext* const cct;
+
+  bool is_time_skew_ok(const utime_t& header_time,
+                       const bool qsr) const;
+
 public:
+  RGWS3V2Extractor(CephContext* const cct)
+    : cct(cct) {
+  }
+
   std::tuple<access_key_id_t,
              signature_t,
-             expires_t,
-             qsr_t> get_auth_data(const req_state* s) const override;
+             string_to_sign_t>
+  get_auth_data(const req_state* s) const override;
 };
 
 
@@ -683,18 +688,22 @@ class RGWGetPolicyV2Extractor : public Version2ndEngine::Extractor {
 private:
   std::string access_key_id;
   std::string signature;
+  std::string string_to_sign;
 
 public:
-  RGWGetPolicyV2Extractor(std::string access_key_id, std::string signature)
+  RGWGetPolicyV2Extractor(std::string access_key_id,
+                          std::string signature,
+                          std::string string_to_sign)
     : access_key_id(std::move(access_key_id)),
-      signature(std::move(signature)) {}
+      signature(std::move(signature)),
+      string_to_sign(std::move(string_to_sign)) {
+  }
 
   std::tuple<access_key_id_t,
              signature_t,
-             expires_t,
-             qsr_t> get_auth_data(const req_state* s) const override {
-                     // FIXME
-    return std::make_tuple(this->access_key_id, this->signature, "", false);
+             string_to_sign_t>
+  get_auth_data(const req_state* s) const override {
+    return std::make_tuple(access_key_id, signature, string_to_sign);
   }
 };
 
@@ -716,11 +725,9 @@ protected:
   acl_strategy_t get_acl_strategy() const;
   auth_info_t get_creds_info(const rgw::RGWToken& token) const noexcept;
 
-  result_t authenticate(std::string access_key_id,
-                        std::string signature,
-                        std::string expires,
-                        bool qsr,
-                        const req_info& info,
+  result_t authenticate(const std::string& access_key_id,
+                        const std::string& signature,
+                        const std::string& string_to_sign,
                         const req_state* s) const override;
 public:
   LDAPEngine(CephContext* const cct,
@@ -745,11 +752,9 @@ class LocalVersion2ndEngine : public Version2ndEngine {
   RGWRados* const store;
   const rgw::auth::LocalApplier::Factory* const apl_factory;
 
-  result_t authenticate(std::string access_key_id,
-                        std::string signature,
-                        std::string expires,
-                        bool qsr,
-                        const req_info& info,
+  result_t authenticate(const std::string& access_key_id,
+                        const std::string& signature,
+                        const std::string& string_to_sign,
                         const req_state* s) const override;
 public:
   LocalVersion2ndEngine(CephContext* const cct,
index 162e1b7189c19099d9d6b6ba74f59508103fb7f3..dc143d033585e679f0be5f1c4ff05ea179270bf2 100644 (file)
@@ -338,10 +338,15 @@ extern int rgw_get_user_info_by_swift(RGWRados * const store,
  * Given an access key, finds the user info associated with it.
  * returns: 0 on success, -ERR# on failure (including nonexistence)
  */
-extern int rgw_get_user_info_by_access_key(RGWRados *store, string& access_key, RGWUserInfo& info,
-                                           RGWObjVersionTracker *objv_tracker, real_time *pmtime)
-{
-  return rgw_get_user_info_from_index(store, access_key, store->get_zone_params().user_keys_pool, info, objv_tracker, pmtime);
+extern int rgw_get_user_info_by_access_key(RGWRados* store,
+                                           const std::string& access_key,
+                                           RGWUserInfo& info,
+                                           RGWObjVersionTracker* objv_tracker,
+                                           real_time *pmtime)
+{
+  return rgw_get_user_info_from_index(store, access_key,
+                                      store->get_zone_params().user_keys_pool,
+                                      info, objv_tracker, pmtime);
 }
 
 int rgw_get_user_attrs_by_uid(RGWRados *store,
index 832b66553017401fb9fe4437ad36e1c292400da4..50cc1b62909f2d08f0ea9f5feff5a145f608678e 100644 (file)
@@ -97,8 +97,11 @@ extern int rgw_get_user_info_by_swift(RGWRados *store,
  * Given an access key, finds the user info associated with it.
  * returns: 0 on success, -ERR# on failure (including nonexistence)
  */
-extern int rgw_get_user_info_by_access_key(RGWRados *store, string& access_key, RGWUserInfo& info,
-                                           RGWObjVersionTracker *objv_tracker = NULL, real_time *pmtime = NULL);
+extern int rgw_get_user_info_by_access_key(RGWRados* store,
+                                           const std::string& access_key,
+                                           RGWUserInfo& info,
+                                           RGWObjVersionTracker* objv_tracker = nullptr,
+                                           real_time* pmtime = nullptr);
 /**
  * Get all the custom metadata stored for user specified in @user_id
  * and put it into @attrs.