ACLPermission& perm = grant->get_permission();
ACLGranteeType& type = grant->get_type();
switch (type.get_type()) {
+ case ACL_TYPE_REFERER:
+ referer_list.emplace_back(grant->get_referer(), perm.get_permissions());
+ break;
case ACL_TYPE_GROUP:
acl_group_map[grant->get_group()] |= perm.get_permissions();
break;
return perm_mask & auth_identity.get_perms_from_aclspec(acl_user_map);
}
-int RGWAccessControlList::get_group_perm(ACLGroupTypeEnum group, int perm_mask) {
- ldout(cct, 5) << "Searching permissions for group=" << (int)group << " mask=" << perm_mask << dendl;
+int RGWAccessControlList::get_group_perm(ACLGroupTypeEnum group, int perm_mask)
+{
+ ldout(cct, 5) << "Searching permissions for group=" << (int)group
+ << " mask=" << perm_mask << dendl;
+
map<uint32_t, int>::iterator iter = acl_group_map.find((uint32_t)group);
if (iter != acl_group_map.end()) {
ldout(cct, 5) << "Found permission: " << iter->second << dendl;
return 0;
}
+int RGWAccessControlList::get_referer_perm(const std::string http_referer,
+ const int perm_mask)
+{
+ ldout(cct, 5) << "Searching permissions for referer=" << http_referer
+ << " mask=" << perm_mask << dendl;
+
+ /* FIXME: C++11 doesn't have std::rbegin nor std::rend. We would like to
+ * switch when C++14 becomes available. */
+ const auto iter = std::find_if(referer_list.crbegin(), referer_list.crend(),
+ [http_referer](const ACLReferer& r) -> bool {
+ return r.is_match(http_referer);
+ }
+ );
+
+ if (referer_list.crend() == iter) {
+ ldout(cct, 5) << "Permissions for referer not found" << dendl;
+ return 0;
+ } else {
+ ldout(cct, 5) << "Found referer permission=" << iter->perm << dendl;
+ return iter->perm & perm_mask;
+ }
+}
+
int RGWAccessControlPolicy::get_perm(const RGWIdentityApplier& auth_identity,
- const int perm_mask)
+ const int perm_mask,
+ const char * const http_referer)
{
int perm = acl.get_perm(auth_identity, perm_mask);
perm |= perm_mask & (RGW_PERM_READ_ACP | RGW_PERM_WRITE_ACP);
}
- if (perm == perm_mask)
+ if (perm == perm_mask) {
return perm;
+ }
/* should we continue looking up? */
if ((perm & perm_mask) != perm_mask) {
}
}
+ /* Should we continue looking up even deeper? */
+ if (nullptr != http_referer && (perm & perm_mask) != perm_mask) {
+ perm |= acl.get_referer_perm(http_referer, perm_mask);
+ }
+
ldout(cct, 5) << "Getting permissions identity=" << auth_identity
<< " owner=" << owner.get_id()
<< " perm=" << perm << dendl;
bool RGWAccessControlPolicy::verify_permission(const RGWIdentityApplier& auth_identity,
const int user_perm_mask,
- const int perm)
+ const int perm,
+ const char * const http_referer)
{
int test_perm = perm | RGW_PERM_READ_OBJS | RGW_PERM_WRITE_OBJS;
- int policy_perm = get_perm(auth_identity, test_perm);
+ int policy_perm = get_perm(auth_identity, test_perm, http_referer);
/* the swift WRITE_OBJS perm is equivalent to the WRITE obj, just
convert those bits. Note that these bits will only be set on
ACL_TYPE_EMAIL_USER = 1,
ACL_TYPE_GROUP = 2,
ACL_TYPE_UNKNOWN = 3,
+ ACL_TYPE_REFERER = 4,
};
enum ACLGroupTypeEnum {
ACLPermission permission;
string name;
ACLGroupTypeEnum group;
+ string url_spec;
public:
ACLGrant() : group(ACL_GROUP_NONE) {}
_id = email; // implies from_str() that parses the 't:u' syntax
return true;
case ACL_TYPE_GROUP:
+ case ACL_TYPE_REFERER:
return false;
default:
_id = id;
ACLPermission& get_permission() { return permission; }
const ACLPermission& get_permission() const { return permission; }
ACLGroupTypeEnum get_group() const { return group; }
+ const string& get_referer() const { return url_spec; }
void encode(bufferlist& bl) const {
- ENCODE_START(4, 3, bl);
+ ENCODE_START(5, 3, bl);
::encode(type, bl);
string s;
id.to_str(s);
::encode(name, bl);
__u32 g = (__u32)group;
::encode(g, bl);
+ ::encode(url_spec, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
- DECODE_START_LEGACY_COMPAT_LEN(4, 3, 3, bl);
+ DECODE_START_LEGACY_COMPAT_LEN(5, 3, 3, bl);
::decode(type, bl);
string s;
::decode(s, bl);
} else {
group = uri_to_group(uri);
}
+ if (struct_v >= 5) {
+ ::decode(url_spec, bl);
+ } else {
+ url_spec.clear();
+ }
DECODE_FINISH(bl);
}
void dump(Formatter *f) const;
group = _group;
permission.set_permissions(perm);
}
+ void set_referer(const std::string& _url_spec, int perm) {
+ type.set(ACL_TYPE_REFERER);
+ url_spec = _url_spec;
+ permission.set_permissions(perm);
+ }
};
WRITE_CLASS_ENCODER(ACLGrant)
+struct ACLReferer {
+ std::string url_spec;
+ int perm;
+
+ ACLReferer() : perm(0) {}
+ ACLReferer(const std::string& url_spec,
+ const int perm)
+ : url_spec(url_spec),
+ perm(perm) {
+ }
+
+ bool is_match(std::string http_referer) const {
+ if (http_referer == url_spec) {
+ return true;
+ }
+
+ if (http_referer.length() < url_spec.length()) {
+ return false;
+ }
+
+ if ('.' == url_spec[0]) {
+ /* Wildcard support: a referer matches the spec when its last char are
+ * perfectly equal to spec. */
+ return !http_referer.compare(http_referer.length() - url_spec.length(),
+ url_spec.length(), url_spec);
+ }
+
+ return false;
+ }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(url_spec, bl);
+ ::encode(perm, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(url_spec, bl);
+ ::decode(perm, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(ACLReferer)
+
class RGWIdentityApplier;
class RGWAccessControlList
CephContext *cct;
map<string, int> acl_user_map;
map<uint32_t, int> acl_group_map;
+ list<ACLReferer> referer_list;
multimap<string, ACLGrant> grant_map;
void _add_grant(ACLGrant *grant);
public:
int get_perm(const RGWIdentityApplier& auth_identity,
int perm_mask);
int get_group_perm(ACLGroupTypeEnum group, int perm_mask);
+ int get_referer_perm(const std::string http_referer, int perm_mask);
void encode(bufferlist& bl) const {
- ENCODE_START(3, 3, bl);
+ ENCODE_START(4, 3, bl);
bool maps_initialized = true;
::encode(maps_initialized, bl);
::encode(acl_user_map, bl);
::encode(grant_map, bl);
::encode(acl_group_map, bl);
+ ::encode(referer_list, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
- DECODE_START_LEGACY_COMPAT_LEN(3, 3, 3, bl);
+ DECODE_START_LEGACY_COMPAT_LEN(4, 3, 3, bl);
bool maps_initialized;
::decode(maps_initialized, bl);
::decode(acl_user_map, bl);
_add_grant(&grant);
}
}
+ if (struct_v >= 4) {
+ ::decode(referer_list, bl);
+ }
DECODE_FINISH(bl);
}
void dump(Formatter *f) const;
void create_default(const rgw_user& id, string name) {
acl_user_map.clear();
acl_group_map.clear();
+ referer_list.clear();
ACLGrant grant;
grant.set_canon(id, name, RGW_PERM_FULL_CONTROL);
}
int get_perm(const RGWIdentityApplier& auth_identity,
- int perm_mask);
+ int perm_mask,
+ const char * http_referer);
int get_group_perm(ACLGroupTypeEnum group, int perm_mask);
bool verify_permission(const RGWIdentityApplier& auth_identity,
int user_perm_mask,
- int perm);
+ int perm,
+ const char * http_referer = nullptr);
void encode(bufferlist& bl) const {
ENCODE_START(2, 2, bl);
sub.compare(".referrer") == 0;
}
+static bool extract_referer_urlspec(const string& uid, string& url_spec)
+{
+ const size_t pos = uid.find(':');
+ if (string::npos == pos) {
+ return false;
+ }
+
+ const auto sub = uid.substr(0, pos);
+ url_spec = uid.substr(pos + 1);
+
+ return sub.compare(".r") == 0 ||
+ sub.compare(".referer") == 0 ||
+ sub.compare(".referrer") == 0;
+}
+
+static bool normalize_referer_urlspec(string& url_spec, bool& is_negative)
+{
+ try {
+ if ('-' == url_spec[0]) {
+ is_negative = true;
+ url_spec = url_spec.substr(1);
+ } else {
+ is_negative = false;
+ }
+ if (url_spec != "*" && '*' == url_spec[0]) {
+ url_spec = url_spec.substr(1);
+ }
+
+ return !url_spec.empty() && url_spec != ".";
+ } catch (std::out_of_range) {
+ return false;
+ }
+}
+
void RGWAccessControlPolicy_SWIFT::add_grants(RGWRados * const store,
const list<string>& uids,
const int perm)
{
for (const auto& uid : uids) {
+ ldout(cct, 20) << "trying to add grant for ACL uid=" << uid << dendl;
ACLGrant grant;
- RGWUserInfo grant_user;
+ string url_spec;
+
if (uid_is_public(uid)) {
grant.set_group(ACL_GROUP_ALL_USERS, perm);
acl.add_grant(&grant);
- } else {
+ } else if (extract_referer_urlspec(uid, url_spec)) {
+ if (0 != (perm & SWIFT_PERM_WRITE)) {
+ ldout(cct, 10) << "cannot grant write access for referers" << dendl;
+ continue;
+ }
+
+ bool is_negative = false;
+ if (false == normalize_referer_urlspec(url_spec, is_negative)) {
+ ldout(cct, 10) << "cannot normalize referer: " << url_spec << dendl;
+ continue;
+ } else {
+ ldout(cct, 10) << "normalized referer to url_spec=" << url_spec
+ << ", is_negative=" << is_negative << dendl;
+ }
+
+ if (is_negative) {
+ /* Forbid access. */
+ grant.set_referer(url_spec, 0);
+ } else {
+ grant.set_referer(url_spec, perm);
+ }
+
+ acl.add_grant(&grant);
+ } else {
rgw_user user(uid);
+ RGWUserInfo grant_user;
+
if (rgw_get_user_info_by_uid(store, user, grant_user) < 0) {
- ldout(cct, 10) << "grant user does not exist:" << uid << dendl;
+ ldout(cct, 10) << "grant user does not exist: " << uid << dendl;
/* skipping silently */
} else {
grant.set_canon(user, grant_user.display_name, perm);
if (!verify_requester_payer_permission(s))
return false;
- if (bucket_acl->verify_permission(*s->auth_identity, perm, perm))
+ if (bucket_acl->verify_permission(*s->auth_identity, perm, perm,
+ s->info.env->get("HTTP_REFERER")))
return true;
if (!user_acl)
/* we already verified the user mask above, so we pass swift_perm as the mask here,
otherwise the mask might not cover the swift permissions bits */
- if (bucket_acl->verify_permission(*s->auth_identity, swift_perm, swift_perm))
+ if (bucket_acl->verify_permission(*s->auth_identity, swift_perm, swift_perm,
+ s->info.env->get("HTTP_REFERER")))
return true;
if (!user_acl)
f->dump_string("name", name);
f->dump_int("group", (int)group);
+ f->dump_string("url_spec", url_spec);
}
void RGWAccessControlList::dump(Formatter *f) const
dump_start(s);
}
-static int get_swift_container_settings(req_state *s, RGWRados *store, RGWAccessControlPolicy *policy, bool *has_policy,
- RGWCORSConfiguration *cors_config, bool *has_cors)
+static int get_swift_container_settings(req_state * const s,
+ RGWRados * const store,
+ RGWAccessControlPolicy * const policy,
+ bool * const has_policy,
+ RGWCORSConfiguration * const cors_config,
+ bool * const has_cors)
{
string read_list, write_list;
- const char *read_attr = s->info.env->get("HTTP_X_CONTAINER_READ");
+ const char * const read_attr = s->info.env->get("HTTP_X_CONTAINER_READ");
if (read_attr) {
read_list = read_attr;
}
- const char *write_attr = s->info.env->get("HTTP_X_CONTAINER_WRITE");
+ const char * const write_attr = s->info.env->get("HTTP_X_CONTAINER_WRITE");
if (write_attr) {
write_list = write_attr;
}
if (read_attr || write_attr) {
RGWAccessControlPolicy_SWIFT swift_policy(s->cct);
- int r = swift_policy.create(store, s->user->user_id, s->user->display_name, read_list, write_list);
- if (r < 0)
+ int r = swift_policy.create(store,
+ s->user->user_id,
+ s->user->display_name,
+ read_list,
+ write_list);
+ if (r < 0) {
return r;
+ }
*policy = swift_policy;
*has_policy = true;