#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
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') ||
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
struct rgw_aws4_auth {
string date;
- string expires;
string credential;
string signedheaders;
string signed_hdrs;
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)
*/
{
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) {
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 */