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