1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
6 #include "common/ceph_json.h"
7 #include "rgw_policy_s3.h"
8 #include "rgw_common.h"
9 #include "rgw_crypt_sanitize.h"
11 #define dout_context g_ceph_context
12 #define dout_subsys ceph_subsys_rgw
16 class RGWPolicyCondition
{
21 virtual bool check(const string
& first
, const string
& second
, string
& err_msg
) = 0;
24 virtual ~RGWPolicyCondition() {}
26 void set_vals(const string
& _v1
, const string
& _v2
) {
31 bool check(RGWPolicyEnv
*env
, map
<string
, bool, ltstr_nocase
>& checked_vars
, string
& err_msg
) {
33 env
->get_value(v1
, first
, checked_vars
);
34 env
->get_value(v2
, second
, checked_vars
);
35 dout(1) << "policy condition check " << v1
<< " ["
36 << rgw::crypt_sanitize::s3_policy
{v1
, first
}
38 << rgw::crypt_sanitize::s3_policy
{v2
, second
}
40 bool ret
= check(first
, second
, err_msg
);
53 class RGWPolicyCondition_StrEqual
: public RGWPolicyCondition
{
55 bool check(const string
& first
, const string
& second
, string
& msg
) override
{
56 bool ret
= first
.compare(second
) == 0;
58 msg
= "Policy condition failed: eq";
64 class RGWPolicyCondition_StrStartsWith
: public RGWPolicyCondition
{
66 bool check(const string
& first
, const string
& second
, string
& msg
) override
{
67 bool ret
= first
.compare(0, second
.size(), second
) == 0;
69 msg
= "Policy condition failed: starts-with";
75 void RGWPolicyEnv::add_var(const string
& name
, const string
& value
)
80 bool RGWPolicyEnv::get_var(const string
& name
, string
& val
)
82 map
<string
, string
, ltstr_nocase
>::iterator iter
= vars
.find(name
);
83 if (iter
== vars
.end())
91 bool RGWPolicyEnv::get_value(const string
& s
, string
& val
, map
<string
, bool, ltstr_nocase
>& checked_vars
)
93 if (s
.empty() || s
[0] != '$') {
98 const string
& var
= s
.substr(1);
99 checked_vars
[var
] = true;
101 return get_var(var
, val
);
105 bool RGWPolicyEnv::match_policy_vars(map
<string
, bool, ltstr_nocase
>& policy_vars
, string
& err_msg
)
107 map
<string
, string
, ltstr_nocase
>::iterator iter
;
108 string ignore_prefix
= "x-ignore-";
109 for (iter
= vars
.begin(); iter
!= vars
.end(); ++iter
) {
110 const string
& var
= iter
->first
;
111 if (strncasecmp(ignore_prefix
.c_str(), var
.c_str(), ignore_prefix
.size()) == 0)
113 if (policy_vars
.count(var
) == 0) {
114 err_msg
= "Policy missing condition: ";
115 err_msg
.append(iter
->first
);
116 dout(1) << "env var missing in policy: " << iter
->first
<< dendl
;
123 RGWPolicy::~RGWPolicy()
125 list
<RGWPolicyCondition
*>::iterator citer
;
126 for (citer
= conditions
.begin(); citer
!= conditions
.end(); ++citer
) {
127 RGWPolicyCondition
*cond
= *citer
;
132 int RGWPolicy::set_expires(const string
& e
)
135 if (!parse_iso8601(e
.c_str(), &t
))
138 expires
= internal_timegm(&t
);
143 int RGWPolicy::add_condition(const string
& op
, const string
& first
, const string
& second
, string
& err_msg
)
145 RGWPolicyCondition
*cond
= NULL
;
146 if (stringcasecmp(op
, "eq") == 0) {
147 cond
= new RGWPolicyCondition_StrEqual
;
148 } else if (stringcasecmp(op
, "starts-with") == 0) {
149 cond
= new RGWPolicyCondition_StrStartsWith
;
150 } else if (stringcasecmp(op
, "content-length-range") == 0) {
152 int r
= stringtoll(first
, &min
);
154 err_msg
= "Bad content-length-range param";
155 dout(0) << "bad content-length-range param: " << first
<< dendl
;
159 r
= stringtoll(second
, &max
);
161 err_msg
= "Bad content-length-range param";
162 dout(0) << "bad content-length-range param: " << second
<< dendl
;
166 if (min
> min_length
)
169 if (max
< max_length
)
176 err_msg
= "Invalid condition: ";
178 dout(0) << "invalid condition: " << op
<< dendl
;
182 cond
->set_vals(first
, second
);
184 conditions
.push_back(cond
);
189 int RGWPolicy::check(RGWPolicyEnv
*env
, string
& err_msg
)
191 uint64_t now
= ceph_clock_now().sec();
192 if (expires
<= now
) {
193 dout(0) << "NOTICE: policy calculated as expired: " << expiration_str
<< dendl
;
194 err_msg
= "Policy expired";
195 return -EACCES
; // change to condition about expired policy following S3
198 list
<pair
<string
, string
> >::iterator viter
;
199 for (viter
= var_checks
.begin(); viter
!= var_checks
.end(); ++viter
) {
200 pair
<string
, string
>& p
= *viter
;
201 const string
& name
= p
.first
;
202 const string
& check_val
= p
.second
;
204 if (!env
->get_var(name
, val
)) {
205 dout(20) << " policy check failed, variable not found: '" << name
<< "'" << dendl
;
206 err_msg
= "Policy check failed, variable not found: ";
207 err_msg
.append(name
);
211 set_var_checked(name
);
213 dout(20) << "comparing " << name
<< " [" << val
<< "], " << check_val
<< dendl
;
214 if (val
.compare(check_val
) != 0) {
215 err_msg
= "Policy check failed, variable not met condition: ";
216 err_msg
.append(name
);
217 dout(1) << "policy check failed, val=" << val
<< " != " << check_val
<< dendl
;
222 list
<RGWPolicyCondition
*>::iterator citer
;
223 for (citer
= conditions
.begin(); citer
!= conditions
.end(); ++citer
) {
224 RGWPolicyCondition
*cond
= *citer
;
225 if (!cond
->check(env
, checked_vars
, err_msg
)) {
230 if (!env
->match_policy_vars(checked_vars
, err_msg
)) {
231 dout(1) << "missing policy condition" << dendl
;
238 int RGWPolicy::from_json(bufferlist
& bl
, string
& err_msg
)
242 if (!parser
.parse(bl
.c_str(), bl
.length())) {
243 err_msg
= "Malformed JSON";
244 dout(0) << "malformed json" << dendl
;
248 // as no time was included in the request, we hope that the user has included a short timeout
249 JSONObjIter iter
= parser
.find_first("expiration");
251 err_msg
= "Policy missing expiration";
252 dout(0) << "expiration not found" << dendl
;
253 return -EINVAL
; // change to a "no expiration" error following S3
256 JSONObj
*obj
= *iter
;
257 expiration_str
= obj
->get_data();
258 int r
= set_expires(expiration_str
);
260 err_msg
= "Failed to parse policy expiration";
264 iter
= parser
.find_first("conditions");
266 err_msg
= "Policy missing conditions";
267 dout(0) << "conditions not found" << dendl
;
268 return -EINVAL
; // change to a "no conditions" error following S3
273 iter
= obj
->find_first();
274 for (; !iter
.end(); ++iter
) {
275 JSONObj
*child
= *iter
;
276 dout(20) << "data=" << child
->get_data() << dendl
;
277 dout(20) << "is_object=" << child
->is_object() << dendl
;
278 dout(20) << "is_array=" << child
->is_array() << dendl
;
279 JSONObjIter citer
= child
->find_first();
280 if (child
->is_array()) {
283 for (i
= 0; !citer
.end() && i
< 3; ++citer
, ++i
) {
285 v
.push_back(o
->get_data());
287 if (i
!= 3 || !citer
.end()) { /* we expect exactly 3 arguments here */
288 err_msg
= "Bad condition array, expecting 3 arguments";
292 int r
= add_condition(v
[0], v
[1], v
[2], err_msg
);
295 } else if (!citer
.end()) {
297 dout(20) << "adding simple_check: " << c
->get_name() << " : " << c
->get_data() << dendl
;
299 add_simple_check(c
->get_name(), c
->get_data());