summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehuda Sadeh <yehuda@inktank.com>2012-10-11 15:36:07 -0700
committerYehuda Sadeh <yehuda@inktank.com>2012-10-23 10:44:23 -0700
commit30d11f424d02c0c1102038d666770acd5fe3bc81 (patch)
tree73d2bb69213e4bdfa76212f94074545cc837b705
parent7bb3504d3f0974e9863f536e9af0ce8889d6888f (diff)
downloadceph-30d11f424d02c0c1102038d666770acd5fe3bc81.tar.gz
rgw: POST fixes, policy range, policy expiration
Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
-rw-r--r--src/rgw/rgw_common.cc53
-rw-r--r--src/rgw/rgw_common.h4
-rw-r--r--src/rgw/rgw_html_errors.h1
-rw-r--r--src/rgw/rgw_json.h14
-rw-r--r--src/rgw/rgw_op.cc20
-rw-r--r--src/rgw/rgw_op.h8
-rw-r--r--src/rgw/rgw_policy_s3.cc88
-rw-r--r--src/rgw/rgw_policy_s3.h15
-rw-r--r--src/rgw/rgw_rest_s3.cc11
9 files changed, 119 insertions, 95 deletions
diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc
index 089714fa354..c0210b7964e 100644
--- a/src/rgw/rgw_common.cc
+++ b/src/rgw/rgw_common.cc
@@ -2,6 +2,7 @@
#include "rgw_common.h"
#include "rgw_acl.h"
+#include "rgw_string.h"
#include "common/ceph_crypto.h"
#include "common/armor.h"
@@ -139,6 +140,17 @@ std::ostream& operator<<(std::ostream& oss, const rgw_err &err)
return oss;
}
+static void trim_whitespace(const string& src, string& dst)
+{
+ const char *spacestr = " \t\n\r\f\v";
+ int start = src.find_first_not_of(spacestr);
+ if (start < 0)
+ return;
+
+ int end = src.find_last_not_of(spacestr);
+ dst = src.substr(start, end - start + 1);
+}
+
static bool check_str_end(const char *s)
{
if (!s)
@@ -207,6 +219,34 @@ bool parse_rfc2616(const char *s, struct tm *t)
return parse_rfc850(s, t) || parse_asctime(s, t) || parse_rfc1123(s, t) || parse_rfc1123_alt(s,t);
}
+bool parse_iso8601(const char *s, struct tm *t)
+{
+ memset(t, 0, sizeof(*t));
+ const char *p = strptime(s, "%Y-%m-%dT%T", t);
+ if (!p) {
+ dout(0) << "parse_iso8601 failed" << dendl;
+ return false;
+ }
+ string str;
+ trim_whitespace(p, str);
+ if (str.size() == 1 && str[0] == 'Z')
+ return true;
+
+ if (str.size() != 5) {
+ return false;
+ }
+ if (str[0] != '.' ||
+ str[str.size() - 1] != 'Z')
+ return false;
+
+ uint32_t ms;
+ int r = stringtoul(str.substr(1, 3), &ms);
+ if (r < 0)
+ return false;
+
+ return true;
+}
+
int parse_time(const char *time_str, time_t *time)
{
struct tm tm;
@@ -219,7 +259,7 @@ int parse_time(const char *time_str, time_t *time)
return 0;
}
-int parse_date(string& date, uint64_t *epoch, string *out_date, string *out_time)
+int parse_date(const string& date, uint64_t *epoch, string *out_date, string *out_time)
{
struct tm tm;
@@ -556,17 +596,6 @@ int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
return 0;
}
-static void trim_whitespace(const string& src, string& dst)
-{
- const char *spacestr = " \t\n\r\f\v";
- int start = src.find_first_not_of(spacestr);
- if (start < 0)
- return;
-
- int end = src.find_last_not_of(spacestr);
- dst = src.substr(start, end - start + 1);
-}
-
int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
{
int pos = cap.find('=');
diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h
index d58bf857445..9b0d3c2c98f 100644
--- a/src/rgw/rgw_common.h
+++ b/src/rgw/rgw_common.h
@@ -105,6 +105,7 @@ using ceph::crypto::MD5;
#define ERR_TOO_LARGE 2019
#define ERR_TOO_MANY_BUCKETS 2020
#define ERR_INVALID_REQUEST 2021
+#define ERR_TOO_SMALL 2022
#define ERR_USER_SUSPENDED 2100
#define ERR_INTERNAL_ERROR 2200
@@ -1025,7 +1026,8 @@ static inline const char *rgw_obj_category_name(RGWObjCategory category)
/** time parsing */
extern int parse_time(const char *time_str, time_t *time);
extern bool parse_rfc2616(const char *s, struct tm *t);
-extern int parse_date(string& date, uint64_t *epoch, string *out_date = NULL, string *out_time = NULL);
+extern bool parse_iso8601(const char *s, struct tm *t);
+extern int parse_date(const string& date, uint64_t *epoch, string *out_date = NULL, string *out_time = NULL);
/** Check if the req_state's user has the necessary permissions
* to do the requested action */
diff --git a/src/rgw/rgw_html_errors.h b/src/rgw/rgw_html_errors.h
index 9fa370940c6..254af46e663 100644
--- a/src/rgw/rgw_html_errors.h
+++ b/src/rgw/rgw_html_errors.h
@@ -28,6 +28,7 @@ const static struct rgw_html_errors RGW_HTML_ERRORS[] = {
{ ERR_INVALID_PART_ORDER, 400, "InvalidPartOrder" },
{ ERR_REQUEST_TIMEOUT, 400, "RequestTimeout" },
{ ERR_TOO_LARGE, 400, "EntityTooLarge" },
+ { ERR_TOO_SMALL, 400, "EntityTooSmall" },
{ ERR_TOO_MANY_BUCKETS, 400, "TooManyBuckets" },
{ ERR_LENGTH_REQUIRED, 411, "MissingContentLength" },
{ EACCES, 403, "AccessDenied" },
diff --git a/src/rgw/rgw_json.h b/src/rgw/rgw_json.h
index a5fea2ed34c..e11ab25b405 100644
--- a/src/rgw/rgw_json.h
+++ b/src/rgw/rgw_json.h
@@ -41,6 +41,8 @@ protected:
string name; // corresponds to obj_type in XMLObj
Value data;
string data_string;
+ multimap<string, JSONObj *> children;
+ map<string, string> attr_map;
void handle_value(Value v);
public:
@@ -65,15 +67,13 @@ public:
bool is_array();
bool is_object();
vector<string> get_array_elements();
-
-private:
- multimap<string, JSONObj *> children;
- map<string, string> attr_map;
};
class RGWJSONParser : public JSONObj
{
int buf_len;
+ string json_buffer;
+ bool success;
public:
RGWJSONParser();
virtual ~RGWJSONParser();
@@ -86,12 +86,6 @@ public:
const char *get_json() { return json_buffer.c_str(); }
void set_failure() { success = false; }
-
-private:
- multimap<string, JSONObj *> children;
- map<string, string> attr_map;
- string json_buffer;
- bool success;
};
diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc
index 0b1f7f6b502..005e390517a 100644
--- a/src/rgw/rgw_op.cc
+++ b/src/rgw/rgw_op.cc
@@ -868,13 +868,6 @@ int RGWPutObj::verify_permission()
int RGWPostObj::verify_permission()
{
- // read in the data from the POST form
- ret = get_params();
- if (ret < 0)
- return -EINVAL;
-
- /* we'll handle permissions later in the process, as user needs to attach policy */
-
return 0;
}
@@ -1315,6 +1308,11 @@ void RGWPostObj::execute()
bufferlist bl, aclbl;
int len = 0;
+ // read in the data from the POST form
+ ret = get_params();
+ if (ret < 0)
+ goto done;
+
ret = verify_params();
if (ret < 0)
goto done;
@@ -1357,12 +1355,16 @@ void RGWPostObj::execute()
ofs += len;
- if (ofs > max_allowable_content_length)
+ if (ofs > max_len) {
+ ret = -ERR_TOO_LARGE;
goto done;
+ }
}
- if (len < min_allowable_content_length)
+ if (len < min_len) {
+ ret = -ERR_TOO_SMALL;
goto done;
+ }
s->obj_size = ofs;
diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h
index e9334384e7e..7b8ed6e48f2 100644
--- a/src/rgw/rgw_op.h
+++ b/src/rgw/rgw_op.h
@@ -9,6 +9,8 @@
#ifndef CEPH_RGW_OP_H
#define CEPH_RGW_OP_H
+#include <limits.h>
+
#include <string>
#include <map>
@@ -310,8 +312,8 @@ class RGWPostObj : public RGWOp {
friend class RGWPutObjProcessor;
protected:
- int min_allowable_content_length;
- int max_allowable_content_length;
+ off_t min_len;
+ off_t max_len;
int ret;
int len;
off_t ofs;
@@ -329,6 +331,8 @@ public:
virtual void init(RGWRados *store, struct req_state *s, RGWHandler *h) {
RGWOp::init(store, s, h);
+ min_len = 0;
+ max_len = LLONG_MAX;
ret = 0;
len = 0;
ofs = 0;
diff --git a/src/rgw/rgw_policy_s3.cc b/src/rgw/rgw_policy_s3.cc
index dbe291c2cc7..c0ec993276b 100644
--- a/src/rgw/rgw_policy_s3.cc
+++ b/src/rgw/rgw_policy_s3.cc
@@ -105,33 +105,15 @@ RGWPolicy::~RGWPolicy()
}
}
-uint64_t RGWPolicy::get_current_epoch()
+int RGWPolicy::set_expires(const string& e)
{
- uint64_t current_epoch = 0;
- time_t local_time = ceph_clock_now(NULL).sec();
- struct tm *gmt_time_struct = gmtime(&local_time);
-
- current_epoch = timegm(gmt_time_struct);
-
-
-// string gmt_time_string = asctime(gmt_time_struct);
-
-// parse_date(gmt_time_string, &current_epoch, NULL, NULL);
-
- return current_epoch;
-}
+ struct tm t;
+ if (!parse_iso8601(e.c_str(), &t))
+ return -EINVAL;
-void RGWPolicy::set_expires(utime_t& e)
-{
- time_t e_time = e.sec();
- struct tm *gmt_time_struct = gmtime(&e_time);
- string gmt_time_string = asctime(gmt_time_struct);
- parse_date(gmt_time_string, &expires, NULL, NULL);
-}
+ expires = timegm(&t);
-void RGWPolicy::set_expires(string& e)
-{
- parse_date(e, &expires, NULL, NULL);
+ return 0;
}
int RGWPolicy::add_condition(const string& op, const string& first, const string& second)
@@ -142,11 +124,22 @@ int RGWPolicy::add_condition(const string& op, const string& first, const string
} else if (stringcasecmp(op, "starts-with") == 0) {
cond = new RGWPolicyCondition_StrStartsWith;
} else if (stringcasecmp(op, "content-length-range") == 0) {
- stringstream min_string(first);
- stringstream max_string(second);
+ off_t min, max;
+ int r = stringtoll(first, &min);
+ if (r < 0)
+ return r;
- if ( !(min_string >> min_length) || !(max_string >> max_length) )
- return -EINVAL;
+ r = stringtoll(second, &max);
+ if (r < 0)
+ return r;
+
+ if (min > min_length)
+ min_length = min;
+
+ if (max < max_length)
+ max_length = max;
+
+ return 0;
}
if (!cond)
@@ -159,8 +152,14 @@ int RGWPolicy::add_condition(const string& op, const string& first, const string
return 0;
}
-bool RGWPolicy::check(RGWPolicyEnv *env)
+int RGWPolicy::check(RGWPolicyEnv *env)
{
+ uint64_t now = ceph_clock_now(NULL).sec();
+ if (expires <= now) {
+ dout(0) << "NOTICE: policy calculated as expired: " << expiration_str << dendl;
+ return -EACCES; // change to condition about expired policy following S3
+ }
+
list<pair<string, string> >::iterator viter;
for (viter = var_checks.begin(); viter != var_checks.end(); ++viter) {
pair<string, string>& p = *viter;
@@ -168,14 +167,14 @@ bool RGWPolicy::check(RGWPolicyEnv *env)
const string& check_val = p.second;
string val;
if (!env->get_var(name, val))
- return false;
+ return -EINVAL;
set_var_checked(name);
dout(20) << "comparing " << name << " [" << val << "], " << check_val << dendl;
if (val.compare(check_val) != 0) {
dout(1) << "policy check failed, val=" << val << " != " << check_val << dendl;
- return false;
+ return -EINVAL;
}
}
@@ -183,15 +182,15 @@ bool RGWPolicy::check(RGWPolicyEnv *env)
for (citer = conditions.begin(); citer != conditions.end(); ++citer) {
RGWPolicyCondition *cond = *citer;
if (!cond->check(env, checked_vars)) {
- return false;
+ return -EINVAL;
}
}
if (!env->match_policy_vars(checked_vars)) {
dout(1) << "missing policy condition" << dendl;
- return false;
+ return -EINVAL;
}
- return true;
+ return 0;
}
@@ -208,19 +207,10 @@ int RGWPolicy::from_json(bufferlist& bl)
return -EINVAL; // change to a "no expiration" error following S3
JSONObj *obj = *iter;
- string expiration_string = obj->get_data();
- set_expires(expiration_string);
-
- uint64_t current_epoch = get_current_epoch();
- if (current_epoch == 0) {
- dout(0) << "NOTICE: failed to get current epoch!" << dendl;
- return -EINVAL;
- }
-
- if (expires < current_epoch) {
- dout(0) << "NOTICE: policy calculated as expired: " << expiration_string << dendl;
- return -EINVAL; // change to condition about expired policy following S3
- }
+ expiration_str = obj->get_data();
+ int r = set_expires(expiration_str);
+ if (r < 0)
+ return r;
iter = parser.find_first("conditions");
if (iter.end())
@@ -244,7 +234,9 @@ int RGWPolicy::from_json(bufferlist& bl)
if (i != 3 || !aiter.end()) /* we expect exactly 3 arguments here */
return -EINVAL;
- add_condition(v[0], v[1], v[2]);
+ int r = add_condition(v[0], v[1], v[2]);
+ if (r < 0)
+ return r;
} else {
add_simple_check(child->get_name(), child->get_data());
}
diff --git a/src/rgw/rgw_policy_s3.h b/src/rgw/rgw_policy_s3.h
index 77fa13dc293..d002bece8fb 100644
--- a/src/rgw/rgw_policy_s3.h
+++ b/src/rgw/rgw_policy_s3.h
@@ -1,6 +1,8 @@
#ifndef CEPH_RGW_POLICY_H
#define CEPH_RGW_POLICY_H
+#include <limits.h>
+
#include <map>
#include <list>
#include <string>
@@ -25,20 +27,19 @@ class RGWPolicyCondition;
class RGWPolicy {
uint64_t expires;
+ string expiration_str;
std::list<RGWPolicyCondition *> conditions;
std::list<pair<std::string, std::string> > var_checks;
std::map<std::string, bool, ltstr_nocase> checked_vars;
public:
- int min_length;
- int max_length;
+ off_t min_length;
+ off_t max_length;
- RGWPolicy() : expires(0), min_length(-1), max_length(-1) {}
+ RGWPolicy() : expires(0), min_length(0), max_length(LLONG_MAX) {}
~RGWPolicy();
- uint64_t get_current_epoch();
- void set_expires(utime_t& e);
- void set_expires(string& e);
+ int set_expires(const string& e);
void set_var_checked(const std::string& var) {
checked_vars[var] = true;
@@ -49,7 +50,7 @@ public:
var_checks.push_back(pair<string, string>(var, value));
}
- bool check(RGWPolicyEnv *env);
+ int check(RGWPolicyEnv *env);
int from_json(bufferlist& bl);
};
#endif
diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc
index 333a55c02fc..04ff70601a6 100644
--- a/src/rgw/rgw_rest_s3.cc
+++ b/src/rgw/rgw_rest_s3.cc
@@ -804,10 +804,8 @@ int RGWPostObj_ObjStore_S3::get_params()
if (r < 0)
return r;
- if (post_policy.min_length > -1 && post_policy.max_length > -1) {
- min_allowable_content_length = post_policy.min_length;
- max_allowable_content_length = post_policy.max_length;
- }
+ min_len = post_policy.min_length;
+ max_len = post_policy.max_length;
return 0;
}
@@ -882,9 +880,10 @@ int RGWPostObj_ObjStore_S3::get_policy()
post_policy.set_var_checked("policy");
post_policy.set_var_checked("signature");
- if (!post_policy.check(&env)) {
+ r = post_policy.check(&env);
+ if (r < 0) {
ldout(s->cct, 0) << "policy check failed" << dendl;
- return -EINVAL;
+ return r;
}
s->user = user_info;