diff --git a/0025-CVE-2021-20288.patch b/0025-CVE-2021-20288.patch new file mode 100644 index 0000000000000000000000000000000000000000..1e3856a17b0111287e742112ed588575895f70ca --- /dev/null +++ b/0025-CVE-2021-20288.patch @@ -0,0 +1,1526 @@ +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. 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 <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 ++#include ++#include ++ + #include "Crypto.h" + #ifdef USE_CRYPTOPP + # include +@@ -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::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( ++ 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 +-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 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