]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_iam_policy.h
4429a57430964976bacb0193681db03b4bfe9717
[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 s3Count = 48;
95 static constexpr std::uint64_t s3All = (1ULL << s3Count) - 1;
96
97 namespace {
98 inline int op_to_perm(std::uint64_t op) {
99 switch (op) {
100 case s3GetObject:
101 case s3GetObjectTorrent:
102 case s3GetObjectVersion:
103 case s3GetObjectVersionTorrent:
104 case s3ListAllMyBuckets:
105 case s3ListBucket:
106 case s3ListBucketMultiPartUploads:
107 case s3ListBucketVersions:
108 case s3ListMultipartUploadParts:
109 return RGW_PERM_READ;
110
111 case s3AbortMultipartUpload:
112 case s3CreateBucket:
113 case s3DeleteBucket:
114 case s3DeleteObject:
115 case s3DeleteObjectVersion:
116 case s3PutObject:
117 case s3RestoreObject:
118 return RGW_PERM_WRITE;
119
120 case s3GetAccelerateConfiguration:
121 case s3GetBucketAcl:
122 case s3GetBucketCORS:
123 case s3GetBucketLocation:
124 case s3GetBucketLogging:
125 case s3GetBucketNotification:
126 case s3GetBucketPolicy:
127 case s3GetBucketRequestPayment:
128 case s3GetBucketTagging:
129 case s3GetBucketVersioning:
130 case s3GetBucketWebsite:
131 case s3GetLifecycleConfiguration:
132 case s3GetObjectAcl:
133 case s3GetObjectVersionAcl:
134 case s3GetReplicationConfiguration:
135 return RGW_PERM_READ_ACP;
136
137 case s3DeleteBucketPolicy:
138 case s3DeleteBucketWebsite:
139 case s3DeleteReplicationConfiguration:
140 case s3PutAccelerateConfiguration:
141 case s3PutBucketAcl:
142 case s3PutBucketCORS:
143 case s3PutBucketLogging:
144 case s3PutBucketNotification:
145 case s3PutBucketPolicy:
146 case s3PutBucketRequestPayment:
147 case s3PutBucketTagging:
148 case s3PutBucketVersioning:
149 case s3PutBucketWebsite:
150 case s3PutLifecycleConfiguration:
151 case s3PutObjectAcl:
152 case s3PutObjectVersionAcl:
153 case s3PutReplicationConfiguration:
154 return RGW_PERM_WRITE_ACP;
155
156 case s3All:
157 return RGW_PERM_FULL_CONTROL;
158 }
159 return RGW_PERM_INVALID;
160 }
161 }
162
163 using Environment = boost::container::flat_map<std::string, std::string>;
164
165 enum struct Partition {
166 aws, aws_cn, aws_us_gov, wildcard
167 // If we wanted our own ARNs for principal type unique to us
168 // (maybe to integrate better with Swift) or for anything else we
169 // provide that doesn't map onto S3, we could add an 'rgw'
170 // partition type.
171 };
172
173 enum struct Service {
174 apigateway, appstream, artifact, autoscaling, aws_portal, acm,
175 cloudformation, cloudfront, cloudhsm, cloudsearch, cloudtrail,
176 cloudwatch, events, logs, codebuild, codecommit, codedeploy,
177 codepipeline, cognito_idp, cognito_identity, cognito_sync,
178 config, datapipeline, dms, devicefarm, directconnect,
179 ds, dynamodb, ec2, ecr, ecs, ssm, elasticbeanstalk, elasticfilesystem,
180 elasticloadbalancing, elasticmapreduce, elastictranscoder, elasticache,
181 es, gamelift, glacier, health, iam, importexport, inspector, iot,
182 kms, kinesisanalytics, firehose, kinesis, lambda, lightsail,
183 machinelearning, aws_marketplace, aws_marketplace_management,
184 mobileanalytics, mobilehub, opsworks, opsworks_cm, polly,
185 redshift, rds, route53, route53domains, sts, servicecatalog,
186 ses, sns, sqs, s3, swf, sdb, states, storagegateway, support,
187 trustedadvisor, waf, workmail, workspaces, wildcard
188 };
189
190 struct ARN {
191 Partition partition;
192 Service service;
193 std::string region;
194 // Once we refity tenant, we should probably use that instead of a
195 // string.
196 std::string account;
197 std::string resource;
198
199 ARN()
200 : partition(Partition::wildcard), service(Service::wildcard) {}
201 ARN(Partition partition, Service service, std::string region,
202 std::string account, std::string resource)
203 : partition(partition), service(service), region(std::move(region)),
204 account(std::move(account)), resource(std::move(resource)) {}
205 ARN(const rgw_obj& o);
206 ARN(const rgw_bucket& b);
207 ARN(const rgw_bucket& b, const std::string& o);
208
209 static boost::optional<ARN> parse(const std::string& s,
210 bool wildcard = false);
211 std::string to_string() const;
212
213 // `this` is the pattern
214 bool match(const ARN& candidate) const;
215 };
216
217 inline std::string to_string(const ARN& a) {
218 return a.to_string();
219 }
220
221 inline std::ostream& operator <<(std::ostream& m, const ARN& a) {
222 return m << to_string(a);
223 }
224
225 bool operator ==(const ARN& l, const ARN& r);
226 bool operator <(const ARN& l, const ARN& r);
227
228 using Address = std::bitset<128>;
229 struct MaskedIP {
230 bool v6;
231 Address addr;
232 // Since we're mapping IPv6 to IPv4 addresses, we may want to
233 // consider making the prefix always be in terms of a v6 address
234 // and just use the v6 bit to rewrite it as a v4 prefix for
235 // output.
236 unsigned int prefix;
237 };
238
239 std::ostream& operator <<(std::ostream& m, const MaskedIP& ip);
240 string to_string(const MaskedIP& m);
241
242 inline bool operator ==(const MaskedIP& l, const MaskedIP& r) {
243 auto shift = std::max((l.v6 ? 128 : 32) - l.prefix,
244 (r.v6 ? 128 : 32) - r.prefix);
245 ceph_assert(shift > 0);
246 return (l.addr >> shift) == (r.addr >> shift);
247 }
248
249 struct Condition {
250 TokenID op;
251 // Originally I was going to use a perfect hash table, but Marcus
252 // says keys are to be added at run-time not compile time.
253
254 // In future development, use symbol internment.
255 std::string key;
256 bool ifexists = false;
257 // Much to my annoyance there is no actual way to do this in a
258 // typed way that is compatible with AWS. I know this because I've
259 // seen examples where the same value is used as a string in one
260 // context and a date in another.
261 std::vector<std::string> vals;
262
263 Condition() = default;
264 Condition(TokenID op, const char* s, std::size_t len) : op(op) {
265 static constexpr char ifexistr[] = "IfExists";
266 auto l = static_cast<const char*>(memmem(static_cast<const void*>(s), len,
267 static_cast<const void*>(ifexistr),
268 sizeof(ifexistr) -1));
269 if (l && ((l + sizeof(ifexistr) - 1 == (s + len)))) {
270 ifexists = true;
271 key.assign(s, static_cast<const char*>(l) - s);
272 } else {
273 key.assign(s, len);
274 }
275 }
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 : public std::binary_function<const std::string,
354 const std::string,
355 bool> {
356 bool operator ()(const std::string& s1,
357 const std::string& s2) const {
358 return boost::iequals(s1, s2);
359 }
360 };
361
362
363 template<typename F>
364 static bool orrible(F&& f, const std::string& c,
365 const std::vector<std::string>& v) {
366 for (const auto& d : v) {
367 if (std::forward<F>(f)(c, d)) {
368 return true;
369 }
370 }
371 return false;
372 }
373
374 template<typename F, typename X>
375 static bool shortible(F&& f, X& x, const std::string& c,
376 const std::vector<std::string>& v) {
377 auto xc = std::forward<X>(x)(c);
378 if (!xc) {
379 return false;
380 }
381
382 for (const auto& d : v) {
383 auto xd = std::forward<X>(x)(d);
384 if (!xd) {
385 continue;
386 }
387
388 if (std::forward<F>(f)(*xc, *xd)) {
389 return true;
390 }
391 }
392 return false;
393 }
394 };
395
396 std::ostream& operator <<(std::ostream& m, const Condition& c);
397
398 std::string to_string(const Condition& c);
399
400 struct Statement {
401 boost::optional<std::string> sid = boost::none;
402
403 boost::container::flat_set<rgw::auth::Principal> princ;
404 boost::container::flat_set<rgw::auth::Principal> noprinc;
405
406 // Every statement MUST provide an effect. I just initialize it to
407 // deny as defensive programming.
408 Effect effect = Effect::Deny;
409
410 std::uint64_t action = 0;
411 std::uint64_t notaction = 0;
412
413 boost::container::flat_set<ARN> resource;
414 boost::container::flat_set<ARN> notresource;
415
416 std::vector<Condition> conditions;
417
418 Effect eval(const Environment& e,
419 boost::optional<const rgw::auth::Identity&> ida,
420 std::uint64_t action, const ARN& resource) const;
421 };
422
423 std::ostream& operator <<(ostream& m, const Statement& s);
424 std::string to_string(const Statement& s);
425
426 struct PolicyParseException : public std::exception {
427 rapidjson::ParseResult pr;
428
429 PolicyParseException(rapidjson::ParseResult&& pr)
430 : pr(pr) { }
431 const char* what() const noexcept override {
432 return rapidjson::GetParseError_En(pr.Code());
433 }
434 };
435
436 struct Policy {
437 std::string text;
438 Version version = Version::v2008_10_17;
439 boost::optional<std::string> id = boost::none;
440
441 std::vector<Statement> statements;
442
443 Policy(CephContext* cct, const std::string& tenant,
444 const bufferlist& text);
445
446 Effect eval(const Environment& e,
447 boost::optional<const rgw::auth::Identity&> ida,
448 std::uint64_t action, const ARN& resource) const;
449 };
450
451 std::ostream& operator <<(ostream& m, const Policy& p);
452 std::string to_string(const Policy& p);
453 }
454 }
455
456 namespace std {
457 template<>
458 struct hash<::rgw::IAM::Service> {
459 size_t operator()(const ::rgw::IAM::Service& s) const noexcept {
460 // Invoke a default-constructed hash object for int.
461 return hash<int>()(static_cast<int>(s));
462 }
463 };
464 }
465
466 #endif