]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
7c673cae FG |
3 | |
4 | #include <errno.h> | |
5 | ||
6 | #include "common/ceph_json.h" | |
7 | #include "rgw_policy_s3.h" | |
8 | #include "rgw_common.h" | |
9 | #include "rgw_crypt_sanitize.h" | |
10 | ||
11 | #define dout_context g_ceph_context | |
12 | #define dout_subsys ceph_subsys_rgw | |
13 | ||
20effc67 TL |
14 | using namespace std; |
15 | ||
7c673cae FG |
16 | class RGWPolicyCondition { |
17 | protected: | |
18 | string v1; | |
19 | string v2; | |
20 | ||
21 | virtual bool check(const string& first, const string& second, string& err_msg) = 0; | |
22 | ||
23 | public: | |
24 | virtual ~RGWPolicyCondition() {} | |
25 | ||
26 | void set_vals(const string& _v1, const string& _v2) { | |
27 | v1 = _v1; | |
28 | v2 = _v2; | |
29 | } | |
30 | ||
31 | bool check(RGWPolicyEnv *env, map<string, bool, ltstr_nocase>& checked_vars, string& err_msg) { | |
32 | string first, second; | |
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} | |
37 | << "] " << v2 << " [" | |
38 | << rgw::crypt_sanitize::s3_policy{v2, second} | |
39 | << "]" << dendl; | |
40 | bool ret = check(first, second, err_msg); | |
41 | if (!ret) { | |
42 | err_msg.append(": "); | |
43 | err_msg.append(v1); | |
44 | err_msg.append(", "); | |
45 | err_msg.append(v2); | |
46 | } | |
47 | return ret; | |
48 | } | |
49 | ||
50 | }; | |
51 | ||
52 | ||
53 | class RGWPolicyCondition_StrEqual : public RGWPolicyCondition { | |
54 | protected: | |
55 | bool check(const string& first, const string& second, string& msg) override { | |
56 | bool ret = first.compare(second) == 0; | |
57 | if (!ret) { | |
58 | msg = "Policy condition failed: eq"; | |
59 | } | |
60 | return ret; | |
61 | } | |
62 | }; | |
63 | ||
64 | class RGWPolicyCondition_StrStartsWith : public RGWPolicyCondition { | |
65 | protected: | |
66 | bool check(const string& first, const string& second, string& msg) override { | |
67 | bool ret = first.compare(0, second.size(), second) == 0; | |
68 | if (!ret) { | |
69 | msg = "Policy condition failed: starts-with"; | |
70 | } | |
71 | return ret; | |
72 | } | |
73 | }; | |
74 | ||
75 | void RGWPolicyEnv::add_var(const string& name, const string& value) | |
76 | { | |
77 | vars[name] = value; | |
78 | } | |
79 | ||
80 | bool RGWPolicyEnv::get_var(const string& name, string& val) | |
81 | { | |
82 | map<string, string, ltstr_nocase>::iterator iter = vars.find(name); | |
83 | if (iter == vars.end()) | |
84 | return false; | |
85 | ||
86 | val = iter->second; | |
87 | ||
88 | return true; | |
89 | } | |
90 | ||
91 | bool RGWPolicyEnv::get_value(const string& s, string& val, map<string, bool, ltstr_nocase>& checked_vars) | |
92 | { | |
93 | if (s.empty() || s[0] != '$') { | |
94 | val = s; | |
95 | return true; | |
96 | } | |
97 | ||
98 | const string& var = s.substr(1); | |
99 | checked_vars[var] = true; | |
100 | ||
101 | return get_var(var, val); | |
102 | } | |
103 | ||
104 | ||
105 | bool RGWPolicyEnv::match_policy_vars(map<string, bool, ltstr_nocase>& policy_vars, string& err_msg) | |
106 | { | |
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) | |
112 | continue; | |
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; | |
117 | return false; | |
118 | } | |
119 | } | |
120 | return true; | |
121 | } | |
122 | ||
123 | RGWPolicy::~RGWPolicy() | |
124 | { | |
125 | list<RGWPolicyCondition *>::iterator citer; | |
126 | for (citer = conditions.begin(); citer != conditions.end(); ++citer) { | |
127 | RGWPolicyCondition *cond = *citer; | |
128 | delete cond; | |
129 | } | |
130 | } | |
131 | ||
132 | int RGWPolicy::set_expires(const string& e) | |
133 | { | |
134 | struct tm t; | |
135 | if (!parse_iso8601(e.c_str(), &t)) | |
136 | return -EINVAL; | |
137 | ||
138 | expires = internal_timegm(&t); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | int RGWPolicy::add_condition(const string& op, const string& first, const string& second, string& err_msg) | |
144 | { | |
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) { | |
151 | off_t min, max; | |
152 | int r = stringtoll(first, &min); | |
153 | if (r < 0) { | |
154 | err_msg = "Bad content-length-range param"; | |
155 | dout(0) << "bad content-length-range param: " << first << dendl; | |
156 | return r; | |
157 | } | |
158 | ||
159 | r = stringtoll(second, &max); | |
160 | if (r < 0) { | |
161 | err_msg = "Bad content-length-range param"; | |
162 | dout(0) << "bad content-length-range param: " << second << dendl; | |
163 | return r; | |
164 | } | |
165 | ||
166 | if (min > min_length) | |
167 | min_length = min; | |
168 | ||
169 | if (max < max_length) | |
170 | max_length = max; | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | if (!cond) { | |
176 | err_msg = "Invalid condition: "; | |
177 | err_msg.append(op); | |
178 | dout(0) << "invalid condition: " << op << dendl; | |
179 | return -EINVAL; | |
180 | } | |
181 | ||
182 | cond->set_vals(first, second); | |
183 | ||
184 | conditions.push_back(cond); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | int RGWPolicy::check(RGWPolicyEnv *env, string& err_msg) | |
190 | { | |
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 | |
196 | } | |
197 | ||
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; | |
203 | string val; | |
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); | |
208 | return -EACCES; | |
209 | } | |
210 | ||
211 | set_var_checked(name); | |
212 | ||
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; | |
218 | return -EACCES; | |
219 | } | |
220 | } | |
221 | ||
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)) { | |
226 | return -EACCES; | |
227 | } | |
228 | } | |
229 | ||
230 | if (!env->match_policy_vars(checked_vars, err_msg)) { | |
231 | dout(1) << "missing policy condition" << dendl; | |
232 | return -EACCES; | |
233 | } | |
234 | return 0; | |
235 | } | |
236 | ||
237 | ||
238 | int RGWPolicy::from_json(bufferlist& bl, string& err_msg) | |
239 | { | |
240 | JSONParser parser; | |
241 | ||
242 | if (!parser.parse(bl.c_str(), bl.length())) { | |
243 | err_msg = "Malformed JSON"; | |
244 | dout(0) << "malformed json" << dendl; | |
245 | return -EINVAL; | |
246 | } | |
247 | ||
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"); | |
250 | if (iter.end()) { | |
251 | err_msg = "Policy missing expiration"; | |
252 | dout(0) << "expiration not found" << dendl; | |
253 | return -EINVAL; // change to a "no expiration" error following S3 | |
254 | } | |
255 | ||
256 | JSONObj *obj = *iter; | |
257 | expiration_str = obj->get_data(); | |
258 | int r = set_expires(expiration_str); | |
259 | if (r < 0) { | |
260 | err_msg = "Failed to parse policy expiration"; | |
261 | return r; | |
262 | } | |
263 | ||
264 | iter = parser.find_first("conditions"); | |
265 | if (iter.end()) { | |
266 | err_msg = "Policy missing conditions"; | |
267 | dout(0) << "conditions not found" << dendl; | |
268 | return -EINVAL; // change to a "no conditions" error following S3 | |
269 | } | |
270 | ||
271 | obj = *iter; | |
272 | ||
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()) { | |
281 | vector<string> v; | |
282 | int i; | |
283 | for (i = 0; !citer.end() && i < 3; ++citer, ++i) { | |
284 | JSONObj *o = *citer; | |
285 | v.push_back(o->get_data()); | |
286 | } | |
287 | if (i != 3 || !citer.end()) { /* we expect exactly 3 arguments here */ | |
288 | err_msg = "Bad condition array, expecting 3 arguments"; | |
289 | return -EINVAL; | |
290 | } | |
291 | ||
292 | int r = add_condition(v[0], v[1], v[2], err_msg); | |
293 | if (r < 0) | |
294 | return r; | |
295 | } else if (!citer.end()) { | |
296 | JSONObj *c = *citer; | |
c07f9fc5 | 297 | dout(20) << "adding simple_check: " << c->get_name() << " : " << c->get_data() << dendl; |
7c673cae FG |
298 | |
299 | add_simple_check(c->get_name(), c->get_data()); | |
300 | } else { | |
301 | return -EINVAL; | |
302 | } | |
303 | } | |
304 | return 0; | |
305 | } |