1586 lines
60 KiB
Diff
1586 lines
60 KiB
Diff
From f3a4166379b12d4a7bba667fe761e5b660552db1 Mon Sep 17 00:00:00 2001
|
|
From: Ilya Dryomov <idryomov@gmail.com>
|
|
Date: Apr, 15 May 2021 08:40:32 +0800
|
|
copy-by: https://github.com/ceph/ceph/commit/f3a4166379b12d4a7bba667fe761e5b660552db1
|
|
* CVE-2021-20288:
|
|
qa/standalone: default to disable insecure global id reclaim
|
|
cephadm: set auth_allow_insecure_global_id_reclaim for mon on bootstrap
|
|
mon/HealthMonitor: raise AUTH_INSECURE_GLOBAL_ID_RENEWAL[_ALLOWED]
|
|
auth/cephx: ignore CEPH_ENTITY_TYPE_AUTH in requested keys
|
|
auth/cephx: rotate auth tickets less often
|
|
mon: fail fast when unauthorized global_id (re)use is disallowed
|
|
auth/cephx: option to disallow unauthorized global_id (re)use
|
|
auth/cephx: make cephx_decode_ticket() take a const ticket_blob
|
|
auth/AuthServiceHandler: keep track of global_id and whether it is new
|
|
auth/AuthServiceHandler: build_cephx_response_header() is cephx-specific
|
|
auth/AuthServiceHandler: drop unused start_session() args
|
|
mon/MonClient: drop global_id arg from _add_conn() and _add_conns()
|
|
mon/MonClient: reset auth state in shutdown()
|
|
mon/MonClient: preserve auth state on reconnects
|
|
mon/MonClient: claim active_con's auth explicitly
|
|
mon/MonClient: resurrect "waiting for monmap|config" timeouts
|
|
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
|
|
Reviewed-by: Sage Weil <sage@redhat.com>
|
|
Subject: [PATCH] fix CVE 2021 20288
|
|
|
|
---
|
|
doc/rados/operations/health-checks.rst | 61 +++++++
|
|
qa/standalone/ceph-helpers.sh | 1 +
|
|
src/auth/AuthClientHandler.cc | 2 +-
|
|
src/auth/AuthClientHandler.h | 9 +-
|
|
src/auth/AuthServiceHandler.cc | 42 +++++
|
|
src/auth/AuthServiceHandler.h | 45 ++++-
|
|
src/auth/AuthSessionHandler.cc | 4 +
|
|
src/auth/Crypto.cc | 5 +-
|
|
src/auth/cephx/CephxClientHandler.cc | 6 -
|
|
src/auth/cephx/CephxClientHandler.h | 6 +-
|
|
src/auth/cephx/CephxKeyServer.cc | 78 ++++-----
|
|
src/auth/cephx/CephxKeyServer.h | 26 +--
|
|
src/auth/cephx/CephxProtocol.cc | 6 +-
|
|
src/auth/cephx/CephxProtocol.h | 20 ++-
|
|
src/auth/cephx/CephxServiceHandler.cc | 166 ++++++++++++++-----
|
|
src/auth/cephx/CephxServiceHandler.h | 20 ++-
|
|
src/auth/none/AuthNoneClientHandler.h | 8 +-
|
|
src/auth/none/AuthNoneServiceHandler.h | 17 +-
|
|
src/auth/unknown/AuthUnknownServiceHandler.h | 10 +-
|
|
src/common/legacy_config_opts.h | 2 +
|
|
src/common/options.cc | 34 +++-
|
|
src/mon/AuthMonitor.cc | 14 +-
|
|
src/mon/HealthMonitor.cc | 38 +++++
|
|
src/mon/MonClient.cc | 24 ++-
|
|
src/mon/MonClient.h | 4 +-
|
|
src/mon/Monitor.cc | 63 +++++--
|
|
src/mon/MonitorDBStore.h | 2 +-
|
|
src/mon/Session.h | 23 ++-
|
|
28 files changed, 578 insertions(+), 158 deletions(-)
|
|
|
|
diff --git a/doc/rados/operations/health-checks.rst b/doc/rados/operations/health-checks.rst
|
|
index c1e22004..4def3fdf 100644
|
|
--- a/doc/rados/operations/health-checks.rst
|
|
+++ b/doc/rados/operations/health-checks.rst
|
|
@@ -21,6 +21,67 @@ that are defined by ceph-mgr python modules.
|
|
Definitions
|
|
===========
|
|
|
|
+Monitor
|
|
+-------
|
|
+
|
|
+AUTH_INSECURE_GLOBAL_ID_RECLAIM
|
|
+_______________________________
|
|
+
|
|
+One or more clients or daemons are connected to the cluster that are
|
|
+not securely reclaiming their global_id (a unique number identifying
|
|
+each entity in the cluster) when reconnecting to a monitor. The
|
|
+client is being permitted to connect anyway because the
|
|
+``auth_allow_insecure_global_id_reclaim`` option is set to true (which may
|
|
+be necessary until all ceph clients have been upgraded), and the
|
|
+``auth_expose_insecure_global_id_reclaim`` option set to ``true`` (which
|
|
+allows monitors to detect clients with insecure reclaim early by forcing them to
|
|
+reconnect right after they first authenticate).
|
|
+
|
|
+You can identify which client(s) are using unpatched ceph client code with::
|
|
+
|
|
+ ceph health detail
|
|
+
|
|
+Clients global_id reclaim rehavior can also seen in the
|
|
+``global_id_status`` field in the dump of clients connected to an
|
|
+individual monitor (``reclaim_insecure`` means the client is
|
|
+unpatched and is contributing to this health alert)::
|
|
+
|
|
+ ceph daemon mon.<id> sessions
|
|
+
|
|
+We strongly recommend that all clients in the system are upgraded to a
|
|
+newer version of Ceph that correctly reclaims global_id values. Once
|
|
+all clients have been updated, you can stop allowing insecure reconnections
|
|
+by setting the following option in the ``[mon]`` section of ``ceph.conf``::
|
|
+
|
|
+ auth_allow_insecure_global_id_reclaim = false
|
|
+
|
|
+Although we do NOT recommend doing so, you can also disable this warning indefinitely
|
|
+by setting the following option in the ``[mon]`` section of ``ceph.conf``::
|
|
+
|
|
+ mon_warn_on_insecure_global_id_reclaim = false
|
|
+
|
|
+AUTH_INSECURE_GLOBAL_ID_RECLAIM_ALLOWED
|
|
+_______________________________________
|
|
+
|
|
+Ceph is currently configured to allow clients to reconnect to monitors using
|
|
+an insecure process to reclaim their previous global_id because the setting
|
|
+``auth_allow_insecure_global_id_reclaim`` is set to ``true``. It may be necessary to
|
|
+leave this setting enabled while existing Ceph clients are upgraded to newer
|
|
+versions of Ceph that correctly and securely reclaim their global_id.
|
|
+
|
|
+If the ``AUTH_INSECURE_GLOBAL_ID_RECLAIM`` health alert has not also been raised and
|
|
+the ``auth_expose_insecure_global_id_reclaim`` setting has not been disabled (it is
|
|
+on by default), then there are currently no clients connected that need to be
|
|
+upgraded, and it is safe to disallow insecure global_id reclaim by setting the
|
|
+following option in the ``[mon]`` section of ``ceph.conf``::
|
|
+
|
|
+ auth_allow_insecure_global_id_reclaim = false
|
|
+
|
|
+Although we do NOT recommend doing so, you can also disable this warning indefinitely
|
|
+by setting the following option in the ``[mon]`` section of ``ceph.conf``::
|
|
+
|
|
+ mon_warn_on_insecure_global_id_reclaim_allowed = false
|
|
+
|
|
|
|
OSDs
|
|
----
|
|
diff --git a/qa/standalone/ceph-helpers.sh b/qa/standalone/ceph-helpers.sh
|
|
index f12f0698..ac2ac180 100755
|
|
--- a/qa/standalone/ceph-helpers.sh
|
|
+++ b/qa/standalone/ceph-helpers.sh
|
|
@@ -461,6 +461,7 @@ function run_mon() {
|
|
--pid-file=$dir/\$name.pid \
|
|
--mon-allow-pool-delete \
|
|
--mon-osd-backfillfull-ratio .99 \
|
|
+ --mon-warn-on-insecure-global-id-reclaim-allowed=false \
|
|
"$@" || return 1
|
|
|
|
cat > $dir/ceph.conf <<EOF
|
|
diff --git a/src/auth/AuthClientHandler.cc b/src/auth/AuthClientHandler.cc
|
|
index a76d1e4c..493ea982 100644
|
|
--- a/src/auth/AuthClientHandler.cc
|
|
+++ b/src/auth/AuthClientHandler.cc
|
|
@@ -26,7 +26,7 @@ AuthClientHandler *get_auth_client_handler(CephContext *cct, int proto,
|
|
case CEPH_AUTH_CEPHX:
|
|
return new CephxClientHandler(cct, rkeys);
|
|
case CEPH_AUTH_NONE:
|
|
- return new AuthNoneClientHandler(cct, rkeys);
|
|
+ return new AuthNoneClientHandler{cct};
|
|
default:
|
|
return NULL;
|
|
}
|
|
diff --git a/src/auth/AuthClientHandler.h b/src/auth/AuthClientHandler.h
|
|
index b397f883..ecae5e63 100644
|
|
--- a/src/auth/AuthClientHandler.h
|
|
+++ b/src/auth/AuthClientHandler.h
|
|
@@ -17,7 +17,6 @@
|
|
|
|
|
|
#include "auth/Auth.h"
|
|
-#include "common/RWLock.h"
|
|
|
|
class CephContext;
|
|
struct MAuthReply;
|
|
@@ -31,18 +30,18 @@ protected:
|
|
uint32_t want;
|
|
uint32_t have;
|
|
uint32_t need;
|
|
- RWLock lock;
|
|
|
|
public:
|
|
explicit AuthClientHandler(CephContext *cct_)
|
|
- : cct(cct_), global_id(0), want(CEPH_ENTITY_TYPE_AUTH), have(0), need(0),
|
|
- lock("AuthClientHandler::lock") {}
|
|
+ : cct(cct_), global_id(0), want(CEPH_ENTITY_TYPE_AUTH), have(0), need(0)
|
|
+ {}
|
|
virtual ~AuthClientHandler() {}
|
|
|
|
+ virtual AuthClientHandler* clone() const = 0;
|
|
+
|
|
void init(const EntityName& n) { name = n; }
|
|
|
|
void set_want_keys(__u32 keys) {
|
|
- RWLock::WLocker l(lock);
|
|
want = keys | CEPH_ENTITY_TYPE_AUTH;
|
|
validate_tickets();
|
|
}
|
|
diff --git a/src/auth/AuthServiceHandler.cc b/src/auth/AuthServiceHandler.cc
|
|
index bd265c4f..73848e4e 100644
|
|
--- a/src/auth/AuthServiceHandler.cc
|
|
+++ b/src/auth/AuthServiceHandler.cc
|
|
@@ -15,10 +15,52 @@
|
|
#include "AuthServiceHandler.h"
|
|
#include "cephx/CephxServiceHandler.h"
|
|
#include "none/AuthNoneServiceHandler.h"
|
|
+#include "common/dout.h"
|
|
|
|
#define dout_subsys ceph_subsys_auth
|
|
|
|
|
|
+std::ostream& operator<<(std::ostream& os,
|
|
+ global_id_status_t global_id_status)
|
|
+{
|
|
+ switch (global_id_status) {
|
|
+ case global_id_status_t::NONE:
|
|
+ return os << "none";
|
|
+ case global_id_status_t::NEW_PENDING:
|
|
+ return os << "new_pending";
|
|
+ case global_id_status_t::NEW_OK:
|
|
+ return os << "new_ok";
|
|
+ case global_id_status_t::NEW_NOT_EXPOSED:
|
|
+ return os << "new_not_exposed";
|
|
+ case global_id_status_t::RECLAIM_PENDING:
|
|
+ return os << "reclaim_pending";
|
|
+ case global_id_status_t::RECLAIM_OK:
|
|
+ return os << "reclaim_ok";
|
|
+ case global_id_status_t::RECLAIM_INSECURE:
|
|
+ return os << "reclaim_insecure";
|
|
+ default:
|
|
+ ceph_abort();
|
|
+ }
|
|
+}
|
|
+
|
|
+int AuthServiceHandler::start_session(const EntityName& entity_name,
|
|
+ uint64_t global_id,
|
|
+ bool is_new_global_id,
|
|
+ bufferlist& result,
|
|
+ AuthCapsInfo& caps)
|
|
+{
|
|
+ ceph_assert(!this->entity_name.get_type() && !this->global_id &&
|
|
+ global_id_status == global_id_status_t::NONE);
|
|
+
|
|
+ ldout(cct, 10) << __func__ << " entity_name=" << entity_name
|
|
+ << " global_id=" << global_id << " is_new_global_id="
|
|
+ << is_new_global_id << dendl;
|
|
+ this->entity_name = entity_name;
|
|
+ this->global_id = global_id;
|
|
+
|
|
+ return do_start_session(is_new_global_id, result, caps);
|
|
+}
|
|
+
|
|
AuthServiceHandler *get_auth_service_handler(int type, CephContext *cct, KeyServer *ks)
|
|
{
|
|
switch (type) {
|
|
diff --git a/src/auth/AuthServiceHandler.h b/src/auth/AuthServiceHandler.h
|
|
index 4d8a6493..ae40adb7 100644
|
|
--- a/src/auth/AuthServiceHandler.h
|
|
+++ b/src/auth/AuthServiceHandler.h
|
|
@@ -24,21 +24,54 @@ class CephContext;
|
|
class KeyServer;
|
|
struct AuthCapsInfo;
|
|
|
|
+enum class global_id_status_t {
|
|
+ NONE,
|
|
+ // fresh client (global_id == 0); waiting for CephXAuthenticate
|
|
+ NEW_PENDING,
|
|
+ // connected client; new enough to correctly reclaim global_id
|
|
+ NEW_OK,
|
|
+ // connected client; unknown whether it can reclaim global_id correctly
|
|
+ NEW_NOT_EXPOSED,
|
|
+ // reconnecting client (global_id != 0); waiting for CephXAuthenticate
|
|
+ RECLAIM_PENDING,
|
|
+ // reconnected client; correctly reclaimed global_id
|
|
+ RECLAIM_OK,
|
|
+ // reconnected client; did not properly prove prior global_id ownership
|
|
+ RECLAIM_INSECURE
|
|
+};
|
|
+
|
|
+std::ostream& operator<<(std::ostream& os,
|
|
+ global_id_status_t global_id_status);
|
|
+
|
|
struct AuthServiceHandler {
|
|
protected:
|
|
CephContext *cct;
|
|
-public:
|
|
EntityName entity_name;
|
|
- uint64_t global_id;
|
|
+ uint64_t global_id = 0;
|
|
+ global_id_status_t global_id_status = global_id_status_t::NONE;
|
|
|
|
- explicit AuthServiceHandler(CephContext *cct_) : cct(cct_), global_id(0) {}
|
|
+public:
|
|
+ explicit AuthServiceHandler(CephContext *cct_) : cct(cct_) {}
|
|
|
|
virtual ~AuthServiceHandler() { }
|
|
|
|
- virtual int start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result, AuthCapsInfo& caps) = 0;
|
|
- virtual int handle_request(bufferlist::iterator& indata, bufferlist& result, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid = NULL) = 0;
|
|
+ int start_session(const EntityName& name,
|
|
+ uint64_t global_id,
|
|
+ bool is_new_global_id,
|
|
+ bufferlist& result,
|
|
+ AuthCapsInfo& caps);
|
|
+ virtual int handle_request(bufferlist::iterator& indata,
|
|
+ bufferlist& result,
|
|
+ AuthCapsInfo& caps,
|
|
+ uint64_t *auid = NULL) = 0;
|
|
+ const EntityName& get_entity_name() { return entity_name; }
|
|
+ uint64_t get_global_id() { return global_id; }
|
|
+ global_id_status_t get_global_id_status() { return global_id_status; }
|
|
|
|
- EntityName& get_entity_name() { return entity_name; }
|
|
+private:
|
|
+ virtual int do_start_session(bool is_new_global_id,
|
|
+ bufferlist& result,
|
|
+ AuthCapsInfo& caps) = 0;
|
|
};
|
|
|
|
extern AuthServiceHandler *get_auth_service_handler(int type, CephContext *cct, KeyServer *ks);
|
|
diff --git a/src/auth/AuthSessionHandler.cc b/src/auth/AuthSessionHandler.cc
|
|
index ab46b60c..76a499c2 100644
|
|
--- a/src/auth/AuthSessionHandler.cc
|
|
+++ b/src/auth/AuthSessionHandler.cc
|
|
@@ -30,6 +30,10 @@ AuthSessionHandler *get_auth_session_handler(CephContext *cct, int protocol, Cry
|
|
|
|
switch (protocol) {
|
|
case CEPH_AUTH_CEPHX:
|
|
+
|
|
+ if (key.get_type() == CEPH_CRYPTO_NONE) {
|
|
+ return nullptr;
|
|
+ }
|
|
return new CephxSessionHandler(cct, key, features);
|
|
case CEPH_AUTH_NONE:
|
|
return new AuthNoneSessionHandler(cct, key);
|
|
diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc
|
|
index 0186b7b2..150052bf 100644
|
|
--- a/src/auth/Crypto.cc
|
|
+++ b/src/auth/Crypto.cc
|
|
@@ -12,6 +12,9 @@
|
|
*/
|
|
|
|
#include <sstream>
|
|
+#include <limits>
|
|
+#include <fcntl.h>
|
|
+
|
|
#include "Crypto.h"
|
|
#ifdef USE_CRYPTOPP
|
|
# include <cryptopp/modes.h>
|
|
@@ -37,7 +40,7 @@
|
|
|
|
int get_random_bytes(char *buf, int len)
|
|
{
|
|
- int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY));
|
|
+ int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY|O_CLOEXEC));
|
|
if (fd < 0)
|
|
return -errno;
|
|
int ret = safe_read_exact(fd, buf, len);
|
|
diff --git a/src/auth/cephx/CephxClientHandler.cc b/src/auth/cephx/CephxClientHandler.cc
|
|
index 89d42903..5b97917d 100644
|
|
--- a/src/auth/cephx/CephxClientHandler.cc
|
|
+++ b/src/auth/cephx/CephxClientHandler.cc
|
|
@@ -31,8 +31,6 @@ int CephxClientHandler::build_request(bufferlist& bl) const
|
|
{
|
|
ldout(cct, 10) << "build_request" << dendl;
|
|
|
|
- RWLock::RLocker l(lock);
|
|
-
|
|
if (need & CEPH_ENTITY_TYPE_AUTH) {
|
|
/* authenticate */
|
|
CephXRequestHeader header;
|
|
@@ -109,7 +107,6 @@ bool CephxClientHandler::_need_tickets() const
|
|
int CephxClientHandler::handle_response(int ret, bufferlist::iterator& indata)
|
|
{
|
|
ldout(cct, 10) << "handle_response ret = " << ret << dendl;
|
|
- RWLock::WLocker l(lock);
|
|
|
|
if (ret < 0)
|
|
return ret; // hrm!
|
|
@@ -203,7 +200,6 @@ int CephxClientHandler::handle_response(int ret, bufferlist::iterator& indata)
|
|
|
|
AuthAuthorizer *CephxClientHandler::build_authorizer(uint32_t service_id) const
|
|
{
|
|
- RWLock::RLocker l(lock);
|
|
ldout(cct, 10) << "build_authorizer for service " << ceph_entity_type_name(service_id) << dendl;
|
|
return tickets.build_authorizer(service_id);
|
|
}
|
|
@@ -220,7 +216,6 @@ bool CephxClientHandler::build_rotating_request(bufferlist& bl) const
|
|
|
|
void CephxClientHandler::prepare_build_request()
|
|
{
|
|
- RWLock::WLocker l(lock);
|
|
ldout(cct, 10) << "validate_tickets: want=" << want << " need=" << need
|
|
<< " have=" << have << dendl;
|
|
validate_tickets();
|
|
@@ -238,7 +233,6 @@ void CephxClientHandler::validate_tickets()
|
|
|
|
bool CephxClientHandler::need_tickets()
|
|
{
|
|
- RWLock::WLocker l(lock);
|
|
validate_tickets();
|
|
|
|
ldout(cct, 20) << "need_tickets: want=" << want
|
|
diff --git a/src/auth/cephx/CephxClientHandler.h b/src/auth/cephx/CephxClientHandler.h
|
|
index 9c652224..a6539b97 100644
|
|
--- a/src/auth/cephx/CephxClientHandler.h
|
|
+++ b/src/auth/cephx/CephxClientHandler.h
|
|
@@ -47,8 +47,11 @@ public:
|
|
reset();
|
|
}
|
|
|
|
+ CephxClientHandler* clone() const override {
|
|
+ return new CephxClientHandler(*this);
|
|
+ }
|
|
+
|
|
void reset() override {
|
|
- RWLock::WLocker l(lock);
|
|
starting = true;
|
|
server_challenge = 0;
|
|
}
|
|
@@ -64,7 +67,6 @@ public:
|
|
bool need_tickets() override;
|
|
|
|
void set_global_id(uint64_t id) override {
|
|
- RWLock::WLocker l(lock);
|
|
global_id = id;
|
|
tickets.global_id = id;
|
|
}
|
|
diff --git a/src/auth/cephx/CephxKeyServer.cc b/src/auth/cephx/CephxKeyServer.cc
|
|
index e06de660..5f530271 100644
|
|
--- a/src/auth/cephx/CephxKeyServer.cc
|
|
+++ b/src/auth/cephx/CephxKeyServer.cc
|
|
@@ -22,7 +22,8 @@
|
|
#define dout_prefix *_dout << "cephx keyserverdata: "
|
|
|
|
bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
|
|
- ExpiringCryptoKey& secret, uint64_t& secret_id) const
|
|
+ CryptoKey& secret, uint64_t& secret_id,
|
|
+ double& ttl) const
|
|
{
|
|
map<uint32_t, RotatingSecrets>::const_iterator iter =
|
|
rotating_secrets.find(service_id);
|
|
@@ -39,25 +40,25 @@ bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
|
|
if (secrets.secrets.size() > 1)
|
|
++riter;
|
|
|
|
- if (riter->second.expiration < ceph_clock_now())
|
|
+ utime_t now = ceph_clock_now();
|
|
+ if (riter->second.expiration < now)
|
|
++riter; // "current" key has expired, use "next" key instead
|
|
|
|
secret_id = riter->first;
|
|
- secret = riter->second;
|
|
- ldout(cct, 30) << "get_service_secret service " << ceph_entity_type_name(service_id)
|
|
- << " id " << secret_id << " " << secret << dendl;
|
|
- return true;
|
|
-}
|
|
-
|
|
-bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
|
|
- CryptoKey& secret, uint64_t& secret_id) const
|
|
-{
|
|
- ExpiringCryptoKey e;
|
|
-
|
|
- if (!get_service_secret(cct, service_id, e, secret_id))
|
|
- return false;
|
|
+ secret = riter->second.key;
|
|
|
|
- secret = e.key;
|
|
+ // ttl may have just been increased by the user
|
|
+ // cap it by expiration of "next" key to prevent handing out a ticket
|
|
+ // with a bogus, possibly way into the future, validity
|
|
+ ttl = service_id == CEPH_ENTITY_TYPE_AUTH ?
|
|
+ cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl;
|
|
+ ttl = min(ttl, static_cast<double>(
|
|
+ secrets.secrets.rbegin()->second.expiration - now));
|
|
+
|
|
+ ldout(cct, 30) << __func__ << " service "
|
|
+ << ceph_entity_type_name(service_id) << " secret_id "
|
|
+ << secret_id << " " << riter->second << " ttl " << ttl
|
|
+ << dendl;
|
|
return true;
|
|
}
|
|
|
|
@@ -233,20 +234,12 @@ bool KeyServer::get_caps(const EntityName& name, const string& type,
|
|
return data.get_caps(cct, name, type, caps_info);
|
|
}
|
|
|
|
-bool KeyServer::get_service_secret(uint32_t service_id,
|
|
- ExpiringCryptoKey& secret, uint64_t& secret_id) const
|
|
+bool KeyServer::get_service_secret(uint32_t service_id, CryptoKey& secret,
|
|
+ uint64_t& secret_id, double& ttl) const
|
|
{
|
|
Mutex::Locker l(lock);
|
|
|
|
- return data.get_service_secret(cct, service_id, secret, secret_id);
|
|
-}
|
|
-
|
|
-bool KeyServer::get_service_secret(uint32_t service_id,
|
|
- CryptoKey& secret, uint64_t& secret_id) const
|
|
-{
|
|
- Mutex::Locker l(lock);
|
|
-
|
|
- return data.get_service_secret(cct, service_id, secret, secret_id);
|
|
+ return data.get_service_secret(cct, service_id, secret, secret_id, ttl);
|
|
}
|
|
|
|
bool KeyServer::get_service_secret(uint32_t service_id,
|
|
@@ -421,12 +414,15 @@ bool KeyServer::get_service_caps(const EntityName& name, uint32_t service_id,
|
|
}
|
|
|
|
|
|
-int KeyServer::_build_session_auth_info(uint32_t service_id, CephXServiceTicketInfo& auth_ticket_info,
|
|
- CephXSessionAuthInfo& info)
|
|
+int KeyServer::_build_session_auth_info(uint32_t service_id,
|
|
+ const AuthTicket& parent_ticket,
|
|
+ CephXSessionAuthInfo& info,
|
|
+ double ttl)
|
|
{
|
|
info.service_id = service_id;
|
|
- info.ticket = auth_ticket_info.ticket;
|
|
- info.ticket.init_timestamps(ceph_clock_now(), cct->_conf->auth_service_ticket_ttl);
|
|
+ info.ticket = parent_ticket;
|
|
+ info.ticket.init_timestamps(ceph_clock_now(), ttl);
|
|
+ info.validity.set_from_double(ttl);
|
|
|
|
generate_secret(info.session_key);
|
|
|
|
@@ -440,25 +436,31 @@ int KeyServer::_build_session_auth_info(uint32_t service_id, CephXServiceTicketI
|
|
return 0;
|
|
}
|
|
|
|
-int KeyServer::build_session_auth_info(uint32_t service_id, CephXServiceTicketInfo& auth_ticket_info,
|
|
+int KeyServer::build_session_auth_info(uint32_t service_id,
|
|
+ const AuthTicket& parent_ticket,
|
|
CephXSessionAuthInfo& info)
|
|
{
|
|
- if (!get_service_secret(service_id, info.service_secret, info.secret_id)) {
|
|
+ double ttl;
|
|
+ if (!get_service_secret(service_id, info.service_secret, info.secret_id,
|
|
+ ttl)) {
|
|
return -EPERM;
|
|
}
|
|
|
|
Mutex::Locker l(lock);
|
|
-
|
|
- return _build_session_auth_info(service_id, auth_ticket_info, info);
|
|
+ return _build_session_auth_info(service_id, parent_ticket, info, ttl);
|
|
}
|
|
|
|
-int KeyServer::build_session_auth_info(uint32_t service_id, CephXServiceTicketInfo& auth_ticket_info, CephXSessionAuthInfo& info,
|
|
- CryptoKey& service_secret, uint64_t secret_id)
|
|
+int KeyServer::build_session_auth_info(uint32_t service_id,
|
|
+ const AuthTicket& parent_ticket,
|
|
+ const CryptoKey& service_secret,
|
|
+ uint64_t secret_id,
|
|
+ CephXSessionAuthInfo& info)
|
|
{
|
|
info.service_secret = service_secret;
|
|
info.secret_id = secret_id;
|
|
|
|
Mutex::Locker l(lock);
|
|
- return _build_session_auth_info(service_id, auth_ticket_info, info);
|
|
+ return _build_session_auth_info(service_id, parent_ticket, info,
|
|
+ cct->_conf->auth_service_ticket_ttl);
|
|
}
|
|
|
|
diff --git a/src/auth/cephx/CephxKeyServer.h b/src/auth/cephx/CephxKeyServer.h
|
|
index ff91e96d..7da08b1b 100644
|
|
--- a/src/auth/cephx/CephxKeyServer.h
|
|
+++ b/src/auth/cephx/CephxKeyServer.h
|
|
@@ -89,9 +89,8 @@ struct KeyServerData {
|
|
}
|
|
|
|
bool get_service_secret(CephContext *cct, uint32_t service_id,
|
|
- ExpiringCryptoKey& secret, uint64_t& secret_id) const;
|
|
- bool get_service_secret(CephContext *cct, uint32_t service_id,
|
|
- CryptoKey& secret, uint64_t& secret_id) const;
|
|
+ CryptoKey& secret, uint64_t& secret_id,
|
|
+ double& ttl) const;
|
|
bool get_service_secret(CephContext *cct, uint32_t service_id,
|
|
uint64_t secret_id, CryptoKey& secret) const;
|
|
bool get_auth(const EntityName& name, EntityAuth& auth) const;
|
|
@@ -193,7 +192,9 @@ class KeyServer : public KeyStore {
|
|
bool _check_rotating_secrets();
|
|
void _dump_rotating_secrets();
|
|
int _build_session_auth_info(uint32_t service_id,
|
|
- CephXServiceTicketInfo& auth_ticket_info, CephXSessionAuthInfo& info);
|
|
+ const AuthTicket& parent_ticket,
|
|
+ CephXSessionAuthInfo& info,
|
|
+ double ttl);
|
|
bool _get_service_caps(const EntityName& name, uint32_t service_id,
|
|
AuthCapsInfo& caps) const;
|
|
public:
|
|
@@ -207,15 +208,18 @@ public:
|
|
int start_server();
|
|
void rotate_timeout(double timeout);
|
|
|
|
- int build_session_auth_info(uint32_t service_id, CephXServiceTicketInfo& auth_ticket_info, CephXSessionAuthInfo& info);
|
|
- int build_session_auth_info(uint32_t service_id, CephXServiceTicketInfo& auth_ticket_info, CephXSessionAuthInfo& info,
|
|
- CryptoKey& service_secret, uint64_t secret_id);
|
|
+ int build_session_auth_info(uint32_t service_id,
|
|
+ const AuthTicket& parent_ticket,
|
|
+ CephXSessionAuthInfo& info);
|
|
+ int build_session_auth_info(uint32_t service_id,
|
|
+ const AuthTicket& parent_ticket,
|
|
+ const CryptoKey& service_secret,
|
|
+ uint64_t secret_id,
|
|
+ CephXSessionAuthInfo& info);
|
|
|
|
/* get current secret for specific service type */
|
|
- bool get_service_secret(uint32_t service_id, ExpiringCryptoKey& service_key,
|
|
- uint64_t& secret_id) const;
|
|
- bool get_service_secret(uint32_t service_id, CryptoKey& service_key,
|
|
- uint64_t& secret_id) const;
|
|
+ bool get_service_secret(uint32_t service_id, CryptoKey& secret,
|
|
+ uint64_t& secret_id, double& ttl) const;
|
|
bool get_service_secret(uint32_t service_id, uint64_t secret_id,
|
|
CryptoKey& secret) const override;
|
|
|
|
diff --git a/src/auth/cephx/CephxProtocol.cc b/src/auth/cephx/CephxProtocol.cc
|
|
index cc5f4496..2905c3c9 100644
|
|
--- a/src/auth/cephx/CephxProtocol.cc
|
|
+++ b/src/auth/cephx/CephxProtocol.cc
|
|
@@ -349,8 +349,10 @@ void CephXTicketManager::validate_tickets(uint32_t mask, uint32_t& have, uint32_
|
|
<< " need " << need << dendl;
|
|
}
|
|
|
|
-bool cephx_decode_ticket(CephContext *cct, KeyStore *keys, uint32_t service_id,
|
|
- CephXTicketBlob& ticket_blob, CephXServiceTicketInfo& ticket_info)
|
|
+bool cephx_decode_ticket(CephContext *cct, KeyStore *keys,
|
|
+ uint32_t service_id,
|
|
+ const CephXTicketBlob& ticket_blob,
|
|
+ CephXServiceTicketInfo& ticket_info)
|
|
{
|
|
uint64_t secret_id = ticket_blob.secret_id;
|
|
CryptoKey service_secret;
|
|
diff --git a/src/auth/cephx/CephxProtocol.h b/src/auth/cephx/CephxProtocol.h
|
|
index b5ec897f..cd20cd43 100644
|
|
--- a/src/auth/cephx/CephxProtocol.h
|
|
+++ b/src/auth/cephx/CephxProtocol.h
|
|
@@ -168,12 +168,16 @@ struct CephXAuthenticate {
|
|
uint64_t key;
|
|
CephXTicketBlob old_ticket;
|
|
|
|
+ bool old_ticket_may_be_omitted;
|
|
+
|
|
void encode(bufferlist& bl) const {
|
|
- __u8 struct_v = 1;
|
|
+ __u8 struct_v = 3;
|
|
::encode(struct_v, bl);
|
|
::encode(client_challenge, bl);
|
|
::encode(key, bl);
|
|
::encode(old_ticket, bl);
|
|
+ uint32_t other_keys = 0;
|
|
+ ::encode(other_keys, bl);
|
|
}
|
|
void decode(bufferlist::iterator& bl) {
|
|
__u8 struct_v;
|
|
@@ -181,6 +185,13 @@ struct CephXAuthenticate {
|
|
::decode(client_challenge, bl);
|
|
::decode(key, bl);
|
|
::decode(old_ticket, bl);
|
|
+
|
|
+ // v2 and v3 encodings are the same, but:
|
|
+ // - some clients that send v1 or v2 don't populate old_ticket
|
|
+ // on reconnects (but do on renewals)
|
|
+ // - any client that sends v3 or later is expected to populate
|
|
+ // old_ticket both on reconnects and renewals
|
|
+ old_ticket_may_be_omitted = struct_v < 3;
|
|
}
|
|
};
|
|
WRITE_CLASS_ENCODER(CephXAuthenticate)
|
|
@@ -429,7 +440,8 @@ WRITE_CLASS_ENCODER(CephXAuthorize)
|
|
* Decode an extract ticket
|
|
*/
|
|
bool cephx_decode_ticket(CephContext *cct, KeyStore *keys,
|
|
- uint32_t service_id, CephXTicketBlob& ticket_blob,
|
|
+ uint32_t service_id,
|
|
+ const CephXTicketBlob& ticket_blob,
|
|
CephXServiceTicketInfo& ticket_info);
|
|
|
|
/*
|
|
@@ -453,8 +465,8 @@ extern bool cephx_verify_authorizer(
|
|
static constexpr uint64_t AUTH_ENC_MAGIC = 0xff009cad8826aa55ull;
|
|
|
|
template <typename T>
|
|
-void decode_decrypt_enc_bl(CephContext *cct, T& t, CryptoKey key, bufferlist& bl_enc,
|
|
- std::string &error)
|
|
+void decode_decrypt_enc_bl(CephContext *cct, T& t, CryptoKey key,
|
|
+ const bufferlist& bl_enc, std::string &error)
|
|
{
|
|
uint64_t magic;
|
|
bufferlist bl;
|
|
diff --git a/src/auth/cephx/CephxServiceHandler.cc b/src/auth/cephx/CephxServiceHandler.cc
|
|
index b06e0080..0a6c3431 100644
|
|
--- a/src/auth/cephx/CephxServiceHandler.cc
|
|
+++ b/src/auth/cephx/CephxServiceHandler.cc
|
|
@@ -26,9 +26,12 @@
|
|
#undef dout_prefix
|
|
#define dout_prefix *_dout << "cephx server " << entity_name << ": "
|
|
|
|
-int CephxServiceHandler::start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result_bl, AuthCapsInfo& caps)
|
|
+int CephxServiceHandler::do_start_session(bool is_new_global_id,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps)
|
|
{
|
|
- entity_name = name;
|
|
+ global_id_status = is_new_global_id ? global_id_status_t::NEW_PENDING :
|
|
+ global_id_status_t::RECLAIM_PENDING;
|
|
|
|
get_random_bytes((char *)&server_challenge, sizeof(server_challenge));
|
|
if (!server_challenge)
|
|
@@ -41,7 +44,89 @@ int CephxServiceHandler::start_session(EntityName& name, bufferlist::iterator& i
|
|
return CEPH_AUTH_CEPHX;
|
|
}
|
|
|
|
-int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist& result_bl, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid)
|
|
+int CephxServiceHandler::verify_old_ticket(const CephXAuthenticate& req,
|
|
+ CephXServiceTicketInfo& old_ticket_info,
|
|
+ bool& should_enc_ticket)
|
|
+{
|
|
+ ldout(cct, 20) << " checking old_ticket: secret_id="
|
|
+ << req.old_ticket.secret_id
|
|
+ << " len=" << req.old_ticket.blob.length()
|
|
+ << ", old_ticket_may_be_omitted="
|
|
+ << req.old_ticket_may_be_omitted << dendl;
|
|
+ ceph_assert(global_id_status != global_id_status_t::NONE);
|
|
+ if (global_id_status == global_id_status_t::NEW_PENDING) {
|
|
+ // old ticket is not needed
|
|
+ if (req.old_ticket.blob.length()) {
|
|
+ ldout(cct, 0) << " superfluous ticket presented" << dendl;
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (req.old_ticket_may_be_omitted) {
|
|
+ ldout(cct, 10) << " new global_id " << global_id
|
|
+ << " (unexposed legacy client)" << dendl;
|
|
+ global_id_status = global_id_status_t::NEW_NOT_EXPOSED;
|
|
+ } else {
|
|
+ ldout(cct, 10) << " new global_id " << global_id << dendl;
|
|
+ global_id_status = global_id_status_t::NEW_OK;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!req.old_ticket.blob.length()) {
|
|
+ // old ticket is needed but not presented
|
|
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
|
|
+ req.old_ticket_may_be_omitted) {
|
|
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
|
|
+ << " with no ticket presented (legacy client, auth_allow_insecure_global_id_reclaim=true)"
|
|
+ << dendl;
|
|
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
|
|
+ return 0;
|
|
+ }
|
|
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
|
|
+ << " without presenting ticket" << dendl;
|
|
+ return -EACCES;
|
|
+ }
|
|
+
|
|
+ if (!cephx_decode_ticket(cct, key_server, CEPH_ENTITY_TYPE_AUTH,
|
|
+ req.old_ticket, old_ticket_info)) {
|
|
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
|
|
+ req.old_ticket_may_be_omitted) {
|
|
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
|
|
+ << " using bad ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
|
|
+ << dendl;
|
|
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
|
|
+ return 0;
|
|
+ }
|
|
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
|
|
+ << " using bad ticket" << dendl;
|
|
+ return -EACCES;
|
|
+ }
|
|
+ ldout(cct, 20) << " decoded old_ticket: global_id="
|
|
+ << old_ticket_info.ticket.global_id << dendl;
|
|
+ if (global_id != old_ticket_info.ticket.global_id) {
|
|
+ if (cct->_conf->auth_allow_insecure_global_id_reclaim &&
|
|
+ req.old_ticket_may_be_omitted) {
|
|
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
|
|
+ << " using mismatching ticket (legacy client, auth_allow_insecure_global_id_reclaim=true)"
|
|
+ << dendl;
|
|
+ global_id_status = global_id_status_t::RECLAIM_INSECURE;
|
|
+ return 0;
|
|
+ }
|
|
+ ldout(cct, 0) << " attempt to reclaim global_id " << global_id
|
|
+ << " using mismatching ticket" << dendl;
|
|
+ return -EACCES;
|
|
+ }
|
|
+ ldout(cct, 10) << " allowing reclaim of global_id " << global_id
|
|
+ << " (valid ticket presented, will encrypt new ticket)"
|
|
+ << dendl;
|
|
+ global_id_status = global_id_status_t::RECLAIM_OK;
|
|
+ should_enc_ticket = true;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int CephxServiceHandler::handle_request(bufferlist::iterator& indata,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps,
|
|
+ uint64_t *auid)
|
|
{
|
|
int ret = 0;
|
|
|
|
@@ -60,13 +145,13 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
CryptoKey secret;
|
|
if (!key_server->get_secret(entity_name, secret)) {
|
|
ldout(cct, 0) << "couldn't find entity name: " << entity_name << dendl;
|
|
- ret = -EPERM;
|
|
- break;
|
|
+ ret = -EPERM;
|
|
+ break;
|
|
}
|
|
|
|
if (!server_challenge) {
|
|
- ret = -EPERM;
|
|
- break;
|
|
+ ret = -EPERM;
|
|
+ break;
|
|
}
|
|
|
|
uint64_t expected_key;
|
|
@@ -74,18 +159,18 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
cephx_calc_client_server_challenge(cct, secret, server_challenge,
|
|
req.client_challenge, &expected_key, error);
|
|
if (!error.empty()) {
|
|
- ldout(cct, 0) << " cephx_calc_client_server_challenge error: " << error << dendl;
|
|
- ret = -EPERM;
|
|
- break;
|
|
+ ldout(cct, 0) << " cephx_calc_client_server_challenge error: " << error << dendl;
|
|
+ ret = -EPERM;
|
|
+ break;
|
|
}
|
|
|
|
ldout(cct, 20) << " checking key: req.key=" << hex << req.key
|
|
<< " expected_key=" << expected_key << dec << dendl;
|
|
if (req.key != expected_key) {
|
|
ldout(cct, 0) << " unexpected key: req.key=" << hex << req.key
|
|
- << " expected_key=" << expected_key << dec << dendl;
|
|
+ << " expected_key=" << expected_key << dec << dendl;
|
|
ret = -EPERM;
|
|
- break;
|
|
+ break;
|
|
}
|
|
|
|
CryptoKey session_key;
|
|
@@ -94,35 +179,38 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
|
|
EntityAuth eauth;
|
|
if (! key_server->get_auth(entity_name, eauth)) {
|
|
- ret = -EPERM;
|
|
- break;
|
|
+ ret = -EPERM;
|
|
+ break;
|
|
}
|
|
+
|
|
CephXServiceTicketInfo old_ticket_info;
|
|
+ ret = verify_old_ticket(req, old_ticket_info, should_enc_ticket);
|
|
+ if (ret) {
|
|
+ ldout(cct, 0) << " could not verify old ticket" << dendl;
|
|
+ break;
|
|
+ }
|
|
|
|
- if (cephx_decode_ticket(cct, key_server, CEPH_ENTITY_TYPE_AUTH,
|
|
- req.old_ticket, old_ticket_info)) {
|
|
- global_id = old_ticket_info.ticket.global_id;
|
|
- ldout(cct, 10) << "decoded old_ticket with global_id=" << global_id << dendl;
|
|
- should_enc_ticket = true;
|
|
+ double ttl;
|
|
+ if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH,
|
|
+ info.service_secret, info.secret_id,
|
|
+ ttl)) {
|
|
+ ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl;
|
|
+ ret = -EIO;
|
|
+ break;
|
|
}
|
|
|
|
- info.ticket.init_timestamps(ceph_clock_now(), cct->_conf->auth_mon_ticket_ttl);
|
|
+ info.service_id = CEPH_ENTITY_TYPE_AUTH;
|
|
info.ticket.name = entity_name;
|
|
info.ticket.global_id = global_id;
|
|
info.ticket.auid = eauth.auid;
|
|
- info.validity += cct->_conf->auth_mon_ticket_ttl;
|
|
+ info.ticket.init_timestamps(ceph_clock_now(), ttl);
|
|
+ info.validity.set_from_double(ttl);
|
|
|
|
if (auid) *auid = eauth.auid;
|
|
|
|
key_server->generate_secret(session_key);
|
|
|
|
info.session_key = session_key;
|
|
- info.service_id = CEPH_ENTITY_TYPE_AUTH;
|
|
- if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH, info.service_secret, info.secret_id)) {
|
|
- ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl;
|
|
- ret = -EIO;
|
|
- break;
|
|
- }
|
|
|
|
vector<CephXSessionAuthInfo> info_vec;
|
|
info_vec.push_back(info);
|
|
@@ -130,7 +218,7 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
build_cephx_response_header(cephx_header.request_type, 0, result_bl);
|
|
if (!cephx_build_service_ticket_reply(cct, eauth.key, info_vec, should_enc_ticket,
|
|
old_ticket_info.session_key, result_bl)) {
|
|
- ret = -EIO;
|
|
+ ret = -EIO;
|
|
}
|
|
|
|
if (!key_server->get_service_caps(entity_name, CEPH_ENTITY_TYPE_MON, caps)) {
|
|
@@ -156,7 +244,7 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
if (!cephx_verify_authorizer(cct, key_server, indata, auth_ticket_info, nullptr,
|
|
tmp_bl)) {
|
|
ret = -EPERM;
|
|
- break;
|
|
+ break;
|
|
}
|
|
|
|
CephXServiceTicketRequest ticket_req;
|
|
@@ -168,13 +256,18 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
int found_services = 0;
|
|
int service_err = 0;
|
|
for (uint32_t service_id = 1; service_id <= ticket_req.keys;
|
|
- service_id <<= 1) {
|
|
- if (ticket_req.keys & service_id) {
|
|
- ldout(cct, 10) << " adding key for service "
|
|
+ service_id <<= 1) {
|
|
+ // skip CEPH_ENTITY_TYPE_AUTH: auth ticket must be obtained with
|
|
+ // CEPHX_GET_AUTH_SESSION_KEY
|
|
+ if ((ticket_req.keys & service_id) &&
|
|
+ service_id != CEPH_ENTITY_TYPE_AUTH) {
|
|
+ ldout(cct, 10) << " adding key for service "
|
|
<< ceph_entity_type_name(service_id) << dendl;
|
|
CephXSessionAuthInfo info;
|
|
- int r = key_server->build_session_auth_info(service_id,
|
|
- auth_ticket_info, info);
|
|
+ int r = key_server->build_session_auth_info(
|
|
+ service_id,
|
|
+ auth_ticket_info.ticket,
|
|
+ info);
|
|
// tolerate missing MGR rotating key for the purposes of upgrades.
|
|
if (r < 0) {
|
|
ldout(cct, 10) << " missing key for service "
|
|
@@ -182,9 +275,8 @@ int CephxServiceHandler::handle_request(bufferlist::iterator& indata, bufferlist
|
|
service_err = r;
|
|
continue;
|
|
}
|
|
- info.validity += cct->_conf->auth_service_ticket_ttl;
|
|
- info_vec.push_back(info);
|
|
- ++found_services;
|
|
+ info_vec.push_back(info);
|
|
+ ++found_services;
|
|
}
|
|
}
|
|
if (!found_services && service_err) {
|
|
diff --git a/src/auth/cephx/CephxServiceHandler.h b/src/auth/cephx/CephxServiceHandler.h
|
|
index 390a6dc1..18f87e39 100644
|
|
--- a/src/auth/cephx/CephxServiceHandler.h
|
|
+++ b/src/auth/cephx/CephxServiceHandler.h
|
|
@@ -19,6 +19,8 @@
|
|
#include "auth/Auth.h"
|
|
|
|
class KeyServer;
|
|
+struct CephXAuthenticate;
|
|
+struct CephXServiceTicketInfo;
|
|
|
|
class CephxServiceHandler : public AuthServiceHandler {
|
|
KeyServer *key_server;
|
|
@@ -29,9 +31,21 @@ public:
|
|
: AuthServiceHandler(cct_), key_server(ks), server_challenge(0) {}
|
|
~CephxServiceHandler() override {}
|
|
|
|
- int start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result_bl, AuthCapsInfo& caps) override;
|
|
- int handle_request(bufferlist::iterator& indata, bufferlist& result_bl, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid = NULL) override;
|
|
- void build_cephx_response_header(int request_type, int status, bufferlist& bl);
|
|
+ int handle_request(bufferlist::iterator& indata,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps,
|
|
+ uint64_t *auid = NULL) override;
|
|
+
|
|
+private:
|
|
+ int do_start_session(bool is_new_global_id,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps) override;
|
|
+
|
|
+ int verify_old_ticket(const CephXAuthenticate& req,
|
|
+ CephXServiceTicketInfo& old_ticket_info,
|
|
+ bool& should_enc_ticket);
|
|
+ void build_cephx_response_header(int request_type, int status,
|
|
+ bufferlist& bl);
|
|
};
|
|
|
|
#endif
|
|
diff --git a/src/auth/none/AuthNoneClientHandler.h b/src/auth/none/AuthNoneClientHandler.h
|
|
index 369aa548..2a52f331 100644
|
|
--- a/src/auth/none/AuthNoneClientHandler.h
|
|
+++ b/src/auth/none/AuthNoneClientHandler.h
|
|
@@ -22,9 +22,13 @@
|
|
|
|
class AuthNoneClientHandler : public AuthClientHandler {
|
|
public:
|
|
- AuthNoneClientHandler(CephContext *cct_, RotatingKeyRing *rkeys)
|
|
+ AuthNoneClientHandler(CephContext *cct_)
|
|
: AuthClientHandler(cct_) {}
|
|
|
|
+ AuthNoneClientHandler* clone() const override {
|
|
+ return new AuthNoneClientHandler(*this);
|
|
+ }
|
|
+
|
|
void reset() override { }
|
|
|
|
void prepare_build_request() override {}
|
|
@@ -35,7 +39,6 @@ public:
|
|
int get_protocol() const override { return CEPH_AUTH_NONE; }
|
|
|
|
AuthAuthorizer *build_authorizer(uint32_t service_id) const override {
|
|
- RWLock::RLocker l(lock);
|
|
AuthNoneAuthorizer *auth = new AuthNoneAuthorizer();
|
|
if (auth) {
|
|
auth->build_authorizer(cct->_conf->name, global_id);
|
|
@@ -46,7 +49,6 @@ public:
|
|
bool need_tickets() override { return false; }
|
|
|
|
void set_global_id(uint64_t id) override {
|
|
- RWLock::WLocker l(lock);
|
|
global_id = id;
|
|
}
|
|
private:
|
|
diff --git a/src/auth/none/AuthNoneServiceHandler.h b/src/auth/none/AuthNoneServiceHandler.h
|
|
index 120a7a98..17ce2f8a 100644
|
|
--- a/src/auth/none/AuthNoneServiceHandler.h
|
|
+++ b/src/auth/none/AuthNoneServiceHandler.h
|
|
@@ -26,15 +26,20 @@ public:
|
|
: AuthServiceHandler(cct_) {}
|
|
~AuthNoneServiceHandler() override {}
|
|
|
|
- int start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result_bl, AuthCapsInfo& caps) override {
|
|
- entity_name = name;
|
|
+ int handle_request(bufferlist::iterator& indata,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps,
|
|
+ uint64_t *auid = NULL) override {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+private:
|
|
+ int do_start_session(bool is_new_global_id,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps) override {
|
|
caps.allow_all = true;
|
|
return CEPH_AUTH_NONE;
|
|
}
|
|
- int handle_request(bufferlist::iterator& indata, bufferlist& result_bl, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid = NULL) override {
|
|
- return 0;
|
|
- }
|
|
- void build_cephx_response_header(int request_type, int status, bufferlist& bl) { }
|
|
};
|
|
|
|
#endif
|
|
diff --git a/src/auth/unknown/AuthUnknownServiceHandler.h b/src/auth/unknown/AuthUnknownServiceHandler.h
|
|
index 5c1e511e..61ca6b29 100644
|
|
--- a/src/auth/unknown/AuthUnknownServiceHandler.h
|
|
+++ b/src/auth/unknown/AuthUnknownServiceHandler.h
|
|
@@ -26,14 +26,18 @@ public:
|
|
: AuthServiceHandler(cct_) {}
|
|
~AuthUnknownServiceHandler() {}
|
|
|
|
- int start_session(EntityName& name, bufferlist::iterator& indata, bufferlist& result_bl, AuthCapsInfo& caps) {
|
|
+ int start_session(EntityName& name,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps) override {
|
|
return CEPH_AUTH_UNKNOWN;
|
|
}
|
|
- int handle_request(bufferlist::iterator& indata, bufferlist& result_bl, uint64_t& global_id, AuthCapsInfo& caps, uint64_t *auid = NULL) {
|
|
+ int handle_request(bufferlist::iterator& indata,
|
|
+ bufferlist& result_bl,
|
|
+ AuthCapsInfo& caps,
|
|
+ uint64_t *auid = NULL) {
|
|
ceph_abort(); // shouldn't get called
|
|
return 0;
|
|
}
|
|
- void build_cephx_response_header(int request_type, int status, bufferlist& bl) { }
|
|
};
|
|
|
|
#endif
|
|
diff --git a/src/common/legacy_config_opts.h b/src/common/legacy_config_opts.h
|
|
index 38b36a60..cbb4528e 100644
|
|
--- a/src/common/legacy_config_opts.h
|
|
+++ b/src/common/legacy_config_opts.h
|
|
@@ -340,6 +340,8 @@ OPTION(cephx_service_require_version, OPT_INT)
|
|
OPTION(cephx_sign_messages, OPT_BOOL) // Default to signing session messages if supported
|
|
OPTION(auth_mon_ticket_ttl, OPT_DOUBLE)
|
|
OPTION(auth_service_ticket_ttl, OPT_DOUBLE)
|
|
+OPTION(auth_allow_insecure_global_id_reclaim, OPT_BOOL)
|
|
+OPTION(auth_expose_insecure_global_id_reclaim, OPT_BOOL)
|
|
OPTION(auth_debug, OPT_BOOL) // if true, assert when weird things happen
|
|
OPTION(mon_client_hunt_parallel, OPT_U32) // how many mons to try to connect to in parallel during hunt
|
|
OPTION(mon_client_hunt_interval, OPT_DOUBLE) // try new mon every N seconds until we connect
|
|
diff --git a/src/common/options.cc b/src/common/options.cc
|
|
index 1ed027c9..cf8f6bbe 100644
|
|
--- a/src/common/options.cc
|
|
+++ b/src/common/options.cc
|
|
@@ -1128,6 +1128,22 @@ std::vector<Option> get_global_options() {
|
|
.set_default(900)
|
|
.set_description(""),
|
|
|
|
+ Option("mon_warn_on_insecure_global_id_reclaim", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
|
|
+ .set_default(true)
|
|
+ .add_service("mon")
|
|
+ .set_description("issue AUTH_INSECURE_GLOBAL_ID_RECLAIM health warning if any connected clients are insecurely reclaiming global_id")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim_allowed")
|
|
+ .add_see_also("auth_allow_insecure_global_id_reclaim")
|
|
+ .add_see_also("auth_expose_insecure_global_id_reclaim"),
|
|
+
|
|
+ Option("mon_warn_on_insecure_global_id_reclaim_allowed", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
|
|
+ .set_default(true)
|
|
+ .add_service("mon")
|
|
+ .set_description("issue AUTH_INSECURE_GLOBAL_ID_RECLAIM_ALLOWED health warning if insecure global_id reclaim is allowed")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim")
|
|
+ .add_see_also("auth_allow_insecure_global_id_reclaim")
|
|
+ .add_see_also("auth_expose_insecure_global_id_reclaim"),
|
|
+
|
|
Option("mon_force_standby_active", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
|
|
.set_default(true)
|
|
.set_description(""),
|
|
@@ -1504,13 +1520,29 @@ std::vector<Option> get_global_options() {
|
|
.set_description(""),
|
|
|
|
Option("auth_mon_ticket_ttl", Option::TYPE_FLOAT, Option::LEVEL_ADVANCED)
|
|
- .set_default(12_hr)
|
|
+ .set_default(72_hr)
|
|
.set_description(""),
|
|
|
|
Option("auth_service_ticket_ttl", Option::TYPE_FLOAT, Option::LEVEL_ADVANCED)
|
|
.set_default(1_hr)
|
|
.set_description(""),
|
|
|
|
+ Option("auth_allow_insecure_global_id_reclaim", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
|
|
+ .set_default(true)
|
|
+ .set_description("Allow reclaiming global_id without presenting a valid ticket proving previous possession of that global_id")
|
|
+ .set_long_description("Allowing unauthorized global_id (re)use poses a security risk. Unfortunately, older clients may omit their ticket on reconnects and therefore rely on this being allowed for preserving their global_id for the lifetime of the client instance. Setting this value to false would immediately prevent new connections from those clients (assuming auth_expose_insecure_global_id_reclaim set to true) and eventually break existing sessions as well (regardless of auth_expose_insecure_global_id_reclaim setting).")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim_allowed")
|
|
+ .add_see_also("auth_expose_insecure_global_id_reclaim"),
|
|
+
|
|
+ Option("auth_expose_insecure_global_id_reclaim", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
|
|
+ .set_default(true)
|
|
+ .set_description("Force older clients that may omit their ticket on reconnects to reconnect as part of establishing a session")
|
|
+ .set_long_description("In permissive mode (auth_allow_insecure_global_id_reclaim set to true), this helps with identifying clients that are not patched. In enforcing mode (auth_allow_insecure_global_id_reclaim set to false), this is a fail-fast mechanism: don't establish a session that will almost inevitably be broken later.")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim")
|
|
+ .add_see_also("mon_warn_on_insecure_global_id_reclaim_allowed")
|
|
+ .add_see_also("auth_allow_insecure_global_id_reclaim"),
|
|
+
|
|
Option("auth_debug", Option::TYPE_BOOL, Option::LEVEL_DEV)
|
|
.set_default(false)
|
|
.set_description(""),
|
|
diff --git a/src/mon/AuthMonitor.cc b/src/mon/AuthMonitor.cc
|
|
index 965958b1..5498f548 100644
|
|
--- a/src/mon/AuthMonitor.cc
|
|
+++ b/src/mon/AuthMonitor.cc
|
|
@@ -375,6 +375,7 @@ bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
|
|
__u32 proto = m->protocol;
|
|
bool start = false;
|
|
EntityName entity_name;
|
|
+ bool is_new_global_id = false;
|
|
|
|
// set up handler?
|
|
if (m->protocol == 0 && !s->auth_handler) {
|
|
@@ -493,6 +494,7 @@ bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
|
|
assert(!paxos_writable);
|
|
return false;
|
|
}
|
|
+ is_new_global_id = true;
|
|
}
|
|
|
|
try {
|
|
@@ -504,13 +506,18 @@ bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
|
|
if (m->monmap_epoch < mon->monmap->get_epoch())
|
|
mon->send_latest_monmap(m->get_connection().get());
|
|
|
|
- proto = s->auth_handler->start_session(entity_name, indata, response_bl, caps_info);
|
|
+ proto = s->auth_handler->start_session(entity_name, s->global_id,
|
|
+ is_new_global_id, response_bl,
|
|
+ caps_info);
|
|
ret = 0;
|
|
- if (caps_info.allow_all)
|
|
+ if (caps_info.allow_all) {
|
|
s->caps.set_allow_all();
|
|
+ s->authenticated = true;
|
|
+ }
|
|
} else {
|
|
// request
|
|
- ret = s->auth_handler->handle_request(indata, response_bl, s->global_id, caps_info, &auid);
|
|
+ ret = s->auth_handler->handle_request(indata, response_bl, caps_info,
|
|
+ &auid);
|
|
}
|
|
if (ret == -EIO) {
|
|
wait_for_active(op, new C_RetryMessage(this,op));
|
|
@@ -527,6 +534,7 @@ bool AuthMonitor::prep_auth(MonOpRequestRef op, bool paxos_writable)
|
|
}
|
|
s->caps.parse(str, NULL);
|
|
s->auid = auid;
|
|
+ s->authenticated = true;
|
|
}
|
|
} catch (const buffer::error &err) {
|
|
ret = -EINVAL;
|
|
diff --git a/src/mon/HealthMonitor.cc b/src/mon/HealthMonitor.cc
|
|
index 883cc056..25437f39 100644
|
|
--- a/src/mon/HealthMonitor.cc
|
|
+++ b/src/mon/HealthMonitor.cc
|
|
@@ -213,6 +213,7 @@ bool HealthMonitor::check_member_health()
|
|
{
|
|
dout(20) << __func__ << dendl;
|
|
bool changed = false;
|
|
+ const auto max = g_conf->get_val<uint64_t>("mon_health_max_detail");
|
|
|
|
// snapshot of usage
|
|
DataStats stats;
|
|
@@ -281,6 +282,43 @@ bool HealthMonitor::check_member_health()
|
|
d.detail.push_back(ds.str());
|
|
}
|
|
}
|
|
+
|
|
+ // AUTH_INSECURE_GLOBAL_ID_RECLAIM
|
|
+ if (g_conf->get_val<bool>("mon_warn_on_insecure_global_id_reclaim") &&
|
|
+ g_conf->get_val<bool>("auth_allow_insecure_global_id_reclaim")) {
|
|
+ // Warn if there are any clients that are insecurely renewing their global_id
|
|
+ Mutex::Locker l(mon->session_map_lock);
|
|
+ list<std::string> detail;
|
|
+ for (auto p = mon->session_map.sessions.begin();
|
|
+ p != mon->session_map.sessions.end();
|
|
+ ++p) {
|
|
+ if ((*p)->global_id_status == global_id_status_t::RECLAIM_INSECURE) {
|
|
+ ostringstream ds;
|
|
+ ds << (*p)->entity_name << " at " << (*p)->inst.addr
|
|
+ << " is using insecure global_id reclaim";
|
|
+ detail.push_back(ds.str());
|
|
+ if (detail.size() >= max) {
|
|
+ detail.push_back("...");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!detail.empty()) {
|
|
+ ostringstream ss;
|
|
+ ss << "client%plurals% %isorare% using insecure global_id reclaim";
|
|
+ auto& d = next.add("AUTH_INSECURE_GLOBAL_ID_RECLAIM", HEALTH_WARN, ss.str());
|
|
+ d.detail.swap(detail);
|
|
+ }
|
|
+ }
|
|
+ // AUTH_INSECURE_GLOBAL_ID_RECLAIM_ALLOWED
|
|
+ if (g_conf->get_val<bool>("mon_warn_on_insecure_global_id_reclaim_allowed") &&
|
|
+ g_conf->get_val<bool>("auth_allow_insecure_global_id_reclaim")) {
|
|
+ ostringstream ss, ds;
|
|
+ ss << "mon%plurals% %isorare% allowing insecure global_id reclaim";
|
|
+ auto& d = next.add("AUTH_INSECURE_GLOBAL_ID_RECLAIM_ALLOWED", HEALTH_WARN, ss.str());
|
|
+ ds << "mon." << mon->name << " has auth_allow_insecure_global_id_reclaim set to true";
|
|
+ d.detail.push_back(ds.str());
|
|
+ }
|
|
|
|
auto p = quorum_checks.find(mon->rank);
|
|
if (p == quorum_checks.end()) {
|
|
diff --git a/src/mon/MonClient.cc b/src/mon/MonClient.cc
|
|
index 952c61a8..2d70f28f 100644
|
|
--- a/src/mon/MonClient.cc
|
|
+++ b/src/mon/MonClient.cc
|
|
@@ -117,7 +117,7 @@ int MonClient::get_monmap_privately()
|
|
std::uniform_int_distribution<unsigned> ranks(0, monmap.size() - 1);
|
|
while (monmap.fsid.is_zero()) {
|
|
auto rank = ranks(rng);
|
|
- auto& pending_con = _add_conn(rank, 0);
|
|
+ auto& pending_con = _add_conn(rank);
|
|
auto con = pending_con.get_con();
|
|
ldout(cct, 10) << "querying mon." << monmap.get_name(rank) << " "
|
|
<< con->get_peer_addr() << dendl;
|
|
@@ -429,7 +429,11 @@ void MonClient::shutdown()
|
|
|
|
active_con.reset();
|
|
pending_cons.clear();
|
|
+
|
|
auth.reset();
|
|
+ global_id = 0;
|
|
+ authenticate_err = 0;
|
|
+ authenticated = false;
|
|
|
|
monc_lock.Unlock();
|
|
|
|
@@ -539,7 +543,7 @@ void MonClient::handle_auth(MAuthReply *m)
|
|
_resend_mon_commands();
|
|
send_log(true);
|
|
if (active_con) {
|
|
- std::swap(auth, active_con->get_auth());
|
|
+ auth = std::move(active_con->get_auth());
|
|
global_id = active_con->get_global_id();
|
|
}
|
|
}
|
|
@@ -596,9 +600,9 @@ void MonClient::_reopen_session(int rank)
|
|
_start_hunting();
|
|
|
|
if (rank >= 0) {
|
|
- _add_conn(rank, global_id);
|
|
+ _add_conn(rank);
|
|
} else {
|
|
- _add_conns(global_id);
|
|
+ _add_conns();
|
|
}
|
|
|
|
// throw out old queued messages
|
|
@@ -628,11 +632,14 @@ void MonClient::_reopen_session(int rank)
|
|
_renew_subs();
|
|
}
|
|
|
|
-MonConnection& MonClient::_add_conn(unsigned rank, uint64_t global_id)
|
|
+MonConnection& MonClient::_add_conn(unsigned rank)
|
|
{
|
|
auto peer = monmap.get_addr(rank);
|
|
auto conn = messenger->get_connection(monmap.get_inst(rank));
|
|
MonConnection mc(cct, conn, global_id);
|
|
+ if (auth) {
|
|
+ mc.get_auth().reset(auth->clone());
|
|
+ }
|
|
auto inserted = pending_cons.insert(make_pair(peer, move(mc)));
|
|
ldout(cct, 10) << "picked mon." << monmap.get_name(rank)
|
|
<< " con " << conn
|
|
@@ -641,7 +648,7 @@ MonConnection& MonClient::_add_conn(unsigned rank, uint64_t global_id)
|
|
return inserted.first->second;
|
|
}
|
|
|
|
-void MonClient::_add_conns(uint64_t global_id)
|
|
+void MonClient::_add_conns()
|
|
{
|
|
uint16_t min_priority = std::numeric_limits<uint16_t>::max();
|
|
for (const auto& m : monmap.mon_info) {
|
|
@@ -663,7 +670,7 @@ void MonClient::_add_conns(uint64_t global_id)
|
|
n = ranks.size();
|
|
}
|
|
for (unsigned i = 0; i < n; i++) {
|
|
- _add_conn(ranks[i], global_id);
|
|
+ _add_conn(ranks[i]);
|
|
}
|
|
}
|
|
|
|
@@ -1243,12 +1250,15 @@ int MonConnection::_negotiate(MAuthReply *m,
|
|
uint32_t want_keys,
|
|
RotatingKeyRing* keyring)
|
|
{
|
|
+ ldout(cct, 10) << __func__ << " protocol " << m->protocol << dendl;
|
|
if (auth && (int)m->protocol == auth->get_protocol()) {
|
|
// good, negotiation completed
|
|
+ ldout(cct, 10) << __func__ << " protocol " << m->protocol << dendl;
|
|
auth->reset();
|
|
return 0;
|
|
}
|
|
|
|
+ ldout(cct, 10) << __func__ << " creating new auth" << dendl;
|
|
auth.reset(get_auth_client_handler(cct, m->protocol, keyring));
|
|
if (!auth) {
|
|
ldout(cct, 10) << "no handler for protocol " << m->protocol << dendl;
|
|
diff --git a/src/mon/MonClient.h b/src/mon/MonClient.h
|
|
index 703da88f..933ef2e4 100644
|
|
--- a/src/mon/MonClient.h
|
|
+++ b/src/mon/MonClient.h
|
|
@@ -209,9 +209,9 @@ private:
|
|
void _finish_hunting();
|
|
void _finish_auth(int auth_err);
|
|
void _reopen_session(int rank = -1);
|
|
- MonConnection& _add_conn(unsigned rank, uint64_t global_id);
|
|
+ MonConnection& _add_conn(unsigned rank);
|
|
void _un_backoff();
|
|
- void _add_conns(uint64_t global_id);
|
|
+ void _add_conns();
|
|
void _send_mon_message(Message *m);
|
|
|
|
public:
|
|
diff --git a/src/mon/Monitor.cc b/src/mon/Monitor.cc
|
|
index da1fac90..946bbe37 100644
|
|
--- a/src/mon/Monitor.cc
|
|
+++ b/src/mon/Monitor.cc
|
|
@@ -321,7 +321,7 @@ void Monitor::do_admin_command(string command, cmdmap_t& cmdmap, string format,
|
|
if (f) {
|
|
f->open_array_section("sessions");
|
|
for (auto p : session_map.sessions) {
|
|
- f->dump_stream("session") << *p;
|
|
+ f->dump_object("session", *p);
|
|
}
|
|
f->close_section();
|
|
f->flush(ss);
|
|
@@ -889,7 +889,9 @@ void Monitor::shutdown()
|
|
|
|
state = STATE_SHUTDOWN;
|
|
|
|
+ lock.Unlock();
|
|
g_conf->remove_observer(this);
|
|
+ lock.Lock();
|
|
|
|
if (admin_hook) {
|
|
AdminSocket* admin_socket = cct->get_admin_socket();
|
|
@@ -929,6 +931,14 @@ void Monitor::shutdown()
|
|
|
|
remove_all_sessions();
|
|
|
|
+ log_client.shutdown();
|
|
+
|
|
+ // unlock before msgr shutdown...
|
|
+ lock.Unlock();
|
|
+
|
|
+ messenger->shutdown(); // last thing! ceph_mon.cc will delete mon.
|
|
+ mgr_messenger->shutdown();
|
|
+
|
|
if (logger) {
|
|
cct->get_perfcounters_collection()->remove(logger);
|
|
delete logger;
|
|
@@ -941,13 +951,6 @@ void Monitor::shutdown()
|
|
cluster_logger = NULL;
|
|
}
|
|
|
|
- log_client.shutdown();
|
|
-
|
|
- // unlock before msgr shutdown...
|
|
- lock.Unlock();
|
|
-
|
|
- messenger->shutdown(); // last thing! ceph_mon.cc will delete mon.
|
|
- mgr_messenger->shutdown();
|
|
}
|
|
|
|
void Monitor::wait_for_paxos_write()
|
|
@@ -3835,6 +3838,7 @@ void Monitor::handle_forward(MonOpRequestRef op)
|
|
c->set_peer_type(m->client.name.type());
|
|
c->set_features(m->con_features);
|
|
|
|
+ s->authenticated = true;
|
|
s->caps = m->client_caps;
|
|
dout(10) << " caps are " << s->caps << dendl;
|
|
s->entity_name = m->entity_name;
|
|
@@ -4183,6 +4187,7 @@ void Monitor::_ms_dispatch(Message *m)
|
|
dout(5) << __func__ << " setting monitor caps on this connection" << dendl;
|
|
if (!s->caps.is_allow_all()) // but no need to repeatedly copy
|
|
s->caps = *mon_caps;
|
|
+ s->authenticated = true;
|
|
}
|
|
s->put();
|
|
} else {
|
|
@@ -4197,8 +4202,13 @@ void Monitor::_ms_dispatch(Message *m)
|
|
|
|
if (s->auth_handler) {
|
|
s->entity_name = s->auth_handler->get_entity_name();
|
|
+ ceph_assert(s->global_id == s->auth_handler->get_global_id());
|
|
+ s->global_id_status = s->auth_handler->get_global_id_status();
|
|
}
|
|
- dout(20) << " caps " << s->caps.get_str() << dendl;
|
|
+ dout(20) << " entity_name " << s->entity_name
|
|
+ << " global_id " << s->global_id
|
|
+ << " (" << s->global_id_status
|
|
+ << ") caps " << s->caps.get_str() << dendl;
|
|
|
|
if ((is_synchronizing() ||
|
|
(s->global_id == 0 && !exited_quorum.is_zero())) &&
|
|
@@ -4259,6 +4269,31 @@ void Monitor::dispatch_op(MonOpRequestRef op)
|
|
if (dealt_with)
|
|
return;
|
|
|
|
+ if (s->authenticated) {
|
|
+ // global_id_status == NONE: all sessions for auth_none,
|
|
+ // mon <-> mon sessions (including proxied sessions) for cephx
|
|
+ ceph_assert(s->global_id_status == global_id_status_t::NONE ||
|
|
+ s->global_id_status == global_id_status_t::NEW_OK ||
|
|
+ s->global_id_status == global_id_status_t::NEW_NOT_EXPOSED ||
|
|
+ s->global_id_status == global_id_status_t::RECLAIM_OK ||
|
|
+ s->global_id_status == global_id_status_t::RECLAIM_INSECURE);
|
|
+
|
|
+ if (cct->_conf->auth_expose_insecure_global_id_reclaim &&
|
|
+ s->global_id_status == global_id_status_t::NEW_NOT_EXPOSED) {
|
|
+ dout(5) << __func__ << " " << op->get_req()->get_source_inst()
|
|
+ << " may omit old_ticket on reconnects, discarding "
|
|
+ << *op->get_req() << " and forcing reconnect" << dendl;
|
|
+ ceph_assert(s->con && !s->proxy_con);
|
|
+ s->con->mark_down();
|
|
+ {
|
|
+ Mutex::Locker l(session_map_lock);
|
|
+ remove_session(s);
|
|
+ }
|
|
+ op->mark_zap();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* well, maybe the op belongs to a service... */
|
|
op->set_type_service();
|
|
/* deal with all messages which caps should be checked somewhere else */
|
|
@@ -5775,7 +5810,7 @@ int Monitor::write_default_keyring(bufferlist& bl)
|
|
os << g_conf->mon_data << "/keyring";
|
|
|
|
int err = 0;
|
|
- int fd = ::open(os.str().c_str(), O_WRONLY|O_CREAT, 0600);
|
|
+ int fd = ::open(os.str().c_str(), O_WRONLY|O_CREAT|O_CLOEXEC, 0600);
|
|
if (fd < 0) {
|
|
err = -errno;
|
|
dout(0) << __func__ << " failed to open " << os.str()
|
|
@@ -5824,7 +5859,7 @@ bool Monitor::ms_get_authorizer(int service_id, AuthAuthorizer **authorizer,
|
|
if (!auth_cluster_required.is_supported_auth(CEPH_AUTH_CEPHX)) {
|
|
// auth_none
|
|
dout(20) << __func__ << " building auth_none authorizer" << dendl;
|
|
- AuthNoneClientHandler handler(g_ceph_context, nullptr);
|
|
+ AuthNoneClientHandler handler{g_ceph_context};
|
|
handler.set_global_id(0);
|
|
*authorizer = handler.build_authorizer(service_id);
|
|
return true;
|
|
@@ -5857,8 +5892,8 @@ bool Monitor::ms_get_authorizer(int service_id, AuthAuthorizer **authorizer,
|
|
return false;
|
|
}
|
|
|
|
- ret = key_server.build_session_auth_info(service_id, auth_ticket_info, info,
|
|
- secret, (uint64_t)-1);
|
|
+ ret = key_server.build_session_auth_info(
|
|
+ service_id, auth_ticket_info.ticket, secret, (uint64_t)-1, info);
|
|
if (ret < 0) {
|
|
dout(0) << __func__ << " failed to build mon session_auth_info "
|
|
<< cpp_strerror(ret) << dendl;
|
|
@@ -5866,7 +5901,7 @@ bool Monitor::ms_get_authorizer(int service_id, AuthAuthorizer **authorizer,
|
|
}
|
|
} else if (service_id == CEPH_ENTITY_TYPE_MGR) {
|
|
// mgr
|
|
- ret = key_server.build_session_auth_info(service_id, auth_ticket_info, info);
|
|
+ ret = key_server.build_session_auth_info(service_id, auth_ticket_info.ticket, info);
|
|
if (ret < 0) {
|
|
derr << __func__ << " failed to build mgr service session_auth_info "
|
|
<< cpp_strerror(ret) << dendl;
|
|
diff --git a/src/mon/MonitorDBStore.h b/src/mon/MonitorDBStore.h
|
|
index 00e56a9d..5d3d81c0 100644
|
|
--- a/src/mon/MonitorDBStore.h
|
|
+++ b/src/mon/MonitorDBStore.h
|
|
@@ -607,7 +607,7 @@ class MonitorDBStore
|
|
if (!g_conf->mon_debug_dump_json) {
|
|
dump_fd_binary = ::open(
|
|
g_conf->mon_debug_dump_location.c_str(),
|
|
- O_CREAT|O_APPEND|O_WRONLY, 0644);
|
|
+ O_CREAT|O_APPEND|O_WRONLY|O_CLOEXEC, 0644);
|
|
if (dump_fd_binary < 0) {
|
|
dump_fd_binary = -errno;
|
|
derr << "Could not open log file, got "
|
|
diff --git a/src/mon/Session.h b/src/mon/Session.h
|
|
index 9b54f962..6e343c64 100644
|
|
--- a/src/mon/Session.h
|
|
+++ b/src/mon/Session.h
|
|
@@ -50,13 +50,16 @@ struct MonSession : public RefCountedObject {
|
|
set<uint64_t> routed_request_tids;
|
|
MonCap caps;
|
|
uint64_t auid;
|
|
- uint64_t global_id;
|
|
+
|
|
+ bool authenticated = false; ///<true id auth handshake is complete
|
|
|
|
map<string, Subscription*> sub_map;
|
|
epoch_t osd_epoch; // the osdmap epoch sent to the mon client
|
|
|
|
AuthServiceHandler *auth_handler;
|
|
EntityName entity_name;
|
|
+ uint64_t global_id = 0;
|
|
+ global_id_status_t global_id_status = global_id_status_t::NONE;
|
|
|
|
ConnectionRef proxy_con;
|
|
uint64_t proxy_tid;
|
|
@@ -68,7 +71,6 @@ struct MonSession : public RefCountedObject {
|
|
con_features(0),
|
|
inst(i), closed(false), item(this),
|
|
auid(0),
|
|
- global_id(0),
|
|
osd_epoch(0),
|
|
auth_handler(NULL),
|
|
proxy_con(NULL), proxy_tid(0) {
|
|
@@ -95,6 +97,23 @@ struct MonSession : public RefCountedObject {
|
|
service, "", args,
|
|
mask & MON_CAP_R, mask & MON_CAP_W, mask & MON_CAP_X);
|
|
}
|
|
+
|
|
+ void dump(Formatter *f) const {
|
|
+ f->dump_stream("name") << inst.name;
|
|
+ f->dump_stream("entity_name") << entity_name;
|
|
+ f->dump_object("addr", inst.addr);
|
|
+ f->dump_string("con_type", ceph_entity_type_name(con_type));
|
|
+ f->dump_unsigned("con_features", con_features);
|
|
+ f->dump_stream("con_features_hex") << std::hex << con_features << std::dec;
|
|
+ f->dump_string("con_features_release",
|
|
+ ceph_release_name(ceph_release_from_features(con_features)));
|
|
+ f->dump_bool("open", !closed);
|
|
+ f->dump_object("caps", caps);
|
|
+ f->dump_bool("authenticated", authenticated);
|
|
+ f->dump_unsigned("global_id", global_id);
|
|
+ f->dump_stream("global_id_status") << global_id_status;
|
|
+ f->dump_unsigned("osd_epoch", osd_epoch);
|
|
+ }
|
|
};
|
|
|
|
|
|
--
|
|
2.30.0
|
|
|