rgw: Rework of s3 LDAP Authentication code.

The LDAP authentication code has been reworked based
on the new authentication infrastructure.

Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
This commit is contained in:
Pritha Srivastava 2016-07-14 16:03:48 +05:30
parent f8ecf532fb
commit bcdc2df306
7 changed files with 257 additions and 141 deletions

View File

@ -149,6 +149,11 @@ void RGWRemoteAuthApplier::create_account(const rgw_user& acct_user,
{
rgw_user new_acct_user = acct_user;
if (info.acct_type) {
//ldap/keystone for s3 users
user_info.type = info.acct_type;
}
/* 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) {

View File

@ -117,16 +117,19 @@ public:
const std::string acct_name;
const uint32_t perm_mask;
const bool is_admin;
const uint32_t acct_type;
public:
AuthInfo(const rgw_user& acct_user,
const std::string& acct_name,
const uint32_t perm_mask,
const bool is_admin)
const bool is_admin,
const uint32_t acct_type=TYPE_NONE)
: acct_user(acct_user),
acct_name(acct_name),
perm_mask(perm_mask),
is_admin(is_admin) {
is_admin(is_admin),
acct_type(acct_type) {
}
};

View File

@ -527,6 +527,14 @@ void encode_json(const char *name, const RGWUserCaps& val, Formatter *f);
void decode_json_obj(obj_version& v, JSONObj *obj);
enum RGWUserSourceType
{
TYPE_NONE=0,
TYPE_RGW=1,
TYPE_KEYSTONE=2,
TYPE_LDAP=3
};
struct RGWUserInfo
{
uint64_t auid;
@ -547,6 +555,7 @@ struct RGWUserInfo
RGWQuotaInfo bucket_quota;
map<int, string> temp_url_keys;
RGWQuotaInfo user_quota;
uint32_t type;
RGWUserInfo()
: auid(0),
@ -554,7 +563,8 @@ struct RGWUserInfo
max_buckets(RGW_DEFAULT_MAX_BUCKETS),
op_mask(RGW_OP_TYPE_ALL),
admin(0),
system(0) {
system(0),
type(TYPE_NONE) {
}
RGWAccessKey* get_key0() {
@ -565,7 +575,7 @@ struct RGWUserInfo
}
void encode(bufferlist& bl) const {
ENCODE_START(18, 9, bl);
ENCODE_START(19, 9, bl);
::encode(auid, bl);
string access_key;
string secret_key;
@ -605,10 +615,11 @@ struct RGWUserInfo
::encode(user_quota, bl);
::encode(user_id.tenant, bl);
::encode(admin, bl);
::encode(type, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
DECODE_START_LEGACY_COMPAT_LEN_32(18, 9, 9, bl);
DECODE_START_LEGACY_COMPAT_LEN_32(19, 9, 9, bl);
if (struct_v >= 2) ::decode(auid, bl);
else auid = CEPH_AUTH_UID_DEFAULT;
string access_key;
@ -678,6 +689,9 @@ struct RGWUserInfo
if (struct_v >= 18) {
::decode(admin, bl);
}
if (struct_v >= 19) {
::decode(type, bl);
}
DECODE_FINISH(bl);
}
void dump(Formatter *f) const;
@ -727,7 +741,7 @@ struct rgw_bucket {
}
void encode(bufferlist& bl) const {
ENCODE_START(8, 3, bl);
ENCODE_START(9, 3, bl);
::encode(name, bl);
::encode(data_pool, bl);
::encode(marker, bl);
@ -738,7 +752,7 @@ struct rgw_bucket {
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
DECODE_START_LEGACY_COMPAT_LEN(8, 3, 3, bl);
DECODE_START_LEGACY_COMPAT_LEN(9, 3, 3, bl);
::decode(name, bl);
::decode(data_pool, bl);
if (struct_v >= 2) {

View File

@ -427,6 +427,26 @@ void RGWUserInfo::dump(Formatter *f) const
encode_json("bucket_quota", bucket_quota, f);
encode_json("user_quota", user_quota, f);
encode_json("temp_url_keys", temp_url_keys, f);
string user_source_type;
switch ((RGWUserSourceType)type) {
case TYPE_RGW:
user_source_type = "rgw";
break;
case TYPE_KEYSTONE:
user_source_type = "keystone";
break;
case TYPE_LDAP:
user_source_type = "ldap";
break;
case TYPE_NONE:
user_source_type = "none";
break;
default:
user_source_type = "none";
break;
}
encode_json("type", user_source_type, f);
}
@ -484,6 +504,18 @@ void RGWUserInfo::decode_json(JSONObj *obj)
JSONDecoder::decode_json("bucket_quota", bucket_quota, obj);
JSONDecoder::decode_json("user_quota", user_quota, obj);
JSONDecoder::decode_json("temp_url_keys", temp_url_keys, obj);
string user_source_type;
JSONDecoder::decode_json("type", user_source_type, obj);
if (user_source_type == "rgw") {
type = TYPE_RGW;
} else if (user_source_type == "keystone") {
type = TYPE_KEYSTONE;
} else if (user_source_type == "ldap") {
type = TYPE_LDAP;
} else if (user_source_type == "none") {
type = TYPE_NONE;
}
}
void RGWQuotaInfo::dump(Formatter *f) const

View File

@ -1705,11 +1705,13 @@ int RGWPostObj_ObjStore_S3::get_policy()
err_msg = "Missing signature";
return -EINVAL;
}
RGWUserInfo user_info;
op_ret = rgw_get_user_info_by_access_key(store, s3_access_key, user_info);
if (op_ret < 0) {
S3AuthFactory aplfact(store, s->account_name);
RGWLDAPTokenExtractor token_extr(s);
RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact);
// try external authenticators
if (store->ctx()->_conf->rgw_s3_auth_use_keystone &&
store->ctx()->_conf->rgw_keystone_url.empty())
@ -1747,53 +1749,15 @@ int RGWPostObj_ObjStore_S3::get_policy()
}
s->perm_mask = RGW_PERM_FULL_CONTROL;
}
} else if (store->ctx()->_conf->rgw_s3_auth_use_ldap &&
(! store->ctx()->_conf->rgw_ldap_uri.empty())) {
ldout(store->ctx(), 15)
<< __func__ << " LDAP auth uri="
<< store->ctx()->_conf->rgw_ldap_uri
<< dendl;
RGWToken token{from_base64(s3_access_key)};
if (! token.valid())
return -EACCES;
rgw::LDAPHelper *ldh = RGW_Auth_S3::get_ldap_ctx(store);
if (unlikely(!ldh)) {
ldout(store->ctx(), 0)
<< __func__ << " RGW_Auth_S3::get_ldap_ctx() failed"
<< dendl;
return -EACCES;
}
ldout(store->ctx(), 10)
<< __func__ << " try LDAP auth uri="
<< store->ctx()->_conf->rgw_ldap_uri
<< " token.id=" << token.id
<< dendl;
if (ldh->auth(token.id, token.key) != 0)
return -EACCES;
/* ok, succeeded */
user_info.user_id = token.id;
user_info.display_name = token.id; // cn?
/* create local account, if none exists */
if (rgw_get_user_info_by_uid(store, user_info.user_id,
user_info) < 0) {
int ret = rgw_store_user_info(store, user_info, nullptr, nullptr,
real_time(), true);
if (ret < 0) {
ldout(store->ctx(), 10)
<< "NOTICE: failed to store new user's info: ret=" << ret
<< dendl;
}
}
/* set request perms */
s->perm_mask = RGW_PERM_FULL_CONTROL;
} else if (ldap.is_applicable()) {
auto applier = ldap.authenticate();
if (! applier) {
return -EACCES;
} else {
applier->load_acct_info(*s->user);
s->perm_mask = applier->get_perm_mask();
s->auth_identity = std::move(applier);
}
} else {
return -EACCES;
}
@ -3085,27 +3049,6 @@ int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s,
return RGWHandler_REST::init(store, s, cio);
}
/* RGW_Auth_S3 static members */
std::mutex RGW_Auth_S3::mtx;
rgw::LDAPHelper* RGW_Auth_S3::ldh;
/* static */
void RGW_Auth_S3::init_impl(RGWRados* store)
{
const string& ldap_uri = store->ctx()->_conf->rgw_ldap_uri;
const string& ldap_binddn = store->ctx()->_conf->rgw_ldap_binddn;
const string& ldap_searchdn = store->ctx()->_conf->rgw_ldap_searchdn;
const string& ldap_dnattr =
store->ctx()->_conf->rgw_ldap_dnattr;
std::string ldap_bindpw = parse_rgw_ldap_bindpw(store->ctx());
ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
ldap_searchdn, ldap_dnattr);
ldh->init();
ldh->bind();
}
/*
* Try to validate S3 auth against keystone s3token interface
*/
@ -3936,52 +3879,23 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s)
}
}
if ((external_auth_result < 0) &&
(store->ctx()->_conf->rgw_s3_auth_use_ldap) &&
(! store->ctx()->_conf->rgw_ldap_uri.empty())) {
S3AuthFactory aplfact(store, s->account_name);
RGWLDAPTokenExtractor token_extr(s);
RGWLDAPAuthEngine ldap(s->cct, store, token_extr, &aplfact);
RGW_Auth_S3::init(store);
ldout(store->ctx(), 15)
<< __func__ << " LDAP auth uri="
<< store->ctx()->_conf->rgw_ldap_uri
<< dendl;
RGWToken token{from_base64(auth_id)};
if (! token.valid())
if (! ldap.is_applicable()) {
external_auth_result = -EACCES;
} else {
auto applier = ldap.authenticate();
if (!applier) {
external_auth_result = -EACCES;
else {
ldout(store->ctx(), 10)
<< __func__ << " try LDAP auth uri="
<< store->ctx()->_conf->rgw_ldap_uri
<< " token.id=" << token.id
<< dendl;
if (ldh->auth(token.id, token.key) != 0)
external_auth_result = -EACCES;
else {
/* ok, succeeded */
external_auth_result = 0;
/* create local account, if none exists */
s->user->user_id = token.id;
s->user->display_name = token.id; // cn?
int ret = rgw_get_user_info_by_uid(store, s->user->user_id, *(s->user));
if (ret < 0) {
ret = rgw_store_user_info(store, *(s->user), nullptr, nullptr,
real_time(), true);
if (ret < 0) {
dout(10) << "NOTICE: failed to store new user's info: ret=" << ret
<< dendl;
}
}
/* set request perms */
s->perm_mask = RGW_PERM_FULL_CONTROL;
} /* success */
} /* token */
} /* ldap */
} else {
applier->load_acct_info(*s->user);
s->perm_mask = applier->get_perm_mask();
s->auth_identity = std::move(applier);
external_auth_result = 0;
}
}
/* keystone failed (or not enabled); check if we want to use rados backend */
if (!store->ctx()->_conf->rgw_s3_auth_use_rados
@ -3997,7 +3911,7 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s)
return external_auth_result;
}
/* now verify signature */
/* now verify signature */
string auth_hdr;
if (!rgw_create_s3_canonical_header(s->info, &s->header_time, auth_hdr,
qsr)) {
@ -4342,3 +4256,98 @@ RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
op->set_get_data(get_data);
return op;
}
rgw::LDAPHelper* RGWLDAPAuthEngine::ldh = nullptr;
std::mutex RGWLDAPAuthEngine::mtx;
void RGWLDAPAuthEngine::init(CephContext* const cct)
{
if (!ldh) {
std::lock_guard<std::mutex> lck(mtx);
if (!ldh) {
const string& ldap_uri = cct->_conf->rgw_ldap_uri;
const string& ldap_binddn = cct->_conf->rgw_ldap_binddn;
const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn;
const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr;
std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct);
ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
ldap_searchdn, ldap_dnattr);
ldh->init();
ldh->bind();
}
}
}
RGWRemoteAuthApplier::acl_strategy_t RGWLDAPAuthEngine::get_acl_strategy() const
{
//This is based on the assumption that the default acl strategy in
// get_perms_from_aclspec, will take care. Extra acl spec is not required.
return nullptr;
}
RGWRemoteAuthApplier::AuthInfo
RGWLDAPAuthEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
{
return RGWRemoteAuthApplier::AuthInfo {
rgw_user(token.id),
token.id,
RGW_PERM_FULL_CONTROL,
true,
TYPE_LDAP
};
}
bool RGWLDAPAuthEngine::is_applicable() const noexcept
{
if (!RGWTokenBasedAuthEngine::is_applicable()) {
return false;
}
if (!cct->_conf->rgw_s3_auth_use_ldap ||
cct->_conf->rgw_ldap_uri.empty()) {
return false;
}
if(!base64_token.valid()) {
return false;
}
return true;
}
RGWAuthApplier::aplptr_t RGWLDAPAuthEngine::authenticate() const
{
//Check if a user of type other than 'ldap' is already present, if yes, then
//return error.
RGWUserInfo user_info;
user_info.user_id = base64_token.id;
if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
if (user_info.type != TYPE_LDAP) {
ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
return nullptr;
}
}
if (ldh->auth(base64_token.id, base64_token.key) != 0) {
return nullptr;
}
return apl_factory->create_apl_remote(cct, get_acl_strategy(), get_creds_info(base64_token));
}
std::string RGWLDAPTokenExtractor::get_token() const
{
string token = "";
if (!s->http_auth || !(*s->http_auth)) {
token = s->info.args.get("AWSAccessKeyId");
} else {
string auth_str(s->http_auth + 4);
int pos = auth_str.rfind(':');
if (pos >=0 ) {
token = auth_str.substr(0, pos);
}
}
return token;
}

