From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/rgw/rgw_acl_swift.cc | 435 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 src/rgw/rgw_acl_swift.cc (limited to 'src/rgw/rgw_acl_swift.cc') diff --git a/src/rgw/rgw_acl_swift.cc b/src/rgw/rgw_acl_swift.cc new file mode 100644 index 000000000..896a38bf9 --- /dev/null +++ b/src/rgw/rgw_acl_swift.cc @@ -0,0 +1,435 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include + +#include + +#include + +#include "common/ceph_json.h" +#include "rgw_common.h" +#include "rgw_user.h" +#include "rgw_acl_swift.h" + +#define dout_subsys ceph_subsys_rgw + + +#define SWIFT_PERM_READ RGW_PERM_READ_OBJS +#define SWIFT_PERM_WRITE RGW_PERM_WRITE_OBJS +/* FIXME: do we really need separate RW? */ +#define SWIFT_PERM_RWRT (SWIFT_PERM_READ | SWIFT_PERM_WRITE) +#define SWIFT_PERM_ADMIN RGW_PERM_FULL_CONTROL + +#define SWIFT_GROUP_ALL_USERS ".r:*" + +static int parse_list(const char* uid_list, + std::vector& uids) /* out */ +{ + char *s = strdup(uid_list); + if (!s) { + return -ENOMEM; + } + + char *tokctx; + const char *p = strtok_r(s, " ,", &tokctx); + while (p) { + if (*p) { + string acl = p; + uids.push_back(acl); + } + p = strtok_r(NULL, " ,", &tokctx); + } + free(s); + return 0; +} + +static bool is_referrer(const std::string& designator) +{ + return designator.compare(".r") == 0 || + designator.compare(".ref") == 0 || + designator.compare(".referer") == 0 || + designator.compare(".referrer") == 0; +} + +static bool uid_is_public(const string& uid) +{ + if (uid[0] != '.' || uid[1] != 'r') + return false; + + int pos = uid.find(':'); + if (pos < 0 || pos == (int)uid.size()) + return false; + + string sub = uid.substr(0, pos); + string after = uid.substr(pos + 1); + + if (after.compare("*") != 0) + return false; + + return is_referrer(sub); +} + +static boost::optional referrer_to_grant(std::string url_spec, + const uint32_t perm) +{ + /* This function takes url_spec as non-ref std::string because of the trim + * operation that is essential to preserve compliance with Swift. It can't + * be easily accomplished with std::string_view. */ + try { + bool is_negative; + ACLGrant grant; + + if ('-' == url_spec[0]) { + url_spec = url_spec.substr(1); + boost::algorithm::trim(url_spec); + + is_negative = true; + } else { + is_negative = false; + } + + if (url_spec != RGW_REFERER_WILDCARD) { + if ('*' == url_spec[0]) { + url_spec = url_spec.substr(1); + boost::algorithm::trim(url_spec); + } + + if (url_spec.empty() || url_spec == ".") { + return boost::none; + } + } else { + /* Please be aware we're specially handling the .r:* in _add_grant() + * of RGWAccessControlList as the S3 API has a similar concept, and + * thus we can have a small portion of compatibility. */ + } + + grant.set_referer(url_spec, is_negative ? 0 : perm); + return grant; + } catch (const std::out_of_range&) { + return boost::none; + } +} + +static ACLGrant user_to_grant(const DoutPrefixProvider *dpp, + CephContext* const cct, + RGWUserCtl* const user_ctl, + const std::string& uid, + const uint32_t perm) +{ + rgw_user user(uid); + RGWUserInfo grant_user; + ACLGrant grant; + + if (user_ctl->get_info_by_uid(dpp, user, &grant_user, null_yield) < 0) { + ldout(cct, 10) << "grant user does not exist: " << uid << dendl; + /* skipping silently */ + grant.set_canon(user, std::string(), perm); + } else { + grant.set_canon(user, grant_user.display_name, perm); + } + + return grant; +} + +int RGWAccessControlPolicy_SWIFT::add_grants(const DoutPrefixProvider *dpp, + RGWUserCtl* const user_ctl, + const std::vector& uids, + const uint32_t perm) +{ + for (const auto& uid : uids) { + boost::optional grant; + ldpp_dout(dpp, 20) << "trying to add grant for ACL uid=" << uid << dendl; + + /* Let's check whether the item has a separator potentially indicating + * a special meaning (like an HTTP referral-based grant). */ + const size_t pos = uid.find(':'); + if (std::string::npos == pos) { + /* No, it don't have -- we've got just a regular user identifier. */ + grant = user_to_grant(dpp, cct, user_ctl, uid, perm); + } else { + /* Yes, *potentially* an HTTP referral. */ + auto designator = uid.substr(0, pos); + auto designatee = uid.substr(pos + 1); + + /* Swift strips whitespaces at both beginning and end. */ + boost::algorithm::trim(designator); + boost::algorithm::trim(designatee); + + if (! boost::algorithm::starts_with(designator, ".")) { + grant = user_to_grant(dpp, cct, user_ctl, uid, perm); + } else if ((perm & SWIFT_PERM_WRITE) == 0 && is_referrer(designator)) { + /* HTTP referrer-based ACLs aren't acceptable for writes. */ + grant = referrer_to_grant(designatee, perm); + } + } + + if (grant) { + acl.add_grant(&*grant); + } else { + return -EINVAL; + } + } + + return 0; +} + + +int RGWAccessControlPolicy_SWIFT::create(const DoutPrefixProvider *dpp, + RGWUserCtl* const user_ctl, + const rgw_user& id, + const std::string& name, + const char* read_list, + const char* write_list, + uint32_t& rw_mask) +{ + acl.create_default(id, name); + owner.set_id(id); + owner.set_name(name); + rw_mask = 0; + + if (read_list) { + std::vector uids; + int r = parse_list(read_list, uids); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: parse_list for read returned r=" + << r << dendl; + return r; + } + + r = add_grants(dpp, user_ctl, uids, SWIFT_PERM_READ); + if (r < 0) { + ldout(cct, 0) << "ERROR: add_grants for read returned r=" + << r << dendl; + return r; + } + rw_mask |= SWIFT_PERM_READ; + } + if (write_list) { + std::vector uids; + int r = parse_list(write_list, uids); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: parse_list for write returned r=" + << r << dendl; + return r; + } + + r = add_grants(dpp, user_ctl, uids, SWIFT_PERM_WRITE); + if (r < 0) { + ldout(cct, 0) << "ERROR: add_grants for write returned r=" + << r << dendl; + return r; + } + rw_mask |= SWIFT_PERM_WRITE; + } + return 0; +} + +void RGWAccessControlPolicy_SWIFT::filter_merge(uint32_t rw_mask, + RGWAccessControlPolicy_SWIFT *old) +{ + /* rw_mask&SWIFT_PERM_READ => setting read acl, + * rw_mask&SWIFT_PERM_WRITE => setting write acl + * when bit is cleared, copy matching elements from old. + */ + if (rw_mask == (SWIFT_PERM_READ|SWIFT_PERM_WRITE)) { + return; + } + rw_mask ^= (SWIFT_PERM_READ|SWIFT_PERM_WRITE); + for (auto &iter: old->acl.get_grant_map()) { + ACLGrant& grant = iter.second; + uint32_t perm = grant.get_permission().get_permissions(); + rgw_user id; + string url_spec; + if (!grant.get_id(id)) { + if (grant.get_group() != ACL_GROUP_ALL_USERS) { + url_spec = grant.get_referer(); + if (url_spec.empty()) { + continue; + } + if (perm == 0) { + /* We need to carry also negative, HTTP referrer-based ACLs. */ + perm = SWIFT_PERM_READ; + } + } + } + if (perm & rw_mask) { + acl.add_grant(&grant); + } + } +} + +void RGWAccessControlPolicy_SWIFT::to_str(string& read, string& write) +{ + multimap& m = acl.get_grant_map(); + multimap::iterator iter; + + for (iter = m.begin(); iter != m.end(); ++iter) { + ACLGrant& grant = iter->second; + const uint32_t perm = grant.get_permission().get_permissions(); + rgw_user id; + string url_spec; + if (!grant.get_id(id)) { + if (grant.get_group() == ACL_GROUP_ALL_USERS) { + id = SWIFT_GROUP_ALL_USERS; + } else { + url_spec = grant.get_referer(); + if (url_spec.empty()) { + continue; + } + id = (perm != 0) ? ".r:" + url_spec : ".r:-" + url_spec; + } + } + if (perm & SWIFT_PERM_READ) { + if (!read.empty()) { + read.append(","); + } + read.append(id.to_str()); + } else if (perm & SWIFT_PERM_WRITE) { + if (!write.empty()) { + write.append(","); + } + write.append(id.to_str()); + } else if (perm == 0 && !url_spec.empty()) { + /* only X-Container-Read headers support referers */ + if (!read.empty()) { + read.append(","); + } + read.append(id.to_str()); + } + } +} + +void RGWAccessControlPolicy_SWIFTAcct::add_grants(const DoutPrefixProvider *dpp, + RGWUserCtl * const user_ctl, + const std::vector& uids, + const uint32_t perm) +{ + for (const auto& uid : uids) { + ACLGrant grant; + RGWUserInfo grant_user; + + if (uid_is_public(uid)) { + grant.set_group(ACL_GROUP_ALL_USERS, perm); + acl.add_grant(&grant); + } else { + rgw_user user(uid); + + if (user_ctl->get_info_by_uid(dpp, user, &grant_user, null_yield) < 0) { + ldout(cct, 10) << "grant user does not exist:" << uid << dendl; + /* skipping silently */ + grant.set_canon(user, std::string(), perm); + acl.add_grant(&grant); + } else { + grant.set_canon(user, grant_user.display_name, perm); + acl.add_grant(&grant); + } + } + } +} + +bool RGWAccessControlPolicy_SWIFTAcct::create(const DoutPrefixProvider *dpp, + RGWUserCtl * const user_ctl, + const rgw_user& id, + const std::string& name, + const std::string& acl_str) +{ + acl.create_default(id, name); + owner.set_id(id); + owner.set_name(name); + + JSONParser parser; + + if (!parser.parse(acl_str.c_str(), acl_str.length())) { + ldpp_dout(dpp, 0) << "ERROR: JSONParser::parse returned error=" << dendl; + return false; + } + + JSONObjIter iter = parser.find_first("admin"); + if (!iter.end() && (*iter)->is_array()) { + std::vector admin; + decode_json_obj(admin, *iter); + ldout(cct, 0) << "admins: " << admin << dendl; + + add_grants(dpp, user_ctl, admin, SWIFT_PERM_ADMIN); + } + + iter = parser.find_first("read-write"); + if (!iter.end() && (*iter)->is_array()) { + std::vector readwrite; + decode_json_obj(readwrite, *iter); + ldout(cct, 0) << "read-write: " << readwrite << dendl; + + add_grants(dpp, user_ctl, readwrite, SWIFT_PERM_RWRT); + } + + iter = parser.find_first("read-only"); + if (!iter.end() && (*iter)->is_array()) { + std::vector readonly; + decode_json_obj(readonly, *iter); + ldout(cct, 0) << "read-only: " << readonly << dendl; + + add_grants(dpp, user_ctl, readonly, SWIFT_PERM_READ); + } + + return true; +} + +boost::optional RGWAccessControlPolicy_SWIFTAcct::to_str() const +{ + std::vector admin; + std::vector readwrite; + std::vector readonly; + + /* Parition the grant map into three not-overlapping groups. */ + for (const auto& item : get_acl().get_grant_map()) { + const ACLGrant& grant = item.second; + const uint32_t perm = grant.get_permission().get_permissions(); + + rgw_user id; + if (!grant.get_id(id)) { + if (grant.get_group() != ACL_GROUP_ALL_USERS) { + continue; + } + id = SWIFT_GROUP_ALL_USERS; + } else if (owner.get_id() == id) { + continue; + } + + if (SWIFT_PERM_ADMIN == (perm & SWIFT_PERM_ADMIN)) { + admin.insert(admin.end(), id.to_str()); + } else if (SWIFT_PERM_RWRT == (perm & SWIFT_PERM_RWRT)) { + readwrite.insert(readwrite.end(), id.to_str()); + } else if (SWIFT_PERM_READ == (perm & SWIFT_PERM_READ)) { + readonly.insert(readonly.end(), id.to_str()); + } else { + // FIXME: print a warning + } + } + + /* If there is no grant to serialize, let's exit earlier to not return + * an empty JSON object which brakes the functional tests of Swift. */ + if (admin.empty() && readwrite.empty() && readonly.empty()) { + return boost::none; + } + + /* Serialize the groups. */ + JSONFormatter formatter; + + formatter.open_object_section("acl"); + if (!readonly.empty()) { + encode_json("read-only", readonly, &formatter); + } + if (!readwrite.empty()) { + encode_json("read-write", readwrite, &formatter); + } + if (!admin.empty()) { + encode_json("admin", admin, &formatter); + } + formatter.close_section(); + + std::ostringstream oss; + formatter.flush(oss); + + return oss.str(); +} -- cgit v1.2.3