diff options
author | Yehuda Sadeh <yehuda@inktank.com> | 2012-10-11 15:36:07 -0700 |
---|---|---|
committer | Yehuda Sadeh <yehuda@inktank.com> | 2012-10-23 10:44:23 -0700 |
commit | 30d11f424d02c0c1102038d666770acd5fe3bc81 (patch) | |
tree | 73d2bb69213e4bdfa76212f94074545cc837b705 | |
parent | 7bb3504d3f0974e9863f536e9af0ce8889d6888f (diff) | |
download | ceph-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.cc | 53 | ||||
-rw-r--r-- | src/rgw/rgw_common.h | 4 | ||||
-rw-r--r-- | src/rgw/rgw_html_errors.h | 1 | ||||
-rw-r--r-- | src/rgw/rgw_json.h | 14 | ||||
-rw-r--r-- | src/rgw/rgw_op.cc | 20 | ||||
-rw-r--r-- | src/rgw/rgw_op.h | 8 | ||||
-rw-r--r-- | src/rgw/rgw_policy_s3.cc | 88 | ||||
-rw-r--r-- | src/rgw/rgw_policy_s3.h | 15 | ||||
-rw-r--r-- | src/rgw/rgw_rest_s3.cc | 11 |
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, ¤t_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; |