]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_iam_policy.h
update sources to v12.1.3
[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 #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, bool ifexists)
277 : op(op), key(s, len), ifexists(ifexists) {}
278
279 bool eval(const Environment& e) const;
280
281 static boost::optional<double> as_number(const std::string& s) {
282 std::size_t p = 0;
283
284 try {
285 double d = std::stod(s, &p);
286 if (p < s.length()) {
287 return boost::none;
288 }
289
290 return d;
291 } catch (const std::logic_error& e) {
292 return boost::none;
293 }
294 }
295
296 static boost::optional<ceph::real_time> as_date(const std::string& s) {
297 std::size_t p = 0;
298
299 try {
300 double d = std::stod(s, &p);
301 if (p == s.length()) {
302 return ceph::real_time(
303 std::chrono::seconds(static_cast<uint64_t>(d)) +
304 std::chrono::nanoseconds(
305 static_cast<uint64_t>((d - static_cast<uint64_t>(d))
306 * 1000000000)));
307 }
308
309 return from_iso_8601(boost::string_ref(s), false);
310 } catch (const std::logic_error& e) {
311 return boost::none;
312 }
313 }
314
315 static boost::optional<bool> as_bool(const std::string& s) {
316 std::size_t p = 0;
317
318 if (s.empty() || boost::iequals(s, "false")) {
319 return false;
320 }
321
322 try {
323 double d = std::stod(s, &p);
324 if (p == s.length()) {
325 return !((d == +0.0) || (d == -0.0) || std::isnan(d));
326 }
327 } catch (const std::logic_error& e) {
328 // Fallthrough
329 }
330
331 return true;
332 }
333
334 static boost::optional<ceph::bufferlist> as_binary(const std::string& s) {
335 // In a just world
336 ceph::bufferlist base64;
337 // I could populate a bufferlist
338 base64.push_back(buffer::create_static(
339 s.length(),
340 const_cast<char*>(s.data()))); // Yuck
341 // From a base64 encoded std::string.
342 ceph::bufferlist bin;
343
344 try {
345 base64.decode_base64(bin);
346 } catch (const ceph::buffer::malformed_input& e) {
347 return boost::none;
348 }
349 return bin;
350 }
351
352 static boost::optional<MaskedIP> as_network(const std::string& s);
353
354
355 struct ci_equal_to {
356 bool operator ()(const std::string& s1,
357 const std::string& s2) const {
358 return boost::iequals(s1, s2);
359 }
360 };
361
362 struct string_like {
363 bool operator ()(const std::string& input,
364 const std::string& pattern) const {
365 return match_wildcards(pattern, input, 0);
366 }
367 };
368
369 template<typename F>
370 static bool orrible(F&& f, const std::string& c,
371 const std::vector<std::string>& v) {
372 for (const auto& d : v) {
373 if (std::forward<F>(f)(c, d)) {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 template<typename F, typename X>
381 static bool shortible(F&& f, X& x, const std::string& c,
382 const std::vector<std::string>& v) {
383 auto xc = std::forward<X>(x)(c);
384 if (!xc) {
385 return false;
386 }
387
388 for (const auto& d : v) {
389 auto xd = std::forward<X>(x)(d);
390 if (!xd) {
391 continue;
392 }
393
394 if (std::forward<F>(f)(*xc, *xd)) {
395 return true;
396 }
397 }
398 return false;
399 }
400 };
401
402 std::ostream& operator <<(std::ostream& m, const Condition& c);
403
404 std::string to_string(const Condition& c);
405
406 struct Statement {
407 boost::optional<std::string> sid = boost::none;
408
409 boost::container::flat_set<rgw::auth::Principal> princ;
410 boost::container::flat_set<rgw::auth::Principal> noprinc;
411
412 // Every statement MUST provide an effect. I just initialize it to
413 // deny as defensive programming.
414 Effect effect = Effect::Deny;
415
416 std::uint64_t action = 0;
417 std::uint64_t notaction = 0;
418
419 boost::container::flat_set<ARN> resource;
420 boost::container::flat_set<ARN> notresource;
421
422 std::vector<Condition> conditions;
423
424 Effect eval(const Environment& e,
425 boost::optional<const rgw::auth::Identity&> ida,
426 std::uint64_t action, const ARN& resource) const;
427 };
428
429 std::ostream& operator <<(ostream& m, const Statement& s);
430 std::string to_string(const Statement& s);
431
432 struct PolicyParseException : public std::exception {
433 rapidjson::ParseResult pr;
434
435 PolicyParseException(rapidjson::ParseResult&& pr)
436 : pr(pr) { }
437 const char* what() const noexcept override {
438 return rapidjson::GetParseError_En(pr.Code());
439 }
440 };
441
442 struct Policy {
443 std::string text;
444 Version version = Version::v2008_10_17;
445 boost::optional<std::string> id = boost::none;
446
447 std::vector<Statement> statements;
448
449 Policy(CephContext* cct, const std::string& tenant,
450 const bufferlist& text);
451
452 Effect eval(const Environment& e,
453 boost::optional<const rgw::auth::Identity&> ida,
454 std::uint64_t action, const ARN& resource) const;
455 };
456
457 std::ostream& operator <<(ostream& m, const Policy& p);
458 std::string to_string(const Policy& p);
459 }
460 }
461
462 namespace std {
463 template<>
464 struct hash<::rgw::IAM::Service> {
465 size_t operator()(const ::rgw::IAM::Service& s) const noexcept {
466 // Invoke a default-constructed hash object for int.
467 return hash<int>()(static_cast<int>(s));
468 }
469 };
470 }
471
472 #endif