]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: dissect basic AWSv4's credentials parsing into separated function.
authorRadoslaw Zarzynski <rzarzynski@mirantis.com>
Fri, 14 Apr 2017 14:08:07 +0000 (16:08 +0200)
committerRadoslaw Zarzynski <rzarzynski@mirantis.com>
Wed, 7 Jun 2017 10:43:15 +0000 (12:43 +0200)
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_auth_s3.h
src/rgw/rgw_common.h
src/rgw/rgw_rest_s3.cc

index c4c1abbb3a536224ed25e0866aabae908c6352f1..5097ed0b73f8b47d7cae83e20b539b7fcd708821 100644 (file)
@@ -10,6 +10,9 @@
 #include "rgw_client_io.h"
 #include "rgw_rest.h"
 #include "rgw_crypt_sanitize.h"
+
+#include "include/str_list.h"
+
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_rgw
 
@@ -239,6 +242,162 @@ namespace rgw {
 namespace auth {
 namespace s3 {
 
+/* FIXME(rzarzynski): duplicated from rgw_rest_s3.h. */
+#define RGW_AUTH_GRACE_MINS 15
+
+int parse_credentials(const req_info& info,             /* in */
+                      std::string& credential,          /* out */
+                      std::string& signedheaders,       /* out */
+                      std::string& signature,           /* out */
+                      std::string& date,                /* out */
+                      bool& using_qs)                   /* out */
+{
+  /* used for pre-signatured url, We shouldn't return -ERR_REQUEST_TIME_SKEWED when 
+     current time <= X-Amz-Expires */
+  bool qsr = false;
+
+  uint64_t now_req = 0;
+  uint64_t now = ceph_clock_now();
+
+  const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
+  if ((!http_auth) || !(*http_auth)) {
+
+    /* auth ships with req params ... */
+
+    /* look for required params */
+
+    using_qs = true;
+    credential = info.args.get("X-Amz-Credential");
+    if (credential.size() == 0) {
+      return -EPERM;
+    }
+
+    date = info.args.get("X-Amz-Date");
+    struct tm date_t;
+    if (!parse_iso8601(date.c_str(), &date_t, NULL, false))
+      return -EPERM;
+
+    std::string expires = info.args.get("X-Amz-Expires");
+    if (!expires.empty()) {
+      /* X-Amz-Expires provides the time period, in seconds, for which
+         the generated presigned URL is valid. The minimum value
+         you can set is 1, and the maximum is 604800 (seven days) */
+      time_t exp = atoll(expires.c_str());
+      if ((exp < 1) || (exp > 7*24*60*60)) {
+        dout(10) << "NOTICE: exp out of range, exp = " << exp << dendl;
+        return -EPERM;
+      }
+      /* handle expiration in epoch time */
+      now_req = (uint64_t)internal_timegm(&date_t);
+      if (now >= now_req + exp) {
+        dout(10) << "NOTICE: now = " << now << ", now_req = " << now_req << ", exp = " << exp << dendl;
+        return -EPERM;
+      }
+      qsr = true;
+    }
+
+    if ((now_req < now - RGW_AUTH_GRACE_MINS * 60 ||
+       now_req > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
+      dout(10) << "NOTICE: request time skew too big." << dendl;
+      dout(10) << "now_req = " << now_req << " 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;
+      return -ERR_REQUEST_TIME_SKEWED;
+    }
+
+    signedheaders = info.args.get("X-Amz-SignedHeaders");
+    if (signedheaders.size() == 0) {
+      return -EPERM;
+    }
+
+    signature = info.args.get("X-Amz-Signature");
+    if (signature.size() == 0) {
+      return -EPERM;
+    }
+
+  } else {
+
+    /* auth ships in headers ... */
+
+    /* ------------------------- handle Credential header */
+
+    using_qs = false;
+
+    string auth_str = http_auth;
+
+#define AWS4_HMAC_SHA256_STR "AWS4-HMAC-SHA256"
+#define CREDENTIALS_PREFIX_LEN (sizeof(AWS4_HMAC_SHA256_STR) - 1)
+    uint64_t min_len = CREDENTIALS_PREFIX_LEN + 1;
+    if (auth_str.length() < min_len) {
+      dout(10) << "credentials string is too short" << dendl;
+      return -EINVAL;
+    }
+
+    list<string> auth_list;
+    get_str_list(auth_str.substr(min_len), ",", auth_list);
+
+    map<string, string> kv;
+
+    for (string& s : auth_list) {
+      string key, val;
+      int ret = parse_key_value(s, key, val);
+      if (ret < 0) {
+        dout(10) << "NOTICE: failed to parse auth header (s=" << s << ")" << dendl;
+        return -EINVAL;
+      }
+      kv[key] = std::move(val);
+    }
+
+    static std::array<string, 3> aws4_presigned_required_keys = {
+      "Credential",
+      "SignedHeaders",
+      "Signature"
+    };
+
+    for (string& k : aws4_presigned_required_keys) {
+      if (kv.find(k) == kv.end()) {
+        dout(10) << "NOTICE: auth header missing key: " << k << dendl;
+        return -EINVAL;
+      }
+    }
+
+    credential = std::move(kv["Credential"]);
+    signedheaders = std::move(kv["SignedHeaders"]);
+    signature = std::move(kv["Signature"]);
+
+    /* sig hex str */
+    dout(10) << "v4 signature format = " << signature << dendl;
+
+    /* ------------------------- handle x-amz-date header */
+
+    /* grab date */
+
+    const char *d = info.env->get("HTTP_X_AMZ_DATE");
+    struct tm t;
+    if (!parse_iso8601(d, &t, NULL, false)) {
+      dout(10) << "error reading date via http_x_amz_date" << dendl;
+      return -EACCES;
+    }
+    date = d;
+  }
+
+  /* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */
+  dout(10) << "v4 credential format = " << credential << dendl;
+
+  if (std::count(credential.begin(), credential.end(), '/') != 4) {
+    return -EINVAL;
+  }
+
+  /* credential must end with 'aws4_request' */
+  if (credential.find("aws4_request") == std::string::npos) {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
 static inline bool char_needs_aws4_escaping(const char c)
 {
   if ((c >= 'a' && c <= 'z') ||
index 4696dfd2e7e6c6517b7ac221b32a3892fad9c281..682f2df0d619153f35011f86d1e79fd448dc2173 100644 (file)
@@ -160,6 +160,13 @@ namespace auth {
 namespace s3 {
 
 
+int parse_credentials(const req_info& info,             /* in */
+                      std::string& credential,          /* out */
+                      std::string& signedheaders,       /* out */
+                      std::string& signature,           /* out */
+                      std::string& date,                /* out */
+                      bool& using_qs);                  /* out */
+
 static inline std::string get_v4_canonical_uri(const req_info& info) {
   /* The code should normalize according to RFC 3986 but S3 does NOT do path
    * normalization that SigV4 typically does. This code follows the same
index 36afd3dbfc7ec19212a28175725bd4b866e04ef8..e54f4b2d0c3ac0f238fe4700f044812c507d1d75 100644 (file)
@@ -1694,7 +1694,6 @@ inline ostream& operator<<(ostream& out, const rgw_obj_index_key &o) {
 
 struct rgw_aws4_auth {
   string date;
-  string expires;
   string credential;
   string signedheaders;
   string signed_hdrs;
index 123954b7139863474e9ccaca596ff54e1eb34151..a937fafc6c6701ff823b594564c6c1311972c351 100644 (file)
@@ -3534,8 +3534,6 @@ static inline bool is_base64_for_content_md5(unsigned char c) {
   return (isalnum(c) || isspace(c) || (c == '+') || (c == '/') || (c == '='));
 }
 
-static std::array<string, 3> aws4_presigned_required_keys = { "Credential", "SignedHeaders", "Signature" };
-
 /*
  * handle v4 signatures (rados auth only)
  */
@@ -3543,12 +3541,6 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s, bool force_b
 {
   string::size_type pos;
   bool using_qs;
-  /* used for pre-signatured url, We shouldn't return -ERR_REQUEST_TIME_SKEWED when 
-     current time <= X-Amz-Expires */
-  bool qsr = false;
-
-  uint64_t now_req = 0;
-  uint64_t now = ceph_clock_now();
 
   /* v4 requires rados auth */
   if (!store->ctx()->_conf->rgw_s3_auth_use_rados) {
@@ -3561,134 +3553,14 @@ int RGW_Auth_S3::authorize_v4(RGWRados *store, struct req_state *s, bool force_b
     return -ENOMEM;
   }
 
-  const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
-  if ((!http_auth) || !(*http_auth)) {
-
-    /* auth ships with req params ... */
-
-    /* look for required params */
-
-    using_qs = true;
-    s->aws4_auth->credential = s->info.args.get("X-Amz-Credential");
-    if (s->aws4_auth->credential.size() == 0) {
-      return -EPERM;
-    }
-
-    s->aws4_auth->date = s->info.args.get("X-Amz-Date");
-    struct tm date_t;
-    if (!parse_iso8601(s->aws4_auth->date.c_str(), &date_t, NULL, false))
-      return -EPERM;
-
-    s->aws4_auth->expires = s->info.args.get("X-Amz-Expires");
-    if (!s->aws4_auth->expires.empty()) {
-      /* X-Amz-Expires provides the time period, in seconds, for which
-         the generated presigned URL is valid. The minimum value
-         you can set is 1, and the maximum is 604800 (seven days) */
-      time_t exp = atoll(s->aws4_auth->expires.c_str());
-      if ((exp < 1) || (exp > 7*24*60*60)) {
-        dout(10) << "NOTICE: exp out of range, exp = " << exp << dendl;
-        return -EPERM;
-      }
-      /* handle expiration in epoch time */
-      now_req = (uint64_t)internal_timegm(&date_t);
-      if (now >= now_req + exp) {
-        dout(10) << "NOTICE: now = " << now << ", now_req = " << now_req << ", exp = " << exp << dendl;
-        return -EPERM;
-      }
-      qsr = true;
-    }
-
-    if ((now_req < now - RGW_AUTH_GRACE_MINS * 60 ||
-       now_req > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
-      dout(10) << "NOTICE: request time skew too big." << dendl;
-      dout(10) << "now_req = " << now_req << " 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;
-      return -ERR_REQUEST_TIME_SKEWED;
-    }
-
-    s->aws4_auth->signedheaders = s->info.args.get("X-Amz-SignedHeaders");
-    if (s->aws4_auth->signedheaders.size() == 0) {
-      return -EPERM;
-    }
-
-    s->aws4_auth->signature = s->info.args.get("X-Amz-Signature");
-    if (s->aws4_auth->signature.size() == 0) {
-      return -EPERM;
-    }
-
-  } else {
-
-    /* auth ships in headers ... */
-
-    /* ------------------------- handle Credential header */
-
-    using_qs = false;
-
-    string auth_str = http_auth;
-
-#define AWS4_HMAC_SHA256_STR "AWS4-HMAC-SHA256"
-#define CREDENTIALS_PREFIX_LEN (sizeof(AWS4_HMAC_SHA256_STR) - 1)
-    uint64_t min_len = CREDENTIALS_PREFIX_LEN + 1;
-    if (auth_str.length() < min_len) {
-      ldout(store->ctx(), 10) << "credentials string is too short" << dendl;
-      return -EINVAL;
-    }
-
-    list<string> auth_list;
-    get_str_list(auth_str.substr(min_len), ",", auth_list);
-
-    map<string, string> kv;
-
-    for (string& s : auth_list) {
-      string key, val;
-      int ret = parse_key_value(s, key, val);
-      if (ret < 0) {
-        ldout(store->ctx(), 10) << "NOTICE: failed to parse auth header (s=" << s << ")" << dendl;
-        return -EINVAL;
-      }
-      kv[key] = std::move(val);
-    }
-
-    for (string& k : aws4_presigned_required_keys) {
-      if (kv.find(k) == kv.end()) {
-        ldout(store->ctx(), 10) << "NOTICE: auth header missing key: " << k << dendl;
-        return -EINVAL;
-      }
-    }
-
-    s->aws4_auth->credential = std::move(kv["Credential"]);
-    s->aws4_auth->signedheaders = std::move(kv["SignedHeaders"]);
-    s->aws4_auth->signature = std::move(kv["Signature"]);
-
-    /* sig hex str */
-    dout(10) << "v4 signature format = " << s->aws4_auth->signature << dendl;
-
-    /* ------------------------- handle x-amz-date header */
-
-    /* grab date */
-
-    const char *d = s->info.env->get("HTTP_X_AMZ_DATE");
-    struct tm t;
-    if (!parse_iso8601(d, &t, NULL, false)) {
-      dout(10) << "error reading date via http_x_amz_date" << dendl;
-      return -EACCES;
-    }
-    s->aws4_auth->date = d;
-  }
-
-  /* AKIAIVKTAZLOCF43WNQD/AAAAMMDD/region/host/aws4_request */
-  dout(10) << "v4 credential format = " << s->aws4_auth->credential << dendl;
-
-  if (std::count(s->aws4_auth->credential.begin(), s->aws4_auth->credential.end(), '/') != 4) {
-    return -EINVAL;
-  }
-
-  /* credential must end with 'aws4_request' */
-  if (s->aws4_auth->credential.find("aws4_request") == std::string::npos) {
-    return -EINVAL;
+  int ret = rgw::auth::s3::parse_credentials(s->info,
+                                             s->aws4_auth->credential,
+                                             s->aws4_auth->signedheaders,
+                                             s->aws4_auth->signature,
+                                             s->aws4_auth->date,
+                                             using_qs);
+  if (ret < 0) {
+    return ret;
   }
 
   /* grab access key id */