]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_iam_policy.h
aa121f5d0add7007010ed671d93a30c8d0ce6471
[ceph.git] / ceph / src / rgw / rgw_iam_policy.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #ifndef CEPH_RGW_IAM_POLICY_H
5 #define CEPH_RGW_IAM_POLICY_H
6
7 #include <bitset>
8 #include <chrono>
9 #include <cstdint>
10 #include <iostream>
11 #include <string>
12
13 #include <boost/algorithm/string/predicate.hpp>
14 #include <boost/container/flat_map.hpp>
15 #include <boost/container/flat_set.hpp>
16 #include <boost/optional.hpp>
17 #include <boost/thread/shared_mutex.hpp>
18 #include <boost/utility/string_ref.hpp>
19 #include <boost/variant.hpp>
20
21 #include "common/ceph_time.h"
22 #include "common/iso_8601.h"
23
24 #include "rapidjson/error/error.h"
25 #include "rapidjson/error/en.h"
26
27 #include "fnmatch.h"
28
29 #include "rgw_acl.h"
30 #include "rgw_basic_types.h"
31 #include "rgw_iam_policy_keywords.h"
32
33 #include "include/assert.h" // razzin' frazzin' ...grrr.
34
35 class RGWRados;
36 namespace rgw {
37 namespace auth {
38 class Identity;
39 }
40 }
41 struct rgw_obj;
42 struct rgw_bucket;
43
44 namespace rgw {
45 namespace IAM {
46 static constexpr std::uint64_t s3None = 0;
47 static constexpr std::uint64_t s3GetObject = 1ULL << 0;
48 static constexpr std::uint64_t s3GetObjectVersion = 1ULL << 1;
49 static constexpr std::uint64_t s3PutObject = 1ULL << 2;
50 static constexpr std::uint64_t s3GetObjectAcl = 1ULL << 3;
51 static constexpr std::uint64_t s3GetObjectVersionAcl = 1ULL << 4;
52 static constexpr std::uint64_t s3PutObjectAcl = 1ULL << 5;
53 static constexpr std::uint64_t s3PutObjectVersionAcl = 1ULL << 6;
54 static constexpr std::uint64_t s3DeleteObject = 1ULL << 7;
55 static constexpr std::uint64_t s3DeleteObjectVersion = 1ULL << 8;
56 static constexpr std::uint64_t s3ListMultipartUploadParts = 1ULL << 9;
57 static constexpr std::uint64_t s3AbortMultipartUpload = 1ULL << 10;
58 static constexpr std::uint64_t s3GetObjectTorrent = 1ULL << 11;
59 static constexpr std::uint64_t s3GetObjectVersionTorrent = 1ULL << 12;
60 static constexpr std::uint64_t s3RestoreObject = 1ULL << 13;
61 static constexpr std::uint64_t s3CreateBucket = 1ULL << 14;
62 static constexpr std::uint64_t s3DeleteBucket = 1ULL << 15;
63 static constexpr std::uint64_t s3ListBucket = 1ULL << 16;
64 static constexpr std::uint64_t s3ListBucketVersions = 1ULL << 17;
65 static constexpr std::uint64_t s3ListAllMyBuckets = 1ULL << 18;
66 static constexpr std::uint64_t s3ListBucketMultiPartUploads = 1ULL << 19;
67 static constexpr std::uint64_t s3GetAccelerateConfiguration = 1ULL << 20;
68 static constexpr std::uint64_t s3PutAccelerateConfiguration = 1ULL << 21;
69 static constexpr std::uint64_t s3GetBucketAcl = 1ULL << 22;
70 static constexpr std::uint64_t s3PutBucketAcl = 1ULL << 23;
71 static constexpr std::uint64_t s3GetBucketCORS = 1ULL << 24;
72 static constexpr std::uint64_t s3PutBucketCORS = 1ULL << 25;
73 static constexpr std::uint64_t s3GetBucketVersioning = 1ULL << 26;
74 static constexpr std::uint64_t s3PutBucketVersioning = 1ULL << 27;
75 static constexpr std::uint64_t s3GetBucketRequestPayment = 1ULL << 28;
76 static constexpr std::uint64_t s3PutBucketRequestPayment = 1ULL << 29;
77 static constexpr std::uint64_t s3GetBucketLocation = 1ULL << 30;
78 static constexpr std::uint64_t s3GetBucketPolicy = 1ULL << 31;
79 static constexpr std::uint64_t s3DeleteBucketPolicy = 1ULL << 32;
80 static constexpr std::uint64_t s3PutBucketPolicy = 1ULL << 33;
81 static constexpr std::uint64_t s3GetBucketNotification = 1ULL << 34;
82 static constexpr std::uint64_t s3PutBucketNotification = 1ULL << 35;
83 static constexpr std::uint64_t s3GetBucketLogging = 1ULL << 36;
84 static constexpr std::uint64_t s3PutBucketLogging = 1ULL << 37;
85 static constexpr std::uint64_t s3GetBucketTagging = 1ULL << 38;
86 static constexpr std::uint64_t s3PutBucketTagging = 1ULL << 39;
87 static constexpr std::uint64_t s3GetBucketWebsite = 1ULL << 40;
88 static constexpr std::uint64_t s3PutBucketWebsite = 1ULL << 41;
89 static constexpr std::uint64_t s3DeleteBucketWebsite = 1ULL << 42;
90 static constexpr std::uint64_t s3GetLifecycleConfiguration = 1ULL << 43;
91 static constexpr std::uint64_t s3PutLifecycleConfiguration = 1ULL << 44;
92 static constexpr std::uint64_t s3PutReplicationConfiguration = 1ULL << 45;
93 static constexpr std::uint64_t s3GetReplicationConfiguration = 1ULL << 46;
94 static constexpr std::uint64_t s3DeleteReplicationConfiguration = 1ULL << 47;
95 static constexpr std::uint64_t s3GetObjectTagging = 1ULL << 48;
96 static constexpr std::uint64_t s3PutObjectTagging = 1ULL << 49;
97 static constexpr std::uint64_t s3DeleteObjectTagging = 1ULL << 50;
98 static constexpr std::uint64_t s3GetObjectVersionTagging = 1ULL << 51;
99 static constexpr std::uint64_t s3PutObjectVersionTagging = 1ULL << 52;
100 static constexpr std::uint64_t s3DeleteObjectVersionTagging = 1ULL << 53;
101 static constexpr std::uint64_t s3Count = 54;
102 static constexpr std::uint64_t s3All = (1ULL << s3Count) - 1;
103
104 namespace {
105 inline int op_to_perm(std::uint64_t op) {
106 switch (op) {
107 case s3GetObject:
108 case s3GetObjectTorrent:
109 case s3GetObjectVersion:
110 case s3GetObjectVersionTorrent:
111 case s3GetObjectTagging:
112 case s3GetObjectVersionTagging:
113 case s3ListAllMyBuckets:
114 case s3ListBucket:
115 case s3ListBucketMultiPartUploads:
116 case s3ListBucketVersions:
117 case s3ListMultipartUploadParts:
118 return RGW_PERM_READ;
119
120 case s3AbortMultipartUpload:
121 case s3CreateBucket:
122 case s3DeleteBucket:
123 case s3DeleteObject:
124 case s3DeleteObjectVersion:
125 case s3PutObject:
126 case s3PutObjectTagging:
127 case s3PutObjectVersionTagging:
128 case s3DeleteObjectTagging:
129 case s3DeleteObjectVersionTagging:
130 case s3RestoreObject:
131 return RGW_PERM_WRITE;
132
133 case s3GetAccelerateConfiguration:
134 case s3GetBucketAcl:
135 case s3GetBucketCORS:
136 case s3GetBucketLocation:
137 case s3GetBucketLogging:
138 case s3GetBucketNotification:
139 case s3GetBucketPolicy:
140 case s3GetBucketRequestPayment:
141 case s3GetBucketTagging:
142 case s3GetBucketVersioning:
143 case s3GetBucketWebsite:
144 case s3GetLifecycleConfiguration:
145 case s3GetObjectAcl:
146 case s3GetObjectVersionAcl:
147 case s3GetReplicationConfiguration:
148 return RGW_PERM_READ_ACP;
149
150 case s3DeleteBucketPolicy:
151 case s3DeleteBucketWebsite:
152 case s3DeleteReplicationConfiguration:
153 case s3PutAccelerateConfiguration:
154 case s3PutBucketAcl:
155 case s3PutBucketCORS:
156 case s3PutBucketLogging:
157 case s3PutBucketNotification:
158 case s3PutBucketPolicy:
159 case s3PutBucketRequestPayment:
160 case s3PutBucketTagging:
161 case s3PutBucketVersioning:
162 case s3PutBucketWebsite:
163 case s3PutLifecycleConfiguration:
164 case s3PutObjectAcl:
165 case s3PutObjectVersionAcl:
166 case s3PutReplicationConfiguration:
167 return RGW_PERM_WRITE_ACP;
168
169 case s3All:
170 return RGW_PERM_FULL_CONTROL;
171 }
172 return RGW_PERM_INVALID;
173 }
174 }
175
176 using Environment = boost::container::flat_map<std::string, std::string>;
177
178 enum struct Partition {
179 aws, aws_cn, aws_us_gov, wildcard
180 // If we wanted our own ARNs for principal type unique to us
181 // (maybe to integrate better with Swift) or for anything else we
182 // provide that doesn't map onto S3, we could add an 'rgw'
183 // partition type.
184 };
185
186 enum struct Service {
187 apigateway, appstream, artifact, autoscaling, aws_portal, acm,
188 cloudformation, cloudfront, cloudhsm, cloudsearch, cloudtrail,
189 cloudwatch, events, logs, codebuild, codecommit, codedeploy,
190 codepipeline, cognito_idp, cognito_identity, cognito_sync,
191 config, datapipeline, dms, devicefarm, directconnect,
192 ds, dynamodb, ec2, ecr, ecs, ssm, elasticbeanstalk, elasticfilesystem,
193 elasticloadbalancing, elasticmapreduce, elastictranscoder, elasticache,
194 es, gamelift, glacier, health, iam, importexport, inspector, iot,
195 kms, kinesisanalytics, firehose, kinesis, lambda, lightsail,
196 machinelearning, aws_marketplace, aws_marketplace_management,
197 mobileanalytics, mobilehub, opsworks, opsworks_cm, polly,
198 redshift, rds, route53, route53domains, sts, servicecatalog,
199 ses, sns, sqs, s3, swf, sdb, states, storagegateway, support,
200 trustedadvisor, waf, workmail, workspaces, wildcard
201 };
202
203 struct ARN {
204 Partition partition;
205 Service service;
206 std::string region;
207 // Once we refity tenant, we should probably use that instead of a
208 // string.
209 std::string account;
210 std::string resource;
211
212 ARN()
213 : partition(Partition::wildcard), service(Service::wildcard) {}
214 ARN(Partition partition, Service service, std::string region,
215 std::string account, std::string resource)
216 : partition(partition), service(service), region(std::move(region)),
217 account(std::move(account)), resource(std::move(resource)) {}
218 ARN(const rgw_obj& o);
219 ARN(const rgw_bucket& b);
220 ARN(const rgw_bucket& b, const std::string& o);
221
222 static boost::optional<ARN> parse(const std::string& s,
223 bool wildcard = false);
224 std::string to_string() const;
225
226 // `this` is the pattern
227 bool match(const ARN& candidate) const;
228 };
229
230 inline std::string to_string(const ARN& a) {
231 return a.to_string();
232 }
233
234 inline std::ostream& operator <<(std::ostream& m, const ARN& a) {
235 return m << to_string(a);
236 }
237
238 bool operator ==(const ARN& l, const ARN& r);
239 bool operator <(const ARN& l, const ARN& r);
240
241 using Address = std::bitset<128>;
242 struct MaskedIP {
243 bool v6;
244 Address addr;
245 // Since we're mapping IPv6 to IPv4 addresses, we may want to
246 // consider making the prefix always be in terms of a v6 address
247 // and just use the v6 bit to rewrite it as a v4 prefix for
248 // output.
249 unsigned int prefix;
250 };
251
252 std::ostream& operator <<(std::ostream& m, const MaskedIP& ip);
253 string to_string(const MaskedIP& m);
254
255 inline bool operator ==(const MaskedIP& l, const MaskedIP& r) {
256 auto shift = std::max((l.v6 ? 128 : 32) - l.prefix,
257 (r.v6 ? 128 : 32) - r.prefix);
258 ceph_assert(shift > 0);
259 return (l.addr >> shift) == (r.addr >> shift);
260 }
261
262 struct Condition {
263 TokenID op;
264 // Originally I was going to use a perfect hash table, but Marcus
265 // says keys are to be added at run-time not compile time.
266
267 // In future development, use symbol internment.
268 std::string key;
269 bool ifexists = false;
270 // Much to my annoyance there is no actual way to do this in a
271 // typed way that is compatible with AWS. I know this because I've
272 // seen examples where the same value is used as a string in one
273 // context and a date in another.
274 std::vector<std::string> vals;
275
276 Condition() = default;
277 Condition(TokenID op, const char* s, std::size_t len, bool ifexists)
278 : op(op), key(s, len), ifexists(ifexists) {}
279
280 bool eval(const Environment& e) const;
281
282 static boost::optional<double> as_number(const std::string& s) {
283 std::size_t p = 0;
284
285 try {
286 double d = std::stod(s, &p);
287 if (p < s.length()) {
288 return boost::none;
289 }
290
291 return d;
292 } catch (const std::logic_error& e) {
293 return boost::none;
294 }
295 }
296
297 static boost::optional<ceph::real_time> as_date(const std::string& s) {
298 std::size_t p = 0;
299
300 try {
301 double d = std::stod(s, &p);
302 if (p == s.length()) {
303 return ceph::real_time(
304 std::chrono::seconds(static_cast<uint64_t>(d)) +
305 std::chrono::nanoseconds(
306 static_cast<uint64_t>((d - static_cast<uint64_t>(d))
307 * 1000000000)));
308 }
309
310 return from_iso_8601(boost::string_ref(s), false);
311 } catch (const std::logic_error& e) {
312 return boost::none;
313 }
314 }
315
316 static boost::optional<bool> as_bool(const std::string& s) {
317 std::size_t p = 0;
318
319 if (s.empty() || boost::iequals(s, "false")) {
320 return false;
321 }
322
323 try {
324 double d = std::stod(s, &p);
325 if (p == s.length()) {
326 return !((d == +0.0) || (d == -0.0) || std::isnan(d));
327 }
328 } catch (const std::logic_error& e) {
329 // Fallthrough
330 }
331
332 return true;
333 }
334
335 static boost::optional<ceph::bufferlist> as_binary(const std::string& s) {
336 // In a just world
337 ceph::bufferlist base64;
338 // I could populate a bufferlist
339 base64.push_back(buffer::create_static(
340 s.length(),
341 const_cast<char*>(s.data()))); // Yuck
342 // From a base64 encoded std::string.
343 ceph::bufferlist bin;
344
345 try {
346 base64.decode_base64(bin);
347 } catch (const ceph::buffer::malformed_input& e) {
348 return boost::none;
349 }
350 return bin;
351 }
352
353 static boost::optional<MaskedIP> as_network(const std::string& s);
354
355
356 struct ci_equal_to : public std::binary_function<const std::string,
357 const std::string,
358 bool> {
359 bool operator ()(const std::string& s1,
360 const std::string& s2) const {
361 return boost::iequals(s1, s2);
362 }
363 };
364
365
366 template<typename F>
367 static bool orrible(F&& f, const std::string& c,
368 const std::vector<std::string>& v) {
369 for (const auto& d : v) {
370 if (std::forward<F>(f)(c, d)) {
371 return true;
372 }
373 }
374 return false;
375 }
376
377 template<typename F, typename X>
378 static bool shortible(F&& f, X& x, const std::string& c,
379 const std::vector<std::string>& v) {
380 auto xc = std::forward<X>(x)(c);
381 if (!xc) {
382 return false;
383 }
384
385 for (const auto& d : v) {
386 auto xd = std::forward<X>(x)(d);
387 if (!xd) {
388 continue;
389 }
390
391 if (std::forward<F>(f)(*xc, *xd)) {
392 return true;
393 }
394 }
395 return false;
396 }
397 };
398
399 std::ostream& operator <<(std::ostream& m, const Condition& c);
400
401 std::string to_string(const Condition& c);
402
403 struct Statement {
404 boost::optional<std::string> sid = boost::none;
405
406 boost::container::flat_set<rgw::auth::Principal> princ;
407 boost::container::flat_set<rgw::auth::Principal> noprinc;
408
409 // Every statement MUST provide an effect. I just initialize it to
410 // deny as defensive programming.
411 Effect effect = Effect::Deny;
412
413 std::uint64_t action = 0;
414 std::uint64_t notaction = 0;
415
416 boost::container::flat_set<ARN> resource;
417 boost::container::flat_set<ARN> notresource;
418
419 std::vector<Condition> conditions;
420
421 Effect eval(const Environment& e,
422 boost::optional<const rgw::auth::Identity&> ida,
423 std::uint64_t action, const ARN& resource) const;
424 };
425
426 std::ostream& operator <<(ostream& m, const Statement& s);
427 std::string to_string(const Statement& s);
428
429 struct PolicyParseException : public std::exception {
430 rapidjson::ParseResult pr;
431
432 PolicyParseException(rapidjson::ParseResult&& pr)
433 : pr(pr) { }
434 const char* what() const noexcept override {
435 return rapidjson::GetParseError_En(pr.Code());
436 }
437 };
438
439 struct Policy {
440 std::string text;
441 Version version = Version::v2008_10_17;
442 boost::optional<std::string> id = boost::none;
443
444 std::vector<Statement> statements;
445
446 Policy(CephContext* cct, const std::string& tenant,
447 const bufferlist& text);
448
449 Effect eval(const Environment& e,
450 boost::optional<const rgw::auth::Identity&> ida,
451 std::uint64_t action, const ARN& resource) const;
452 };
453
454 std::ostream& operator <<(ostream& m, const Policy& p);
455 std::string to_string(const Policy& p);
456 }
457 }
458
459 namespace std {
460 template<>
461 struct hash<::rgw::IAM::Service> {
462 size_t operator()(const ::rgw::IAM::Service& s) const noexcept {
463 // Invoke a default-constructed hash object for int.
464 return hash<int>()(static_cast<int>(s));
465 }
466 };
467 }
468
469 #endif