View File

@ -16,6 +16,12 @@
#include "rgw_rest_conn.h"
#include "rgw_ldap.h"
#include "rgw_token.h"
#include "include/assert.h"
#include "rgw_auth.h"
#include "rgw_auth_decoimpl.h"
#define RGW_AUTH_GRACE_MINS 15
void rgw_get_errno_s3(struct rgw_http_errors *e, int err_no);
@ -417,9 +423,6 @@ public:
class RGW_Auth_S3 {
private:
static std::mutex mtx;
static rgw::LDAPHelper* ldh;
static int authorize_v2(RGWRados *store, struct req_state *s);
static int authorize_v4(RGWRados *store, struct req_state *s);
static int authorize_v4_complete(RGWRados *store, struct req_state *s,
@ -428,22 +431,6 @@ private:
public:
static int authorize(RGWRados *store, struct req_state *s);
static int authorize_aws4_auth_complete(RGWRados *store, struct req_state *s);
static inline void init(RGWRados* store) {
if (! ldh) {
std::lock_guard<std::mutex> lck(mtx);
if (! ldh) {
init_impl(store);
}
}
}
static inline rgw::LDAPHelper* get_ldap_ctx(RGWRados* store) {
init(store);
return ldh;
}
static void init_impl(RGWRados* store);
};
class RGWHandler_Auth_S3 : public RGWHandler_REST {
@ -627,4 +614,70 @@ static inline int valid_s3_bucket_name(const string& name, bool relaxed=false)
return 0;
}
class RGWLDAPAuthEngine: RGWTokenBasedAuthEngine
{
static rgw::LDAPHelper* ldh;
static std::mutex mtx;
rgw::RGWToken base64_token;
static void init(CephContext* const cct);
protected:
RGWRados* const store;
const RGWRemoteAuthApplier::Factory * const apl_factory;
RGWRemoteAuthApplier::acl_strategy_t get_acl_strategy() const;
RGWRemoteAuthApplier::AuthInfo get_creds_info(const rgw::RGWToken& token) const noexcept;
public:
RGWLDAPAuthEngine(CephContext* const cct,
RGWRados* const store,
Extractor &ex,
const RGWRemoteAuthApplier::Factory * const apl_factory)
: RGWTokenBasedAuthEngine(cct, ex),
store(store),
apl_factory(apl_factory) {
init(cct);
base64_token = rgw::from_base64(token);
}
const char* get_name() const noexcept override {
return "RGWLDAPAuthEngine";
}
bool is_applicable() const noexcept override;
RGWAuthApplier::aplptr_t authenticate() const override;
};
class RGWLDAPTokenExtractor : public RGWTokenBasedAuthEngine::Extractor {
protected:
const req_state * const s;
public:
RGWLDAPTokenExtractor(const req_state * const s)
: s(s) {
}
std::string get_token() const override;
};
class S3AuthFactory : public RGWRemoteAuthApplier::Factory {
typedef RGWAuthApplier::aplptr_t aplptr_t;
RGWRados * const store;
const std::string acct_override;
public:
S3AuthFactory(RGWRados * const store,
const std::string& acct_override)
: store(store),
acct_override(acct_override) {
}
aplptr_t create_apl_remote(CephContext * const cct,
RGWRemoteAuthApplier::acl_strategy_t&& acl_alg,
const RGWRemoteAuthApplier::AuthInfo info
) const override {
return aplptr_t(
new RGWThirdPartyAccountAuthApplier<RGWRemoteAuthApplier>(
RGWRemoteAuthApplier(cct, store, std::move(acl_alg), info),
store, acct_override));
}
};
#endif /* CEPH_RGW_REST_S3_H */

View File

@ -71,7 +71,7 @@ namespace rgw {
virtual uint32_t version() const { return 1; };
bool valid() {
bool valid() const{
return ((type != TOKEN_NONE) &&
(! id.empty()) &&
(! key.empty()));