]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
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 | ||
14 | class RGWPolicyCondition { | |
15 | protected: | |
16 | string v1; | |
17 | string v2; | |
18 | ||
19 | virtual bool check(const string& first, const string& second, string& err_msg) = 0; | |
20 | ||
21 | public: | |
22 | virtual ~RGWPolicyCondition() {} | |
23 | ||
24 | void set_vals(const string& _v1, const string& _v2) { | |
25 | v1 = _v1; | |
26 | v2 = _v2; | |
27 | } | |
28 | ||
29 | bool check(RGWPolicyEnv *env, map<string, bool, ltstr_nocase>& checked_vars, string& err_msg) { | |
30 | string first, second; | |
31 | env->get_value(v1, first, checked_vars); | |
32 | env->get_value(v2, second, checked_vars); | |
33 | dout(1) << "policy condition check " << v1 << " [" | |
34 | << rgw::crypt_sanitize::s3_policy{v1, first} | |
35 | << "] " << v2 << " [" | |
36 | << rgw::crypt_sanitize::s3_policy{v2, second} | |
37 | << "]" << dendl; | |
38 | bool ret = check(first, second, err_msg); | |
39 | if (!ret) { | |
40 | err_msg.append(": "); | |
41 | err_msg.append(v1); | |
42 | err_msg.append(", "); | |
43 | err_msg.append(v2); | |
44 | } | |
45 | return ret; | |
46 | } | |
47 | ||
48 | }; | |
49 | ||
50 | ||
51 | class RGWPolicyCondition_StrEqual : public RGWPolicyCondition { | |
52 | protected: | |
53 | bool check(const string& first, const string& second, string& msg) override { | |
54 | bool ret = first.compare(second) == 0; | |
55 | if (!ret) { | |
56 | msg = "Policy condition failed: eq"; | |
57 | } | |
58 | return ret; | |
59 | } | |
60 | }; | |
61 | ||
62 | class RGWPolicyCondition_StrStartsWith : public RGWPolicyCondition { | |
63 | protected: | |
64 | bool check(const string& first, const string& second, string& msg) override { | |
65 | bool ret = first.compare(0, second.size(), second) == 0; | |
66 | if (!ret) { | |
67 | msg = "Policy condition failed: starts-with"; | |
68 | } | |
69 | return ret; | |
70 | } | |
71 | }; | |
72 | ||
73 | void RGWPolicyEnv::add_var(const string& name, const string& value) | |
74 | { | |
75 | vars[name] = value; | |
76 | } | |
77 | ||
78 | bool RGWPolicyEnv::get_var(const string& name, string& val) | |
79 | { | |
80 | map<string, string, ltstr_nocase>::iterator iter = vars.find(name); | |
81 | if (iter == vars.end()) | |
82 | return false; | |
83 | ||
84 | val = iter->second; | |
85 | ||
86 | return true; | |
87 | } | |
88 | ||
89 | bool RGWPolicyEnv::get_value(const string& s, string& val, map<string, bool, ltstr_nocase>& checked_vars) | |
90 | { | |
91 | if (s.empty() || s[0] != '$') { | |
92 | val = s; | |
93 | return true; | |
94 | } | |
95 | ||
96 | const string& var = s.substr(1); | |
97 | checked_vars[var] = true; | |
98 | ||
99 | return get_var(var, val); | |
100 | } | |
101 | ||
102 | ||
103 | bool RGWPolicyEnv::match_policy_vars(map<string, bool, ltstr_nocase>& policy_vars, string& err_msg) | |
104 | { | |
105 | map<string, string, ltstr_nocase>::iterator iter; | |
106 | string ignore_prefix = "x-ignore-"; | |
107 | for (iter = vars.begin(); iter != vars.end(); ++iter) { | |
108 | const string& var = iter->first; | |
109 | if (strncasecmp(ignore_prefix.c_str(), var.c_str(), ignore_prefix.size()) == 0) | |
110 | continue; | |
111 | if (policy_vars.count(var) == 0) { | |
112 | err_msg = "Policy missing condition: "; | |
113 | err_msg.append(iter->first); | |
114 | dout(1) << "env var missing in policy: " << iter->first << dendl; | |
115 | return false; | |
116 | } | |
117 | } | |
118 | return true; | |
119 | } | |
120 | ||
121 | RGWPolicy::~RGWPolicy() | |
122 | { | |
123 | list<RGWPolicyCondition *>::iterator citer; | |
124 | for (citer = conditions.begin(); citer != conditions.end(); ++citer) { | |
125 | RGWPolicyCondition *cond = *citer; | |
126 | delete cond; | |
127 | } | |
128 | } | |
129 | ||
130 | int RGWPolicy::set_expires(const string& e) | |
131 | { | |
132 | struct tm t; | |
133 | if (!parse_iso8601(e.c_str(), &t)) | |
134 | return -EINVAL; | |
135 | ||
136 | expires = internal_timegm(&t); | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | int RGWPolicy::add_condition(const string& op, const string& first, const string& second, string& err_msg) | |
142 | { | |
143 | RGWPolicyCondition *cond = NULL; | |
144 | if (stringcasecmp(op, "eq") == 0) { | |
145 | cond = new RGWPolicyCondition_StrEqual; | |
146 | } else if (stringcasecmp(op, "starts-with") == 0) { | |
147 | cond = new RGWPolicyCondition_StrStartsWith; | |
148 | } else if (stringcasecmp(op, "content-length-range") == 0) { | |
149 | off_t min, max; | |
150 | int r = stringtoll(first, &min); | |
151 | if (r < 0) { | |
152 | err_msg = "Bad content-length-range param"; | |
153 | dout(0) << "bad content-length-range param: " << first << dendl; | |
154 | return r; | |
155 | } | |
156 | ||
157 | r = stringtoll(second, &max); | |
158 | if (r < 0) { | |
159 | err_msg = "Bad content-length-range param"; | |
160 | dout(0) << "bad content-length-range param: " << second << dendl; | |
161 | return r; | |
162 | } | |
163 | ||
164 | if (min > min_length) | |
165 | min_length = min; | |
166 | ||
167 | if (max < max_length) | |
168 | max_length = max; | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | if (!cond) { | |
174 | err_msg = "Invalid condition: "; | |
175 | err_msg.append(op); | |
176 | dout(0) << "invalid condition: " << op << dendl; | |
177 | return -EINVAL; | |
178 | } | |
179 | ||
180 | cond->set_vals(first, second); | |
181 | ||
182 | conditions.push_back(cond); | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | int RGWPolicy::check(RGWPolicyEnv *env, string& err_msg) | |
188 | { | |
189 | uint64_t now = ceph_clock_now().sec(); | |
190 | if (expires <= now) { | |
191 | dout(0) << "NOTICE: policy calculated as expired: " << expiration_str << dendl; | |
192 | err_msg = "Policy expired"; | |
193 | return -EACCES; // change to condition about expired policy following S3 | |
194 | } | |
195 | ||
196 | list<pair<string, string> >::iterator viter; | |
197 | for (viter = var_checks.begin(); viter != var_checks.end(); ++viter) { | |
198 | pair<string, string>& p = *viter; | |
199 | const string& name = p.first; | |
200 | const string& check_val = p.second; | |
201 | string val; | |
202 | if (!env->get_var(name, val)) { | |
203 | dout(20) << " policy check failed, variable not found: '" << name << "'" << dendl; | |
204 | err_msg = "Policy check failed, variable not found: "; | |
205 | err_msg.append(name); | |
206 | return -EACCES; | |
207 | } | |
208 | ||
209 | set_var_checked(name); | |
210 | ||
211 | dout(20) << "comparing " << name << " [" << val << "], " << check_val << dendl; | |
212 | if (val.compare(check_val) != 0) { | |
213 | err_msg = "Policy check failed, variable not met condition: "; | |
214 | err_msg.append(name); | |
215 | dout(1) << "policy check failed, val=" << val << " != " << check_val << dendl; | |
216 | return -EACCES; | |
217 | } | |
218 | } | |
219 | ||
220 | list<RGWPolicyCondition *>::iterator citer; | |
221 | for (citer = conditions.begin(); citer != conditions.end(); ++citer) { | |
222 | RGWPolicyCondition *cond = *citer; | |
223 | if (!cond->check(env, checked_vars, err_msg)) { | |
224 | return -EACCES; | |
225 | } | |
226 | } | |
227 | ||
228 | if (!env->match_policy_vars(checked_vars, err_msg)) { | |
229 | dout(1) << "missing policy condition" << dendl; | |
230 | return -EACCES; | |
231 | } | |
232 | return 0; | |
233 | } | |
234 | ||
235 | ||
236 | int RGWPolicy::from_json(bufferlist& bl, string& err_msg) | |
237 | { | |
238 | JSONParser parser; | |
239 | ||
240 | if (!parser.parse(bl.c_str(), bl.length())) { | |
241 | err_msg = "Malformed JSON"; | |
242 | dout(0) << "malformed json" << dendl; | |
243 | return -EINVAL; | |
244 | } | |
245 | ||
246 | // as no time was included in the request, we hope that the user has included a short timeout | |
247 | JSONObjIter iter = parser.find_first("expiration"); | |
248 | if (iter.end()) { | |
249 | err_msg = "Policy missing expiration"; | |
250 | dout(0) << "expiration not found" << dendl; | |
251 | return -EINVAL; // change to a "no expiration" error following S3 | |
252 | } | |
253 | ||
254 | JSONObj *obj = *iter; | |
255 | expiration_str = obj->get_data(); | |
256 | int r = set_expires(expiration_str); | |
257 | if (r < 0) { | |
258 | err_msg = "Failed to parse policy expiration"; | |
259 | return r; | |
260 | } | |
261 | ||
262 | iter = parser.find_first("conditions"); | |
263 | if (iter.end()) { | |
264 | err_msg = "Policy missing conditions"; | |
265 | dout(0) << "conditions not found" << dendl; | |
266 | return -EINVAL; // change to a "no conditions" error following S3 | |
267 | } | |
268 | ||
269 | obj = *iter; | |
270 | ||
271 | iter = obj->find_first(); | |
272 | for (; !iter.end(); ++iter) { | |
273 | JSONObj *child = *iter; | |
274 | dout(20) << "data=" << child->get_data() << dendl; | |
275 | dout(20) << "is_object=" << child->is_object() << dendl; | |
276 | dout(20) << "is_array=" << child->is_array() << dendl; | |
277 | JSONObjIter citer = child->find_first(); | |
278 | if (child->is_array()) { | |
279 | vector<string> v; | |
280 | int i; | |
281 | for (i = 0; !citer.end() && i < 3; ++citer, ++i) { | |
282 | JSONObj *o = *citer; | |
283 | v.push_back(o->get_data()); | |
284 | } | |
285 | if (i != 3 || !citer.end()) { /* we expect exactly 3 arguments here */ | |
286 | err_msg = "Bad condition array, expecting 3 arguments"; | |
287 | return -EINVAL; | |
288 | } | |
289 | ||
290 | int r = add_condition(v[0], v[1], v[2], err_msg); | |
291 | if (r < 0) | |
292 | return r; | |
293 | } else if (!citer.end()) { | |
294 | JSONObj *c = *citer; | |
c07f9fc5 | 295 | dout(20) << "adding simple_check: " << c->get_name() << " : " << c->get_data() << dendl; |
7c673cae FG |
296 | |
297 | add_simple_check(c->get_name(), c->get_data()); | |
298 | } else { | |
299 | return -EINVAL; | |
300 | } | |
301 | } | |
302 | return 0; | |
303 | } |