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