rgw/rgw_acl.cc
rgw/rgw_acl_s3.cc
rgw/rgw_acl_swift.cc
+ rgw/rgw_auth.cc
rgw/rgw_auth_s3.cc
rgw/rgw_basic_types.cc
rgw/rgw_bucket.cc
rgw/rgw_acl.cc \
rgw/rgw_acl_s3.cc \
rgw/rgw_acl_swift.cc \
+ rgw/rgw_auth.cc \
rgw/rgw_coroutine.cc \
rgw/rgw_cr_rados.cc \
rgw/rgw_tools.cc \
rgw/rgw_acl_s3.h \
rgw/rgw_acl_swift.h \
rgw/rgw_auth.h \
+ rgw/rgw_auth_decoimpl.h \
rgw/rgw_b64.h \
rgw/rgw_client_io.h \
rgw/rgw_coroutine.h \
#include "rgw_common.h"
#include "rgw_auth.h"
+#include "rgw_user.h"
+#include "rgw_http_client.h"
+#include "rgw_keystone.h"
+#include "rgw_swift.h"
#define dout_subsys ceph_subsys_rgw
* through any security check. */
s->system_request));
}
+
+
+/* RGWRemoteAuthApplier */
+int RGWRemoteAuthApplier::get_perms_from_aclspec(const aclspec_t& aclspec) const
+{
+ const auto iter = aclspec.find(info.auth_user.to_str());
+ if (std::end(aclspec) == iter) {
+ return iter->second;
+ }
+
+ return 0;
+}
+
+bool RGWRemoteAuthApplier::is_admin_of(const rgw_user& uid) const
+{
+ return info.is_admin;
+}
+
+bool RGWRemoteAuthApplier::is_owner_of(const rgw_user& uid) const
+{
+ if (info.acct_user.tenant.empty()) {
+ const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id);
+
+ if (tenanted_acct_user == uid) {
+ return true;
+ }
+ }
+
+ return info.acct_user == uid;
+}
+
+void RGWRemoteAuthApplier::create_account(const rgw_user acct_user,
+ RGWUserInfo& user_info) const /* out */
+{
+ rgw_user new_acct_user = acct_user;
+
+ /* Administrator may enforce creating new accounts within their own tenants.
+ * The config parameter name is kept due to legacy. */
+ if (new_acct_user.tenant.empty() && g_conf->rgw_keystone_implicit_tenants) {
+ new_acct_user.tenant = new_acct_user.id;
+ }
+
+ user_info.user_id = new_acct_user;
+ user_info.display_name = info.acct_name;
+
+ int ret = rgw_store_user_info(store, user_info, nullptr, nullptr,
+ real_time(), true);
+ if (ret < 0) {
+ ldout(cct, 0) << "ERROR: failed to store new user info: user="
+ << user_info.user_id << " ret=" << ret << dendl;
+ throw ret;
+ }
+}
+
+/* TODO(rzarzynski): we need to handle display_name changes. */
+void RGWRemoteAuthApplier::load_acct_info(RGWUserInfo& user_info) const /* out */
+{
+ /* It's supposed that RGWRemoteAuthApplier tries to load account info
+ * that belongs to the authenticated identity. Another policy may be
+ * applied by using a RGWThirdPartyAccountAuthApplier decorator. */
+ const rgw_user& acct_user = info.acct_user;
+
+ /* Normally, empty "tenant" field of acct_user means the authenticated
+ * identity has the legacy, global tenant. However, due to inclusion
+ * of multi-tenancy, we got some special compatibility kludge for remote
+ * backends like Keystone.
+ * If the global tenant is the requested one, we try the same tenant as
+ * the user name first. If that RGWUserInfo exists, we use it. This way,
+ * migrated OpenStack users can get their namespaced containers and nobody's
+ * the wiser.
+ * If that fails, we look up in the requested (possibly empty) tenant.
+ * If that fails too, we create the account within the global or separated
+ * namespace depending on rgw_keystone_implicit_tenants. */
+ if (acct_user.tenant.empty()) {
+ const rgw_user tenanted_uid(acct_user.id, acct_user.id);
+
+ if (rgw_get_user_info_by_uid(store, tenanted_uid, user_info) >= 0) {
+ /* Succeeded. */
+ return;
+ }
+ }
+
+ if (rgw_get_user_info_by_uid(store, acct_user, user_info) < 0) {
+ ldout(cct, 0) << "NOTICE: couldn't map swift user " << acct_user << dendl;
+ create_account(acct_user, user_info);
+ }
+
+ /* Succeeded if we are here (create_account() hasn't throwed). */
+}
+
+
+/* LocalAuthApplier */
+/* static declaration */
+const std::string RGWLocalAuthApplier::NO_SUBUSER;
+
+int RGWLocalAuthApplier::get_perms_from_aclspec(const aclspec_t& aclspec) const
+{
+ const auto iter = aclspec.find(user_info.user_id.to_str());
+ if (std::end(aclspec) != iter) {
+ ldout(cct, 20) << "from user ACL got perm=" << iter->second << dendl;
+ return iter->second;
+ }
+
+ return 0;
+}
+
+bool RGWLocalAuthApplier::is_admin_of(const rgw_user& uid) const
+{
+ return user_info.admin;
+}
+
+bool RGWLocalAuthApplier::is_owner_of(const rgw_user& uid) const
+{
+ return uid == user_info.user_id;
+}
+
+uint32_t RGWLocalAuthApplier::get_perm_mask(const std::string& subuser_name,
+ const RGWUserInfo &uinfo) const
+{
+ if (!subuser_name.empty() && subuser_name != NO_SUBUSER) {
+ const auto iter = uinfo.subusers.find(subuser_name);
+
+ if (iter != std::end(uinfo.subusers)) {
+ return iter->second.perm_mask;
+ } else {
+ /* Subuser specified but not found. */
+ return RGW_PERM_NONE;
+ }
+ } else {
+ /* Due to backward compatibility. */
+ return RGW_PERM_FULL_CONTROL;
+ }
+}
+
+void RGWLocalAuthApplier::load_acct_info(RGWUserInfo& user_info) const /* out */
+{
+ /* Load the account that belongs to the authenticated identity. An extra call
+ * to RADOS may be safely skipped in this case. */
+ user_info = this->user_info;
+}
+
+
+RGWAuthApplier::aplptr_t RGWAnonymousAuthEngine::authenticate() const
+{
+ RGWUserInfo user_info;
+ rgw_get_anon_user(user_info);
+
+ return apl_factory->create_apl_local(cct, user_info,
+ RGWLocalAuthApplier::NO_SUBUSER);
+}
#ifndef CEPH_RGW_AUTH_H
#define CEPH_RGW_AUTH_H
+#include <type_traits>
+
#include "rgw_common.h"
+#include "rgw_keystone.h"
+
#define RGW_USER_ANON_ID "anonymous"
* (account in Swift's terminology) specified in @uid. */
virtual bool is_owner_of(const rgw_user& uid) const = 0;
+ /* Return the permission mask that is used to narrow down the set of
+ * operations allowed for a given identity. This method reflects the idea
+ * of subuser tied to RGWUserInfo. */
+ virtual int get_perm_mask() const = 0;
+
virtual bool is_anonymous() const final {
/* If the identity owns the anonymous account (rgw_user), it's considered
* the anonymous identity. */
return is_owner_of(rgw_user(RGW_USER_ANON_ID));
}
-
- virtual int get_perm_mask() const = 0;
};
inline ostream& operator<<(ostream& out, const RGWIdentityApplier &id) {
std::unique_ptr<RGWIdentityApplier>
rgw_auth_transform_old_authinfo(req_state * const s);
+
+/* Interface for classes applying changes to request state/RADOS store imposed
+ * by a particular RGWAuthEngine.
+ *
+ * Implementations must also conform to RGWIdentityApplier interface to apply
+ * authorization policy (ACLs, account's ownership and entitlement).
+ *
+ * In contrast to RGWAuthEngine, implementations of this interface are allowed
+ * to handle req_state or RGWRados in the read-write manner. */
+class RGWAuthApplier : public RGWIdentityApplier {
+ template <typename DecorateeT> friend class RGWDecoratingAuthApplier;
+
+protected:
+ CephContext * const cct;
+
+public:
+ typedef std::unique_ptr<RGWAuthApplier> aplptr_t;
+
+ RGWAuthApplier(CephContext * const cct) : cct(cct) {}
+ virtual ~RGWAuthApplier() {};
+
+ /* Fill provided RGWUserInfo with information about the account that
+ * RGWOp will operate on. Errors are handled solely through exceptions.
+ *
+ * XXX: be aware that the "account" term refers to rgw_user. The naming
+ * is legacy. */
+ virtual void load_acct_info(RGWUserInfo& user_info) const = 0; /* out */
+
+ /* Apply any changes to request state. This method will be most useful for
+ * TempURL of Swift API or AWSv4. */
+ virtual void modify_request_state(req_state * s) const {} /* in/out */
+};
+
+
+/* RGWRemoteAuthApplier targets those authentication engines which don't need
+ * to ask the RADOS store while performing the auth process. Instead, they
+ * obtain credentials from an external source like Keystone or LDAP.
+ *
+ * As the authenticated user may not have an account yet, RGWRemoteAuthApplier
+ * must be able to create it basing on data passed by an auth engine. Those
+ * data will be used to fill RGWUserInfo structure. */
+class RGWRemoteAuthApplier : public RGWAuthApplier {
+public:
+ class AuthInfo {
+ friend class RGWRemoteAuthApplier;
+ protected:
+ const rgw_user acct_user;
+ const std::string acct_name;
+ const uint32_t perm_mask;
+ const bool is_admin;
+
+ public:
+ AuthInfo(const rgw_user acct_user,
+ const std::string acct_name,
+ const uint32_t perm_mask,
+ const bool is_admin)
+ : acct_user(acct_user),
+ acct_name(acct_name),
+ perm_mask(perm_mask),
+ is_admin(is_admin) {
+ }
+ };
+
+protected:
+ /* Read-write is intensional here due to RGWUserInfo creation process. */
+ RGWRados * const store;
+ const AuthInfo info;
+
+
+ virtual void create_account(const rgw_user acct_user,
+ RGWUserInfo& user_info) const; /* out */
+
+public:
+ RGWRemoteAuthApplier(CephContext * const cct,
+ RGWRados * const store,
+ const AuthInfo info)
+ : RGWAuthApplier(cct),
+ store(store),
+ info(info) {
+ }
+
+ virtual int get_perms_from_aclspec(const aclspec_t& aclspec) const override;
+ virtual bool is_admin_of(const rgw_user& uid) const override;
+ virtual bool is_owner_of(const rgw_user& uid) const override;
+ virtual int get_perm_mask() const { return info.perm_mask; }
+ virtual void load_acct_info(RGWUserInfo& user_info) const override; /* out */
+
+ struct Factory {
+ virtual ~Factory() {}
+ virtual aplptr_t create_apl_remote(CephContext * const cct,
+ const AuthInfo info) const = 0;
+ };
+};
+
+
+/* Local auth applier targets those auth engines that store user information
+ * in the RADOS store. As a consequence of performing the authentication, they
+ * will have the RGWUserInfo structure loaded. Exploiting this trait allows to
+ * avoid additional call to underlying RADOS store. */
+class RGWLocalAuthApplier : public RGWAuthApplier {
+protected:
+ const RGWUserInfo user_info;
+ const std::string subuser;
+
+ uint32_t get_perm_mask(const std::string& subuser_name,
+ const RGWUserInfo &uinfo) const;
+
+public:
+ static const std::string NO_SUBUSER;
+
+ RGWLocalAuthApplier(CephContext * const cct,
+ const RGWUserInfo& user_info,
+ const std::string subuser)
+ : RGWAuthApplier(cct),
+ user_info(user_info),
+ subuser(subuser) {
+ }
+
+
+ virtual int get_perms_from_aclspec(const aclspec_t& aclspec) const override;
+ virtual bool is_admin_of(const rgw_user& uid) const override;
+ virtual bool is_owner_of(const rgw_user& uid) const override;
+ virtual int get_perm_mask() const override {
+ return get_perm_mask(subuser, user_info);
+ }
+ virtual void load_acct_info(RGWUserInfo& user_info) const override; /* out */
+
+ struct Factory {
+ virtual ~Factory() {}
+ virtual aplptr_t create_apl_local(CephContext * const cct,
+ const RGWUserInfo& user_info,
+ const std::string& subuser) const = 0;
+ };
+};
+
+
+/* Abstract class for authentication backends (auth engines) in RadosGW.
+ *
+ * An engine is supposed only to:
+ * - authenticate (not authorize!) a given request basing on req_state,
+ * - provide an upper layer with RGWAuthApplier to commit all changes to
+ * data structures (like req_state) and to the RADOS store (creating
+ * an account, synchronizing user personal info).
+ * Auth engine MUST NOT make any changes to req_state nor RADOS store.
+ *
+ * Separation between authentication and global state modification has been
+ * introduced because many auth engines are perfectly orthogonal to applier
+ * and thus they can be decoupled. Additional motivation is clearly distinguish
+ * all places which can modify underlying data structures. */
+class RGWAuthEngine {
+protected:
+ CephContext * const cct;
+
+ RGWAuthEngine(CephContext * const cct)
+ : cct(cct) {
+ }
+ /* Make the engines non-copyable and non-moveable due to const-correctness
+ * and aggregating applier factories less costly and error-prone. */
+ RGWAuthEngine(const RGWAuthEngine&) = delete;
+ RGWAuthEngine& operator=(const RGWAuthEngine&) = delete;
+
+public:
+ /* Fast, non-throwing method for screening whether a concrete engine may
+ * be interested in handling a specific request. */
+ virtual bool is_applicable() const noexcept = 0;
+
+ /* Throwing method for identity verification. When the check is positive
+ * an implementation should return RGWAuthApplier::aplptr_t containing
+ * a non-null pointer to object conforming the RGWAuthApplier interface.
+ * Otherwise, the authentication is treated as failed.
+ * An error may be signalised by throwing an exception of int type with
+ * errno value inside. Those value are always negative. */
+ virtual RGWAuthApplier::aplptr_t authenticate() const = 0;
+
+ virtual ~RGWAuthEngine() {};
+};
+
+
+/* Abstract base class for all token-based auth engines. */
+class RGWTokenBasedAuthEngine : public RGWAuthEngine {
+protected:
+ const std::string token;
+
+public:
+ RGWTokenBasedAuthEngine(CephContext * const cct,
+ const std::string token)
+ : RGWAuthEngine(cct),
+ token(token) {
+ }
+
+ bool is_applicable() const noexcept override {
+ return !token.empty();
+ }
+};
+
+/* TODO: introduce extractors for TokenBased. */
+
+/* Keystone. */
+class RGWKeystoneAuthEngine : public RGWTokenBasedAuthEngine {
+protected:
+ const RGWRemoteAuthApplier::Factory * const apl_factory;
+
+ /* Helper methods. */
+ KeystoneToken decode_pki_token(const std::string token) const;
+ KeystoneToken get_from_keystone(const std::string token) const;
+ RGWRemoteAuthApplier::AuthInfo get_creds_info(const KeystoneToken& token,
+ const std::vector<std::string>& admin_roles
+ ) const noexcept;
+public:
+ RGWKeystoneAuthEngine(CephContext * const cct,
+ const std::string token,
+ const RGWRemoteAuthApplier::Factory * const apl_factory)
+ : RGWTokenBasedAuthEngine(cct, token),
+ apl_factory(apl_factory) {
+ }
+
+ bool is_applicable() const noexcept override;
+ RGWAuthApplier::aplptr_t authenticate() const override;
+};
+
+
+/* Anonymous */
+class RGWAnonymousAuthEngine : public RGWAuthEngine {
+ const RGWLocalAuthApplier::Factory * const apl_factory;
+
+public:
+ RGWAnonymousAuthEngine(CephContext * const cct,
+ const RGWLocalAuthApplier::Factory * const apl_factory)
+ : RGWAuthEngine(cct),
+ apl_factory(apl_factory) {
+ }
+
+ bool is_applicable() const noexcept override {
+ return true;
+ }
+
+ RGWAuthApplier::aplptr_t authenticate() const override;
+};
+
#endif /* CEPH_RGW_AUTH_H */
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+
+#ifndef CEPH_RGW_AUTH_DECOIMPL_H
+#define CEPH_RGW_AUTH_DECOIMPL_H
+
+#include "rgw_auth.h"
+
+
+/* Abstract decorator over any implementation of RGWAuthApplier. */
+template <typename DecorateeT>
+class RGWDecoratingAuthApplier : public RGWAuthApplier {
+ static_assert(std::is_base_of<RGWAuthApplier, DecorateeT>::value,
+ "DecorateeT must be a subclass of RGWAuthApplier");
+ DecorateeT decoratee;
+
+public:
+ RGWDecoratingAuthApplier(const DecorateeT& decoratee)
+ : RGWAuthApplier(decoratee.cct),
+ decoratee(decoratee) {
+ }
+
+ virtual int get_perms_from_aclspec(const aclspec_t& aclspec) const override {
+ return decoratee.get_perms_from_aclspec(aclspec);
+ }
+
+ virtual bool is_admin_of(const rgw_user& uid) const override {
+ return decoratee.is_admin_of(uid);
+ }
+
+ virtual bool is_owner_of(const rgw_user& uid) const override {
+ return decoratee.is_owner_of(uid);
+ }
+
+ virtual int get_perm_mask() const override {
+ return decoratee.get_perm_mask();
+ }
+
+ virtual void load_acct_info(RGWUserInfo& user_info) const override { /* out */
+ return decoratee.load_acct_info(user_info);
+ }
+
+ virtual void modify_request_state(req_state * s) const override { /* in/out */
+ return decoratee.modify_request_state(s);
+ }
+};
+
+
+/* Decorator specialization for dealing with pointers to an applier. Useful
+ * for decorating the applier returned after successfull authenication. */
+template <>
+class RGWDecoratingAuthApplier<RGWAuthApplier::aplptr_t> : public RGWAuthApplier {
+ aplptr_t decoratee;
+
+public:
+ RGWDecoratingAuthApplier(aplptr_t&& decoratee)
+ : RGWAuthApplier(decoratee->cct),
+ decoratee(std::move(decoratee)) {
+ }
+
+ virtual int get_perms_from_aclspec(const aclspec_t& aclspec) const override {
+ return decoratee->get_perms_from_aclspec(aclspec);
+ }
+
+ virtual bool is_admin_of(const rgw_user& uid) const override {
+ return decoratee->is_admin_of(uid);
+ }
+
+ virtual bool is_owner_of(const rgw_user& uid) const override {
+ return decoratee->is_owner_of(uid);
+ }
+
+ virtual int get_perm_mask() const override {
+ return decoratee->get_perm_mask();
+ }
+
+ virtual void load_acct_info(RGWUserInfo& user_info) const override { /* out */
+ return decoratee->load_acct_info(user_info);
+ }
+
+ virtual void modify_request_state(req_state * s) const override { /* in/out */
+ return decoratee->modify_request_state(s);
+ }
+};
+
+
+template <typename T>
+class RGWThirdPartyAccountAuthApplier : public RGWDecoratingAuthApplier<T> {
+ /* const */RGWRados * const store;
+ const rgw_user acct_user_override;
+public:
+ /* FIXME: comment this. */
+ static const rgw_user UNKNOWN_ACCT;
+
+ template <typename U>
+ RGWThirdPartyAccountAuthApplier(U&& decoratee,
+ RGWRados * const store,
+ const rgw_user acct_user_override)
+ : RGWDecoratingAuthApplier<T>(std::move(decoratee)),
+ store(store),
+ acct_user_override(acct_user_override) {
+ }
+
+ virtual void load_acct_info(RGWUserInfo& user_info) const override; /* out */
+};
+
+/* static declaration */
+template <typename T>
+const rgw_user RGWThirdPartyAccountAuthApplier<T>::UNKNOWN_ACCT;
+
+template <typename T>
+void RGWThirdPartyAccountAuthApplier<T>::load_acct_info(RGWUserInfo& user_info) const
+{
+ if (UNKNOWN_ACCT == acct_user_override) {
+ /* There is no override specified by the upper layer. This means that we'll
+ * load the account owned by the authenticated identity (aka auth_user). */
+ RGWDecoratingAuthApplier<T>::load_acct_info(user_info);
+ } else if (RGWDecoratingAuthApplier<T>::is_owner_of(acct_user_override)) {
+ /* The override has been specified but the account belongs to the authenticated
+ * identity. We may safely forward the call to a next stage. */
+ RGWDecoratingAuthApplier<T>::load_acct_info(user_info);
+ } else {
+ /* Compatibility mechanism for multi-tenancy. For more details refer to
+ * load_acct_info method of RGWRemoteAuthApplier. */
+ if (acct_user_override.tenant.empty()) {
+ const rgw_user tenanted_uid(acct_user_override.id, acct_user_override.id);
+
+ if (rgw_get_user_info_by_uid(store, tenanted_uid, user_info) >= 0) {
+ /* Succeeded. */
+ return;
+ }
+ }
+
+ int ret = rgw_get_user_info_by_uid(store, acct_user_override, user_info);
+ if (ret < 0) {
+ /* We aren't trying to recover from ENOENT here. It's supposed that creating
+ * someone else's account isn't a thing we want to support in this filter. */
+ throw ret;
+ }
+
+ }
+}
+
+#endif /* CEPH_RGW_AUTH_DECOIMPL_H */