msg/async: msgr2: cephx authentication

Signed-off-by: Ricardo Dias <rdias@suse.com>
This commit is contained in:
Ricardo Dias 2018-11-13 15:51:39 +00:00
parent 2063fa9ecf
commit 01b247b110
No known key found for this signature in database
GPG Key ID: 74390C579BD37B68
5 changed files with 276 additions and 119 deletions

View File

@ -1,4 +1,4 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
@ -7,9 +7,9 @@
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*
*/
@ -167,7 +167,7 @@ public:
virtual void ms_handle_fast_accept(Connection *con) {}
/*
* this indicates that the ordered+reliable delivery semantics have
* this indicates that the ordered+reliable delivery semantics have
* been violated. Messages may have been lost due to a fault
* in the network connection.
* Only called on lossy Connections.
@ -187,7 +187,7 @@ public:
* a reference to it.
*/
virtual void ms_handle_remote_reset(Connection *con) = 0;
/**
* This indicates that the connection is both broken and further
* connection attempts are failing because other side refuses
@ -202,6 +202,15 @@ public:
* @defgroup Authentication
* @{
*/
/**
* Return the allowed authentication methods for peer
**/
virtual int ms_get_auth_allowed_methods(
uint32_t peer_type, std::vector<uint32_t> &allowed_methods) {
return 0;
}
/**
* handle successful authentication (msgr2)
*

View File

@ -118,6 +118,37 @@ int Messenger::bindv(const entity_addrvec_t& addrs)
return bind(addrs.legacy_addr());
}
void Messenger::ms_deliver_get_auth_allowed_methods(
int peer_type, std::vector<uint32_t> &allowed_methods)
{
for (const auto &dispatcher : dispatchers) {
if (dispatcher->ms_get_auth_allowed_methods(peer_type, allowed_methods))
return;
}
if (allowed_methods.empty()) {
if (get_mytype() == CEPH_ENTITY_TYPE_MON &&
peer_type != CEPH_ENTITY_TYPE_MON) {
allowed_methods.push_back(CEPH_AUTH_NONE);
return;
}
std::string method;
if (!cct->_conf->auth_supported.empty()) {
method = cct->_conf->auth_supported;
} else if (peer_type == CEPH_ENTITY_TYPE_OSD ||
peer_type == CEPH_ENTITY_TYPE_MDS ||
peer_type == CEPH_ENTITY_TYPE_MON ||
peer_type == CEPH_ENTITY_TYPE_MGR) {
method = cct->_conf->auth_cluster_required;
} else {
method = cct->_conf->auth_client_required;
}
AuthMethodList auth_list(cct, method);
for (auto pt : auth_list.get_supported_set()) {
allowed_methods.push_back(pt);
}
}
}
bool Messenger::ms_deliver_verify_authorizer(
Connection *con,
int peer_type,

View File

@ -1,4 +1,4 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
@ -7,9 +7,9 @@
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*
*/
@ -334,7 +334,7 @@ public:
*
* @param d The Dispatcher to insert into the list.
*/
void add_dispatcher_head(Dispatcher *d) {
void add_dispatcher_head(Dispatcher *d) {
bool first = dispatchers.empty();
dispatchers.push_front(d);
if (d->ms_can_fast_dispatch_any())
@ -349,7 +349,7 @@ public:
*
* @param d The Dispatcher to insert into the list.
*/
void add_dispatcher_tail(Dispatcher *d) {
void add_dispatcher_tail(Dispatcher *d) {
bool first = dispatchers.empty();
dispatchers.push_back(d);
if (d->ms_can_fast_dispatch_any())
@ -745,6 +745,12 @@ public:
}
}
/**
* Get allowed authentication methods for a specific peer type
**/
void ms_deliver_get_auth_allowed_methods(
int peer_type, std::vector<uint32_t> &allowed_methods);
/**
* Get the AuthAuthorizer for a new outgoing Connection.
*

View File

@ -53,6 +53,8 @@ ProtocolV2::ProtocolV2(AsyncConnection *connection)
temp_buffer(nullptr),
state(NONE),
peer_required_features(0),
authorizer(nullptr),
got_bad_auth(false),
cookie(0),
connect_seq(0),
peer_global_seq(0),
@ -65,9 +67,23 @@ ProtocolV2::ProtocolV2(AsyncConnection *connection)
temp_buffer = new char[4096];
}
ProtocolV2::~ProtocolV2() { delete[] temp_buffer; }
ProtocolV2::~ProtocolV2() {
delete[] temp_buffer;
if (authorizer) {
delete authorizer;
}
}
void ProtocolV2::connect() { state = START_CONNECT; }
void ProtocolV2::connect() {
state = START_CONNECT;
got_bad_auth = false;
if (authorizer) {
delete authorizer;
authorizer = nullptr;
}
global_seq = messenger->get_global_seq();
}
void ProtocolV2::accept() { state = START_ACCEPT; }
@ -180,6 +196,14 @@ uint64_t ProtocolV2::discard_requeued_up_to(uint64_t out_seq, uint64_t seq) {
}
void ProtocolV2::reset_recv_state() {
if (state == CONNECTING) {
if (authorizer) {
delete authorizer;
}
authorizer = nullptr;
got_bad_auth = false;
}
// clean read and write callbacks
connection->pendingReadLen.reset();
connection->writeCallback.reset();
@ -863,39 +887,52 @@ CtPtr ProtocolV2::handle_auth_more(char *payload, uint32_t length) {
ldout(cct, 20) << __func__ << " payload_len=" << length << dendl;
AuthMoreFrame auth_more(payload, length);
ldout(cct, 1) << __func__ << " auth more len=" << auth_more.len << dendl;
ldout(cct, 1) << __func__
<< " auth more len=" << auth_more.auth_payload.length()
<< dendl;
/* BEGIN TO REMOVE */
auto p = auth_more.auth_payload.cbegin();
int32_t i;
std::string s;
try {
decode(i, p);
decode(s, p);
} catch (const buffer::error &e) {
lderr(cct) << __func__ << " decode auth_payload failed" << dendl;
return _fault();
if (state == CONNECTING) {
ldout(cct, 10) << __func__ << " connect got auth challenge" << dendl;
ceph_assert(authorizer);
authorizer->add_challenge(cct, auth_more.auth_payload);
AuthMoreFrame more_reply(authorizer->bl);
return WRITE(more_reply.get_buffer(), handle_auth_more_write);
} else if (state == ACCEPTING) {
connection->lock.unlock();
bool authorizer_valid;
bufferlist authorizer_reply;
ceph_assert((bool)authorizer_challenge);
if (!messenger->ms_deliver_verify_authorizer(
connection, connection->peer_type, auth_method,
auth_more.auth_payload, authorizer_reply, authorizer_valid,
session_key, &authorizer_challenge) ||
!authorizer_valid) {
connection->lock.lock();
ldout(cct, 0) << __func__ << " got bad authorizer, auth_reply_len="
<< authorizer_reply.length() << dendl;
session_security.reset();
AuthBadAuthFrame bad_auth(EPERM, "Bad Authorizer");
bufferlist &bl = bad_auth.get_buffer();
return WRITE(bl, handle_auth_bad_auth_write);
}
connection->lock.lock();
session_security.reset(get_auth_session_handler(
cct, auth_method, session_key,
CEPH_FEATURE_MSG_AUTH | CEPH_FEATURE_CEPHX_V2));
AuthDoneFrame auth_done(0, authorizer_reply);
bufferlist &bl = auth_done.get_buffer();
return WRITE(bl, handle_auth_done_write);
} else {
ceph_abort();
}
ldout(cct, 10) << __func__ << " (TO REMOVE) auth_more (" << (int32_t)i << ", "
<< s << ")" << dendl;
if (i == 45 && s == "hello server more") {
bufferlist auth_bl;
encode((int32_t)55, auth_bl, 0);
std::string hello("hello client more");
encode(hello, auth_bl, 0);
/* END TO REMOVE */
AuthMoreFrame more(auth_bl);
bufferlist &bl = more.get_buffer();
return WRITE(bl, handle_auth_more_write);
}
/* END TO REMOVE */
AuthDoneFrame auth_done(0);
bufferlist &bl = auth_done.get_buffer();
return WRITE(bl, handle_auth_done_write);
return nullptr;
}
CtPtr ProtocolV2::handle_auth_more_write(int r) {
@ -1412,8 +1449,6 @@ CtPtr ProtocolV2::start_client_banner_exchange() {
ldout(cct, 20) << __func__ << dendl;
state = CONNECTING;
global_seq = messenger->get_global_seq();
return _banner_exchange(CONTINUATION(post_client_banner_exchange));
}
@ -1426,28 +1461,49 @@ CtPtr ProtocolV2::post_client_banner_exchange() {
return send_auth_request();
}
CtPtr ProtocolV2::send_auth_request(std::vector<__u32> allowed_methods) {
CtPtr ProtocolV2::send_auth_request(std::vector<uint32_t> allowed_methods) {
ldout(cct, 20) << __func__ << dendl;
// We need to get an authorizer at this point.
// this->messenger->get_authorizer(...)
bufferlist auth_bl;
/* BEGIN TO REMOVE */
encode((int32_t)35, auth_bl, 0);
std::string hello("hello");
encode(hello, auth_bl, 0);
/* END TO REMOVE */
__le32 method;
if (allowed_methods.empty()) {
// choose client's preferred method
method = 23; // 23 is just for testing purposes (REMOVE THIS)
} else {
// choose one of the allowed methods
method = allowed_methods[0];
if (!authorizer) {
authorizer =
messenger->ms_deliver_get_authorizer(connection->peer_type);
}
AuthRequestFrame authFrame(method, auth_bl);
auth_method = CEPH_AUTH_NONE;
if (!authorizer) {
if (!allowed_methods.empty() &&
std::find(allowed_methods.begin(), allowed_methods.end(),
auth_method) == allowed_methods.end()) {
ldout(cct, 0) << __func__
<< " peer requires authentication, stopping connection"
<< dendl;
stop();
connection->dispatch_queue->queue_reset(connection);
return nullptr;
}
ldout(cct, 10) << __func__ << " authorizer not found for peer_type="
<< connection->peer_type << dendl;
AuthRequestFrame authFrame(auth_method);
bufferlist &bl = authFrame.get_buffer();
return WRITE(bl, handle_auth_request_write);
}
auth_method = authorizer->protocol;
if (!allowed_methods.empty() &&
std::find(allowed_methods.begin(), allowed_methods.end(), auth_method) ==
allowed_methods.end()) {
ldout(cct, 0) << __func__ << " peer does not allow authentication method="
<< auth_method << dendl;
stop();
connection->dispatch_queue->queue_reset(connection);
return nullptr;
}
ldout(cct, 10) << __func__
<< " sending auth request len=" << authorizer->bl.length()
<< dendl;
AuthRequestFrame authFrame(auth_method, authorizer->bl);
bufferlist &bl = authFrame.get_buffer();
return WRITE(bl, handle_auth_request_write);
}
@ -1483,16 +1539,53 @@ CtPtr ProtocolV2::handle_auth_bad_auth(char *payload, uint32_t length) {
<< " error code=" << bad_auth.error_code
<< " error message=" << bad_auth.error_msg << dendl;
return _fault();
if (got_bad_auth) {
return _fault();
}
got_bad_auth = true;
delete authorizer;
authorizer = messenger->ms_deliver_get_authorizer(connection->peer_type,
true); // try harder
ldout(cct, 10) << __func__
<< " sending auth request len=" << authorizer->bl.length()
<< dendl;
AuthRequestFrame authFrame(auth_method, authorizer->bl);
bufferlist &bl = authFrame.get_buffer();
return WRITE(bl, handle_auth_request_write);
}
CtPtr ProtocolV2::handle_auth_done(char *payload, uint32_t length) {
ldout(cct, 20) << __func__ << " payload_len=" << length << dendl;
AuthDoneFrame auth_done(payload, length);
if (authorizer) {
auto iter = auth_done.auth_payload.cbegin();
if (!authorizer->verify_reply(iter)) {
ldout(cct, 0) << __func__ << " failed verifying authorize reply" << dendl;
return _fault();
}
}
ldout(cct, 1) << __func__ << " authentication done,"
<< " flags=" << auth_done.flags << dendl;
if (authorizer) {
ldout(cct, 10) << __func__ << " setting up session_security with auth "
<< authorizer << dendl;
session_security.reset(get_auth_session_handler(
cct, authorizer->protocol, authorizer->session_key,
CEPH_FEATURE_MSG_AUTH | CEPH_FEATURE_CEPHX_V2));
} else {
// We have no authorizer, so we shouldn't be applying security to messages
// in this connection.
ldout(cct, 10) << __func__ << " no authorizer, clearing session_security"
<< dendl;
session_security.reset();
}
if (!cookie) {
ceph_assert(connect_seq == 0);
return send_client_ident();
@ -1707,42 +1800,12 @@ CtPtr ProtocolV2::handle_auth_request(char *payload, uint32_t length) {
ldout(cct, 10) << __func__ << " AuthRequest(method=" << auth_request.method
<< ", auth_len=" << auth_request.len << ")" << dendl;
/* BEGIN TO REMOVE */
auto p = auth_request.auth_payload.cbegin();
int32_t i;
std::string s;
try {
decode(i, p);
decode(s, p);
} catch (const buffer::error &e) {
lderr(cct) << __func__ << " decode auth_payload failed" << dendl;
return _fault();
}
ldout(cct, 10) << __func__ << " (TO REMOVE) auth_payload (" << (int32_t)i
<< ", " << s << ")" << dendl;
/* END TO REMOVE */
/*
* Get the auth methods from somewhere.
* In V1 the allowed auth methods depend on the peer_type.
* In V2, at this stage, we still don't know the peer_type so either
* we define the set of allowed auth methods for any entity type,
* or we need to exchange the entity type before reaching this point.
*/
std::vector<__u32> allowed_methods = {CEPH_AUTH_NONE, CEPH_AUTH_CEPHX};
bool found = false;
for (const auto &a_method : allowed_methods) {
if (a_method == auth_request.method) {
// auth method allowed by the server
found = true;
break;
}
}
std::vector<uint32_t> allowed_methods;
messenger->ms_deliver_get_auth_allowed_methods(connection->peer_type,
allowed_methods);
bool found = std::find(allowed_methods.begin(), allowed_methods.end(),
auth_request.method) != allowed_methods.end();
if (!found) {
ldout(cct, 1) << __func__ << " auth method=" << auth_request.method
<< " not allowed" << dendl;
@ -1753,24 +1816,54 @@ CtPtr ProtocolV2::handle_auth_request(char *payload, uint32_t length) {
ldout(cct, 10) << __func__ << " auth method=" << auth_request.method
<< " accepted" << dendl;
// verify authorization blob
bool valid = i == 35;
if (!valid) {
AuthBadAuthFrame bad_auth(12, "Permission denied");
bufferlist &bl = bad_auth.get_buffer();
return WRITE(bl, handle_auth_bad_auth_write);
auth_method = auth_request.method;
if (auth_request.method == CEPH_AUTH_NONE) {
ldout(cct, 1) << __func__ << " proceeding without authentication" << dendl;
session_security.reset();
bufferlist empty_bl;
AuthDoneFrame auth_done(0, empty_bl);
bufferlist &bl = auth_done.get_buffer();
return WRITE(bl, handle_auth_done_write);
}
bufferlist auth_bl;
/* BEGIN TO REMOVE */
encode((int32_t)45, auth_bl, 0);
std::string hello("hello server more");
encode(hello, auth_bl, 0);
/* END TO REMOVE */
AuthMoreFrame more(auth_bl);
bufferlist &bl = more.get_buffer();
return WRITE(bl, handle_auth_more_write);
connection->lock.unlock();
bool authorizer_valid;
bufferlist authorizer_reply;
bool had_challenge = (bool)authorizer_challenge;
if (!messenger->ms_deliver_verify_authorizer(
connection, connection->peer_type, auth_request.method,
auth_request.auth_payload, authorizer_reply, authorizer_valid,
session_key, &authorizer_challenge) ||
!authorizer_valid) {
connection->lock.lock();
if (!had_challenge && authorizer_challenge) {
ldout(cct, 10) << __func__ << " challenging authorizer" << dendl;
ceph_assert(authorizer_reply.length());
AuthMoreFrame more(authorizer_reply);
return WRITE(more.get_buffer(), handle_auth_more_write);
} else {
ldout(cct, 0) << __func__ << " got bad authorizer, auth_reply_len="
<< authorizer_reply.length() << dendl;
session_security.reset();
AuthBadAuthFrame bad_auth(EPERM, "Bad Authorizer");
bufferlist &bl = bad_auth.get_buffer();
return WRITE(bl, handle_auth_bad_auth_write);
}
}
connection->lock.lock();
session_security.reset(
get_auth_session_handler(cct, auth_method, session_key,
CEPH_FEATURE_MSG_AUTH | CEPH_FEATURE_CEPHX_V2));
AuthDoneFrame auth_done(0, authorizer_reply);
bufferlist &bl = auth_done.get_buffer();
return WRITE(bl, handle_auth_done_write);
}
CtPtr ProtocolV2::handle_auth_bad_method_write(int r) {

View File

@ -104,6 +104,11 @@ private:
payload.claim_append(auth_payload);
}
AuthRequestFrame(uint32_t method) : Frame(Tag::AUTH_REQUEST) {
encode(method, payload, 0);
encode((uint32_t)0, payload, 0);
}
AuthRequestFrame(char *payload, uint32_t length) : Frame() {
method = *(uint32_t *)payload;
len = *(uint32_t *)(payload + sizeof(uint32_t));
@ -114,9 +119,9 @@ private:
struct AuthBadMethodFrame : public Frame {
uint32_t method;
std::vector<__u32> allowed_methods;
std::vector<uint32_t> allowed_methods;
AuthBadMethodFrame(uint32_t method, std::vector<__u32> methods)
AuthBadMethodFrame(uint32_t method, std::vector<uint32_t> methods)
: Frame(Tag::AUTH_BAD_METHOD) {
encode(method, payload, 0);
encode((uint32_t)methods.size(), payload, 0);
@ -154,7 +159,6 @@ private:
};
struct AuthMoreFrame : public Frame {
uint32_t len;
bufferlist auth_payload;
AuthMoreFrame(bufferlist &auth_payload) : Frame(Tag::AUTH_MORE) {
@ -163,7 +167,7 @@ private:
}
AuthMoreFrame(char *payload, uint32_t length) : Frame() {
len = *(uint32_t *)payload;
uint32_t len = *(uint32_t *)payload;
ceph_assert((length - sizeof(uint32_t)) == len);
auth_payload.append(payload + sizeof(uint32_t), len);
}
@ -171,13 +175,21 @@ private:
struct AuthDoneFrame : public Frame {
uint64_t flags;
bufferlist auth_payload;
AuthDoneFrame(uint64_t flags) : Frame(Tag::AUTH_DONE) {
AuthDoneFrame(uint64_t flags, bufferlist &auth_payload)
: Frame(Tag::AUTH_DONE) {
encode(flags, payload, 0);
encode(auth_payload.length(), payload, 0);
payload.claim_append(auth_payload);
}
AuthDoneFrame(char *payload, uint32_t length) : Frame() {
flags = *(uint64_t *)payload;
payload += sizeof(uint64_t);
uint32_t len = *(uint32_t *)payload;
ceph_assert((length - sizeof(uint32_t) - sizeof(uint64_t)) == len);
auth_payload.append(payload + sizeof(uint32_t), len);
}
};
@ -435,6 +447,12 @@ private:
char *temp_buffer;
State state;
uint64_t peer_required_features;
AuthAuthorizer *authorizer;
uint32_t auth_method;
bool got_bad_auth;
CryptoKey session_key;
std::shared_ptr<AuthSessionHandler> session_security;
std::unique_ptr<AuthAuthorizerChallenge> authorizer_challenge;
uint64_t connection_features;
uint64_t cookie;
uint64_t global_seq;
@ -562,7 +580,7 @@ private:
Ct<ProtocolV2> *start_client_banner_exchange();
Ct<ProtocolV2> *post_client_banner_exchange();
Ct<ProtocolV2> *send_auth_request(std::vector<__u32> allowed_methods = {});
Ct<ProtocolV2> *send_auth_request(std::vector<uint32_t> allowed_methods = {});
Ct<ProtocolV2> *handle_auth_request_write(int r);
Ct<ProtocolV2> *handle_auth_bad_method(char *payload, uint32_t length);
Ct<ProtocolV2> *handle_auth_bad_auth(char *payload, uint32_t length);